4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
35 * Default Message Processing
36 * -- EN_KILLFOCUS should be handled in WM_COMMAND
37 * -- WM_CREATE: create the icon and small icon image lists at this point only if
38 * the LVS_SHAREIMAGELISTS style is not specified.
39 * -- WM_ERASEBKGND: forward this message to the parent window if the bkgnd
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_[GS]etColumnOrderArray stubs
58 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
59 * -- LISTVIEW_SetIconSpacing is incomplete
60 * -- LISTVIEW_SortItems is broken
61 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
64 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
65 * linear in the number of items in the list, and this is
66 * unacceptable for large lists.
67 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
68 * instead of inserting in the right spot
69 * -- we should keep an ordered array of coordinates in iconic mode
70 * this would allow to frame items (iterator_frameditems),
71 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
79 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
86 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
89 * -- LVS_TYPESTYLEMASK
92 * -- LVS_EX_BORDERSELECT
95 * -- LVS_EX_HEADERDRAGDROP
98 * -- LVS_EX_MULTIWORKAREAS
99 * -- LVS_EX_ONECLICKACTIVATE
101 * -- LVS_EX_SIMPLESELECT
102 * -- LVS_EX_TRACKSELECT
103 * -- LVS_EX_TWOCLICKACTIVATE
104 * -- LVS_EX_UNDERLINECOLD
105 * -- LVS_EX_UNDERLINEHOT
108 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
111 * -- LVN_MARQUEEBEGIN
118 * -- LVM_CANCELEDITLABEL
119 * -- LVM_ENABLEGROUPVIEW
120 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
121 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
122 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
123 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
124 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
125 * -- LVM_GETINSERTMARKRECT
126 * -- LVM_GETNUMBEROFWORKAREAS
127 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
128 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
129 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
130 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
131 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
132 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
133 * -- LVM_GETVIEW, LVM_SETVIEW
134 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
135 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
136 * -- LVM_INSERTGROUPSORTED
137 * -- LVM_INSERTMARKHITTEST
138 * -- LVM_ISGROUPVIEWENABLED
139 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
141 * -- LVM_MOVEITEMTOGROUP
143 * -- LVM_SETTILEWIDTH
148 * -- ListView_GetCheckSate, ListView_SetCheckState
149 * -- ListView_GetHoverTime, ListView_SetHoverTime
150 * -- ListView_GetISearchString
151 * -- ListView_GetNumberOfWorkAreas
152 * -- ListView_GetOrigin
153 * -- ListView_GetTextBkColor
154 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
155 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
156 * -- ListView_SortItemsEx
161 * Known differences in message stream from native control (not known if
162 * these differences cause problems):
163 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
164 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
165 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
166 * processing for "USEDOUBLECLICKTIME".
170 #include "wine/port.h"
185 #include "commctrl.h"
186 #include "comctl32.h"
189 #include "wine/debug.h"
190 #include "wine/unicode.h"
192 WINE_DEFAULT_DEBUG_CHANNEL(listview);
194 /* make sure you set this to 0 for production use! */
195 #define DEBUG_RANGES 1
197 typedef struct tagCOLUMN_INFO
199 RECT rcHeader; /* tracks the header's rectangle */
200 int fmt; /* same as LVCOLUMN.fmt */
203 typedef struct tagITEMHDR
207 } ITEMHDR, *LPITEMHDR;
209 typedef struct tagSUBITEM_INFO
215 typedef struct tagITEM_INFO
223 typedef struct tagRANGE
229 typedef struct tagRANGES
234 typedef struct tagITERATOR
243 typedef struct tagDELAYED_ITEM_EDIT
249 typedef struct tagLISTVIEW_INFO
256 HIMAGELIST himlNormal;
257 HIMAGELIST himlSmall;
258 HIMAGELIST himlState;
261 POINT ptClickPos; /* point where the user clicked */
262 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
265 RANGES selectionRanges;
270 RECT rcList; /* This rectangle is really the window
271 * client rectangle possibly reduced by the
272 * horizontal scroll bar and/or header - see
273 * LISTVIEW_UpdateSize. This rectangle offset
274 * by the LISTVIEW_GetOrigin value is in
275 * client coordinates */
284 INT ntmHeight; /* Some cached metrics of the font used */
285 INT ntmMaxCharWidth; /* by the listview to draw items */
287 BOOL bRedraw; /* Turns on/off repaints & invalidations */
288 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
290 BOOL bDoChangeNotify; /* send change notification messages? */
293 DWORD dwStyle; /* the cached window GWL_STYLE */
294 DWORD dwLvExStyle; /* extended listview style */
295 INT nItemCount; /* the number of items in the list */
296 HDPA hdpaItems; /* array ITEM_INFO pointers */
297 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
298 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
299 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
300 POINT currIconPos; /* this is the position next icon will be placed */
301 PFNLVCOMPARE pfnCompare;
309 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
311 DWORD lastKeyPressTimestamp;
313 INT nSearchParamLength;
314 WCHAR szSearchParam[ MAX_PATH ];
316 INT nMeasureItemHeight;
317 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
318 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
324 /* How many we debug buffer to allocate */
325 #define DEBUG_BUFFERS 20
326 /* The size of a single debug bbuffer */
327 #define DEBUG_BUFFER_SIZE 256
329 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
330 #define SB_INTERNAL -1
332 /* maximum size of a label */
333 #define DISP_TEXT_SIZE 512
335 /* padding for items in list and small icon display modes */
336 #define WIDTH_PADDING 12
338 /* padding for items in list, report and small icon display modes */
339 #define HEIGHT_PADDING 1
341 /* offset of items in report display mode */
342 #define REPORT_MARGINX 2
344 /* padding for icon in large icon display mode
345 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
346 * that HITTEST will see.
347 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
348 * ICON_TOP_PADDING - sum of the two above.
349 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
350 * LABEL_HOR_PADDING - between text and sides of box
351 * LABEL_VERT_PADDING - between bottom of text and end of box
353 * ICON_LR_PADDING - additional width above icon size.
354 * ICON_LR_HALF - half of the above value
356 #define ICON_TOP_PADDING_NOTHITABLE 2
357 #define ICON_TOP_PADDING_HITABLE 2
358 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
359 #define ICON_BOTTOM_PADDING 4
360 #define LABEL_HOR_PADDING 5
361 #define LABEL_VERT_PADDING 7
362 #define ICON_LR_PADDING 16
363 #define ICON_LR_HALF (ICON_LR_PADDING/2)
365 /* default label width for items in list and small icon display modes */
366 #define DEFAULT_LABEL_WIDTH 40
368 /* default column width for items in list display mode */
369 #define DEFAULT_COLUMN_WIDTH 128
371 /* Size of "line" scroll for V & H scrolls */
372 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
374 /* Padding betwen image and label */
375 #define IMAGE_PADDING 2
377 /* Padding behind the label */
378 #define TRAILING_LABEL_PADDING 12
379 #define TRAILING_HEADER_PADDING 11
381 /* Border for the icon caption */
382 #define CAPTION_BORDER 2
384 /* Standard DrawText flags */
385 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
386 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
387 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
389 /* Image index from state */
390 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
392 /* The time in milliseconds to reset the search in the list */
393 #define KEY_DELAY 450
395 /* Dump the LISTVIEW_INFO structure to the debug channel */
396 #define LISTVIEW_DUMP(iP) do { \
397 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
398 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
399 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
400 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
401 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
402 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
403 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
404 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
405 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
406 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
409 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
412 * forward declarations
414 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
415 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
416 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
417 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
418 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
419 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
420 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
421 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
422 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
423 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
424 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
425 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
426 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
427 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
428 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
429 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
430 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
431 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
432 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
433 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
434 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
435 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
436 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
437 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
438 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
439 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
440 static INT LISTVIEW_HitTest(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
442 /******** Text handling functions *************************************/
444 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
445 * text string. The string may be ANSI or Unicode, in which case
446 * the boolean isW tells us the type of the string.
448 * The name of the function tell what type of strings it expects:
449 * W: Unicode, T: ANSI/Unicode - function of isW
452 static inline BOOL is_textW(LPCWSTR text)
454 return text != NULL && text != LPSTR_TEXTCALLBACKW;
457 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
459 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
460 return is_textW(text);
463 static inline int textlenT(LPCWSTR text, BOOL isW)
465 return !is_textT(text, isW) ? 0 :
466 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
469 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
472 if (isSrcW) lstrcpynW(dest, src, max);
473 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
475 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
476 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
479 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
481 LPWSTR wstr = (LPWSTR)text;
483 if (!isW && is_textT(text, isW))
485 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
486 wstr = Alloc(len * sizeof(WCHAR));
487 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
489 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
493 static inline void textfreeT(LPWSTR wstr, BOOL isW)
495 if (!isW && is_textT(wstr, isW)) Free (wstr);
499 * dest is a pointer to a Unicode string
500 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
502 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
506 if (src == LPSTR_TEXTCALLBACKW)
508 if (is_textW(*dest)) Free(*dest);
509 *dest = LPSTR_TEXTCALLBACKW;
513 LPWSTR pszText = textdupTtoW(src, isW);
514 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
515 bResult = Str_SetPtrW(dest, pszText);
516 textfreeT(pszText, isW);
522 * compares a Unicode to a Unicode/ANSI text string
524 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
526 if (!aw) return bt ? -1 : 0;
527 if (!bt) return aw ? 1 : 0;
528 if (aw == LPSTR_TEXTCALLBACKW)
529 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
530 if (bt != LPSTR_TEXTCALLBACKW)
532 LPWSTR bw = textdupTtoW(bt, isW);
533 int r = bw ? lstrcmpW(aw, bw) : 1;
541 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
545 n = min(min(n, strlenW(s1)), strlenW(s2));
546 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
547 return res ? res - sizeof(WCHAR) : res;
550 /******** Debugging functions *****************************************/
552 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
554 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
555 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
558 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
560 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
561 n = min(textlenT(text, isW), n);
562 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
565 static char* debug_getbuf(void)
567 static int index = 0;
568 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
569 return buffers[index++ % DEBUG_BUFFERS];
572 static inline const char* debugrange(const RANGE *lprng)
574 if (!lprng) return "(null)";
575 return wine_dbg_sprintf("[%d, %d)", lprng->lower, lprng->upper);
578 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
580 char* buf = debug_getbuf(), *text = buf;
581 int len, size = DEBUG_BUFFER_SIZE;
583 if (pScrollInfo == NULL) return "(null)";
584 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
585 if (len == -1) goto end; buf += len; size -= len;
586 if (pScrollInfo->fMask & SIF_RANGE)
587 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
589 if (len == -1) goto end; buf += len; size -= len;
590 if (pScrollInfo->fMask & SIF_PAGE)
591 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
593 if (len == -1) goto end; buf += len; size -= len;
594 if (pScrollInfo->fMask & SIF_POS)
595 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
597 if (len == -1) goto end; buf += len; size -= len;
598 if (pScrollInfo->fMask & SIF_TRACKPOS)
599 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
601 if (len == -1) goto end; buf += len; size -= len;
604 buf = text + strlen(text);
606 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
610 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
612 if (!plvnm) return "(null)";
613 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
614 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
615 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
616 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
619 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
621 char* buf = debug_getbuf(), *text = buf;
622 int len, size = DEBUG_BUFFER_SIZE;
624 if (lpLVItem == NULL) return "(null)";
625 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
626 if (len == -1) goto end; buf += len; size -= len;
627 if (lpLVItem->mask & LVIF_STATE)
628 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
630 if (len == -1) goto end; buf += len; size -= len;
631 if (lpLVItem->mask & LVIF_TEXT)
632 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
634 if (len == -1) goto end; buf += len; size -= len;
635 if (lpLVItem->mask & LVIF_IMAGE)
636 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
638 if (len == -1) goto end; buf += len; size -= len;
639 if (lpLVItem->mask & LVIF_PARAM)
640 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
642 if (len == -1) goto end; buf += len; size -= len;
643 if (lpLVItem->mask & LVIF_INDENT)
644 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
646 if (len == -1) goto end; buf += len; size -= len;
649 buf = text + strlen(text);
651 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
655 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
657 char* buf = debug_getbuf(), *text = buf;
658 int len, size = DEBUG_BUFFER_SIZE;
660 if (lpColumn == NULL) return "(null)";
661 len = snprintf(buf, size, "{");
662 if (len == -1) goto end; buf += len; size -= len;
663 if (lpColumn->mask & LVCF_SUBITEM)
664 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
666 if (len == -1) goto end; buf += len; size -= len;
667 if (lpColumn->mask & LVCF_FMT)
668 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
670 if (len == -1) goto end; buf += len; size -= len;
671 if (lpColumn->mask & LVCF_WIDTH)
672 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
674 if (len == -1) goto end; buf += len; size -= len;
675 if (lpColumn->mask & LVCF_TEXT)
676 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
678 if (len == -1) goto end; buf += len; size -= len;
679 if (lpColumn->mask & LVCF_IMAGE)
680 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
682 if (len == -1) goto end; buf += len; size -= len;
683 if (lpColumn->mask & LVCF_ORDER)
684 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
686 if (len == -1) goto end; buf += len; size -= len;
689 buf = text + strlen(text);
691 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
695 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
697 if (!lpht) return "(null)";
699 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
700 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
703 /* Return the corresponding text for a given scroll value */
704 static inline LPCSTR debugscrollcode(int nScrollCode)
708 case SB_LINELEFT: return "SB_LINELEFT";
709 case SB_LINERIGHT: return "SB_LINERIGHT";
710 case SB_PAGELEFT: return "SB_PAGELEFT";
711 case SB_PAGERIGHT: return "SB_PAGERIGHT";
712 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
713 case SB_THUMBTRACK: return "SB_THUMBTRACK";
714 case SB_ENDSCROLL: return "SB_ENDSCROLL";
715 case SB_INTERNAL: return "SB_INTERNAL";
716 default: return "unknown";
721 /******** Notification functions i************************************/
723 static LRESULT notify_forward_header(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
725 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
726 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
729 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
733 TRACE("(code=%d)\n", code);
735 pnmh->hwndFrom = infoPtr->hwndSelf;
736 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
738 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
739 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
741 TRACE(" <= %ld\n", result);
746 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code)
749 HWND hwnd = infoPtr->hwndSelf;
750 notify_hdr(infoPtr, code, &nmh);
751 return IsWindow(hwnd);
754 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
765 item.mask = LVIF_PARAM|LVIF_STATE;
766 item.iItem = htInfo->iItem;
768 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
769 nmia.lParam = item.lParam;
770 nmia.uOldState = item.state;
771 nmia.uNewState = item.state | LVIS_ACTIVATING;
772 nmia.uChanged = LVIF_STATE;
775 nmia.iItem = htInfo->iItem;
776 nmia.iSubItem = htInfo->iSubItem;
777 nmia.ptAction = htInfo->pt;
779 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
780 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
781 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
783 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
786 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
788 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
789 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
792 static BOOL notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
796 HWND hwnd = infoPtr->hwndSelf;
798 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
799 ZeroMemory(&nmlv, sizeof(nmlv));
800 nmlv.iItem = lvht->iItem;
801 nmlv.iSubItem = lvht->iSubItem;
802 nmlv.ptAction = lvht->pt;
803 item.mask = LVIF_PARAM;
804 item.iItem = lvht->iItem;
806 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
807 notify_listview(infoPtr, code, &nmlv);
808 return IsWindow(hwnd);
811 static BOOL notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
815 HWND hwnd = infoPtr->hwndSelf;
817 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
819 item.mask = LVIF_PARAM;
822 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
823 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
824 return IsWindow(hwnd);
827 static int get_ansi_notification(INT unicodeNotificationCode)
829 switch (unicodeNotificationCode)
831 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
832 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
833 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
834 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
835 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
836 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
838 ERR("unknown notification %x\n", unicodeNotificationCode);
844 Send notification. depends on dispinfoW having same
845 structure as dispinfoA.
846 infoPtr : listview struct
847 notificationCode : *Unicode* notification code
848 pdi : dispinfo structure (can be unicode or ansi)
849 isW : TRUE if dispinfo is Unicode
851 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
853 BOOL bResult = FALSE;
854 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
855 INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
856 LPWSTR pszTempBuf = NULL, savPszText = NULL;
858 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
860 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
861 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
864 if (convertToAnsi || convertToUnicode)
866 if (notificationCode != LVN_GETDISPINFOW)
868 cchTempBufMax = convertToUnicode ?
869 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
870 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
874 cchTempBufMax = pdi->item.cchTextMax;
875 *pdi->item.pszText = 0; /* make sure we don't process garbage */
878 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
879 if (!pszTempBuf) return FALSE;
881 if (convertToUnicode)
882 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
883 pszTempBuf, cchTempBufMax);
885 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
886 cchTempBufMax, NULL, NULL);
888 savCchTextMax = pdi->item.cchTextMax;
889 savPszText = pdi->item.pszText;
890 pdi->item.pszText = pszTempBuf;
891 pdi->item.cchTextMax = cchTempBufMax;
894 if (infoPtr->notifyFormat == NFR_ANSI)
895 realNotifCode = get_ansi_notification(notificationCode);
897 realNotifCode = notificationCode;
898 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
899 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
901 if (convertToUnicode || convertToAnsi)
903 if (convertToUnicode) /* note : pointer can be changed by app ! */
904 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
905 savCchTextMax, NULL, NULL);
907 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
908 savPszText, savCchTextMax);
909 pdi->item.pszText = savPszText; /* restores our buffer */
910 pdi->item.cchTextMax = savCchTextMax;
916 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
917 const RECT *rcBounds, const LVITEMW *lplvItem)
919 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
920 lpnmlvcd->nmcd.hdc = hdc;
921 lpnmlvcd->nmcd.rc = *rcBounds;
922 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
923 lpnmlvcd->clrText = infoPtr->clrText;
924 if (!lplvItem) return;
925 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
926 lpnmlvcd->iSubItem = lplvItem->iSubItem;
927 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
928 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
929 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
930 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
933 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
935 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
938 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
939 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
940 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
941 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
942 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
943 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
947 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
949 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
950 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
951 if (lpnmlvcd->clrText == CLR_DEFAULT)
952 lpnmlvcd->clrText = comctl32_color.clrWindowText;
954 /* apprently, for selected items, we have to override the returned values */
955 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
959 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
960 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
962 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
964 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
965 lpnmlvcd->clrText = comctl32_color.clrBtnText;
969 /* Set the text attributes */
970 if (lpnmlvcd->clrTextBk != CLR_NONE)
972 SetBkMode(hdc, OPAQUE);
973 SetBkColor(hdc,lpnmlvcd->clrTextBk);
976 SetBkMode(hdc, TRANSPARENT);
977 SetTextColor(hdc, lpnmlvcd->clrText);
980 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
982 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
985 /******** Item iterator functions **********************************/
987 static RANGES ranges_create(int count);
988 static void ranges_destroy(RANGES ranges);
989 static BOOL ranges_add(RANGES ranges, RANGE range);
990 static BOOL ranges_del(RANGES ranges, RANGE range);
991 static void ranges_dump(RANGES ranges);
993 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
995 RANGE range = { nItem, nItem + 1 };
997 return ranges_add(ranges, range);
1000 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1002 RANGE range = { nItem, nItem + 1 };
1004 return ranges_del(ranges, range);
1008 * ITERATOR DOCUMENTATION
1010 * The iterator functions allow for easy, and convenient iteration
1011 * over items of iterest in the list. Typically, you create a
1012 * iterator, use it, and destroy it, as such:
1015 * iterator_xxxitems(&i, ...);
1016 * while (iterator_{prev,next}(&i)
1018 * //code which uses i.nItem
1020 * iterator_destroy(&i);
1022 * where xxx is either: framed, or visible.
1023 * Note that it is important that the code destroys the iterator
1024 * after it's done with it, as the creation of the iterator may
1025 * allocate memory, which thus needs to be freed.
1027 * You can iterate both forwards, and backwards through the list,
1028 * by using iterator_next or iterator_prev respectively.
1030 * Lower numbered items are draw on top of higher number items in
1031 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1032 * items may overlap). So, to test items, you should use
1034 * which lists the items top to bottom (in Z-order).
1035 * For drawing items, you should use
1037 * which lists the items bottom to top (in Z-order).
1038 * If you keep iterating over the items after the end-of-items
1039 * marker (-1) is returned, the iterator will start from the
1040 * beginning. Typically, you don't need to test for -1,
1041 * because iterator_{next,prev} will return TRUE if more items
1042 * are to be iterated over, or FALSE otherwise.
1044 * Note: the iterator is defined to be bidirectional. That is,
1045 * any number of prev followed by any number of next, or
1046 * five versa, should leave the iterator at the same item:
1047 * prev * n, next * n = next * n, prev * n
1049 * The iterator has a notion of an out-of-order, special item,
1050 * which sits at the start of the list. This is used in
1051 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1052 * which needs to be first, as it may overlap other items.
1054 * The code is a bit messy because we have:
1055 * - a special item to deal with
1056 * - simple range, or composite range
1058 * If you find bugs, or want to add features, please make sure you
1059 * always check/modify *both* iterator_prev, and iterator_next.
1063 * This function iterates through the items in increasing order,
1064 * but prefixed by the special item, then -1. That is:
1065 * special, 1, 2, 3, ..., n, -1.
1066 * Each item is listed only once.
1068 static inline BOOL iterator_next(ITERATOR* i)
1072 i->nItem = i->nSpecial;
1073 if (i->nItem != -1) return TRUE;
1075 if (i->nItem == i->nSpecial)
1077 if (i->ranges) i->index = 0;
1083 if (i->nItem == i->nSpecial) i->nItem++;
1084 if (i->nItem < i->range.upper) return TRUE;
1089 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1090 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1093 else if (i->nItem >= i->range.upper) goto end;
1095 i->nItem = i->range.lower;
1096 if (i->nItem >= 0) goto testitem;
1103 * This function iterates through the items in decreasing order,
1104 * followed by the special item, then -1. That is:
1105 * n, n-1, ..., 3, 2, 1, special, -1.
1106 * Each item is listed only once.
1108 static inline BOOL iterator_prev(ITERATOR* i)
1115 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1118 if (i->nItem == i->nSpecial)
1126 if (i->nItem == i->nSpecial) i->nItem--;
1127 if (i->nItem >= i->range.lower) return TRUE;
1133 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1136 else if (!start && i->nItem < i->range.lower) goto end;
1138 i->nItem = i->range.upper;
1139 if (i->nItem > 0) goto testitem;
1141 return (i->nItem = i->nSpecial) != -1;
1144 static RANGE iterator_range(ITERATOR* i)
1148 if (!i->ranges) return i->range;
1150 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1152 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1153 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1155 else range.lower = range.upper = 0;
1161 * Releases resources associated with this ierator.
1163 static inline void iterator_destroy(ITERATOR* i)
1165 ranges_destroy(i->ranges);
1169 * Create an empty iterator.
1171 static inline BOOL iterator_empty(ITERATOR* i)
1173 ZeroMemory(i, sizeof(*i));
1174 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1179 * Create an iterator over a range.
1181 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1189 * Create an iterator over a bunch of ranges.
1190 * Please note that the iterator will take ownership of the ranges,
1191 * and will free them upon destruction.
1193 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1201 * Creates an iterator over the items which intersect lprc.
1203 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1205 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1206 RECT frame = *lprc, rcItem, rcTemp;
1209 /* in case we fail, we want to return an empty iterator */
1210 if (!iterator_empty(i)) return FALSE;
1212 LISTVIEW_GetOrigin(infoPtr, &Origin);
1214 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1215 OffsetRect(&frame, -Origin.x, -Origin.y);
1217 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1221 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1223 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1224 if (IntersectRect(&rcTemp, &rcItem, lprc))
1225 i->nSpecial = infoPtr->nFocusedItem;
1227 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1228 /* to do better here, we need to have PosX, and PosY sorted */
1229 TRACE("building icon ranges:\n");
1230 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1232 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1233 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1234 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1235 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1236 if (IntersectRect(&rcTemp, &rcItem, &frame))
1237 ranges_additem(i->ranges, nItem);
1241 else if (uView == LVS_REPORT)
1245 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1246 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1248 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1249 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1250 if (range.upper <= range.lower) return TRUE;
1251 if (!iterator_rangeitems(i, range)) return FALSE;
1252 TRACE(" report=%s\n", debugrange(&i->range));
1256 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1257 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1258 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1259 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1260 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1261 INT lower = nFirstCol * nPerCol + nFirstRow;
1265 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1266 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1268 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1270 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1271 TRACE("building list ranges:\n");
1272 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1274 item_range.lower = nCol * nPerCol + nFirstRow;
1275 if(item_range.lower >= infoPtr->nItemCount) break;
1276 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1277 TRACE(" list=%s\n", debugrange(&item_range));
1278 ranges_add(i->ranges, item_range);
1286 * Creates an iterator over the items which intersect the visible region of hdc.
1288 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1290 POINT Origin, Position;
1291 RECT rcItem, rcClip;
1294 rgntype = GetClipBox(hdc, &rcClip);
1295 if (rgntype == NULLREGION) return iterator_empty(i);
1296 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1297 if (rgntype == SIMPLEREGION) return TRUE;
1299 /* first deal with the special item */
1300 if (i->nSpecial != -1)
1302 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1303 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1306 /* if we can't deal with the region, we'll just go with the simple range */
1307 LISTVIEW_GetOrigin(infoPtr, &Origin);
1308 TRACE("building visible range:\n");
1309 if (!i->ranges && i->range.lower < i->range.upper)
1311 if (!(i->ranges = ranges_create(50))) return TRUE;
1312 if (!ranges_add(i->ranges, i->range))
1314 ranges_destroy(i->ranges);
1320 /* now delete the invisible items from the list */
1321 while(iterator_next(i))
1323 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1324 rcItem.left = Position.x + Origin.x;
1325 rcItem.top = Position.y + Origin.y;
1326 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1327 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1328 if (!RectVisible(hdc, &rcItem))
1329 ranges_delitem(i->ranges, i->nItem);
1331 /* the iterator should restart on the next iterator_next */
1337 /******** Misc helper functions ************************************/
1339 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1340 WPARAM wParam, LPARAM lParam, BOOL isW)
1342 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1343 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1346 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1348 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1350 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1351 (uView == LVS_ICON || uView == LVS_SMALLICON);
1354 /******** Internal API functions ************************************/
1356 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1358 static COLUMN_INFO mainItem;
1360 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1361 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1362 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1365 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1367 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1370 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1372 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1375 /* Listview invalidation functions: use _only_ these functions to invalidate */
1377 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1379 return infoPtr->bRedraw;
1382 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1384 if(!is_redrawing(infoPtr)) return;
1385 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1386 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1389 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1393 if(!is_redrawing(infoPtr)) return;
1394 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1395 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1398 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1400 POINT Origin, Position;
1403 if(!is_redrawing(infoPtr)) return;
1404 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1405 LISTVIEW_GetOrigin(infoPtr, &Origin);
1406 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1407 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1409 rcBox.bottom = infoPtr->nItemHeight;
1410 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1411 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1414 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1416 LISTVIEW_InvalidateRect(infoPtr, NULL);
1419 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1423 if(!is_redrawing(infoPtr)) return;
1424 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1425 rcCol.top = infoPtr->rcList.top;
1426 rcCol.bottom = infoPtr->rcList.bottom;
1427 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1432 * Retrieves the number of items that can fit vertically in the client area.
1435 * [I] infoPtr : valid pointer to the listview structure
1438 * Number of items per row.
1440 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1442 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1444 return max(nListWidth/infoPtr->nItemWidth, 1);
1449 * Retrieves the number of items that can fit horizontally in the client
1453 * [I] infoPtr : valid pointer to the listview structure
1456 * Number of items per column.
1458 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1460 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1462 return max(nListHeight / infoPtr->nItemHeight, 1);
1466 /*************************************************************************
1467 * LISTVIEW_ProcessLetterKeys
1469 * Processes keyboard messages generated by pressing the letter keys
1471 * What this does is perform a case insensitive search from the
1472 * current position with the following quirks:
1473 * - If two chars or more are pressed in quick succession we search
1474 * for the corresponding string (e.g. 'abc').
1475 * - If there is a delay we wipe away the current search string and
1476 * restart with just that char.
1477 * - If the user keeps pressing the same character, whether slowly or
1478 * fast, so that the search string is entirely composed of this
1479 * character ('aaaaa' for instance), then we search for first item
1480 * that starting with that character.
1481 * - If the user types the above character in quick succession, then
1482 * we must also search for the corresponding string ('aaaaa'), and
1483 * go to that string if there is a match.
1486 * [I] hwnd : handle to the window
1487 * [I] charCode : the character code, the actual character
1488 * [I] keyData : key data
1496 * - The current implementation has a list of characters it will
1497 * accept and it ignores averything else. In particular it will
1498 * ignore accentuated characters which seems to match what
1499 * Windows does. But I'm not sure it makes sense to follow
1501 * - We don't sound a beep when the search fails.
1505 * TREEVIEW_ProcessLetterKeys
1507 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1512 WCHAR buffer[MAX_PATH];
1513 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1515 /* simple parameter checking */
1516 if (!charCode || !keyData) return 0;
1518 /* only allow the valid WM_CHARs through */
1519 if (!isalnum(charCode) &&
1520 charCode != '.' && charCode != '`' && charCode != '!' &&
1521 charCode != '@' && charCode != '#' && charCode != '$' &&
1522 charCode != '%' && charCode != '^' && charCode != '&' &&
1523 charCode != '*' && charCode != '(' && charCode != ')' &&
1524 charCode != '-' && charCode != '_' && charCode != '+' &&
1525 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1526 charCode != '}' && charCode != '[' && charCode != '{' &&
1527 charCode != '/' && charCode != '?' && charCode != '>' &&
1528 charCode != '<' && charCode != ',' && charCode != '~')
1531 /* if there's one item or less, there is no where to go */
1532 if (infoPtr->nItemCount <= 1) return 0;
1534 /* update the search parameters */
1535 infoPtr->lastKeyPressTimestamp = GetTickCount();
1536 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1537 if (infoPtr->nSearchParamLength < MAX_PATH)
1538 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1539 if (infoPtr->charCode != charCode)
1540 infoPtr->charCode = charCode = 0;
1542 infoPtr->charCode=charCode;
1543 infoPtr->szSearchParam[0]=charCode;
1544 infoPtr->nSearchParamLength=1;
1545 /* Redundant with the 1 char string */
1549 /* and search from the current position */
1551 if (infoPtr->nFocusedItem >= 0) {
1552 endidx=infoPtr->nFocusedItem;
1554 /* if looking for single character match,
1555 * then we must always move forward
1557 if (infoPtr->nSearchParamLength == 1)
1560 endidx=infoPtr->nItemCount;
1564 if (idx == infoPtr->nItemCount) {
1565 if (endidx == infoPtr->nItemCount || endidx == 0)
1571 item.mask = LVIF_TEXT;
1574 item.pszText = buffer;
1575 item.cchTextMax = MAX_PATH;
1576 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1578 /* check for a match */
1579 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1582 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1583 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1584 /* This would work but we must keep looking for a longer match */
1588 } while (idx != endidx);
1591 LISTVIEW_KeySelection(infoPtr, nItem);
1596 /*************************************************************************
1597 * LISTVIEW_UpdateHeaderSize [Internal]
1599 * Function to resize the header control
1602 * [I] hwnd : handle to a window
1603 * [I] nNewScrollPos : scroll pos to set
1608 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1613 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1615 GetWindowRect(infoPtr->hwndHeader, &winRect);
1616 point[0].x = winRect.left;
1617 point[0].y = winRect.top;
1618 point[1].x = winRect.right;
1619 point[1].y = winRect.bottom;
1621 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1622 point[0].x = -nNewScrollPos;
1623 point[1].x += nNewScrollPos;
1625 SetWindowPos(infoPtr->hwndHeader,0,
1626 point[0].x,point[0].y,point[1].x,point[1].y,
1627 SWP_NOZORDER | SWP_NOACTIVATE);
1632 * Update the scrollbars. This functions should be called whenever
1633 * the content, size or view changes.
1636 * [I] infoPtr : valid pointer to the listview structure
1641 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1643 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1644 SCROLLINFO horzInfo, vertInfo;
1647 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1649 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1650 horzInfo.cbSize = sizeof(SCROLLINFO);
1651 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1653 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1654 if (uView == LVS_LIST)
1656 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1657 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1659 /* scroll by at least one column per page */
1660 if(horzInfo.nPage < infoPtr->nItemWidth)
1661 horzInfo.nPage = infoPtr->nItemWidth;
1663 horzInfo.nPage /= infoPtr->nItemWidth;
1665 else if (uView == LVS_REPORT)
1667 horzInfo.nMax = infoPtr->nItemWidth;
1669 else /* LVS_ICON, or LVS_SMALLICON */
1673 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1676 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1677 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1678 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1679 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1680 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1682 /* Setting the horizontal scroll can change the listview size
1683 * (and potentially everything else) so we need to recompute
1684 * everything again for the vertical scroll
1687 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1688 vertInfo.cbSize = sizeof(SCROLLINFO);
1689 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1691 if (uView == LVS_REPORT)
1693 vertInfo.nMax = infoPtr->nItemCount;
1695 /* scroll by at least one page */
1696 if(vertInfo.nPage < infoPtr->nItemHeight)
1697 vertInfo.nPage = infoPtr->nItemHeight;
1699 vertInfo.nPage /= infoPtr->nItemHeight;
1701 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1705 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1708 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1709 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1710 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1711 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1712 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1714 /* Change of the range may have changed the scroll pos. If so move the content */
1715 if (dx != 0 || dy != 0)
1718 listRect = infoPtr->rcList;
1719 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1720 SW_ERASE | SW_INVALIDATE);
1723 /* Update the Header Control */
1724 if (uView == LVS_REPORT)
1726 horzInfo.fMask = SIF_POS;
1727 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1728 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1735 * Shows/hides the focus rectangle.
1738 * [I] infoPtr : valid pointer to the listview structure
1739 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1744 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1746 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1749 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1751 if (infoPtr->nFocusedItem < 0) return;
1753 /* we need some gymnastics in ICON mode to handle large items */
1754 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1758 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1759 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1761 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1766 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1768 /* for some reason, owner draw should work only in report mode */
1769 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1774 item.iItem = infoPtr->nFocusedItem;
1776 item.mask = LVIF_PARAM;
1777 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1779 ZeroMemory(&dis, sizeof(dis));
1780 dis.CtlType = ODT_LISTVIEW;
1781 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1782 dis.itemID = item.iItem;
1783 dis.itemAction = ODA_FOCUS;
1784 if (fShow) dis.itemState |= ODS_FOCUS;
1785 dis.hwndItem = infoPtr->hwndSelf;
1787 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1788 dis.itemData = item.lParam;
1790 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1794 DrawFocusRect(hdc, &infoPtr->rcFocus);
1797 ReleaseDC(infoPtr->hwndSelf, hdc);
1801 * Invalidates all visible selected items.
1803 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1807 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1808 while(iterator_next(&i))
1810 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1811 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1813 iterator_destroy(&i);
1818 * DESCRIPTION: [INTERNAL]
1819 * Computes an item's (left,top) corner, relative to rcView.
1820 * That is, the position has NOT been made relative to the Origin.
1821 * This is deliberate, to avoid computing the Origin over, and
1822 * over again, when this function is call in a loop. Instead,
1823 * one ca factor the computation of the Origin before the loop,
1824 * and offset the value retured by this function, on every iteration.
1827 * [I] infoPtr : valid pointer to the listview structure
1828 * [I] nItem : item number
1829 * [O] lpptOrig : item top, left corner
1834 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1836 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1838 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1840 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1842 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1843 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1845 else if (uView == LVS_LIST)
1847 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1848 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1849 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1851 else /* LVS_REPORT */
1853 lpptPosition->x = 0;
1854 lpptPosition->y = nItem * infoPtr->nItemHeight;
1859 * DESCRIPTION: [INTERNAL]
1860 * Compute the rectangles of an item. This is to localize all
1861 * the computations in one place. If you are not interested in some
1862 * of these values, simply pass in a NULL -- the fucntion is smart
1863 * enough to compute only what's necessary. The function computes
1864 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1865 * one, the BOX rectangle. This rectangle is very cheap to compute,
1866 * and is guaranteed to contain all the other rectangles. Computing
1867 * the ICON rect is also cheap, but all the others are potentaily
1868 * expensive. This gives an easy and effective optimization when
1869 * searching (like point inclusion, or rectangle intersection):
1870 * first test against the BOX, and if TRUE, test agains the desired
1872 * If the function does not have all the necessary information
1873 * to computed the requested rectangles, will crash with a
1874 * failed assertion. This is done so we catch all programming
1875 * errors, given that the function is called only from our code.
1877 * We have the following 'special' meanings for a few fields:
1878 * * If LVIS_FOCUSED is set, we assume the item has the focus
1879 * This is important in ICON mode, where it might get a larger
1880 * then usual rectange
1882 * Please note that subitem support works only in REPORT mode.
1885 * [I] infoPtr : valid pointer to the listview structure
1886 * [I] lpLVItem : item to compute the measures for
1887 * [O] lprcBox : ptr to Box rectangle
1888 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1889 * [0] lprcSelectBox : ptr to select box rectangle
1890 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1891 * [O] lprcIcon : ptr to Icon rectangle
1892 * Same as LVM_GETITEMRECT with LVIR_ICON
1893 * [O] lprcStateIcon: ptr to State Icon rectangle
1894 * [O] lprcLabel : ptr to Label rectangle
1895 * Same as LVM_GETITEMRECT with LVIR_LABEL
1900 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1901 LPRECT lprcBox, LPRECT lprcSelectBox,
1902 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
1904 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1905 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1906 RECT Box, SelectBox, Icon, Label;
1907 COLUMN_INFO *lpColumnInfo = NULL;
1908 SIZE labelSize = { 0, 0 };
1910 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1912 /* Be smart and try to figure out the minimum we have to do */
1913 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1914 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1916 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1917 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1919 if (lprcSelectBox) doSelectBox = TRUE;
1920 if (lprcLabel) doLabel = TRUE;
1921 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
1928 /************************************************************/
1929 /* compute the box rectangle (it should be cheap to do) */
1930 /************************************************************/
1931 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1932 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1934 if (lpLVItem->iSubItem)
1936 Box = lpColumnInfo->rcHeader;
1941 Box.right = infoPtr->nItemWidth;
1944 Box.bottom = infoPtr->nItemHeight;
1946 /******************************************************************/
1947 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
1948 /******************************************************************/
1951 LONG state_width = 0;
1953 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1954 state_width = infoPtr->iconStateSize.cx;
1956 if (uView == LVS_ICON)
1958 Icon.left = Box.left + state_width;
1959 if (infoPtr->himlNormal)
1960 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
1961 Icon.top = Box.top + ICON_TOP_PADDING;
1962 Icon.right = Icon.left;
1963 Icon.bottom = Icon.top;
1964 if (infoPtr->himlNormal)
1966 Icon.right += infoPtr->iconSize.cx;
1967 Icon.bottom += infoPtr->iconSize.cy;
1970 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1972 Icon.left = Box.left + state_width;
1974 if (uView == LVS_REPORT)
1975 Icon.left += REPORT_MARGINX;
1978 Icon.right = Icon.left;
1979 if (infoPtr->himlSmall &&
1980 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1981 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1982 Icon.right += infoPtr->iconSize.cx;
1983 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1985 if(lprcIcon) *lprcIcon = Icon;
1986 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
1988 /* TODO: is this correct? */
1991 lprcStateIcon->left = Icon.left - state_width;
1992 lprcStateIcon->right = Icon.left;
1993 lprcStateIcon->top = Icon.top;
1994 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
1995 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
1998 else Icon.right = 0;
2000 /************************************************************/
2001 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2002 /************************************************************/
2005 /* calculate how far to the right can the label strech */
2006 Label.right = Box.right;
2007 if (uView == LVS_REPORT)
2009 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2012 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2014 labelSize.cx = infoPtr->nItemWidth;
2015 labelSize.cy = infoPtr->nItemHeight;
2019 /* we need the text in non owner draw mode */
2020 assert(lpLVItem->mask & LVIF_TEXT);
2021 if (is_textT(lpLVItem->pszText, TRUE))
2023 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2024 HDC hdc = GetDC(infoPtr->hwndSelf);
2025 HFONT hOldFont = SelectObject(hdc, hFont);
2029 /* compute rough rectangle where the label will go */
2030 SetRectEmpty(&rcText);
2031 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2032 rcText.bottom = infoPtr->nItemHeight;
2033 if (uView == LVS_ICON)
2034 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2036 /* now figure out the flags */
2037 if (uView == LVS_ICON)
2038 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2040 uFormat = LV_SL_DT_FLAGS;
2042 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2044 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2045 labelSize.cy = rcText.bottom - rcText.top;
2047 SelectObject(hdc, hOldFont);
2048 ReleaseDC(infoPtr->hwndSelf, hdc);
2052 if (uView == LVS_ICON)
2054 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2055 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2056 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2057 Label.right = Label.left + labelSize.cx;
2058 Label.bottom = Label.top + infoPtr->nItemHeight;
2059 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2061 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2062 labelSize.cy /= infoPtr->ntmHeight;
2063 labelSize.cy = max(labelSize.cy, 1);
2064 labelSize.cy *= infoPtr->ntmHeight;
2066 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2068 else if (uView == LVS_REPORT)
2070 Label.left = Icon.right;
2071 Label.top = Box.top;
2072 Label.right = lpColumnInfo->rcHeader.right;
2073 Label.bottom = Label.top + infoPtr->nItemHeight;
2075 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2077 Label.left = Icon.right;
2078 Label.top = Box.top;
2079 Label.right = min(Label.left + labelSize.cx, Label.right);
2080 Label.bottom = Label.top + infoPtr->nItemHeight;
2083 if (lprcLabel) *lprcLabel = Label;
2084 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2087 /************************************************************/
2088 /* compute STATEICON bounding box */
2089 /************************************************************/
2092 if (uView == LVS_REPORT)
2094 SelectBox.left = Icon.right; /* FIXME: should be Icon.left */
2095 SelectBox.top = Box.top;
2096 SelectBox.bottom = Box.bottom;
2097 if (lpLVItem->iSubItem == 0)
2099 /* we need the indent in report mode */
2100 assert(lpLVItem->mask & LVIF_INDENT);
2101 SelectBox.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
2103 SelectBox.right = min(SelectBox.left + labelSize.cx, Label.right);
2107 UnionRect(&SelectBox, &Icon, &Label);
2109 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2110 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2113 /* Fix the Box if necessary */
2116 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2117 else *lprcBox = Box;
2119 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2123 * DESCRIPTION: [INTERNAL]
2126 * [I] infoPtr : valid pointer to the listview structure
2127 * [I] nItem : item number
2128 * [O] lprcBox : ptr to Box rectangle
2133 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2135 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2136 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2137 POINT Position, Origin;
2140 LISTVIEW_GetOrigin(infoPtr, &Origin);
2141 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2143 /* Be smart and try to figure out the minimum we have to do */
2145 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2146 lvItem.mask |= LVIF_TEXT;
2147 lvItem.iItem = nItem;
2148 lvItem.iSubItem = 0;
2149 lvItem.pszText = szDispText;
2150 lvItem.cchTextMax = DISP_TEXT_SIZE;
2151 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2152 if (uView == LVS_ICON)
2154 lvItem.mask |= LVIF_STATE;
2155 lvItem.stateMask = LVIS_FOCUSED;
2156 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2158 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2160 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2166 * Returns the current icon position, and advances it along the top.
2167 * The returned position is not offset by Origin.
2170 * [I] infoPtr : valid pointer to the listview structure
2171 * [O] lpPos : will get the current icon position
2176 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2178 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2180 *lpPos = infoPtr->currIconPos;
2182 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2183 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2185 infoPtr->currIconPos.x = 0;
2186 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2192 * Returns the current icon position, and advances it down the left edge.
2193 * The returned position is not offset by Origin.
2196 * [I] infoPtr : valid pointer to the listview structure
2197 * [O] lpPos : will get the current icon position
2202 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2204 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2206 *lpPos = infoPtr->currIconPos;
2208 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2209 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2211 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2212 infoPtr->currIconPos.y = 0;
2218 * Moves an icon to the specified position.
2219 * It takes care of invalidating the item, etc.
2222 * [I] infoPtr : valid pointer to the listview structure
2223 * [I] nItem : the item to move
2224 * [I] lpPos : the new icon position
2225 * [I] isNew : flags the item as being new
2231 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2237 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2238 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2240 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2241 LISTVIEW_InvalidateItem(infoPtr, nItem);
2244 /* Allocating a POINTER for every item is too resource intensive,
2245 * so we'll keep the (x,y) in different arrays */
2246 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2247 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2249 LISTVIEW_InvalidateItem(infoPtr, nItem);
2256 * Arranges listview items in icon display mode.
2259 * [I] infoPtr : valid pointer to the listview structure
2260 * [I] nAlignCode : alignment code
2266 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2268 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2269 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2273 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2275 TRACE("nAlignCode=%d\n", nAlignCode);
2277 if (nAlignCode == LVA_DEFAULT)
2279 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2280 else nAlignCode = LVA_ALIGNTOP;
2285 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2286 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2287 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2288 default: return FALSE;
2291 infoPtr->bAutoarrange = TRUE;
2292 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2293 for (i = 0; i < infoPtr->nItemCount; i++)
2295 next_pos(infoPtr, &pos);
2296 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2304 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2307 * [I] infoPtr : valid pointer to the listview structure
2308 * [O] lprcView : bounding rectangle
2314 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2318 SetRectEmpty(lprcView);
2320 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2324 for (i = 0; i < infoPtr->nItemCount; i++)
2326 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2327 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2328 lprcView->right = max(lprcView->right, x);
2329 lprcView->bottom = max(lprcView->bottom, y);
2331 if (infoPtr->nItemCount > 0)
2333 lprcView->right += infoPtr->nItemWidth;
2334 lprcView->bottom += infoPtr->nItemHeight;
2339 y = LISTVIEW_GetCountPerColumn(infoPtr);
2340 x = infoPtr->nItemCount / y;
2341 if (infoPtr->nItemCount % y) x++;
2342 lprcView->right = x * infoPtr->nItemWidth;
2343 lprcView->bottom = y * infoPtr->nItemHeight;
2347 lprcView->right = infoPtr->nItemWidth;
2348 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2355 * Retrieves the bounding rectangle of all the items.
2358 * [I] infoPtr : valid pointer to the listview structure
2359 * [O] lprcView : bounding rectangle
2365 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2369 TRACE("(lprcView=%p)\n", lprcView);
2371 if (!lprcView) return FALSE;
2373 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2374 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2375 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2377 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2384 * Retrieves the subitem pointer associated with the subitem index.
2387 * [I] hdpaSubItems : DPA handle for a specific item
2388 * [I] nSubItem : index of subitem
2391 * SUCCESS : subitem pointer
2394 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2396 SUBITEM_INFO *lpSubItem;
2399 /* we should binary search here if need be */
2400 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2402 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2403 if (lpSubItem->iSubItem == nSubItem)
2413 * Calculates the desired item width.
2416 * [I] infoPtr : valid pointer to the listview structure
2419 * The desired item width.
2421 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2423 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2426 TRACE("uView=%d\n", uView);
2428 if (uView == LVS_ICON)
2429 nItemWidth = infoPtr->iconSpacing.cx;
2430 else if (uView == LVS_REPORT)
2434 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2436 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2437 nItemWidth = rcHeader.right;
2440 else /* LVS_SMALLICON, or LVS_LIST */
2444 for (i = 0; i < infoPtr->nItemCount; i++)
2445 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2447 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2448 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2450 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2453 return max(nItemWidth, 1);
2458 * Calculates the desired item height.
2461 * [I] infoPtr : valid pointer to the listview structure
2464 * The desired item height.
2466 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2468 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2471 TRACE("uView=%d\n", uView);
2473 if (uView == LVS_ICON)
2474 nItemHeight = infoPtr->iconSpacing.cy;
2477 nItemHeight = infoPtr->ntmHeight;
2478 if (infoPtr->himlState)
2479 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2480 if (infoPtr->himlSmall)
2481 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2482 if (infoPtr->himlState || infoPtr->himlSmall)
2483 nItemHeight += HEIGHT_PADDING;
2484 if (infoPtr->nMeasureItemHeight > 0)
2485 nItemHeight = infoPtr->nMeasureItemHeight;
2488 return max(nItemHeight, 1);
2493 * Updates the width, and height of an item.
2496 * [I] infoPtr : valid pointer to the listview structure
2501 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2503 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2504 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2510 * Retrieves and saves important text metrics info for the current
2514 * [I] infoPtr : valid pointer to the listview structure
2517 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2519 HDC hdc = GetDC(infoPtr->hwndSelf);
2520 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2521 HFONT hOldFont = SelectObject(hdc, hFont);
2525 if (GetTextMetricsW(hdc, &tm))
2527 infoPtr->ntmHeight = tm.tmHeight;
2528 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2531 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2532 infoPtr->nEllipsisWidth = sz.cx;
2534 SelectObject(hdc, hOldFont);
2535 ReleaseDC(infoPtr->hwndSelf, hdc);
2537 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2542 * A compare function for ranges
2545 * [I] range1 : pointer to range 1;
2546 * [I] range2 : pointer to range 2;
2550 * > 0 : if range 1 > range 2
2551 * < 0 : if range 2 > range 1
2552 * = 0 : if range intersects range 2
2554 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2558 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2560 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2565 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2571 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2573 #define ranges_check(ranges, desc) do { } while(0)
2576 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2581 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2583 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2584 ranges_dump(ranges);
2585 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2586 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2587 assert (prev->lower >= 0 && prev->lower < prev->upper);
2588 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2590 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2591 assert (prev->upper <= curr->lower);
2592 assert (curr->lower < curr->upper);
2595 TRACE("--- Done checking---\n");
2598 static RANGES ranges_create(int count)
2600 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2601 if (!ranges) return NULL;
2602 ranges->hdpa = DPA_Create(count);
2603 if (ranges->hdpa) return ranges;
2608 static void ranges_clear(RANGES ranges)
2612 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2613 Free(DPA_GetPtr(ranges->hdpa, i));
2614 DPA_DeleteAllPtrs(ranges->hdpa);
2618 static void ranges_destroy(RANGES ranges)
2620 if (!ranges) return;
2621 ranges_clear(ranges);
2622 DPA_Destroy(ranges->hdpa);
2626 static RANGES ranges_clone(RANGES ranges)
2631 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2633 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2635 RANGE *newrng = Alloc(sizeof(RANGE));
2636 if (!newrng) goto fail;
2637 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2638 DPA_SetPtr(clone->hdpa, i, newrng);
2643 TRACE ("clone failed\n");
2644 ranges_destroy(clone);
2648 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2652 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2653 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2658 static void ranges_dump(RANGES ranges)
2662 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2663 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2666 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2668 RANGE srchrng = { nItem, nItem + 1 };
2670 TRACE("(nItem=%d)\n", nItem);
2671 ranges_check(ranges, "before contain");
2672 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2675 static INT ranges_itemcount(RANGES ranges)
2679 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2681 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2682 count += sel->upper - sel->lower;
2688 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2690 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2693 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2694 if (index == -1) return TRUE;
2696 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2698 chkrng = DPA_GetPtr(ranges->hdpa, index);
2699 if (chkrng->lower >= nItem)
2700 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2701 if (chkrng->upper > nItem)
2702 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2707 static BOOL ranges_add(RANGES ranges, RANGE range)
2712 TRACE("(%s)\n", debugrange(&range));
2713 ranges_check(ranges, "before add");
2715 /* try find overlapping regions first */
2716 srchrgn.lower = range.lower - 1;
2717 srchrgn.upper = range.upper + 1;
2718 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2724 TRACE("Adding new range\n");
2726 /* create the brand new range to insert */
2727 newrgn = Alloc(sizeof(RANGE));
2728 if(!newrgn) goto fail;
2731 /* figure out where to insert it */
2732 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2733 TRACE("index=%d\n", index);
2734 if (index == -1) index = 0;
2736 /* and get it over with */
2737 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2745 RANGE *chkrgn, *mrgrgn;
2746 INT fromindex, mergeindex;
2748 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2749 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2751 chkrgn->lower = min(range.lower, chkrgn->lower);
2752 chkrgn->upper = max(range.upper, chkrgn->upper);
2754 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2756 /* merge now common anges */
2758 srchrgn.lower = chkrgn->lower - 1;
2759 srchrgn.upper = chkrgn->upper + 1;
2763 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2764 if (mergeindex == -1) break;
2765 if (mergeindex == index)
2767 fromindex = index + 1;
2771 TRACE("Merge with index %i\n", mergeindex);
2773 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2774 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2775 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2777 DPA_DeletePtr(ranges->hdpa, mergeindex);
2778 if (mergeindex < index) index --;
2782 ranges_check(ranges, "after add");
2786 ranges_check(ranges, "failed add");
2790 static BOOL ranges_del(RANGES ranges, RANGE range)
2795 TRACE("(%s)\n", debugrange(&range));
2796 ranges_check(ranges, "before del");
2798 /* we don't use DPAS_SORTED here, since we need *
2799 * to find the first overlapping range */
2800 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2803 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2805 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2807 /* case 1: Same range */
2808 if ( (chkrgn->upper == range.upper) &&
2809 (chkrgn->lower == range.lower) )
2811 DPA_DeletePtr(ranges->hdpa, index);
2814 /* case 2: engulf */
2815 else if ( (chkrgn->upper <= range.upper) &&
2816 (chkrgn->lower >= range.lower) )
2818 DPA_DeletePtr(ranges->hdpa, index);
2820 /* case 3: overlap upper */
2821 else if ( (chkrgn->upper <= range.upper) &&
2822 (chkrgn->lower < range.lower) )
2824 chkrgn->upper = range.lower;
2826 /* case 4: overlap lower */
2827 else if ( (chkrgn->upper > range.upper) &&
2828 (chkrgn->lower >= range.lower) )
2830 chkrgn->lower = range.upper;
2833 /* case 5: fully internal */
2836 RANGE tmprgn = *chkrgn, *newrgn;
2838 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
2839 newrgn->lower = chkrgn->lower;
2840 newrgn->upper = range.lower;
2841 chkrgn->lower = range.upper;
2842 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2851 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2854 ranges_check(ranges, "after del");
2858 ranges_check(ranges, "failed del");
2864 * Removes all selection ranges
2867 * [I] infoPtr : valid pointer to the listview structure
2868 * [I] toSkip : item range to skip removing the selection
2874 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2883 lvItem.stateMask = LVIS_SELECTED;
2885 /* need to clone the DPA because callbacks can change it */
2886 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2887 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2888 while(iterator_next(&i))
2889 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2890 /* note that the iterator destructor will free the cloned range */
2891 iterator_destroy(&i);
2896 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2900 if (!(toSkip = ranges_create(1))) return FALSE;
2901 if (nItem != -1) ranges_additem(toSkip, nItem);
2902 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2903 ranges_destroy(toSkip);
2907 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2909 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2914 * Retrieves the number of items that are marked as selected.
2917 * [I] infoPtr : valid pointer to the listview structure
2920 * Number of items selected.
2922 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2924 INT nSelectedCount = 0;
2926 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2929 for (i = 0; i < infoPtr->nItemCount; i++)
2931 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2936 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2938 TRACE("nSelectedCount=%d\n", nSelectedCount);
2939 return nSelectedCount;
2944 * Manages the item focus.
2947 * [I] infoPtr : valid pointer to the listview structure
2948 * [I] nItem : item index
2951 * TRUE : focused item changed
2952 * FALSE : focused item has NOT changed
2954 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2956 INT oldFocus = infoPtr->nFocusedItem;
2959 if (nItem == infoPtr->nFocusedItem) return FALSE;
2961 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2962 lvItem.stateMask = LVIS_FOCUSED;
2963 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2965 return oldFocus != infoPtr->nFocusedItem;
2968 /* Helper function for LISTVIEW_ShiftIndices *only* */
2969 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2971 if (nShiftItem < nItem) return nShiftItem;
2973 if (nShiftItem > nItem) return nShiftItem + direction;
2975 if (direction > 0) return nShiftItem + direction;
2977 return min(nShiftItem, infoPtr->nItemCount - 1);
2982 * Updates the various indices after an item has been inserted or deleted.
2985 * [I] infoPtr : valid pointer to the listview structure
2986 * [I] nItem : item index
2987 * [I] direction : Direction of shift, +1 or -1.
2992 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2997 /* temporarily disable change notification while shifting items */
2998 bOldChange = infoPtr->bDoChangeNotify;
2999 infoPtr->bDoChangeNotify = FALSE;
3001 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3003 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3005 assert(abs(direction) == 1);
3007 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3009 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3010 if (nNewFocus != infoPtr->nFocusedItem)
3011 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3013 /* But we are not supposed to modify nHotItem! */
3015 infoPtr->bDoChangeNotify = bOldChange;
3021 * Adds a block of selections.
3024 * [I] infoPtr : valid pointer to the listview structure
3025 * [I] nItem : item index
3028 * Whether the window is still valid.
3030 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3032 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3033 INT nLast = max(infoPtr->nSelectionMark, nItem);
3034 HWND hwndSelf = infoPtr->hwndSelf;
3035 NMLVODSTATECHANGE nmlv;
3040 /* Temporarily disable change notification
3041 * If the control is LVS_OWNERDATA, we need to send
3042 * only one LVN_ODSTATECHANGED notification.
3043 * See MSDN documentation for LVN_ITEMCHANGED.
3045 bOldChange = infoPtr->bDoChangeNotify;
3046 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3048 if (nFirst == -1) nFirst = nItem;
3050 item.state = LVIS_SELECTED;
3051 item.stateMask = LVIS_SELECTED;
3053 for (i = nFirst; i <= nLast; i++)
3054 LISTVIEW_SetItemState(infoPtr,i,&item);
3056 ZeroMemory(&nmlv, sizeof(nmlv));
3057 nmlv.iFrom = nFirst;
3060 nmlv.uOldState = item.state;
3062 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3063 if (!IsWindow(hwndSelf))
3065 infoPtr->bDoChangeNotify = bOldChange;
3072 * Sets a single group selection.
3075 * [I] infoPtr : valid pointer to the listview structure
3076 * [I] nItem : item index
3081 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3083 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3089 if (!(selection = ranges_create(100))) return;
3091 item.state = LVIS_SELECTED;
3092 item.stateMask = LVIS_SELECTED;
3094 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3096 if (infoPtr->nSelectionMark == -1)
3098 infoPtr->nSelectionMark = nItem;
3099 ranges_additem(selection, nItem);
3105 sel.lower = min(infoPtr->nSelectionMark, nItem);
3106 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3107 ranges_add(selection, sel);
3112 RECT rcItem, rcSel, rcSelMark;
3115 rcItem.left = LVIR_BOUNDS;
3116 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3117 rcSelMark.left = LVIR_BOUNDS;
3118 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3119 UnionRect(&rcSel, &rcItem, &rcSelMark);
3120 iterator_frameditems(&i, infoPtr, &rcSel);
3121 while(iterator_next(&i))
3123 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3124 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3126 iterator_destroy(&i);
3129 bOldChange = infoPtr->bDoChangeNotify;
3130 infoPtr->bDoChangeNotify = FALSE;
3132 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3135 iterator_rangesitems(&i, selection);
3136 while(iterator_next(&i))
3137 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3138 /* this will also destroy the selection */
3139 iterator_destroy(&i);
3141 infoPtr->bDoChangeNotify = bOldChange;
3143 LISTVIEW_SetItemFocus(infoPtr, nItem);
3148 * Sets a single selection.
3151 * [I] infoPtr : valid pointer to the listview structure
3152 * [I] nItem : item index
3157 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3161 TRACE("nItem=%d\n", nItem);
3163 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3165 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3166 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3167 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3169 infoPtr->nSelectionMark = nItem;
3174 * Set selection(s) with keyboard.
3177 * [I] infoPtr : valid pointer to the listview structure
3178 * [I] nItem : item index
3181 * SUCCESS : TRUE (needs to be repainted)
3182 * FAILURE : FALSE (nothing has changed)
3184 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3186 /* FIXME: pass in the state */
3187 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3188 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3189 BOOL bResult = FALSE;
3191 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3192 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3194 if (infoPtr->dwStyle & LVS_SINGLESEL)
3197 LISTVIEW_SetSelection(infoPtr, nItem);
3204 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3209 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3210 lvItem.stateMask = LVIS_SELECTED;
3211 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3213 if (lvItem.state & LVIS_SELECTED)
3214 infoPtr->nSelectionMark = nItem;
3216 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3221 LISTVIEW_SetSelection(infoPtr, nItem);
3224 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3227 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3231 static BOOL LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3233 LVHITTESTINFO lvHitTestInfo;
3235 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3236 lvHitTestInfo.pt.x = pt.x;
3237 lvHitTestInfo.pt.y = pt.y;
3239 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3241 lpLVItem->mask = LVIF_PARAM;
3242 lpLVItem->iItem = lvHitTestInfo.iItem;
3243 lpLVItem->iSubItem = 0;
3245 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3250 * Called when the mouse is being actively tracked and has hovered for a specified
3254 * [I] infoPtr : valid pointer to the listview structure
3255 * [I] fwKeys : key indicator
3256 * [I] x,y : mouse position
3259 * 0 if the message was processed, non-zero if there was an error
3262 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3263 * over the item for a certain period of time.
3266 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, INT x, INT y)
3268 if (infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3276 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3277 LISTVIEW_SetSelection(infoPtr, item.iItem);
3285 * Called whenever WM_MOUSEMOVE is received.
3288 * [I] infoPtr : valid pointer to the listview structure
3289 * [I] fwKeys : key indicator
3290 * [I] x,y : mouse position
3293 * 0 if the message is processed, non-zero if there was an error
3295 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3297 TRACKMOUSEEVENT trackinfo;
3299 if (!(fwKeys & MK_LBUTTON))
3300 infoPtr->bLButtonDown = FALSE;
3302 if (infoPtr->bLButtonDown && DragDetect(infoPtr->hwndSelf, infoPtr->ptClickPos))
3304 LVHITTESTINFO lvHitTestInfo;
3307 lvHitTestInfo.pt = infoPtr->ptClickPos;
3308 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3310 ZeroMemory(&nmlv, sizeof(nmlv));
3311 nmlv.iItem = lvHitTestInfo.iItem;
3312 nmlv.ptAction = infoPtr->ptClickPos;
3314 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3319 infoPtr->bLButtonDown = FALSE;
3321 /* see if we are supposed to be tracking mouse hovering */
3322 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3323 /* fill in the trackinfo struct */
3324 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3325 trackinfo.dwFlags = TME_QUERY;
3326 trackinfo.hwndTrack = infoPtr->hwndSelf;
3327 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3329 /* see if we are already tracking this hwnd */
3330 _TrackMouseEvent(&trackinfo);
3332 if(!(trackinfo.dwFlags & TME_HOVER)) {
3333 trackinfo.dwFlags = TME_HOVER;
3335 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3336 _TrackMouseEvent(&trackinfo);
3345 * Tests wheather the item is assignable to a list with style lStyle
3347 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3349 if ( (lpLVItem->mask & LVIF_TEXT) &&
3350 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3351 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3359 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3362 * [I] infoPtr : valid pointer to the listview structure
3363 * [I] lpLVItem : valid pointer to new item atttributes
3364 * [I] isNew : the item being set is being inserted
3365 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3366 * [O] bChanged : will be set to TRUE if the item really changed
3372 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3374 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3382 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3384 if (lpLVItem->mask == 0) return TRUE;
3386 if (infoPtr->dwStyle & LVS_OWNERDATA)
3388 /* a virtual listview we stores only selection and focus */
3389 if (lpLVItem->mask & ~LVIF_STATE)
3395 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3396 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3400 /* we need to get the lParam and state of the item */
3401 item.iItem = lpLVItem->iItem;
3402 item.iSubItem = lpLVItem->iSubItem;
3403 item.mask = LVIF_STATE | LVIF_PARAM;
3404 item.stateMask = ~0;
3407 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3409 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3410 /* determine what fields will change */
3411 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3412 uChanged |= LVIF_STATE;
3414 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3415 uChanged |= LVIF_IMAGE;
3417 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3418 uChanged |= LVIF_PARAM;
3420 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3421 uChanged |= LVIF_INDENT;
3423 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3424 uChanged |= LVIF_TEXT;
3426 TRACE("uChanged=0x%x\n", uChanged);
3427 if (!uChanged) return TRUE;
3430 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3431 nmlv.iItem = lpLVItem->iItem;
3432 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3433 nmlv.uOldState = item.state;
3434 nmlv.uChanged = uChanged;
3435 nmlv.lParam = item.lParam;
3437 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3438 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3440 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3442 HWND hwndSelf = infoPtr->hwndSelf;
3444 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3446 if (!IsWindow(hwndSelf))
3450 /* copy information */
3451 if (lpLVItem->mask & LVIF_TEXT)
3452 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3454 if (lpLVItem->mask & LVIF_IMAGE)
3455 lpItem->hdr.iImage = lpLVItem->iImage;
3457 if (lpLVItem->mask & LVIF_PARAM)
3458 lpItem->lParam = lpLVItem->lParam;
3460 if (lpLVItem->mask & LVIF_INDENT)
3461 lpItem->iIndent = lpLVItem->iIndent;
3463 if (uChanged & LVIF_STATE)
3465 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3467 lpItem->state &= ~lpLVItem->stateMask;
3468 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3470 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3472 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3473 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3475 else if (lpLVItem->stateMask & LVIS_SELECTED)
3476 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3478 /* if we are asked to change focus, and we manage it, do it */
3479 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3481 if (lpLVItem->state & LVIS_FOCUSED)
3483 LISTVIEW_SetItemFocus(infoPtr, -1);
3484 infoPtr->nFocusedItem = lpLVItem->iItem;
3485 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3487 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3488 infoPtr->nFocusedItem = -1;
3492 /* if we're inserting the item, we're done */
3493 if (isNew) return TRUE;
3495 /* send LVN_ITEMCHANGED notification */
3496 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3497 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3504 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3507 * [I] infoPtr : valid pointer to the listview structure
3508 * [I] lpLVItem : valid pointer to new subitem atttributes
3509 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3510 * [O] bChanged : will be set to TRUE if the item really changed
3516 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3519 SUBITEM_INFO *lpSubItem;
3521 /* we do not support subitems for virtual listviews */
3522 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3524 /* set subitem only if column is present */
3525 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3527 /* First do some sanity checks */
3528 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3529 particularly useful. We currently do not actually do anything with
3530 the flag on subitems.
3532 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3533 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3535 /* get the subitem structure, and create it if not there */
3536 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3537 assert (hdpaSubItems);
3539 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3542 SUBITEM_INFO *tmpSubItem;
3545 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3546 if (!lpSubItem) return FALSE;
3547 /* we could binary search here, if need be...*/
3548 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3550 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3551 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3553 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3558 lpSubItem->iSubItem = lpLVItem->iSubItem;
3559 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3563 if (lpLVItem->mask & LVIF_IMAGE)
3564 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3566 lpSubItem->hdr.iImage = lpLVItem->iImage;
3570 if (lpLVItem->mask & LVIF_TEXT)
3571 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3573 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3582 * Sets item attributes.
3585 * [I] infoPtr : valid pointer to the listview structure
3586 * [I] lpLVItem : new item atttributes
3587 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3593 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3595 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3596 HWND hwndSelf = infoPtr->hwndSelf;
3597 LPWSTR pszText = NULL;
3598 BOOL bResult, bChanged = FALSE;
3600 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3602 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3605 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3606 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3608 pszText = lpLVItem->pszText;
3609 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3612 /* actually set the fields */
3613 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3615 if (lpLVItem->iSubItem)
3616 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3618 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3619 if (!IsWindow(hwndSelf))
3622 /* redraw item, if necessary */
3623 if (bChanged && !infoPtr->bIsDrawing)
3625 /* this little optimization eliminates some nasty flicker */
3626 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3627 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3628 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3629 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3631 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3636 textfreeT(lpLVItem->pszText, isW);
3637 ((LVITEMW *)lpLVItem)->pszText = pszText;
3645 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3648 * [I] infoPtr : valid pointer to the listview structure
3653 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3655 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3657 SCROLLINFO scrollInfo;
3659 scrollInfo.cbSize = sizeof(SCROLLINFO);
3660 scrollInfo.fMask = SIF_POS;
3662 if (uView == LVS_LIST)
3664 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3665 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3667 else if (uView == LVS_REPORT)
3669 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3670 nItem = scrollInfo.nPos;
3674 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3675 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3678 TRACE("nItem=%d\n", nItem);
3686 * Erases the background of the given rectangle
3689 * [I] infoPtr : valid pointer to the listview structure
3690 * [I] hdc : device context handle
3691 * [I] lprcBox : clipping rectangle
3697 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3699 if (!infoPtr->hBkBrush) return FALSE;
3701 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3703 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3711 * [I] infoPtr : valid pointer to the listview structure
3712 * [I] hdc : device context handle
3713 * [I] nItem : item index
3714 * [I] nSubItem : subitem index
3715 * [I] pos : item position in client coordinates
3716 * [I] cdmode : custom draw mode
3722 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3724 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3725 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3726 static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3727 DWORD cdsubitemmode = CDRF_DODEFAULT;
3728 RECT *lprcFocus, rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3729 NMLVCUSTOMDRAW nmlvcd;
3734 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3736 /* get information needed for drawing the item */
3737 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3738 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3739 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3740 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3741 lvItem.iItem = nItem;
3742 lvItem.iSubItem = nSubItem;
3745 lvItem.cchTextMax = DISP_TEXT_SIZE;
3746 lvItem.pszText = szDispText;
3747 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3748 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3749 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3750 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3751 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3753 /* now check if we need to update the focus rectangle */
3754 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3756 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3757 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3758 OffsetRect(&rcBox, pos.x, pos.y);
3759 OffsetRect(&rcSelect, pos.x, pos.y);
3760 OffsetRect(&rcIcon, pos.x, pos.y);
3761 OffsetRect(&rcStateIcon, pos.x, pos.y);
3762 OffsetRect(&rcLabel, pos.x, pos.y);
3763 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3764 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3765 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3767 /* fill in the custom draw structure */
3768 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3770 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3771 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3772 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3773 if (cdmode & CDRF_NOTIFYITEMDRAW)
3774 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3775 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3776 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3777 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3778 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3780 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3781 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3783 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3784 prepaint_setup(infoPtr, hdc, &nmlvcd);
3786 /* in full row select, subitems, will just use main item's colors */
3787 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3788 nmlvcd.clrTextBk = CLR_NONE;
3791 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3793 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3796 TRACE("uStateImage=%d\n", uStateImage);
3797 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3798 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
3803 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3804 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3806 TRACE("iImage=%d\n", lvItem.iImage);
3807 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3808 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3809 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3812 /* Don't bother painting item being edited */
3813 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3815 /* FIXME: temporary hack */
3816 rcSelect.left = rcLabel.left;
3818 /* draw the selection background, if we're drawing the main item */
3821 /* in icon mode, the label rect is really what we want to draw the
3823 if (uView == LVS_ICON)
3826 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3827 rcSelect.right = rcBox.right;
3829 if (nmlvcd.clrTextBk != CLR_NONE)
3830 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3831 if(lprcFocus) *lprcFocus = rcSelect;
3834 /* figure out the text drawing flags */
3835 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3836 if (uView == LVS_ICON)
3837 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3840 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3842 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3843 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3844 default: uFormat |= DT_LEFT;
3847 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3849 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3850 else rcLabel.left += LABEL_HOR_PADDING;
3852 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3853 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3856 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3857 notify_postpaint(infoPtr, &nmlvcd);
3858 if (cdsubitemmode & CDRF_NEWFONT)
3859 SelectObject(hdc, hOldFont);
3865 * Draws listview items when in owner draw mode.
3868 * [I] infoPtr : valid pointer to the listview structure
3869 * [I] hdc : device context handle
3874 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3876 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3877 DWORD cditemmode = CDRF_DODEFAULT;
3878 NMLVCUSTOMDRAW nmlvcd;
3879 POINT Origin, Position;
3885 ZeroMemory(&dis, sizeof(dis));
3887 /* Get scroll info once before loop */
3888 LISTVIEW_GetOrigin(infoPtr, &Origin);
3890 /* iterate through the invalidated rows */
3891 while(iterator_next(i))
3893 item.iItem = i->nItem;
3895 item.mask = LVIF_PARAM | LVIF_STATE;
3896 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3897 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3899 dis.CtlType = ODT_LISTVIEW;
3901 dis.itemID = item.iItem;
3902 dis.itemAction = ODA_DRAWENTIRE;
3904 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3905 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3906 dis.hwndItem = infoPtr->hwndSelf;
3908 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3909 dis.rcItem.left = Position.x + Origin.x;
3910 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3911 dis.rcItem.top = Position.y + Origin.y;
3912 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3913 dis.itemData = item.lParam;
3915 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3918 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3919 * structure for the rest. of the paint cycle
3921 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3922 if (cdmode & CDRF_NOTIFYITEMDRAW)
3923 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3925 if (!(cditemmode & CDRF_SKIPDEFAULT))
3927 prepaint_setup (infoPtr, hdc, &nmlvcd);
3928 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3931 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3932 notify_postpaint(infoPtr, &nmlvcd);
3938 * Draws listview items when in report display mode.
3941 * [I] infoPtr : valid pointer to the listview structure
3942 * [I] hdc : device context handle
3943 * [I] cdmode : custom draw mode
3948 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3951 RECT rcClip, rcItem;
3952 POINT Origin, Position;
3958 /* figure out what to draw */
3959 rgntype = GetClipBox(hdc, &rcClip);
3960 if (rgntype == NULLREGION) return;
3962 /* Get scroll info once before loop */
3963 LISTVIEW_GetOrigin(infoPtr, &Origin);
3965 /* narrow down the columns we need to paint */
3966 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3968 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3969 if (rcItem.right + Origin.x >= rcClip.left) break;
3971 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3973 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3974 if (rcItem.left + Origin.x < rcClip.right) break;
3976 iterator_rangeitems(&j, colRange);
3978 /* in full row select, we _have_ to draw the main item */
3979 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3982 /* iterate through the invalidated rows */
3983 while(iterator_next(i))
3985 /* iterate through the invalidated columns */
3986 while(iterator_next(&j))
3988 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3989 Position.x += Origin.x;
3990 Position.y += Origin.y;
3992 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3994 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3996 rcItem.bottom = infoPtr->nItemHeight;
3997 OffsetRect(&rcItem, Position.x, Position.y);
3998 if (!RectVisible(hdc, &rcItem)) continue;
4001 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4004 iterator_destroy(&j);
4009 * Draws listview items when in list display mode.
4012 * [I] infoPtr : valid pointer to the listview structure
4013 * [I] hdc : device context handle
4014 * [I] cdmode : custom draw mode
4019 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4021 POINT Origin, Position;
4023 /* Get scroll info once before loop */
4024 LISTVIEW_GetOrigin(infoPtr, &Origin);
4026 while(iterator_prev(i))
4028 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4029 Position.x += Origin.x;
4030 Position.y += Origin.y;
4032 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4039 * Draws listview items.
4042 * [I] infoPtr : valid pointer to the listview structure
4043 * [I] hdc : device context handle
4044 * [I] prcErase : rect to be erased before refresh (may be NULL)
4049 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, RECT *prcErase)
4051 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4052 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4053 NMLVCUSTOMDRAW nmlvcd;
4060 HBITMAP hbmp = NULL;
4062 LISTVIEW_DUMP(infoPtr);
4064 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4065 TRACE("double buffering\n");
4067 hdc = CreateCompatibleDC(hdcOrig);
4069 ERR("Failed to create DC for backbuffer\n");
4072 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4073 infoPtr->rcList.bottom);
4075 ERR("Failed to create bitmap for backbuffer\n");
4080 SelectObject(hdc, hbmp);
4081 SelectObject(hdc, infoPtr->hFont);
4083 /* Save dc values we're gonna trash while drawing
4084 * FIXME: Should be done in LISTVIEW_DrawItem() */
4085 hOldFont = SelectObject(hdc, infoPtr->hFont);
4086 oldBkMode = GetBkMode(hdc);
4087 oldBkColor = GetBkColor(hdc);
4088 oldTextColor = GetTextColor(hdc);
4091 infoPtr->bIsDrawing = TRUE;
4094 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4095 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4096 /* If no erasing was done (usually because RedrawWindow was called
4097 * with RDW_INVALIDATE only) we need to copy the old contents into
4098 * the backbuffer before continuing. */
4099 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4100 infoPtr->rcList.right - infoPtr->rcList.left,
4101 infoPtr->rcList.bottom - infoPtr->rcList.top,
4102 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4105 /* FIXME: Shouldn't need to do this */
4106 oldClrTextBk = infoPtr->clrTextBk;
4107 oldClrText = infoPtr->clrText;
4109 infoPtr->cditemmode = CDRF_DODEFAULT;
4111 GetClientRect(infoPtr->hwndSelf, &rcClient);
4112 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4113 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4114 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4115 prepaint_setup(infoPtr, hdc, &nmlvcd);
4117 /* Use these colors to draw the items */
4118 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4119 infoPtr->clrText = nmlvcd.clrText;
4121 /* nothing to draw */
4122 if(infoPtr->nItemCount == 0) goto enddraw;
4124 /* figure out what we need to draw */
4125 iterator_visibleitems(&i, infoPtr, hdc);
4127 /* send cache hint notification */
4128 if (infoPtr->dwStyle & LVS_OWNERDATA)
4130 RANGE range = iterator_range(&i);
4133 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4134 nmlv.iFrom = range.lower;
4135 nmlv.iTo = range.upper - 1;
4136 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4139 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4140 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4143 if (uView == LVS_REPORT)
4144 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4145 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4146 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4148 /* if we have a focus rect, draw it */
4149 if (infoPtr->bFocus)
4150 DrawFocusRect(hdc, &infoPtr->rcFocus);
4152 iterator_destroy(&i);
4155 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4156 notify_postpaint(infoPtr, &nmlvcd);
4158 infoPtr->clrTextBk = oldClrTextBk;
4159 infoPtr->clrText = oldClrText;
4162 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4163 infoPtr->rcList.right - infoPtr->rcList.left,
4164 infoPtr->rcList.bottom - infoPtr->rcList.top,
4165 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4170 SelectObject(hdc, hOldFont);
4171 SetBkMode(hdc, oldBkMode);
4172 SetBkColor(hdc, oldBkColor);
4173 SetTextColor(hdc, oldTextColor);
4176 infoPtr->bIsDrawing = FALSE;
4182 * Calculates the approximate width and height of a given number of items.
4185 * [I] infoPtr : valid pointer to the listview structure
4186 * [I] nItemCount : number of items
4187 * [I] wWidth : width
4188 * [I] wHeight : height
4191 * Returns a DWORD. The width in the low word and the height in high word.
4193 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
4194 WORD wWidth, WORD wHeight)
4196 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4197 INT nItemCountPerColumn = 1;
4198 INT nColumnCount = 0;
4199 DWORD dwViewRect = 0;
4201 if (nItemCount == -1)
4202 nItemCount = infoPtr->nItemCount;
4204 if (uView == LVS_LIST)
4206 if (wHeight == 0xFFFF)
4208 /* use current height */
4209 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4212 if (wHeight < infoPtr->nItemHeight)
4213 wHeight = infoPtr->nItemHeight;
4217 if (infoPtr->nItemHeight > 0)
4219 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4220 if (nItemCountPerColumn == 0)
4221 nItemCountPerColumn = 1;
4223 if (nItemCount % nItemCountPerColumn != 0)
4224 nColumnCount = nItemCount / nItemCountPerColumn;
4226 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4230 /* Microsoft padding magic */
4231 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4232 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4234 dwViewRect = MAKELONG(wWidth, wHeight);
4236 else if (uView == LVS_REPORT)
4240 if (infoPtr->nItemCount > 0)
4242 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4243 wWidth = rcBox.right - rcBox.left;
4244 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4248 /* use current height and width */
4249 if (wHeight == 0xffff)
4250 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4251 if (wWidth == 0xffff)
4252 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4255 dwViewRect = MAKELONG(wWidth, wHeight);
4257 else if (uView == LVS_SMALLICON)
4258 FIXME("uView == LVS_SMALLICON: not implemented\n");
4259 else if (uView == LVS_ICON)
4260 FIXME("uView == LVS_ICON: not implemented\n");
4268 * Create a drag image list for the specified item.
4271 * [I] infoPtr : valid pointer to the listview structure
4272 * [I] iItem : index of item
4273 * [O] lppt : Upperr-left corner of the image
4276 * Returns a handle to the image list if successful, NULL otherwise.
4278 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4284 HBITMAP hbmp, hOldbmp;
4285 HIMAGELIST dragList = 0;
4286 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4288 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4291 rcItem.left = LVIR_BOUNDS;
4292 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4295 lppt->x = rcItem.left;
4296 lppt->y = rcItem.top;
4298 size.cx = rcItem.right - rcItem.left;
4299 size.cy = rcItem.bottom - rcItem.top;
4301 hdcOrig = GetDC(infoPtr->hwndSelf);
4302 hdc = CreateCompatibleDC(hdcOrig);
4303 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4304 hOldbmp = SelectObject(hdc, hbmp);
4306 rcItem.left = rcItem.top = 0;
4307 rcItem.right = size.cx;
4308 rcItem.bottom = size.cy;
4309 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4312 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4314 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4315 SelectObject(hdc, hOldbmp);
4316 ImageList_Add(dragList, hbmp, 0);
4319 SelectObject(hdc, hOldbmp);
4323 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4325 TRACE("ret=%p\n", dragList);
4333 * Removes all listview items and subitems.
4336 * [I] infoPtr : valid pointer to the listview structure
4342 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4345 HDPA hdpaSubItems = NULL;
4352 /* we do it directly, to avoid notifications */
4353 ranges_clear(infoPtr->selectionRanges);
4354 infoPtr->nSelectionMark = -1;
4355 infoPtr->nFocusedItem = -1;
4356 SetRectEmpty(&infoPtr->rcFocus);
4357 /* But we are supposed to leave nHotItem as is! */
4360 /* send LVN_DELETEALLITEMS notification */
4361 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4363 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4365 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4367 /* send LVN_DELETEITEM notification, if not suppressed */
4368 if (!bSuppress) notify_deleteitem(infoPtr, i);
4369 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4371 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4372 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4374 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4375 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4378 DPA_Destroy(hdpaSubItems);
4379 DPA_DeletePtr(infoPtr->hdpaItems, i);
4381 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4382 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4383 infoPtr->nItemCount --;
4386 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4387 LISTVIEW_UpdateScroll(infoPtr);
4388 LISTVIEW_InvalidateList(infoPtr);
4395 * Scrolls, and updates the columns, when a column is changing width.
4398 * [I] infoPtr : valid pointer to the listview structure
4399 * [I] nColumn : column to scroll
4400 * [I] dx : amount of scroll, in pixels
4405 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4407 COLUMN_INFO *lpColumnInfo;
4412 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4413 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4414 rcCol = lpColumnInfo->rcHeader;
4415 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4416 rcCol.left = rcCol.right;
4418 /* ajust the other columns */
4419 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4421 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4422 lpColumnInfo->rcHeader.left += dx;
4423 lpColumnInfo->rcHeader.right += dx;
4426 /* do not update screen if not in report mode */
4427 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4429 /* if we have a focus, must first erase the focus rect */
4430 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4432 /* Need to reset the item width when inserting a new column */
4433 infoPtr->nItemWidth += dx;
4435 LISTVIEW_UpdateScroll(infoPtr);
4436 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4438 /* scroll to cover the deleted column, and invalidate for redraw */
4439 rcOld = infoPtr->rcList;
4440 rcOld.left = ptOrigin.x + rcCol.left + dx;
4441 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4443 /* we can restore focus now */
4444 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4449 * Removes a column from the listview control.
4452 * [I] infoPtr : valid pointer to the listview structure
4453 * [I] nColumn : column index
4459 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4463 TRACE("nColumn=%d\n", nColumn);
4465 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4466 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4468 /* While the MSDN specifically says that column zero should not be deleted,
4469 what actually happens is that the column itself is deleted but no items or subitems
4473 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4475 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4478 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4479 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4481 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4483 SUBITEM_INFO *lpSubItem, *lpDelItem;
4485 INT nItem, nSubItem, i;
4487 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4489 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4492 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4494 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4495 if (lpSubItem->iSubItem == nColumn)
4498 lpDelItem = lpSubItem;
4500 else if (lpSubItem->iSubItem > nColumn)
4502 lpSubItem->iSubItem--;
4506 /* if we found our subitem, zapp it */
4510 if (is_textW(lpDelItem->hdr.pszText))
4511 Free(lpDelItem->hdr.pszText);
4516 /* free dpa memory */
4517 DPA_DeletePtr(hdpaSubItems, nSubItem);
4522 /* update the other column info */
4523 LISTVIEW_UpdateItemSize(infoPtr);
4524 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4525 LISTVIEW_InvalidateList(infoPtr);
4527 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4534 * Invalidates the listview after an item's insertion or deletion.
4537 * [I] infoPtr : valid pointer to the listview structure
4538 * [I] nItem : item index
4539 * [I] dir : -1 if deleting, 1 if inserting
4544 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4546 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4547 INT nPerCol, nItemCol, nItemRow;
4551 /* if we don't refresh, what's the point of scrolling? */
4552 if (!is_redrawing(infoPtr)) return;
4554 assert (abs(dir) == 1);
4556 /* arrange icons if autoarrange is on */
4557 if (is_autoarrange(infoPtr))
4559 BOOL arrange = TRUE;
4560 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4561 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4562 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4565 /* scrollbars need updating */
4566 LISTVIEW_UpdateScroll(infoPtr);
4568 /* figure out the item's position */
4569 if (uView == LVS_REPORT)
4570 nPerCol = infoPtr->nItemCount + 1;
4571 else if (uView == LVS_LIST)
4572 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4573 else /* LVS_ICON, or LVS_SMALLICON */
4576 nItemCol = nItem / nPerCol;
4577 nItemRow = nItem % nPerCol;
4578 LISTVIEW_GetOrigin(infoPtr, &Origin);
4580 /* move the items below up a slot */
4581 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4582 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4583 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4584 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4585 OffsetRect(&rcScroll, Origin.x, Origin.y);
4586 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4587 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4589 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4590 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4591 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4594 /* report has only that column, so we're done */
4595 if (uView == LVS_REPORT) return;
4597 /* now for LISTs, we have to deal with the columns to the right */
4598 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4600 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4601 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4602 OffsetRect(&rcScroll, Origin.x, Origin.y);
4603 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4604 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4605 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4610 * Removes an item from the listview control.
4613 * [I] infoPtr : valid pointer to the listview structure
4614 * [I] nItem : item index
4620 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4622 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4625 TRACE("(nItem=%d)\n", nItem);
4627 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4629 /* remove selection, and focus */
4631 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4632 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4634 /* send LVN_DELETEITEM notification. */
4635 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4637 /* we need to do this here, because we'll be deleting stuff */
4638 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4639 LISTVIEW_InvalidateItem(infoPtr, nItem);
4641 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4647 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4648 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4650 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4651 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4654 DPA_Destroy(hdpaSubItems);
4657 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4659 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4660 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4663 infoPtr->nItemCount--;
4664 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4666 /* now is the invalidation fun */
4667 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4674 * Callback implementation for editlabel control
4677 * [I] infoPtr : valid pointer to the listview structure
4678 * [I] pszText : modified text
4679 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4685 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4687 HWND hwndSelf = infoPtr->hwndSelf;
4688 NMLVDISPINFOW dispInfo;
4690 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4692 ZeroMemory(&dispInfo, sizeof(dispInfo));
4693 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4694 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4695 dispInfo.item.iSubItem = 0;
4696 dispInfo.item.stateMask = ~0;
4697 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4698 /* add the text from the edit in */
4699 dispInfo.item.mask |= LVIF_TEXT;
4700 dispInfo.item.pszText = pszText;
4701 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4703 /* Do we need to update the Item Text */
4704 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4705 if (!IsWindow(hwndSelf))
4707 if (!pszText) return TRUE;
4709 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4711 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4712 ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4713 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4715 LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4720 ZeroMemory(&dispInfo, sizeof(dispInfo));
4721 dispInfo.item.mask = LVIF_TEXT;
4722 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4723 dispInfo.item.iSubItem = 0;
4724 dispInfo.item.pszText = pszText;
4725 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4726 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4731 * Begin in place editing of specified list view item
4734 * [I] infoPtr : valid pointer to the listview structure
4735 * [I] nItem : item index
4736 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4742 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4744 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4745 NMLVDISPINFOW dispInfo;
4747 HWND hwndSelf = infoPtr->hwndSelf;
4749 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4751 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4752 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4754 infoPtr->nEditLabelItem = nItem;
4756 /* Is the EditBox still there, if so remove it */
4757 if(infoPtr->hwndEdit != 0)
4759 SetFocus(infoPtr->hwndSelf);
4760 infoPtr->hwndEdit = 0;
4763 LISTVIEW_SetSelection(infoPtr, nItem);
4764 LISTVIEW_SetItemFocus(infoPtr, nItem);
4765 LISTVIEW_InvalidateItem(infoPtr, nItem);
4767 rect.left = LVIR_LABEL;
4768 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4770 ZeroMemory(&dispInfo, sizeof(dispInfo));
4771 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4772 dispInfo.item.iItem = nItem;
4773 dispInfo.item.iSubItem = 0;
4774 dispInfo.item.stateMask = ~0;
4775 dispInfo.item.pszText = szDispText;
4776 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4777 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4779 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4780 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4781 if (!infoPtr->hwndEdit) return 0;
4783 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4785 if (!IsWindow(hwndSelf))
4787 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4788 infoPtr->hwndEdit = 0;
4792 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4793 SetFocus(infoPtr->hwndEdit);
4794 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4795 return infoPtr->hwndEdit;
4801 * Ensures the specified item is visible, scrolling into view if necessary.
4804 * [I] infoPtr : valid pointer to the listview structure
4805 * [I] nItem : item index
4806 * [I] bPartial : partially or entirely visible
4812 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4814 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4815 INT nScrollPosHeight = 0;
4816 INT nScrollPosWidth = 0;
4817 INT nHorzAdjust = 0;
4818 INT nVertAdjust = 0;
4821 RECT rcItem, rcTemp;
4823 rcItem.left = LVIR_BOUNDS;
4824 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4826 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4828 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4830 /* scroll left/right, but in LVS_REPORT mode */
4831 if (uView == LVS_LIST)
4832 nScrollPosWidth = infoPtr->nItemWidth;
4833 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4834 nScrollPosWidth = 1;
4836 if (rcItem.left < infoPtr->rcList.left)
4839 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4844 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4848 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4850 /* scroll up/down, but not in LVS_LIST mode */
4851 if (uView == LVS_REPORT)
4852 nScrollPosHeight = infoPtr->nItemHeight;
4853 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4854 nScrollPosHeight = 1;
4856 if (rcItem.top < infoPtr->rcList.top)
4859 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4864 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4868 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4870 if (nScrollPosWidth)
4872 INT diff = nHorzDiff / nScrollPosWidth;
4873 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4874 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4877 if (nScrollPosHeight)
4879 INT diff = nVertDiff / nScrollPosHeight;
4880 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4881 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4889 * Searches for an item with specific characteristics.
4892 * [I] hwnd : window handle
4893 * [I] nStart : base item index
4894 * [I] lpFindInfo : item information to look for
4897 * SUCCESS : index of item
4900 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4901 const LVFINDINFOW *lpFindInfo)
4903 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4904 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4905 BOOL bWrap = FALSE, bNearest = FALSE;
4906 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4907 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4908 POINT Position, Destination;
4911 if (!lpFindInfo || nItem < 0) return -1;
4914 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4916 lvItem.mask |= LVIF_TEXT;
4917 lvItem.pszText = szDispText;
4918 lvItem.cchTextMax = DISP_TEXT_SIZE;
4921 if (lpFindInfo->flags & LVFI_WRAP)
4924 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4925 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4930 LISTVIEW_GetOrigin(infoPtr, &Origin);
4931 Destination.x = lpFindInfo->pt.x - Origin.x;
4932 Destination.y = lpFindInfo->pt.y - Origin.y;
4933 switch(lpFindInfo->vkDirection)
4935 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4936 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4937 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4938 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4939 case VK_HOME: Destination.x = Destination.y = 0; break;
4940 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4941 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4943 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4944 Destination.x = rcArea.right;
4945 Destination.y = rcArea.bottom;
4947 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4951 else Destination.x = Destination.y = 0;
4953 /* if LVFI_PARAM is specified, all other flags are ignored */
4954 if (lpFindInfo->flags & LVFI_PARAM)
4956 lvItem.mask |= LVIF_PARAM;
4958 lvItem.mask &= ~LVIF_TEXT;
4962 for (; nItem < nLast; nItem++)
4964 lvItem.iItem = nItem;
4965 lvItem.iSubItem = 0;
4966 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4968 if (lvItem.mask & LVIF_PARAM)
4970 if (lpFindInfo->lParam == lvItem.lParam)
4976 if (lvItem.mask & LVIF_TEXT)
4978 if (lpFindInfo->flags & LVFI_PARTIAL)
4980 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4984 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4988 if (!bNearest) return nItem;
4990 /* This is very inefficient. To do a good job here,
4991 * we need a sorted array of (x,y) item positions */
4992 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4994 /* compute the distance^2 to the destination */
4995 xdist = Destination.x - Position.x;
4996 ydist = Destination.y - Position.y;
4997 dist = xdist * xdist + ydist * ydist;
4999 /* remember the distance, and item if it's closer */
5003 nNearestItem = nItem;
5010 nLast = min(nStart + 1, infoPtr->nItemCount);
5015 return nNearestItem;
5020 * Searches for an item with specific characteristics.
5023 * [I] hwnd : window handle
5024 * [I] nStart : base item index
5025 * [I] lpFindInfo : item information to look for
5028 * SUCCESS : index of item
5031 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
5032 const LVFINDINFOA *lpFindInfo)
5034 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5038 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5039 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5040 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5041 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
5047 * Retrieves the background image of the listview control.
5050 * [I] infoPtr : valid pointer to the listview structure
5051 * [O] lpBkImage : background image attributes
5057 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5059 /* FIXME (listview, "empty stub!\n"); */
5065 * Retrieves column attributes.
5068 * [I] infoPtr : valid pointer to the listview structure
5069 * [I] nColumn : column index
5070 * [IO] lpColumn : column information
5071 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5072 * otherwise it is in fact a LPLVCOLUMNA
5078 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5080 COLUMN_INFO *lpColumnInfo;
5083 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5084 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5086 /* initialize memory */
5087 ZeroMemory(&hdi, sizeof(hdi));
5089 if (lpColumn->mask & LVCF_TEXT)
5091 hdi.mask |= HDI_TEXT;
5092 hdi.pszText = lpColumn->pszText;
5093 hdi.cchTextMax = lpColumn->cchTextMax;
5096 if (lpColumn->mask & LVCF_IMAGE)
5097 hdi.mask |= HDI_IMAGE;
5099 if (lpColumn->mask & LVCF_ORDER)
5100 hdi.mask |= HDI_ORDER;
5102 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5104 if (lpColumn->mask & LVCF_FMT)
5105 lpColumn->fmt = lpColumnInfo->fmt;
5107 if (lpColumn->mask & LVCF_WIDTH)
5108 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5110 if (lpColumn->mask & LVCF_IMAGE)
5111 lpColumn->iImage = hdi.iImage;
5113 if (lpColumn->mask & LVCF_ORDER)
5114 lpColumn->iOrder = hdi.iOrder;
5120 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5127 /* FIXME: little hack */
5128 for (i = 0; i < iCount; i++)
5136 * Retrieves the column width.
5139 * [I] infoPtr : valid pointer to the listview structure
5140 * [I] int : column index
5143 * SUCCESS : column width
5146 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
5148 INT nColumnWidth = 0;
5151 TRACE("nColumn=%d\n", nColumn);
5153 /* we have a 'column' in LIST and REPORT mode only */
5154 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5157 nColumnWidth = infoPtr->nItemWidth;
5160 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
5161 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
5162 nColumnWidth = rcHeader.right - rcHeader.left;
5166 TRACE("nColumnWidth=%d\n", nColumnWidth);
5167 return nColumnWidth;
5172 * In list or report display mode, retrieves the number of items that can fit
5173 * vertically in the visible area. In icon or small icon display mode,
5174 * retrieves the total number of visible items.
5177 * [I] infoPtr : valid pointer to the listview structure
5180 * Number of fully visible items.
5182 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5184 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5188 return infoPtr->nItemCount;
5190 return LISTVIEW_GetCountPerColumn(infoPtr);
5192 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5200 * Retrieves an image list handle.
5203 * [I] infoPtr : valid pointer to the listview structure
5204 * [I] nImageList : image list identifier
5207 * SUCCESS : image list handle
5210 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5214 case LVSIL_NORMAL: return infoPtr->himlNormal;
5215 case LVSIL_SMALL: return infoPtr->himlSmall;
5216 case LVSIL_STATE: return infoPtr->himlState;
5221 /* LISTVIEW_GetISearchString */
5225 * Retrieves item attributes.
5228 * [I] hwnd : window handle
5229 * [IO] lpLVItem : item info
5230 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5231 * if FALSE, then lpLVItem is a LPLVITEMA.
5234 * This is the internal 'GetItem' interface -- it tries to
5235 * be smart and avoid text copies, if possible, by modifying
5236 * lpLVItem->pszText to point to the text string. Please note
5237 * that this is not always possible (e.g. OWNERDATA), so on
5238 * entry you *must* supply valid values for pszText, and cchTextMax.
5239 * The only difference to the documented interface is that upon
5240 * return, you should use *only* the lpLVItem->pszText, rather than
5241 * the buffer pointer you provided on input. Most code already does
5242 * that, so it's not a problem.
5243 * For the two cases when the text must be copied (that is,
5244 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5250 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5252 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5253 NMLVDISPINFOW dispInfo;
5259 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5261 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5264 if (lpLVItem->mask == 0) return TRUE;
5266 /* make a local copy */
5267 isubitem = lpLVItem->iSubItem;
5269 /* a quick optimization if all we're asked is the focus state
5270 * these queries are worth optimising since they are common,
5271 * and can be answered in constant time, without the heavy accesses */
5272 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5273 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5275 lpLVItem->state = 0;
5276 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5277 lpLVItem->state |= LVIS_FOCUSED;
5281 ZeroMemory(&dispInfo, sizeof(dispInfo));
5283 /* if the app stores all the data, handle it separately */
5284 if (infoPtr->dwStyle & LVS_OWNERDATA)
5286 dispInfo.item.state = 0;
5288 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5289 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5291 /* NOTE: copy only fields which we _know_ are initialized, some apps
5292 * depend on the uninitialized fields being 0 */
5293 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5294 dispInfo.item.iItem = lpLVItem->iItem;
5295 dispInfo.item.iSubItem = isubitem;
5296 if (lpLVItem->mask & LVIF_TEXT)
5298 dispInfo.item.pszText = lpLVItem->pszText;
5299 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5301 if (lpLVItem->mask & LVIF_STATE)
5302 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5303 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5304 dispInfo.item.stateMask = lpLVItem->stateMask;
5305 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5307 /* full size structure expected - _WIN32IE >= 0x560 */
5308 *lpLVItem = dispInfo.item;
5310 else if (lpLVItem->mask & LVIF_INDENT)
5312 /* indent member expected - _WIN32IE >= 0x300 */
5313 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5317 /* minimal structure expected */
5318 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5320 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5323 /* make sure lParam is zeroed out */
5324 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5326 /* we store only a little state, so if we're not asked, we're done */
5327 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5329 /* if focus is handled by us, report it */
5330 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5332 lpLVItem->state &= ~LVIS_FOCUSED;
5333 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5334 lpLVItem->state |= LVIS_FOCUSED;
5337 /* and do the same for selection, if we handle it */
5338 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5340 lpLVItem->state &= ~LVIS_SELECTED;
5341 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5342 lpLVItem->state |= LVIS_SELECTED;
5348 /* find the item and subitem structures before we proceed */
5349 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5350 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5355 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5356 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5359 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5364 pItemHdr = &lpItem->hdr;
5366 /* Do we need to query the state from the app? */
5367 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5369 dispInfo.item.mask |= LVIF_STATE;
5370 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5373 /* Do we need to enquire about the image? */
5374 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5375 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5377 dispInfo.item.mask |= LVIF_IMAGE;
5378 dispInfo.item.iImage = I_IMAGECALLBACK;
5381 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5382 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5384 dispInfo.item.mask |= LVIF_TEXT;
5385 dispInfo.item.pszText = lpLVItem->pszText;
5386 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5387 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5388 *dispInfo.item.pszText = '\0';
5391 /* If we don't have all the requested info, query the application */
5392 if (dispInfo.item.mask != 0)
5394 dispInfo.item.iItem = lpLVItem->iItem;
5395 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5396 dispInfo.item.lParam = lpItem->lParam;
5397 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5398 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5401 /* we should not store values for subitems */
5402 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5404 /* Now, handle the iImage field */
5405 if (dispInfo.item.mask & LVIF_IMAGE)
5407 lpLVItem->iImage = dispInfo.item.iImage;
5408 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5409 pItemHdr->iImage = dispInfo.item.iImage;
5411 else if (lpLVItem->mask & LVIF_IMAGE)
5413 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5414 lpLVItem->iImage = pItemHdr->iImage;
5416 lpLVItem->iImage = 0;
5419 /* The pszText field */
5420 if (dispInfo.item.mask & LVIF_TEXT)
5422 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5423 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5425 lpLVItem->pszText = dispInfo.item.pszText;
5427 else if (lpLVItem->mask & LVIF_TEXT)
5429 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5430 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5433 /* Next is the lParam field */
5434 if (dispInfo.item.mask & LVIF_PARAM)
5436 lpLVItem->lParam = dispInfo.item.lParam;
5437 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5438 lpItem->lParam = dispInfo.item.lParam;
5440 else if (lpLVItem->mask & LVIF_PARAM)
5441 lpLVItem->lParam = lpItem->lParam;
5443 /* if this is a subitem, we're done */
5444 if (isubitem) return TRUE;
5446 /* ... the state field (this one is different due to uCallbackmask) */
5447 if (lpLVItem->mask & LVIF_STATE)
5449 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5450 if (dispInfo.item.mask & LVIF_STATE)
5452 lpLVItem->state &= ~dispInfo.item.stateMask;
5453 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5455 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5457 lpLVItem->state &= ~LVIS_FOCUSED;
5458 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5459 lpLVItem->state |= LVIS_FOCUSED;
5461 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5463 lpLVItem->state &= ~LVIS_SELECTED;
5464 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5465 lpLVItem->state |= LVIS_SELECTED;
5469 /* and last, but not least, the indent field */
5470 if (lpLVItem->mask & LVIF_INDENT)
5471 lpLVItem->iIndent = lpItem->iIndent;
5478 * Retrieves item attributes.
5481 * [I] hwnd : window handle
5482 * [IO] lpLVItem : item info
5483 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5484 * if FALSE, then lpLVItem is a LPLVITEMA.
5487 * This is the external 'GetItem' interface -- it properly copies
5488 * the text in the provided buffer.
5494 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5499 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5502 pszText = lpLVItem->pszText;
5503 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5504 if (bResult && lpLVItem->pszText != pszText)
5505 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5506 lpLVItem->pszText = pszText;
5514 * Retrieves the position (upper-left) of the listview control item.
5515 * Note that for LVS_ICON style, the upper-left is that of the icon
5516 * and not the bounding box.
5519 * [I] infoPtr : valid pointer to the listview structure
5520 * [I] nItem : item index
5521 * [O] lpptPosition : coordinate information
5527 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5529 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5532 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5534 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5536 LISTVIEW_GetOrigin(infoPtr, &Origin);
5537 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5539 if (uView == LVS_ICON)
5541 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5542 lpptPosition->y += ICON_TOP_PADDING;
5544 lpptPosition->x += Origin.x;
5545 lpptPosition->y += Origin.y;
5547 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5554 * Retrieves the bounding rectangle for a listview control item.
5557 * [I] infoPtr : valid pointer to the listview structure
5558 * [I] nItem : item index
5559 * [IO] lprc : bounding rectangle coordinates
5560 * lprc->left specifies the portion of the item for which the bounding
5561 * rectangle will be retrieved.
5563 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5564 * including the icon and label.
5567 * * Experiment shows that native control returns:
5568 * * width = min (48, length of text line)
5569 * * .left = position.x - (width - iconsize.cx)/2
5570 * * .right = .left + width
5571 * * height = #lines of text * ntmHeight + icon height + 8
5572 * * .top = position.y - 2
5573 * * .bottom = .top + height
5574 * * separation between items .y = itemSpacing.cy - height
5575 * * .x = itemSpacing.cx - width
5576 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5579 * * Experiment shows that native control returns:
5580 * * width = iconSize.cx + 16
5581 * * .left = position.x - (width - iconsize.cx)/2
5582 * * .right = .left + width
5583 * * height = iconSize.cy + 4
5584 * * .top = position.y - 2
5585 * * .bottom = .top + height
5586 * * separation between items .y = itemSpacing.cy - height
5587 * * .x = itemSpacing.cx - width
5588 * LVIR_LABEL Returns the bounding rectangle of the item text.
5591 * * Experiment shows that native control returns:
5592 * * width = text length
5593 * * .left = position.x - width/2
5594 * * .right = .left + width
5595 * * height = ntmH * linecount + 2
5596 * * .top = position.y + iconSize.cy + 6
5597 * * .bottom = .top + height
5598 * * separation between items .y = itemSpacing.cy - height
5599 * * .x = itemSpacing.cx - width
5600 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5601 * rectangles, but excludes columns in report view.
5608 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5609 * upon whether the window has the focus currently and on whether the item
5610 * is the one with the focus. Ensure that the control's record of which
5611 * item has the focus agrees with the items' records.
5613 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5615 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5616 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5617 BOOL doLabel = TRUE, oversizedBox = FALSE;
5618 POINT Position, Origin;
5621 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5623 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5625 LISTVIEW_GetOrigin(infoPtr, &Origin);
5626 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5628 /* Be smart and try to figure out the minimum we have to do */
5629 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5630 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5631 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5632 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5633 oversizedBox = TRUE;
5635 /* get what we need from the item before hand, so we make
5636 * only one request. This can speed up things, if data
5637 * is stored on the app side */
5639 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5640 if (doLabel) lvItem.mask |= LVIF_TEXT;
5641 lvItem.iItem = nItem;
5642 lvItem.iSubItem = 0;
5643 lvItem.pszText = szDispText;
5644 lvItem.cchTextMax = DISP_TEXT_SIZE;
5645 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5646 /* we got the state already up, simulate it here, to avoid a reget */
5647 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5649 lvItem.mask |= LVIF_STATE;
5650 lvItem.stateMask = LVIS_FOCUSED;
5651 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5654 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5655 lprc->left = LVIR_BOUNDS;
5659 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5663 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
5667 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5670 case LVIR_SELECTBOUNDS:
5671 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
5675 WARN("Unknown value: %d\n", lprc->left);
5679 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5681 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5688 * Retrieves the spacing between listview control items.
5691 * [I] infoPtr : valid pointer to the listview structure
5692 * [IO] lprc : rectangle to receive the output
5693 * on input, lprc->top = nSubItem
5694 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5696 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5697 * not only those of the first column.
5698 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5704 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5710 if (!lprc) return FALSE;
5712 nColumn = lprc->top;
5714 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5715 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5717 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5719 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5721 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5723 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5726 lvItem.iItem = nItem;
5727 lvItem.iSubItem = nColumn;
5729 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5733 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5738 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5742 ERR("Unknown bounds=%d\n", lprc->left);
5746 OffsetRect(lprc, Position.x, Position.y);
5753 * Retrieves the width of a label.
5756 * [I] infoPtr : valid pointer to the listview structure
5759 * SUCCESS : string width (in pixels)
5762 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5764 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5767 TRACE("(nItem=%d)\n", nItem);
5769 lvItem.mask = LVIF_TEXT;
5770 lvItem.iItem = nItem;
5771 lvItem.iSubItem = 0;
5772 lvItem.pszText = szDispText;
5773 lvItem.cchTextMax = DISP_TEXT_SIZE;
5774 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5776 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5781 * Retrieves the spacing between listview control items.
5784 * [I] infoPtr : valid pointer to the listview structure
5785 * [I] bSmall : flag for small or large icon
5788 * Horizontal + vertical spacing
5790 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5796 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5800 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5801 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5803 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5810 * Retrieves the state of a listview control item.
5813 * [I] infoPtr : valid pointer to the listview structure
5814 * [I] nItem : item index
5815 * [I] uMask : state mask
5818 * State specified by the mask.
5820 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5824 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5826 lvItem.iItem = nItem;
5827 lvItem.iSubItem = 0;
5828 lvItem.mask = LVIF_STATE;
5829 lvItem.stateMask = uMask;
5830 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5832 return lvItem.state & uMask;
5837 * Retrieves the text of a listview control item or subitem.
5840 * [I] hwnd : window handle
5841 * [I] nItem : item index
5842 * [IO] lpLVItem : item information
5843 * [I] isW : TRUE if lpLVItem is Unicode
5846 * SUCCESS : string length
5849 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5851 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5853 lpLVItem->mask = LVIF_TEXT;
5854 lpLVItem->iItem = nItem;
5855 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5857 return textlenT(lpLVItem->pszText, isW);
5862 * Searches for an item based on properties + relationships.
5865 * [I] infoPtr : valid pointer to the listview structure
5866 * [I] nItem : item index
5867 * [I] uFlags : relationship flag
5870 * SUCCESS : item index
5873 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5875 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5877 LVFINDINFOW lvFindInfo;
5878 INT nCountPerColumn;
5882 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5883 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5885 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5887 if (uFlags & LVNI_CUT)
5890 if (uFlags & LVNI_DROPHILITED)
5891 uMask |= LVIS_DROPHILITED;
5893 if (uFlags & LVNI_FOCUSED)
5894 uMask |= LVIS_FOCUSED;
5896 if (uFlags & LVNI_SELECTED)
5897 uMask |= LVIS_SELECTED;
5899 /* if we're asked for the focused item, that's only one,
5900 * so it's worth optimizing */
5901 if (uFlags & LVNI_FOCUSED)
5903 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5904 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5907 if (uFlags & LVNI_ABOVE)
5909 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5914 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5920 /* Special case for autoarrange - move 'til the top of a list */
5921 if (is_autoarrange(infoPtr))
5923 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5924 while (nItem - nCountPerRow >= 0)
5926 nItem -= nCountPerRow;
5927 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5932 lvFindInfo.flags = LVFI_NEARESTXY;
5933 lvFindInfo.vkDirection = VK_UP;
5934 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5935 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5937 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5942 else if (uFlags & LVNI_BELOW)
5944 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5946 while (nItem < infoPtr->nItemCount)
5949 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5955 /* Special case for autoarrange - move 'til the bottom of a list */
5956 if (is_autoarrange(infoPtr))
5958 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5959 while (nItem + nCountPerRow < infoPtr->nItemCount )
5961 nItem += nCountPerRow;
5962 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5967 lvFindInfo.flags = LVFI_NEARESTXY;
5968 lvFindInfo.vkDirection = VK_DOWN;
5969 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5970 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5972 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5977 else if (uFlags & LVNI_TOLEFT)
5979 if (uView == LVS_LIST)
5981 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5982 while (nItem - nCountPerColumn >= 0)
5984 nItem -= nCountPerColumn;
5985 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5989 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5991 /* Special case for autoarrange - move 'ti the beginning of a row */
5992 if (is_autoarrange(infoPtr))
5994 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5995 while (nItem % nCountPerRow > 0)
5998 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6003 lvFindInfo.flags = LVFI_NEARESTXY;
6004 lvFindInfo.vkDirection = VK_LEFT;
6005 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6006 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6008 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6013 else if (uFlags & LVNI_TORIGHT)
6015 if (uView == LVS_LIST)
6017 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6018 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6020 nItem += nCountPerColumn;
6021 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6025 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6027 /* Special case for autoarrange - move 'til the end of a row */
6028 if (is_autoarrange(infoPtr))
6030 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6031 while (nItem % nCountPerRow < nCountPerRow - 1 )
6034 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6039 lvFindInfo.flags = LVFI_NEARESTXY;
6040 lvFindInfo.vkDirection = VK_RIGHT;
6041 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6042 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6044 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6053 /* search by index */
6054 for (i = nItem; i < infoPtr->nItemCount; i++)
6056 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6064 /* LISTVIEW_GetNumberOfWorkAreas */
6068 * Retrieves the origin coordinates when in icon or small icon display mode.
6071 * [I] infoPtr : valid pointer to the listview structure
6072 * [O] lpptOrigin : coordinate information
6077 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6079 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6080 INT nHorzPos = 0, nVertPos = 0;
6081 SCROLLINFO scrollInfo;
6083 scrollInfo.cbSize = sizeof(SCROLLINFO);
6084 scrollInfo.fMask = SIF_POS;
6086 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6087 nHorzPos = scrollInfo.nPos;
6088 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6089 nVertPos = scrollInfo.nPos;
6091 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6093 lpptOrigin->x = infoPtr->rcList.left;
6094 lpptOrigin->y = infoPtr->rcList.top;
6095 if (uView == LVS_LIST)
6096 nHorzPos *= infoPtr->nItemWidth;
6097 else if (uView == LVS_REPORT)
6098 nVertPos *= infoPtr->nItemHeight;
6100 lpptOrigin->x -= nHorzPos;
6101 lpptOrigin->y -= nVertPos;
6103 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6108 * Retrieves the width of a string.
6111 * [I] hwnd : window handle
6112 * [I] lpszText : text string to process
6113 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6116 * SUCCESS : string width (in pixels)
6119 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6124 if (is_textT(lpszText, isW))
6126 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6127 HDC hdc = GetDC(infoPtr->hwndSelf);
6128 HFONT hOldFont = SelectObject(hdc, hFont);
6131 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6133 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6134 SelectObject(hdc, hOldFont);
6135 ReleaseDC(infoPtr->hwndSelf, hdc);
6137 return stringSize.cx;
6142 * Determines which listview item is located at the specified position.
6145 * [I] infoPtr : valid pointer to the listview structure
6146 * [IO] lpht : hit test information
6147 * [I] subitem : fill out iSubItem.
6148 * [I] select : return the index only if the hit selects the item
6151 * (mm 20001022): We must not allow iSubItem to be touched, for
6152 * an app might pass only a structure with space up to iItem!
6153 * (MS Office 97 does that for instance in the file open dialog)
6156 * SUCCESS : item index
6159 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6161 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6162 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6163 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6164 POINT Origin, Position, opt;
6169 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6173 if (subitem) lpht->iSubItem = 0;
6175 if (infoPtr->rcList.left > lpht->pt.x)
6176 lpht->flags |= LVHT_TOLEFT;
6177 else if (infoPtr->rcList.right < lpht->pt.x)
6178 lpht->flags |= LVHT_TORIGHT;
6180 if (infoPtr->rcList.top > lpht->pt.y)
6181 lpht->flags |= LVHT_ABOVE;
6182 else if (infoPtr->rcList.bottom < lpht->pt.y)
6183 lpht->flags |= LVHT_BELOW;
6185 TRACE("lpht->flags=0x%x\n", lpht->flags);
6186 if (lpht->flags) return -1;
6188 lpht->flags |= LVHT_NOWHERE;
6190 LISTVIEW_GetOrigin(infoPtr, &Origin);
6192 /* first deal with the large items */
6193 rcSearch.left = lpht->pt.x;
6194 rcSearch.top = lpht->pt.y;
6195 rcSearch.right = rcSearch.left + 1;
6196 rcSearch.bottom = rcSearch.top + 1;
6198 iterator_frameditems(&i, infoPtr, &rcSearch);
6199 iterator_next(&i); /* go to first item in the sequence */
6201 iterator_destroy(&i);
6203 TRACE("lpht->iItem=%d\n", iItem);
6204 if (iItem == -1) return -1;
6206 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6207 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6208 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6209 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6210 lvItem.iItem = iItem;
6211 lvItem.iSubItem = 0;
6212 lvItem.pszText = szDispText;
6213 lvItem.cchTextMax = DISP_TEXT_SIZE;
6214 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6215 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6217 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6218 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6219 opt.x = lpht->pt.x - Position.x - Origin.x;
6220 opt.y = lpht->pt.y - Position.y - Origin.y;
6222 if (uView == LVS_REPORT)
6225 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6226 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6227 if (!PtInRect(&rcBounds, opt)) return -1;
6229 if (PtInRect(&rcIcon, opt))
6230 lpht->flags |= LVHT_ONITEMICON;
6231 else if (PtInRect(&rcLabel, opt))
6232 lpht->flags |= LVHT_ONITEMLABEL;
6233 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6234 lpht->flags |= LVHT_ONITEMSTATEICON;
6235 if (lpht->flags & LVHT_ONITEM)
6236 lpht->flags &= ~LVHT_NOWHERE;
6238 TRACE("lpht->flags=0x%x\n", lpht->flags);
6239 if (uView == LVS_REPORT && subitem)
6243 rcBounds.right = rcBounds.left;
6244 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6246 rcBounds.left = rcBounds.right;
6247 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6248 if (PtInRect(&rcBounds, opt))
6256 if (select && !(uView == LVS_REPORT &&
6257 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6258 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6260 if (uView == LVS_REPORT)
6262 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6263 UnionRect(&rcBounds, &rcBounds, &rcState);
6265 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6267 return lpht->iItem = iItem;
6271 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6272 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6273 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6274 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6275 their own sort proc. when sending LVM_SORTITEMS.
6278 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6280 LVS_SORTXXX must be specified,
6281 LVS_OWNERDRAW is not set,
6282 <item>.pszText is not LPSTR_TEXTCALLBACK.
6284 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6285 are sorted based on item text..."
6287 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6289 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6290 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6291 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6293 /* if we're sorting descending, negate the return value */
6294 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6299 * Inserts a new item in the listview control.
6302 * [I] infoPtr : valid pointer to the listview structure
6303 * [I] lpLVItem : item information
6304 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6307 * SUCCESS : new item index
6310 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6312 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6317 BOOL is_sorted, has_changed;
6319 HWND hwndSelf = infoPtr->hwndSelf;
6321 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6323 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6325 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6326 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6328 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6330 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6332 /* insert item in listview control data structure */
6333 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6334 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6336 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6337 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6339 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6341 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6342 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6343 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6344 if (nItem == -1) goto fail;
6345 infoPtr->nItemCount++;
6347 /* shift indices first so they don't get tangled */
6348 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6350 /* set the item attributes */
6351 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6353 /* full size structure expected - _WIN32IE >= 0x560 */
6356 else if (lpLVItem->mask & LVIF_INDENT)
6358 /* indent member expected - _WIN32IE >= 0x300 */
6359 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6363 /* minimal structure expected */
6364 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6367 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6369 item.mask |= LVIF_STATE;
6370 item.stateMask |= LVIS_STATEIMAGEMASK;
6371 item.state &= ~LVIS_STATEIMAGEMASK;
6372 item.state |= INDEXTOSTATEIMAGEMASK(1);
6374 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6376 /* if we're sorted, sort the list, and update the index */
6379 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6380 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6381 assert(nItem != -1);
6384 /* make room for the position, if we are in the right mode */
6385 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6387 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6389 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6391 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6396 /* send LVN_INSERTITEM notification */
6397 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6399 nmlv.lParam = lpItem->lParam;
6400 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6401 if (!IsWindow(hwndSelf))
6404 /* align items (set position of each item) */
6405 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6409 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6410 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6412 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6414 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6417 /* now is the invalidation fun */
6418 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6422 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6423 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6424 infoPtr->nItemCount--;
6426 DPA_DeletePtr(hdpaSubItems, 0);
6427 DPA_Destroy (hdpaSubItems);
6434 * Redraws a range of items.
6437 * [I] infoPtr : valid pointer to the listview structure
6438 * [I] nFirst : first item
6439 * [I] nLast : last item
6445 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6449 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6450 max(nFirst, nLast) >= infoPtr->nItemCount)
6453 for (i = nFirst; i <= nLast; i++)
6454 LISTVIEW_InvalidateItem(infoPtr, i);
6461 * Scroll the content of a listview.
6464 * [I] infoPtr : valid pointer to the listview structure
6465 * [I] dx : horizontal scroll amount in pixels
6466 * [I] dy : vertical scroll amount in pixels
6473 * If the control is in report mode (LVS_REPORT) the control can
6474 * be scrolled only in line increments. "dy" will be rounded to the
6475 * nearest number of pixels that are a whole line. Ex: if line height
6476 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6477 * is passed the the scroll will be 0. (per MSDN 7/2002)
6479 * For: (per experimentaion with native control and CSpy ListView)
6480 * LVS_ICON dy=1 = 1 pixel (vertical only)
6482 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6484 * LVS_LIST dx=1 = 1 column (horizontal only)
6485 * but will only scroll 1 column per message
6486 * no matter what the value.
6487 * dy must be 0 or FALSE returned.
6488 * LVS_REPORT dx=1 = 1 pixel
6492 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6494 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6496 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6497 dy /= infoPtr->nItemHeight;
6500 if (dy != 0) return FALSE;
6507 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6508 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6515 * Sets the background color.
6518 * [I] infoPtr : valid pointer to the listview structure
6519 * [I] clrBk : background color
6525 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6527 TRACE("(clrBk=%x)\n", clrBk);
6529 if(infoPtr->clrBk != clrBk) {
6530 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6531 infoPtr->clrBk = clrBk;
6532 if (clrBk == CLR_NONE)
6533 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6535 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6536 LISTVIEW_InvalidateList(infoPtr);
6542 /* LISTVIEW_SetBkImage */
6544 /*** Helper for {Insert,Set}ColumnT *only* */
6545 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6547 if (lpColumn->mask & LVCF_FMT)
6549 /* format member is valid */
6550 lphdi->mask |= HDI_FORMAT;
6552 /* set text alignment (leftmost column must be left-aligned) */
6553 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6554 lphdi->fmt |= HDF_LEFT;
6555 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6556 lphdi->fmt |= HDF_RIGHT;
6557 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6558 lphdi->fmt |= HDF_CENTER;
6560 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6561 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6563 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6565 lphdi->fmt |= HDF_IMAGE;
6566 lphdi->iImage = I_IMAGECALLBACK;
6570 if (lpColumn->mask & LVCF_WIDTH)
6572 lphdi->mask |= HDI_WIDTH;
6573 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6575 /* make it fill the remainder of the controls width */
6579 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6581 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6582 lphdi->cxy += rcHeader.right - rcHeader.left;
6585 /* retrieve the layout of the header */
6586 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6587 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6589 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6592 lphdi->cxy = lpColumn->cx;
6595 if (lpColumn->mask & LVCF_TEXT)
6597 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6598 lphdi->fmt |= HDF_STRING;
6599 lphdi->pszText = lpColumn->pszText;
6600 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6603 if (lpColumn->mask & LVCF_IMAGE)
6605 lphdi->mask |= HDI_IMAGE;
6606 lphdi->iImage = lpColumn->iImage;
6609 if (lpColumn->mask & LVCF_ORDER)
6611 lphdi->mask |= HDI_ORDER;
6612 lphdi->iOrder = lpColumn->iOrder;
6619 * Inserts a new column.
6622 * [I] infoPtr : valid pointer to the listview structure
6623 * [I] nColumn : column index
6624 * [I] lpColumn : column information
6625 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6628 * SUCCESS : new column index
6631 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6632 const LVCOLUMNW *lpColumn, BOOL isW)
6634 COLUMN_INFO *lpColumnInfo;
6638 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6640 if (!lpColumn || nColumn < 0) return -1;
6641 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6643 ZeroMemory(&hdi, sizeof(HDITEMW));
6644 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6647 * when the iSubItem is available Windows copies it to the header lParam. It seems
6648 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6650 if (lpColumn->mask & LVCF_SUBITEM)
6652 hdi.mask |= HDI_LPARAM;
6653 hdi.lParam = lpColumn->iSubItem;
6656 /* insert item in header control */
6657 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6658 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6659 (WPARAM)nColumn, (LPARAM)&hdi);
6660 if (nNewColumn == -1) return -1;
6661 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6663 /* create our own column info */
6664 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6665 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6667 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6668 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6670 /* now we have to actually adjust the data */
6671 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6673 SUBITEM_INFO *lpSubItem;
6677 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6679 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6680 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6682 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6683 if (lpSubItem->iSubItem >= nNewColumn)
6684 lpSubItem->iSubItem++;
6689 /* make space for the new column */
6690 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6691 LISTVIEW_UpdateItemSize(infoPtr);
6696 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6699 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6707 * Sets the attributes of a header item.
6710 * [I] infoPtr : valid pointer to the listview structure
6711 * [I] nColumn : column index
6712 * [I] lpColumn : column attributes
6713 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6719 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6720 const LVCOLUMNW *lpColumn, BOOL isW)
6722 HDITEMW hdi, hdiget;
6725 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6727 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6729 ZeroMemory(&hdi, sizeof(HDITEMW));
6730 if (lpColumn->mask & LVCF_FMT)
6732 hdi.mask |= HDI_FORMAT;
6733 hdiget.mask = HDI_FORMAT;
6734 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6735 hdi.fmt = hdiget.fmt & HDF_STRING;
6737 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6739 /* set header item attributes */
6740 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6741 if (!bResult) return FALSE;
6743 if (lpColumn->mask & LVCF_FMT)
6745 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6746 int oldFmt = lpColumnInfo->fmt;
6748 lpColumnInfo->fmt = lpColumn->fmt;
6749 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6751 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6752 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6761 * Sets the column order array
6764 * [I] infoPtr : valid pointer to the listview structure
6765 * [I] iCount : number of elements in column order array
6766 * [I] lpiArray : pointer to column order array
6772 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6774 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6785 * Sets the width of a column
6788 * [I] infoPtr : valid pointer to the listview structure
6789 * [I] nColumn : column index
6790 * [I] cx : column width
6796 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6798 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6799 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6803 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6805 /* set column width only if in report or list mode */
6806 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6808 /* take care of invalid cx values */
6809 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6810 else if (uView == LVS_LIST && cx < 1) return FALSE;
6812 /* resize all columns if in LVS_LIST mode */
6813 if(uView == LVS_LIST)
6815 infoPtr->nItemWidth = cx;
6816 LISTVIEW_InvalidateList(infoPtr);
6820 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6822 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6827 lvItem.mask = LVIF_TEXT;
6829 lvItem.iSubItem = nColumn;
6830 lvItem.pszText = szDispText;
6831 lvItem.cchTextMax = DISP_TEXT_SIZE;
6832 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6834 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6835 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6836 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6838 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6839 max_cx += infoPtr->iconSize.cx;
6840 max_cx += TRAILING_LABEL_PADDING;
6843 /* autosize based on listview items width */
6844 if(cx == LVSCW_AUTOSIZE)
6846 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6848 /* if iCol is the last column make it fill the remainder of the controls width */
6849 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6854 LISTVIEW_GetOrigin(infoPtr, &Origin);
6855 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6857 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6861 /* Despite what the MS docs say, if this is not the last
6862 column, then MS resizes the column to the width of the
6863 largest text string in the column, including headers
6864 and items. This is different from LVSCW_AUTOSIZE in that
6865 LVSCW_AUTOSIZE ignores the header string length. */
6868 /* retrieve header text */
6869 hdi.mask = HDI_TEXT;
6870 hdi.cchTextMax = DISP_TEXT_SIZE;
6871 hdi.pszText = szDispText;
6872 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
6874 HDC hdc = GetDC(infoPtr->hwndSelf);
6875 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6878 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6879 cx = size.cx + TRAILING_HEADER_PADDING;
6880 /* FIXME: Take into account the header image, if one is present */
6881 SelectObject(hdc, old_font);
6882 ReleaseDC(infoPtr->hwndSelf, hdc);
6884 cx = max (cx, max_cx);
6888 if (cx < 0) return FALSE;
6890 /* call header to update the column change */
6891 hdi.mask = HDI_WIDTH;
6893 TRACE("hdi.cxy=%d\n", hdi.cxy);
6894 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6898 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6901 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6904 HBITMAP hbm_im, hbm_mask, hbm_orig;
6906 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6907 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6910 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6911 ILC_COLOR | ILC_MASK, 2, 2);
6912 hdc_wnd = GetDC(infoPtr->hwndSelf);
6913 hdc = CreateCompatibleDC(hdc_wnd);
6914 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6915 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6916 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6918 rc.left = rc.top = 0;
6919 rc.right = GetSystemMetrics(SM_CXSMICON);
6920 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6922 hbm_orig = SelectObject(hdc, hbm_mask);
6923 FillRect(hdc, &rc, hbr_white);
6924 InflateRect(&rc, -3, -3);
6925 FillRect(hdc, &rc, hbr_black);
6927 SelectObject(hdc, hbm_im);
6928 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6929 SelectObject(hdc, hbm_orig);
6930 ImageList_Add(himl, hbm_im, hbm_mask);
6932 SelectObject(hdc, hbm_im);
6933 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6934 SelectObject(hdc, hbm_orig);
6935 ImageList_Add(himl, hbm_im, hbm_mask);
6937 DeleteObject(hbm_mask);
6938 DeleteObject(hbm_im);
6946 * Sets the extended listview style.
6949 * [I] infoPtr : valid pointer to the listview structure
6951 * [I] dwStyle : style
6954 * SUCCESS : previous style
6957 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6959 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6963 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6965 infoPtr->dwLvExStyle = dwStyle;
6967 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6969 HIMAGELIST himl = 0;
6970 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6973 item.mask = LVIF_STATE;
6974 item.stateMask = LVIS_STATEIMAGEMASK;
6975 item.state = INDEXTOSTATEIMAGEMASK(1);
6976 LISTVIEW_SetItemState(infoPtr, -1, &item);
6978 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6980 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6983 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_HEADERDRAGDROP)
6985 DWORD dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
6986 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
6987 dwStyle |= HDS_DRAGDROP;
6989 dwStyle &= ~HDS_DRAGDROP;
6990 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
6998 * Sets the new hot cursor used during hot tracking and hover selection.
7001 * [I] infoPtr : valid pointer to the listview structure
7002 * [I] hCursor : the new hot cursor handle
7005 * Returns the previous hot cursor
7007 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7009 HCURSOR oldCursor = infoPtr->hHotCursor;
7011 infoPtr->hHotCursor = hCursor;
7019 * Sets the hot item index.
7022 * [I] infoPtr : valid pointer to the listview structure
7023 * [I] iIndex : index
7026 * SUCCESS : previous hot item index
7027 * FAILURE : -1 (no hot item)
7029 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7031 INT iOldIndex = infoPtr->nHotItem;
7033 infoPtr->nHotItem = iIndex;
7041 * Sets the amount of time the cursor must hover over an item before it is selected.
7044 * [I] infoPtr : valid pointer to the listview structure
7045 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7048 * Returns the previous hover time
7050 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7052 DWORD oldHoverTime = infoPtr->dwHoverTime;
7054 infoPtr->dwHoverTime = dwHoverTime;
7056 return oldHoverTime;
7061 * Sets spacing for icons of LVS_ICON style.
7064 * [I] infoPtr : valid pointer to the listview structure
7065 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7066 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7069 * MAKELONG(oldcx, oldcy)
7071 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7073 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7074 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7076 TRACE("requested=(%d,%d)\n", cx, cy);
7078 /* this is supported only for LVS_ICON style */
7079 if (uView != LVS_ICON) return oldspacing;
7081 /* set to defaults, if instructed to */
7082 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7083 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7085 /* if 0 then compute width
7086 * FIXME: Should scan each item and determine max width of
7087 * icon or label, then make that the width */
7089 cx = infoPtr->iconSpacing.cx;
7091 /* if 0 then compute height */
7093 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7094 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7097 infoPtr->iconSpacing.cx = cx;
7098 infoPtr->iconSpacing.cy = cy;
7100 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7101 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7102 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7103 infoPtr->ntmHeight);
7105 /* these depend on the iconSpacing */
7106 LISTVIEW_UpdateItemSize(infoPtr);
7111 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7115 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7122 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7123 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7132 * [I] infoPtr : valid pointer to the listview structure
7133 * [I] nType : image list type
7134 * [I] himl : image list handle
7137 * SUCCESS : old image list
7140 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7142 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7143 INT oldHeight = infoPtr->nItemHeight;
7144 HIMAGELIST himlOld = 0;
7146 TRACE("(nType=%d, himl=%p\n", nType, himl);
7151 himlOld = infoPtr->himlNormal;
7152 infoPtr->himlNormal = himl;
7153 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7154 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7158 himlOld = infoPtr->himlSmall;
7159 infoPtr->himlSmall = himl;
7160 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7164 himlOld = infoPtr->himlState;
7165 infoPtr->himlState = himl;
7166 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7167 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7171 ERR("Unknown icon type=%d\n", nType);
7175 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7176 if (infoPtr->nItemHeight != oldHeight)
7177 LISTVIEW_UpdateScroll(infoPtr);
7184 * Preallocates memory (does *not* set the actual count of items !)
7187 * [I] infoPtr : valid pointer to the listview structure
7188 * [I] nItems : item count (projected number of items to allocate)
7189 * [I] dwFlags : update flags
7195 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7197 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7199 if (infoPtr->dwStyle & LVS_OWNERDATA)
7201 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7202 INT nOldCount = infoPtr->nItemCount;
7204 if (nItems < nOldCount)
7206 RANGE range = { nItems, nOldCount };
7207 ranges_del(infoPtr->selectionRanges, range);
7208 if (infoPtr->nFocusedItem >= nItems)
7210 infoPtr->nFocusedItem = -1;
7211 SetRectEmpty(&infoPtr->rcFocus);
7215 infoPtr->nItemCount = nItems;
7216 LISTVIEW_UpdateScroll(infoPtr);
7218 /* the flags are valid only in ownerdata report and list modes */
7219 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7221 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7222 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7224 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7225 LISTVIEW_InvalidateList(infoPtr);
7232 LISTVIEW_GetOrigin(infoPtr, &Origin);
7233 nFrom = min(nOldCount, nItems);
7234 nTo = max(nOldCount, nItems);
7236 if (uView == LVS_REPORT)
7239 rcErase.top = nFrom * infoPtr->nItemHeight;
7240 rcErase.right = infoPtr->nItemWidth;
7241 rcErase.bottom = nTo * infoPtr->nItemHeight;
7242 OffsetRect(&rcErase, Origin.x, Origin.y);
7243 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7244 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7248 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7250 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7251 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7252 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7253 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7254 OffsetRect(&rcErase, Origin.x, Origin.y);
7255 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7256 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7258 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7260 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7261 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7262 OffsetRect(&rcErase, Origin.x, Origin.y);
7263 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7264 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7270 /* According to MSDN for non-LVS_OWNERDATA this is just
7271 * a performance issue. The control allocates its internal
7272 * data structures for the number of items specified. It
7273 * cuts down on the number of memory allocations. Therefore
7274 * we will just issue a WARN here
7276 WARN("for non-ownerdata performance option not implemented.\n");
7284 * Sets the position of an item.
7287 * [I] infoPtr : valid pointer to the listview structure
7288 * [I] nItem : item index
7289 * [I] pt : coordinate
7295 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7297 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7300 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7302 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7303 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7305 LISTVIEW_GetOrigin(infoPtr, &Origin);
7307 /* This point value seems to be an undocumented feature.
7308 * The best guess is that it means either at the origin,
7309 * or at true beginning of the list. I will assume the origin. */
7310 if ((pt.x == -1) && (pt.y == -1))
7313 if (uView == LVS_ICON)
7315 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7316 pt.y -= ICON_TOP_PADDING;
7321 infoPtr->bAutoarrange = FALSE;
7323 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7328 * Sets the state of one or many items.
7331 * [I] infoPtr : valid pointer to the listview structure
7332 * [I] nItem : item index
7333 * [I] lpLVItem : item or subitem info
7339 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7341 BOOL bResult = TRUE;
7344 lvItem.iItem = nItem;
7345 lvItem.iSubItem = 0;
7346 lvItem.mask = LVIF_STATE;
7347 lvItem.state = lpLVItem->state;
7348 lvItem.stateMask = lpLVItem->stateMask;
7349 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7353 /* apply to all items */
7354 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7355 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7358 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7361 * Update selection mark
7363 * Investigation on windows 2k showed that selection mark was updated
7364 * whenever a new selection was made, but if the selected item was
7365 * unselected it was not updated.
7367 * we are probably still not 100% accurate, but this at least sets the
7368 * proper selection mark when it is needed
7371 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7372 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7375 infoPtr->nSelectionMark = -1;
7376 for (i = 0; i < infoPtr->nItemCount; i++)
7378 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7380 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7382 infoPtr->nSelectionMark = i;
7386 else if (ranges_contain(infoPtr->selectionRanges, i))
7388 infoPtr->nSelectionMark = i;
7399 * Sets the text of an item or subitem.
7402 * [I] hwnd : window handle
7403 * [I] nItem : item index
7404 * [I] lpLVItem : item or subitem info
7405 * [I] isW : TRUE if input is Unicode
7411 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7415 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7417 lvItem.iItem = nItem;
7418 lvItem.iSubItem = lpLVItem->iSubItem;
7419 lvItem.mask = LVIF_TEXT;
7420 lvItem.pszText = lpLVItem->pszText;
7421 lvItem.cchTextMax = lpLVItem->cchTextMax;
7423 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7425 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7430 * Set item index that marks the start of a multiple selection.
7433 * [I] infoPtr : valid pointer to the listview structure
7434 * [I] nIndex : index
7437 * Index number or -1 if there is no selection mark.
7439 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7441 INT nOldIndex = infoPtr->nSelectionMark;
7443 TRACE("(nIndex=%d)\n", nIndex);
7445 infoPtr->nSelectionMark = nIndex;
7452 * Sets the text background color.
7455 * [I] infoPtr : valid pointer to the listview structure
7456 * [I] clrTextBk : text background color
7462 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7464 TRACE("(clrTextBk=%x)\n", clrTextBk);
7466 if (infoPtr->clrTextBk != clrTextBk)
7468 infoPtr->clrTextBk = clrTextBk;
7469 LISTVIEW_InvalidateList(infoPtr);
7477 * Sets the text foreground color.
7480 * [I] infoPtr : valid pointer to the listview structure
7481 * [I] clrText : text color
7487 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7489 TRACE("(clrText=%x)\n", clrText);
7491 if (infoPtr->clrText != clrText)
7493 infoPtr->clrText = clrText;
7494 LISTVIEW_InvalidateList(infoPtr);
7502 * Determines which listview item is located at the specified position.
7505 * [I] infoPtr : valid pointer to the listview structure
7506 * [I] hwndNewToolTip : handle to new ToolTip
7511 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7513 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7514 infoPtr->hwndToolTip = hwndNewToolTip;
7515 return hwndOldToolTip;
7520 * sets the Unicode character format flag for the control
7522 * [I] infoPtr :valid pointer to the listview structure
7523 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7526 * Old Unicode Format
7528 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7530 BOOL rc = infoPtr->notifyFormat;
7531 infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7535 /* LISTVIEW_SetWorkAreas */
7539 * Callback internally used by LISTVIEW_SortItems()
7542 * [I] first : pointer to first ITEM_INFO to compare
7543 * [I] second : pointer to second ITEM_INFO to compare
7544 * [I] lParam : HWND of control
7547 * if first comes before second : negative
7548 * if first comes after second : positive
7549 * if first and second are equivalent : zero
7551 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7553 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7554 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7555 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7557 /* Forward the call to the client defined callback */
7558 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7563 * Sorts the listview items.
7566 * [I] infoPtr : valid pointer to the listview structure
7567 * [I] pfnCompare : application-defined value
7568 * [I] lParamSort : pointer to comparision callback
7574 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7576 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7579 LPVOID selectionMarkItem;
7583 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7585 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7587 if (!pfnCompare) return FALSE;
7588 if (!infoPtr->hdpaItems) return FALSE;
7590 /* if there are 0 or 1 items, there is no need to sort */
7591 if (infoPtr->nItemCount < 2) return TRUE;
7593 if (infoPtr->nFocusedItem >= 0)
7595 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7596 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7597 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7599 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7600 /* clear the lpItem->state for non-selected ones */
7601 /* remove the selection ranges */
7603 infoPtr->pfnCompare = pfnCompare;
7604 infoPtr->lParamSort = lParamSort;
7605 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7607 /* Adjust selections and indices so that they are the way they should
7608 * be after the sort (otherwise, the list items move around, but
7609 * whatever is at the item's previous original position will be
7612 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7613 for (i=0; i < infoPtr->nItemCount; i++)
7615 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7616 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7618 if (lpItem->state & LVIS_SELECTED)
7620 item.state = LVIS_SELECTED;
7621 item.stateMask = LVIS_SELECTED;
7622 LISTVIEW_SetItemState(infoPtr, i, &item);
7624 if (lpItem->state & LVIS_FOCUSED)
7626 infoPtr->nFocusedItem = i;
7627 lpItem->state &= ~LVIS_FOCUSED;
7630 if (selectionMarkItem != NULL)
7631 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7632 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7634 /* refresh the display */
7635 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7636 LISTVIEW_InvalidateList(infoPtr);
7643 * Update theme handle after a theme change.
7646 * [I] infoPtr : valid pointer to the listview structure
7650 * FAILURE : something else
7652 static LRESULT LISTVIEW_ThemeChanged(LISTVIEW_INFO *infoPtr)
7654 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7655 CloseThemeData(theme);
7656 OpenThemeData(infoPtr->hwndSelf, themeClass);
7662 * Updates an items or rearranges the listview control.
7665 * [I] infoPtr : valid pointer to the listview structure
7666 * [I] nItem : item index
7672 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7674 TRACE("(nItem=%d)\n", nItem);
7676 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7678 /* rearrange with default alignment style */
7679 if (is_autoarrange(infoPtr))
7680 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7682 LISTVIEW_InvalidateItem(infoPtr, nItem);
7689 * Draw the track line at the place defined in the infoPtr structure.
7690 * The line is drawn with a XOR pen so drawing the line for the second time
7691 * in the same place erases the line.
7694 * [I] infoPtr : valid pointer to the listview structure
7700 static BOOL LISTVIEW_DrawTrackLine(LISTVIEW_INFO *infoPtr)
7706 if (infoPtr->xTrackLine == -1)
7709 if (!(hdc = GetDC(infoPtr->hwndSelf)))
7711 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7712 oldROP = SetROP2(hdc, R2_XORPEN);
7713 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7714 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7715 SetROP2(hdc, oldROP);
7716 SelectObject(hdc, hOldPen);
7717 ReleaseDC(infoPtr->hwndSelf, hdc);
7723 * Called when an edit control should be displayed. This function is called after
7724 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7727 * [I] hwnd : Handle to the listview
7728 * [I] uMsg : WM_TIMER (ignored)
7729 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7730 * [I] dwTimer : The elapsed time (ignored)
7735 static CALLBACK VOID LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
7737 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
7738 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7740 KillTimer(hwnd, idEvent);
7741 editItem->fEnabled = FALSE;
7742 /* check if the item is still selected */
7743 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
7744 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
7749 * Creates the listview control - the WM_NCCREATE phase.
7752 * [I] hwnd : window handle
7753 * [I] lpcs : the create parameters
7759 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
7761 LISTVIEW_INFO *infoPtr;
7764 TRACE("(lpcs=%p)\n", lpcs);
7766 /* initialize info pointer */
7767 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
7768 if (!infoPtr) return FALSE;
7770 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7772 infoPtr->hwndSelf = hwnd;
7773 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
7774 /* determine the type of structures to use */
7775 infoPtr->hwndNotify = lpcs->hwndParent;
7776 /* infoPtr->notifyFormat will be filled in WM_CREATE */
7778 /* initialize color information */
7779 infoPtr->clrBk = CLR_NONE;
7780 infoPtr->clrText = CLR_DEFAULT;
7781 infoPtr->clrTextBk = CLR_DEFAULT;
7782 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7784 /* set default values */
7785 infoPtr->nFocusedItem = -1;
7786 infoPtr->nSelectionMark = -1;
7787 infoPtr->nHotItem = -1;
7788 infoPtr->bRedraw = TRUE;
7789 infoPtr->bNoItemMetrics = TRUE;
7790 infoPtr->bDoChangeNotify = TRUE;
7791 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7792 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7793 infoPtr->nEditLabelItem = -1;
7794 infoPtr->dwHoverTime = -1; /* default system hover time */
7795 infoPtr->nMeasureItemHeight = 0;
7796 infoPtr->xTrackLine = -1; /* no track line */
7797 infoPtr->itemEdit.fEnabled = FALSE;
7799 /* get default font (icon title) */
7800 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7801 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7802 infoPtr->hFont = infoPtr->hDefaultFont;
7803 LISTVIEW_SaveTextMetrics(infoPtr);
7805 /* allocate memory for the data structure */
7806 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7807 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7808 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7809 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7810 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7814 DestroyWindow(infoPtr->hwndHeader);
7815 ranges_destroy(infoPtr->selectionRanges);
7816 DPA_Destroy(infoPtr->hdpaItems);
7817 DPA_Destroy(infoPtr->hdpaPosX);
7818 DPA_Destroy(infoPtr->hdpaPosY);
7819 DPA_Destroy(infoPtr->hdpaColumns);
7826 * Creates the listview control - the WM_CREATE phase. Most of the data is
7827 * already set up in LISTVIEW_NCCreate
7830 * [I] hwnd : window handle
7831 * [I] lpcs : the create parameters
7837 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7839 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7840 UINT uView = lpcs->style & LVS_TYPEMASK;
7842 TRACE("(lpcs=%p)\n", lpcs);
7844 infoPtr->dwStyle = lpcs->style;
7845 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7846 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7849 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7850 WS_CHILD | HDS_HORZ | HDS_FULLDRAG | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7851 0, 0, 0, 0, hwnd, NULL,
7852 lpcs->hInstance, NULL);
7853 if (!infoPtr->hwndHeader) return -1;
7855 /* set header unicode format */
7856 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7858 /* set header font */
7859 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7861 /* init item size to avoid division by 0 */
7862 LISTVIEW_UpdateItemSize (infoPtr);
7864 if (uView == LVS_REPORT)
7866 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7868 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7872 /* set HDS_HIDDEN flag to hide the header bar */
7873 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7874 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7878 OpenThemeData(hwnd, themeClass);
7880 /* initialize the icon sizes */
7881 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7882 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7888 * Destroys the listview control.
7891 * [I] infoPtr : valid pointer to the listview structure
7897 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
7899 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7900 CloseThemeData(theme);
7906 * Enables the listview control.
7909 * [I] infoPtr : valid pointer to the listview structure
7910 * [I] bEnable : specifies whether to enable or disable the window
7916 static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable)
7918 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7919 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7925 * Erases the background of the listview control.
7928 * [I] infoPtr : valid pointer to the listview structure
7929 * [I] hdc : device context handle
7935 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7939 TRACE("(hdc=%p)\n", hdc);
7941 if (!GetClipBox(hdc, &rc)) return FALSE;
7943 /* for double buffered controls we need to do this during refresh */
7944 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
7946 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7952 * Helper function for LISTVIEW_[HV]Scroll *only*.
7953 * Performs vertical/horizontal scrolling by a give amount.
7956 * [I] infoPtr : valid pointer to the listview structure
7957 * [I] dx : amount of horizontal scroll
7958 * [I] dy : amount of vertical scroll
7960 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7962 /* now we can scroll the list */
7963 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7964 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7965 /* if we have focus, adjust rect */
7966 OffsetRect(&infoPtr->rcFocus, dx, dy);
7967 UpdateWindow(infoPtr->hwndSelf);
7972 * Performs vertical scrolling.
7975 * [I] infoPtr : valid pointer to the listview structure
7976 * [I] nScrollCode : scroll code
7977 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7978 * [I] hScrollWnd : scrollbar control window handle
7984 * SB_LINEUP/SB_LINEDOWN:
7985 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7986 * for LVS_REPORT is 1 line
7987 * for LVS_LIST cannot occur
7990 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7991 INT nScrollDiff, HWND hScrollWnd)
7993 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7994 INT nOldScrollPos, nNewScrollPos;
7995 SCROLLINFO scrollInfo;
7998 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7999 debugscrollcode(nScrollCode), nScrollDiff);
8001 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8003 scrollInfo.cbSize = sizeof(SCROLLINFO);
8004 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8006 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8008 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8010 nOldScrollPos = scrollInfo.nPos;
8011 switch (nScrollCode)
8017 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8021 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8025 nScrollDiff = -scrollInfo.nPage;
8029 nScrollDiff = scrollInfo.nPage;
8032 case SB_THUMBPOSITION:
8034 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8041 /* quit right away if pos isn't changing */
8042 if (nScrollDiff == 0) return 0;
8044 /* calculate new position, and handle overflows */
8045 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8046 if (nScrollDiff > 0) {
8047 if (nNewScrollPos < nOldScrollPos ||
8048 nNewScrollPos > scrollInfo.nMax)
8049 nNewScrollPos = scrollInfo.nMax;
8051 if (nNewScrollPos > nOldScrollPos ||
8052 nNewScrollPos < scrollInfo.nMin)
8053 nNewScrollPos = scrollInfo.nMin;
8056 /* set the new position, and reread in case it changed */
8057 scrollInfo.fMask = SIF_POS;
8058 scrollInfo.nPos = nNewScrollPos;
8059 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8061 /* carry on only if it really changed */
8062 if (nNewScrollPos == nOldScrollPos) return 0;
8064 /* now adjust to client coordinates */
8065 nScrollDiff = nOldScrollPos - nNewScrollPos;
8066 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8068 /* and scroll the window */
8069 scroll_list(infoPtr, 0, nScrollDiff);
8076 * Performs horizontal scrolling.
8079 * [I] infoPtr : valid pointer to the listview structure
8080 * [I] nScrollCode : scroll code
8081 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8082 * [I] hScrollWnd : scrollbar control window handle
8088 * SB_LINELEFT/SB_LINERIGHT:
8089 * for LVS_ICON, LVS_SMALLICON 1 pixel
8090 * for LVS_REPORT is 1 pixel
8091 * for LVS_LIST is 1 column --> which is a 1 because the
8092 * scroll is based on columns not pixels
8095 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8096 INT nScrollDiff, HWND hScrollWnd)
8098 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8099 INT nOldScrollPos, nNewScrollPos;
8100 SCROLLINFO scrollInfo;
8102 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8103 debugscrollcode(nScrollCode), nScrollDiff);
8105 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8107 scrollInfo.cbSize = sizeof(SCROLLINFO);
8108 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8110 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8112 nOldScrollPos = scrollInfo.nPos;
8114 switch (nScrollCode)
8128 nScrollDiff = -scrollInfo.nPage;
8132 nScrollDiff = scrollInfo.nPage;
8135 case SB_THUMBPOSITION:
8137 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8144 /* quit right away if pos isn't changing */
8145 if (nScrollDiff == 0) return 0;
8147 /* calculate new position, and handle overflows */
8148 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8149 if (nScrollDiff > 0) {
8150 if (nNewScrollPos < nOldScrollPos ||
8151 nNewScrollPos > scrollInfo.nMax)
8152 nNewScrollPos = scrollInfo.nMax;
8154 if (nNewScrollPos > nOldScrollPos ||
8155 nNewScrollPos < scrollInfo.nMin)
8156 nNewScrollPos = scrollInfo.nMin;
8159 /* set the new position, and reread in case it changed */
8160 scrollInfo.fMask = SIF_POS;
8161 scrollInfo.nPos = nNewScrollPos;
8162 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8164 /* carry on only if it really changed */
8165 if (nNewScrollPos == nOldScrollPos) return 0;
8167 if(uView == LVS_REPORT)
8168 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8170 /* now adjust to client coordinates */
8171 nScrollDiff = nOldScrollPos - nNewScrollPos;
8172 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8174 /* and scroll the window */
8175 scroll_list(infoPtr, nScrollDiff, 0);
8180 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8182 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8183 INT gcWheelDelta = 0;
8184 INT pulScrollLines = 3;
8185 SCROLLINFO scrollInfo;
8187 TRACE("(wheelDelta=%d)\n", wheelDelta);
8189 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8190 gcWheelDelta -= wheelDelta;
8192 scrollInfo.cbSize = sizeof(SCROLLINFO);
8193 scrollInfo.fMask = SIF_POS;
8200 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8201 * should be fixed in the future.
8203 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8204 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8208 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8210 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8211 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8212 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8217 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8228 * [I] infoPtr : valid pointer to the listview structure
8229 * [I] nVirtualKey : virtual key
8230 * [I] lKeyData : key data
8235 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8237 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8238 HWND hwndSelf = infoPtr->hwndSelf;
8240 NMLVKEYDOWN nmKeyDown;
8242 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8244 /* send LVN_KEYDOWN notification */
8245 nmKeyDown.wVKey = nVirtualKey;
8246 nmKeyDown.flags = 0;
8247 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8248 if (!IsWindow(hwndSelf))
8251 switch (nVirtualKey)
8254 nItem = infoPtr->nFocusedItem;
8258 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8260 if (!notify(infoPtr, NM_RETURN)) return 0;
8261 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8266 if (infoPtr->nItemCount > 0)
8271 if (infoPtr->nItemCount > 0)
8272 nItem = infoPtr->nItemCount - 1;
8276 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8280 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8284 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8288 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8292 if (uView == LVS_REPORT)
8294 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8295 if (infoPtr->nFocusedItem == topidx)
8296 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8301 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8302 * LISTVIEW_GetCountPerRow(infoPtr);
8303 if(nItem < 0) nItem = 0;
8307 if (uView == LVS_REPORT)
8309 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8310 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8311 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8312 nItem = infoPtr->nFocusedItem + cnt - 1;
8314 nItem = topidx + cnt - 1;
8317 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8318 * LISTVIEW_GetCountPerRow(infoPtr);
8319 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8323 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8324 LISTVIEW_KeySelection(infoPtr, nItem);
8334 * [I] infoPtr : valid pointer to the listview structure
8339 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8343 /* if we did not have the focus, there's nothing to do */
8344 if (!infoPtr->bFocus) return 0;
8346 /* send NM_KILLFOCUS notification */
8347 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8349 /* if we have a focus rectagle, get rid of it */
8350 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8352 /* set window focus flag */
8353 infoPtr->bFocus = FALSE;
8355 /* invalidate the selected items before reseting focus flag */
8356 LISTVIEW_InvalidateSelectedItems(infoPtr);
8363 * Processes double click messages (left mouse button).
8366 * [I] infoPtr : valid pointer to the listview structure
8367 * [I] wKey : key flag
8368 * [I] x,y : mouse coordinate
8373 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8375 LVHITTESTINFO htInfo;
8377 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8379 /* Cancel the item edition if any */
8380 if (infoPtr->itemEdit.fEnabled)
8382 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8383 infoPtr->itemEdit.fEnabled = FALSE;
8386 /* send NM_RELEASEDCAPTURE notification */
8387 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8392 /* send NM_DBLCLK notification */
8393 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8394 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8396 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8397 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8404 * Processes mouse down messages (left mouse button).
8407 * infoPtr [I ] valid pointer to the listview structure
8408 * wKey [I ] key flag
8409 * x,y [I ] mouse coordinate
8414 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8416 LVHITTESTINFO lvHitTestInfo;
8417 static BOOL bGroupSelect = TRUE;
8418 BOOL bReceivedFocus = FALSE;
8419 POINT pt = { x, y };
8422 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8424 /* send NM_RELEASEDCAPTURE notification */
8425 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8427 if (!infoPtr->bFocus)
8429 bReceivedFocus = TRUE;
8430 SetFocus(infoPtr->hwndSelf);
8433 /* set left button down flag and record the click position */
8434 infoPtr->bLButtonDown = TRUE;
8435 infoPtr->ptClickPos = pt;
8437 lvHitTestInfo.pt.x = x;
8438 lvHitTestInfo.pt.y = y;
8440 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8441 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8442 infoPtr->nEditLabelItem = -1;
8443 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8445 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8447 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8448 if(state == 1 || state == 2)
8452 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8453 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8454 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8459 if (infoPtr->dwStyle & LVS_SINGLESEL)
8461 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8462 infoPtr->nEditLabelItem = nItem;
8464 LISTVIEW_SetSelection(infoPtr, nItem);
8468 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8472 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8473 LISTVIEW_SetItemFocus(infoPtr, nItem);
8474 infoPtr->nSelectionMark = nItem;
8480 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8481 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8483 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8484 infoPtr->nSelectionMark = nItem;
8487 else if (wKey & MK_CONTROL)
8491 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8493 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8494 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8495 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8496 infoPtr->nSelectionMark = nItem;
8498 else if (wKey & MK_SHIFT)
8500 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8504 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8505 infoPtr->nEditLabelItem = nItem;
8507 /* set selection (clears other pre-existing selections) */
8508 LISTVIEW_SetSelection(infoPtr, nItem);
8514 /* remove all selections */
8515 LISTVIEW_DeselectAll(infoPtr);
8520 infoPtr->nEditLabelItem = -1;
8527 * Processes mouse up messages (left mouse button).
8530 * infoPtr [I ] valid pointer to the listview structure
8531 * wKey [I ] key flag
8532 * x,y [I ] mouse coordinate
8537 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8539 LVHITTESTINFO lvHitTestInfo;
8541 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8543 if (!infoPtr->bLButtonDown) return 0;
8545 lvHitTestInfo.pt.x = x;
8546 lvHitTestInfo.pt.y = y;
8548 /* send NM_CLICK notification */
8549 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8550 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8552 /* set left button flag */
8553 infoPtr->bLButtonDown = FALSE;
8555 /* if we clicked on a selected item, edit the label */
8556 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8558 /* we want to make sure the user doesn't want to do a double click. So we will
8559 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8561 infoPtr->itemEdit.fEnabled = TRUE;
8562 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8563 SetTimer(infoPtr->hwndSelf,
8564 (UINT_PTR)&infoPtr->itemEdit,
8565 GetDoubleClickTime(),
8566 LISTVIEW_DelayedEditItem);
8574 * Destroys the listview control (called after WM_DESTROY).
8577 * [I] infoPtr : valid pointer to the listview structure
8582 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8586 /* delete all items */
8587 LISTVIEW_DeleteAllItems(infoPtr);
8589 /* destroy data structure */
8590 DPA_Destroy(infoPtr->hdpaItems);
8591 DPA_Destroy(infoPtr->hdpaPosX);
8592 DPA_Destroy(infoPtr->hdpaPosY);
8593 DPA_Destroy(infoPtr->hdpaColumns);
8594 ranges_destroy(infoPtr->selectionRanges);
8596 /* destroy image lists */
8597 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8599 if (infoPtr->himlNormal)
8600 ImageList_Destroy(infoPtr->himlNormal);
8601 if (infoPtr->himlSmall)
8602 ImageList_Destroy(infoPtr->himlSmall);
8603 if (infoPtr->himlState)
8604 ImageList_Destroy(infoPtr->himlState);
8607 /* destroy font, bkgnd brush */
8609 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8610 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8612 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8614 /* free listview info pointer*/
8622 * Handles notifications from header.
8625 * [I] infoPtr : valid pointer to the listview structure
8626 * [I] nCtrlId : control identifier
8627 * [I] lpnmh : notification information
8632 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8634 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8635 HWND hwndSelf = infoPtr->hwndSelf;
8637 TRACE("(lpnmh=%p)\n", lpnmh);
8639 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8641 switch (lpnmh->hdr.code)
8646 COLUMN_INFO *lpColumnInfo;
8650 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8653 /* remove the old line (if any) */
8654 LISTVIEW_DrawTrackLine(infoPtr);
8656 /* compute & draw the new line */
8657 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8658 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8659 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8660 infoPtr->xTrackLine = x + ptOrigin.x;
8661 LISTVIEW_DrawTrackLine(infoPtr);
8667 /* remove the track line (if any) */
8668 LISTVIEW_DrawTrackLine(infoPtr);
8669 infoPtr->xTrackLine = -1;
8673 FIXME("Changing column order not implemented\n");
8676 case HDN_ITEMCHANGINGW:
8677 case HDN_ITEMCHANGINGA:
8678 return notify_forward_header(infoPtr, lpnmh);
8680 case HDN_ITEMCHANGEDW:
8681 case HDN_ITEMCHANGEDA:
8683 COLUMN_INFO *lpColumnInfo;
8686 notify_forward_header(infoPtr, lpnmh);
8687 if (!IsWindow(hwndSelf))
8690 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8694 hdi.mask = HDI_WIDTH;
8695 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8699 cxy = lpnmh->pitem->cxy;
8701 /* determine how much we change since the last know position */
8702 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8703 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8706 lpColumnInfo->rcHeader.right += dx;
8707 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
8708 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8711 /* only needs to update the scrolls */
8712 infoPtr->nItemWidth += dx;
8713 LISTVIEW_UpdateScroll(infoPtr);
8715 LISTVIEW_UpdateItemSize(infoPtr);
8716 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8719 RECT rcCol = lpColumnInfo->rcHeader;
8721 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8722 OffsetRect(&rcCol, ptOrigin.x, 0);
8724 rcCol.top = infoPtr->rcList.top;
8725 rcCol.bottom = infoPtr->rcList.bottom;
8727 /* resizing left-aligned columns leaves most of the left side untouched */
8728 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8730 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
8733 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8736 /* when shrinking the last column clear the now unused field */
8737 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1 && dx < 0)
8740 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8746 case HDN_ITEMCLICKW:
8747 case HDN_ITEMCLICKA:
8749 /* Handle sorting by Header Column */
8752 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8754 nmlv.iSubItem = lpnmh->iItem;
8755 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8759 case HDN_DIVIDERDBLCLICKW:
8760 case HDN_DIVIDERDBLCLICKA:
8761 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8770 * Paint non-client area of control.
8773 * [I] infoPtr : valid pointer to the listview structureof the sender
8774 * [I] region : update region
8777 * TRUE - frame was painted
8778 * FALSE - call default window proc
8780 static BOOL LISTVIEW_NCPaint(LISTVIEW_INFO *infoPtr, HRGN region)
8782 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8786 int cxEdge = GetSystemMetrics (SM_CXEDGE),
8787 cyEdge = GetSystemMetrics (SM_CYEDGE);
8789 if (!theme) return FALSE;
8791 GetWindowRect(infoPtr->hwndSelf, &r);
8793 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8794 r.right - cxEdge, r.bottom - cyEdge);
8795 if (region != (HRGN)1)
8796 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8797 OffsetRect(&r, -r.left, -r.top);
8799 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8800 OffsetRect(&r, -r.left, -r.top);
8802 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8803 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8804 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8805 ReleaseDC(infoPtr->hwndSelf, dc);
8807 /* Call default proc to get the scrollbars etc. painted */
8808 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8815 * Determines the type of structure to use.
8818 * [I] infoPtr : valid pointer to the listview structureof the sender
8819 * [I] hwndFrom : listview window handle
8820 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8825 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8827 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8829 if (nCommand != NF_REQUERY) return 0;
8831 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8838 * Paints/Repaints the listview control.
8841 * [I] infoPtr : valid pointer to the listview structure
8842 * [I] hdc : device context handle
8847 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8849 TRACE("(hdc=%p)\n", hdc);
8851 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8853 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8855 infoPtr->bNoItemMetrics = FALSE;
8856 LISTVIEW_UpdateItemSize(infoPtr);
8857 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8858 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8859 LISTVIEW_UpdateScroll(infoPtr);
8862 UpdateWindow(infoPtr->hwndHeader);
8865 LISTVIEW_Refresh(infoPtr, hdc, NULL);
8870 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8872 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
8873 EndPaint(infoPtr->hwndSelf, &ps);
8882 * Paints/Repaints the listview control.
8885 * [I] infoPtr : valid pointer to the listview structure
8886 * [I] hdc : device context handle
8887 * [I] options : drawing options
8892 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8894 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
8896 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8899 if (options & PRF_ERASEBKGND)
8900 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8902 if (options & PRF_CLIENT)
8903 LISTVIEW_Paint(infoPtr, hdc);
8911 * Processes double click messages (right mouse button).
8914 * [I] infoPtr : valid pointer to the listview structure
8915 * [I] wKey : key flag
8916 * [I] x,y : mouse coordinate
8921 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8923 LVHITTESTINFO lvHitTestInfo;
8925 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8927 /* send NM_RELEASEDCAPTURE notification */
8928 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8930 /* send NM_RDBLCLK notification */
8931 lvHitTestInfo.pt.x = x;
8932 lvHitTestInfo.pt.y = y;
8933 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8934 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8941 * Processes mouse down messages (right mouse button).
8944 * [I] infoPtr : valid pointer to the listview structure
8945 * [I] wKey : key flag
8946 * [I] x,y : mouse coordinate
8951 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8953 LVHITTESTINFO lvHitTestInfo;
8956 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8958 /* send NM_RELEASEDCAPTURE notification */
8959 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8961 /* make sure the listview control window has the focus */
8962 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8964 /* set right button down flag */
8965 infoPtr->bRButtonDown = TRUE;
8967 /* determine the index of the selected item */
8968 lvHitTestInfo.pt.x = x;
8969 lvHitTestInfo.pt.y = y;
8970 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8972 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8974 LISTVIEW_SetItemFocus(infoPtr, nItem);
8975 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8976 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8977 LISTVIEW_SetSelection(infoPtr, nItem);
8981 LISTVIEW_DeselectAll(infoPtr);
8989 * Processes mouse up messages (right mouse button).
8992 * [I] infoPtr : valid pointer to the listview structure
8993 * [I] wKey : key flag
8994 * [I] x,y : mouse coordinate
8999 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9001 LVHITTESTINFO lvHitTestInfo;
9004 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9006 if (!infoPtr->bRButtonDown) return 0;
9008 /* set button flag */
9009 infoPtr->bRButtonDown = FALSE;
9011 /* Send NM_RClICK notification */
9012 lvHitTestInfo.pt.x = x;
9013 lvHitTestInfo.pt.y = y;
9014 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9015 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9017 /* Change to screen coordinate for WM_CONTEXTMENU */
9018 pt = lvHitTestInfo.pt;
9019 ClientToScreen(infoPtr->hwndSelf, &pt);
9021 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9022 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9023 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9034 * [I] infoPtr : valid pointer to the listview structure
9035 * [I] hwnd : window handle of window containing the cursor
9036 * [I] nHittest : hit-test code
9037 * [I] wMouseMsg : ideintifier of the mouse message
9040 * TRUE if cursor is set
9043 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9045 LVHITTESTINFO lvHitTestInfo;
9047 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
9049 if(!infoPtr->hHotCursor) return FALSE;
9051 GetCursorPos(&lvHitTestInfo.pt);
9052 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9054 SetCursor(infoPtr->hHotCursor);
9064 * [I] infoPtr : valid pointer to the listview structure
9065 * [I] hwndLoseFocus : handle of previously focused window
9070 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9072 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9074 /* if we have the focus already, there's nothing to do */
9075 if (infoPtr->bFocus) return 0;
9077 /* send NM_SETFOCUS notification */
9078 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9080 /* set window focus flag */
9081 infoPtr->bFocus = TRUE;
9083 /* put the focus rect back on */
9084 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9086 /* redraw all visible selected items */
9087 LISTVIEW_InvalidateSelectedItems(infoPtr);
9097 * [I] infoPtr : valid pointer to the listview structure
9098 * [I] fRedraw : font handle
9099 * [I] fRedraw : redraw flag
9104 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9106 HFONT oldFont = infoPtr->hFont;
9108 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9110 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9111 if (infoPtr->hFont == oldFont) return 0;
9113 LISTVIEW_SaveTextMetrics(infoPtr);
9115 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9116 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9118 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9125 * Message handling for WM_SETREDRAW.
9126 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9129 * [I] infoPtr : valid pointer to the listview structure
9130 * [I] bRedraw: state of redraw flag
9133 * DefWinProc return value
9135 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9137 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9139 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9140 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9142 infoPtr->bRedraw = bRedraw;
9144 if(!bRedraw) return 0;
9146 if (is_autoarrange(infoPtr))
9147 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9148 LISTVIEW_UpdateScroll(infoPtr);
9150 /* despite what the WM_SETREDRAW docs says, apps expect us
9151 * to invalidate the listview here... stupid! */
9152 LISTVIEW_InvalidateList(infoPtr);
9159 * Resizes the listview control. This function processes WM_SIZE
9160 * messages. At this time, the width and height are not used.
9163 * [I] infoPtr : valid pointer to the listview structure
9164 * [I] Width : new width
9165 * [I] Height : new height
9170 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9172 RECT rcOld = infoPtr->rcList;
9174 TRACE("(width=%d, height=%d)\n", Width, Height);
9176 LISTVIEW_UpdateSize(infoPtr);
9177 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9179 /* do not bother with display related stuff if we're not redrawing */
9180 if (!is_redrawing(infoPtr)) return 0;
9182 if (is_autoarrange(infoPtr))
9183 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9185 LISTVIEW_UpdateScroll(infoPtr);
9187 /* refresh all only for lists whose height changed significantly */
9188 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
9189 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9190 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9191 LISTVIEW_InvalidateList(infoPtr);
9198 * Sets the size information.
9201 * [I] infoPtr : valid pointer to the listview structure
9206 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9208 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9210 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9212 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9214 if (uView == LVS_LIST)
9216 /* Apparently the "LIST" style is supposed to have the same
9217 * number of items in a column even if there is no scroll bar.
9218 * Since if a scroll bar already exists then the bottom is already
9219 * reduced, only reduce if the scroll bar does not currently exist.
9220 * The "2" is there to mimic the native control. I think it may be
9221 * related to either padding or edges. (GLA 7/2002)
9223 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9224 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9225 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9227 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
9232 hl.prc = &infoPtr->rcList;
9234 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9236 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9238 infoPtr->rcList.top = max(wp.cy, 0);
9241 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9246 * Processes WM_STYLECHANGED messages.
9249 * [I] infoPtr : valid pointer to the listview structure
9250 * [I] wStyleType : window style type (normal or extended)
9251 * [I] lpss : window style information
9256 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9257 const STYLESTRUCT *lpss)
9259 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9260 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9262 TRACE("(styletype=%x, styleOld=0x%08x, styleNew=0x%08x)\n",
9263 wStyleType, lpss->styleOld, lpss->styleNew);
9265 if (wStyleType != GWL_STYLE) return 0;
9267 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9268 /* what if LVS_OWNERDATA changed? */
9269 /* or LVS_SINGLESEL */
9270 /* or LVS_SORT{AS,DES}CENDING */
9272 infoPtr->dwStyle = lpss->styleNew;
9274 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9275 ((lpss->styleNew & WS_HSCROLL) == 0))
9276 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9278 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9279 ((lpss->styleNew & WS_VSCROLL) == 0))
9280 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9282 if (uNewView != uOldView)
9284 SIZE oldIconSize = infoPtr->iconSize;
9287 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9288 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9290 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9291 SetRectEmpty(&infoPtr->rcFocus);
9293 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9294 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9296 if (uNewView == LVS_ICON)
9298 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9300 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9301 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9302 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9305 else if (uNewView == LVS_REPORT)
9310 hl.prc = &infoPtr->rcList;
9312 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9313 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9316 LISTVIEW_UpdateItemSize(infoPtr);
9319 if (uNewView == LVS_REPORT)
9320 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
9322 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9323 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9324 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9326 /* update the size of the client area */
9327 LISTVIEW_UpdateSize(infoPtr);
9329 /* add scrollbars if needed */
9330 LISTVIEW_UpdateScroll(infoPtr);
9332 /* invalidate client area + erase background */
9333 LISTVIEW_InvalidateList(infoPtr);
9340 * Window procedure of the listview control.
9343 static LRESULT WINAPI
9344 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9346 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9348 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
9350 if (!infoPtr && (uMsg != WM_NCCREATE))
9351 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9355 case LVM_APPROXIMATEVIEWRECT:
9356 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9357 LOWORD(lParam), HIWORD(lParam));
9359 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9361 /* case LVM_CANCELEDITLABEL: */
9363 case LVM_CREATEDRAGIMAGE:
9364 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9366 case LVM_DELETEALLITEMS:
9367 return LISTVIEW_DeleteAllItems(infoPtr);
9369 case LVM_DELETECOLUMN:
9370 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9372 case LVM_DELETEITEM:
9373 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9375 case LVM_EDITLABELW:
9376 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9378 case LVM_EDITLABELA:
9379 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9381 /* case LVM_ENABLEGROUPVIEW: */
9383 case LVM_ENSUREVISIBLE:
9384 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9387 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9390 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9392 case LVM_GETBKCOLOR:
9393 return infoPtr->clrBk;
9395 /* case LVM_GETBKIMAGE: */
9397 case LVM_GETCALLBACKMASK:
9398 return infoPtr->uCallbackMask;
9400 case LVM_GETCOLUMNA:
9401 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9403 case LVM_GETCOLUMNW:
9404 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9406 case LVM_GETCOLUMNORDERARRAY:
9407 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9409 case LVM_GETCOLUMNWIDTH:
9410 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9412 case LVM_GETCOUNTPERPAGE:
9413 return LISTVIEW_GetCountPerPage(infoPtr);
9415 case LVM_GETEDITCONTROL:
9416 return (LRESULT)infoPtr->hwndEdit;
9418 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9419 return infoPtr->dwLvExStyle;
9421 /* case LVM_GETGROUPINFO: */
9423 /* case LVM_GETGROUPMETRICS: */
9426 return (LRESULT)infoPtr->hwndHeader;
9428 case LVM_GETHOTCURSOR:
9429 return (LRESULT)infoPtr->hHotCursor;
9431 case LVM_GETHOTITEM:
9432 return infoPtr->nHotItem;
9434 case LVM_GETHOVERTIME:
9435 return infoPtr->dwHoverTime;
9437 case LVM_GETIMAGELIST:
9438 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9440 /* case LVM_GETINSERTMARK: */
9442 /* case LVM_GETINSERTMARKCOLOR: */
9444 /* case LVM_GETINSERTMARKRECT: */
9446 case LVM_GETISEARCHSTRINGA:
9447 case LVM_GETISEARCHSTRINGW:
9448 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9452 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9455 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9457 case LVM_GETITEMCOUNT:
9458 return infoPtr->nItemCount;
9460 case LVM_GETITEMPOSITION:
9461 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9463 case LVM_GETITEMRECT:
9464 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9466 case LVM_GETITEMSPACING:
9467 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9469 case LVM_GETITEMSTATE:
9470 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9472 case LVM_GETITEMTEXTA:
9473 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9475 case LVM_GETITEMTEXTW:
9476 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9478 case LVM_GETNEXTITEM:
9479 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9481 case LVM_GETNUMBEROFWORKAREAS:
9482 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9486 if (!lParam) return FALSE;
9487 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9490 /* case LVM_GETOUTLINECOLOR: */
9492 /* case LVM_GETSELECTEDCOLUMN: */
9494 case LVM_GETSELECTEDCOUNT:
9495 return LISTVIEW_GetSelectedCount(infoPtr);
9497 case LVM_GETSELECTIONMARK:
9498 return infoPtr->nSelectionMark;
9500 case LVM_GETSTRINGWIDTHA:
9501 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9503 case LVM_GETSTRINGWIDTHW:
9504 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9506 case LVM_GETSUBITEMRECT:
9507 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9509 case LVM_GETTEXTBKCOLOR:
9510 return infoPtr->clrTextBk;
9512 case LVM_GETTEXTCOLOR:
9513 return infoPtr->clrText;
9515 /* case LVM_GETTILEINFO: */
9517 /* case LVM_GETTILEVIEWINFO: */
9519 case LVM_GETTOOLTIPS:
9520 if( !infoPtr->hwndToolTip )
9521 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9522 return (LRESULT)infoPtr->hwndToolTip;
9524 case LVM_GETTOPINDEX:
9525 return LISTVIEW_GetTopIndex(infoPtr);
9527 /*case LVM_GETUNICODEFORMAT:
9528 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9531 /* case LVM_GETVIEW: */
9533 case LVM_GETVIEWRECT:
9534 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9536 case LVM_GETWORKAREAS:
9537 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9540 /* case LVM_HASGROUP: */
9543 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9545 case LVM_INSERTCOLUMNA:
9546 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9548 case LVM_INSERTCOLUMNW:
9549 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9551 /* case LVM_INSERTGROUP: */
9553 /* case LVM_INSERTGROUPSORTED: */
9555 case LVM_INSERTITEMA:
9556 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9558 case LVM_INSERTITEMW:
9559 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9561 /* case LVM_INSERTMARKHITTEST: */
9563 /* case LVM_ISGROUPVIEWENABLED: */
9565 /* case LVM_MAPIDTOINDEX: */
9567 /* case LVM_MAPINDEXTOID: */
9569 /* case LVM_MOVEGROUP: */
9571 /* case LVM_MOVEITEMTOGROUP: */
9573 case LVM_REDRAWITEMS:
9574 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9576 /* case LVM_REMOVEALLGROUPS: */
9578 /* case LVM_REMOVEGROUP: */
9581 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9583 case LVM_SETBKCOLOR:
9584 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9586 /* case LVM_SETBKIMAGE: */
9588 case LVM_SETCALLBACKMASK:
9589 infoPtr->uCallbackMask = (UINT)wParam;
9592 case LVM_SETCOLUMNA:
9593 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9595 case LVM_SETCOLUMNW:
9596 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9598 case LVM_SETCOLUMNORDERARRAY:
9599 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9601 case LVM_SETCOLUMNWIDTH:
9602 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9604 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9605 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9607 /* case LVM_SETGROUPINFO: */
9609 /* case LVM_SETGROUPMETRICS: */
9611 case LVM_SETHOTCURSOR:
9612 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9614 case LVM_SETHOTITEM:
9615 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9617 case LVM_SETHOVERTIME:
9618 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9620 case LVM_SETICONSPACING:
9621 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9623 case LVM_SETIMAGELIST:
9624 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9626 /* case LVM_SETINFOTIP: */
9628 /* case LVM_SETINSERTMARK: */
9630 /* case LVM_SETINSERTMARKCOLOR: */
9633 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9636 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9638 case LVM_SETITEMCOUNT:
9639 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9641 case LVM_SETITEMPOSITION:
9644 pt.x = (short)LOWORD(lParam);
9645 pt.y = (short)HIWORD(lParam);
9646 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9649 case LVM_SETITEMPOSITION32:
9650 if (lParam == 0) return FALSE;
9651 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9653 case LVM_SETITEMSTATE:
9654 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9656 case LVM_SETITEMTEXTA:
9657 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9659 case LVM_SETITEMTEXTW:
9660 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9662 /* case LVM_SETOUTLINECOLOR: */
9664 /* case LVM_SETSELECTEDCOLUMN: */
9666 case LVM_SETSELECTIONMARK:
9667 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9669 case LVM_SETTEXTBKCOLOR:
9670 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9672 case LVM_SETTEXTCOLOR:
9673 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9675 /* case LVM_SETTILEINFO: */
9677 /* case LVM_SETTILEVIEWINFO: */
9679 /* case LVM_SETTILEWIDTH: */
9681 case LVM_SETTOOLTIPS:
9682 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9684 case LVM_SETUNICODEFORMAT:
9685 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
9687 /* case LVM_SETVIEW: */
9689 /* case LVM_SETWORKAREAS: */
9691 /* case LVM_SORTGROUPS: */
9694 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9696 /* LVM_SORTITEMSEX: */
9698 case LVM_SUBITEMHITTEST:
9699 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9702 return LISTVIEW_Update(infoPtr, (INT)wParam);
9705 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9708 return LISTVIEW_Command(infoPtr, wParam, lParam);
9711 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
9714 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9717 return LISTVIEW_Destroy(infoPtr);
9720 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9723 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9726 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9729 return (LRESULT)infoPtr->hFont;
9732 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9735 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9738 return LISTVIEW_KillFocus(infoPtr);
9740 case WM_LBUTTONDBLCLK:
9741 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9743 case WM_LBUTTONDOWN:
9744 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9747 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9750 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9753 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9756 return LISTVIEW_NCDestroy(infoPtr);
9759 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9764 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9765 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9768 case WM_NOTIFYFORMAT:
9769 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9771 case WM_PRINTCLIENT:
9772 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9775 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9777 case WM_RBUTTONDBLCLK:
9778 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9780 case WM_RBUTTONDOWN:
9781 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9784 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9787 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9792 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9795 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9798 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9801 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9803 case WM_STYLECHANGED:
9804 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9806 case WM_SYSCOLORCHANGE:
9807 COMCTL32_RefreshSysColors();
9810 /* case WM_TIMER: */
9811 case WM_THEMECHANGED:
9812 return LISTVIEW_ThemeChanged(infoPtr);
9815 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9818 if (wParam & (MK_SHIFT | MK_CONTROL))
9819 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9820 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9822 case WM_WINDOWPOSCHANGED:
9823 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9825 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9826 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9827 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9829 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9831 MEASUREITEMSTRUCT mis;
9832 mis.CtlType = ODT_LISTVIEW;
9833 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9837 mis.itemHeight= infoPtr->nItemHeight;
9838 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9839 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9840 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9843 LISTVIEW_UpdateSize(infoPtr);
9844 LISTVIEW_UpdateScroll(infoPtr);
9846 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9848 /* case WM_WININICHANGE: */
9851 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9852 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9855 /* call default window procedure */
9856 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9863 * Registers the window class.
9871 void LISTVIEW_Register(void)
9875 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9876 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9877 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9878 wndClass.cbClsExtra = 0;
9879 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9880 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9881 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9882 wndClass.lpszClassName = WC_LISTVIEWW;
9883 RegisterClassW(&wndClass);
9888 * Unregisters the window class.
9896 void LISTVIEW_Unregister(void)
9898 UnregisterClassW(WC_LISTVIEWW, NULL);
9903 * Handle any WM_COMMAND messages
9906 * [I] infoPtr : valid pointer to the listview structure
9907 * [I] wParam : the first message parameter
9908 * [I] lParam : the second message parameter
9913 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9915 switch (HIWORD(wParam))
9920 * Adjust the edit window size
9923 HDC hdc = GetDC(infoPtr->hwndEdit);
9924 HFONT hFont, hOldFont = 0;
9929 if (!infoPtr->hwndEdit || !hdc) return 0;
9930 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9931 GetWindowRect(infoPtr->hwndEdit, &rect);
9933 /* Select font to get the right dimension of the string */
9934 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9937 hOldFont = SelectObject(hdc, hFont);
9940 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9942 TEXTMETRICW textMetric;
9944 /* Add Extra spacing for the next character */
9945 GetTextMetricsW(hdc, &textMetric);
9946 sz.cx += (textMetric.tmMaxCharWidth * 2);
9954 rect.bottom - rect.top,
9955 SWP_DRAWFRAME|SWP_NOMOVE);
9958 SelectObject(hdc, hOldFont);
9960 ReleaseDC(infoPtr->hwndEdit, hdc);
9966 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9975 * Subclassed edit control windproc function
9978 * [I] hwnd : the edit window handle
9979 * [I] uMsg : the message that is to be processed
9980 * [I] wParam : first message parameter
9981 * [I] lParam : second message parameter
9982 * [I] isW : TRUE if input is Unicode
9987 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9989 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9990 BOOL cancel = FALSE;
9992 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9993 hwnd, uMsg, wParam, lParam, isW);
9998 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10005 WNDPROC editProc = infoPtr->EditWndProc;
10006 infoPtr->EditWndProc = 0;
10007 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10008 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10012 if (VK_ESCAPE == (INT)wParam)
10017 else if (VK_RETURN == (INT)wParam)
10021 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10024 /* kill the edit */
10025 if (infoPtr->hwndEdit)
10027 LPWSTR buffer = NULL;
10029 infoPtr->hwndEdit = 0;
10032 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10036 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10038 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10039 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10043 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10048 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10054 * Subclassed edit control Unicode windproc function
10057 * [I] hwnd : the edit window handle
10058 * [I] uMsg : the message that is to be processed
10059 * [I] wParam : first message parameter
10060 * [I] lParam : second message parameter
10064 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10066 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10071 * Subclassed edit control ANSI windproc function
10074 * [I] hwnd : the edit window handle
10075 * [I] uMsg : the message that is to be processed
10076 * [I] wParam : first message parameter
10077 * [I] lParam : second message parameter
10081 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10083 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10088 * Creates a subclassed edit cotrol
10091 * [I] infoPtr : valid pointer to the listview structure
10092 * [I] text : initial text for the edit
10093 * [I] style : the window style
10094 * [I] isW : TRUE if input is Unicode
10098 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10099 INT x, INT y, INT width, INT height, BOOL isW)
10101 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10106 TEXTMETRICW textMetric;
10107 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10109 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10111 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10112 hdc = GetDC(infoPtr->hwndSelf);
10114 /* Select the font to get appropriate metric dimensions */
10115 if(infoPtr->hFont != 0)
10116 hOldFont = SelectObject(hdc, infoPtr->hFont);
10118 /*Get String Length in pixels */
10119 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10121 /*Add Extra spacing for the next character */
10122 GetTextMetricsW(hdc, &textMetric);
10123 sz.cx += (textMetric.tmMaxCharWidth * 2);
10125 if(infoPtr->hFont != 0)
10126 SelectObject(hdc, hOldFont);
10128 ReleaseDC(infoPtr->hwndSelf, hdc);
10130 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10132 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10134 if (!hedit) return 0;
10136 infoPtr->EditWndProc = (WNDPROC)
10137 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10138 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10140 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);