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(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
415 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
416 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
417 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
418 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
419 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *, INT);
420 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
421 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
422 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
423 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LVITEMW *, BOOL);
424 static void LISTVIEW_UpdateScroll(const 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(const LISTVIEW_INFO *, WPARAM, LPARAM);
429 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
430 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
431 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
432 static UINT LISTVIEW_GetItemState(const 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(const 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(const 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, LPCWSTR 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(const 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(const 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(const 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(const LISTVIEW_INFO *infoPtr, const 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(const 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(const LISTVIEW_INFO *infoPtr, INT code, const 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(const 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(const 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, const 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 (const 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 (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
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 */
957 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
961 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
962 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
964 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
966 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
967 lpnmlvcd->clrText = comctl32_color.clrBtnText;
972 /* Set the text attributes */
973 if (lpnmlvcd->clrTextBk != CLR_NONE)
975 SetBkMode(hdc, OPAQUE);
976 SetBkColor(hdc,lpnmlvcd->clrTextBk);
979 SetBkMode(hdc, TRANSPARENT);
980 SetTextColor(hdc, lpnmlvcd->clrText);
983 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
985 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
988 /******** Item iterator functions **********************************/
990 static RANGES ranges_create(int count);
991 static void ranges_destroy(RANGES ranges);
992 static BOOL ranges_add(RANGES ranges, RANGE range);
993 static BOOL ranges_del(RANGES ranges, RANGE range);
994 static void ranges_dump(RANGES ranges);
996 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
998 RANGE range = { nItem, nItem + 1 };
1000 return ranges_add(ranges, range);
1003 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1005 RANGE range = { nItem, nItem + 1 };
1007 return ranges_del(ranges, range);
1011 * ITERATOR DOCUMENTATION
1013 * The iterator functions allow for easy, and convenient iteration
1014 * over items of iterest in the list. Typically, you create a
1015 * iterator, use it, and destroy it, as such:
1018 * iterator_xxxitems(&i, ...);
1019 * while (iterator_{prev,next}(&i)
1021 * //code which uses i.nItem
1023 * iterator_destroy(&i);
1025 * where xxx is either: framed, or visible.
1026 * Note that it is important that the code destroys the iterator
1027 * after it's done with it, as the creation of the iterator may
1028 * allocate memory, which thus needs to be freed.
1030 * You can iterate both forwards, and backwards through the list,
1031 * by using iterator_next or iterator_prev respectively.
1033 * Lower numbered items are draw on top of higher number items in
1034 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1035 * items may overlap). So, to test items, you should use
1037 * which lists the items top to bottom (in Z-order).
1038 * For drawing items, you should use
1040 * which lists the items bottom to top (in Z-order).
1041 * If you keep iterating over the items after the end-of-items
1042 * marker (-1) is returned, the iterator will start from the
1043 * beginning. Typically, you don't need to test for -1,
1044 * because iterator_{next,prev} will return TRUE if more items
1045 * are to be iterated over, or FALSE otherwise.
1047 * Note: the iterator is defined to be bidirectional. That is,
1048 * any number of prev followed by any number of next, or
1049 * five versa, should leave the iterator at the same item:
1050 * prev * n, next * n = next * n, prev * n
1052 * The iterator has a notion of an out-of-order, special item,
1053 * which sits at the start of the list. This is used in
1054 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1055 * which needs to be first, as it may overlap other items.
1057 * The code is a bit messy because we have:
1058 * - a special item to deal with
1059 * - simple range, or composite range
1061 * If you find bugs, or want to add features, please make sure you
1062 * always check/modify *both* iterator_prev, and iterator_next.
1066 * This function iterates through the items in increasing order,
1067 * but prefixed by the special item, then -1. That is:
1068 * special, 1, 2, 3, ..., n, -1.
1069 * Each item is listed only once.
1071 static inline BOOL iterator_next(ITERATOR* i)
1075 i->nItem = i->nSpecial;
1076 if (i->nItem != -1) return TRUE;
1078 if (i->nItem == i->nSpecial)
1080 if (i->ranges) i->index = 0;
1086 if (i->nItem == i->nSpecial) i->nItem++;
1087 if (i->nItem < i->range.upper) return TRUE;
1092 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1093 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1096 else if (i->nItem >= i->range.upper) goto end;
1098 i->nItem = i->range.lower;
1099 if (i->nItem >= 0) goto testitem;
1106 * This function iterates through the items in decreasing order,
1107 * followed by the special item, then -1. That is:
1108 * n, n-1, ..., 3, 2, 1, special, -1.
1109 * Each item is listed only once.
1111 static inline BOOL iterator_prev(ITERATOR* i)
1118 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1121 if (i->nItem == i->nSpecial)
1129 if (i->nItem == i->nSpecial) i->nItem--;
1130 if (i->nItem >= i->range.lower) return TRUE;
1136 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1139 else if (!start && i->nItem < i->range.lower) goto end;
1141 i->nItem = i->range.upper;
1142 if (i->nItem > 0) goto testitem;
1144 return (i->nItem = i->nSpecial) != -1;
1147 static RANGE iterator_range(const ITERATOR *i)
1151 if (!i->ranges) return i->range;
1153 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1155 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1156 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1158 else range.lower = range.upper = 0;
1164 * Releases resources associated with this ierator.
1166 static inline void iterator_destroy(const ITERATOR *i)
1168 ranges_destroy(i->ranges);
1172 * Create an empty iterator.
1174 static inline BOOL iterator_empty(ITERATOR* i)
1176 ZeroMemory(i, sizeof(*i));
1177 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1182 * Create an iterator over a range.
1184 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1192 * Create an iterator over a bunch of ranges.
1193 * Please note that the iterator will take ownership of the ranges,
1194 * and will free them upon destruction.
1196 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1204 * Creates an iterator over the items which intersect lprc.
1206 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1208 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1209 RECT frame = *lprc, rcItem, rcTemp;
1212 /* in case we fail, we want to return an empty iterator */
1213 if (!iterator_empty(i)) return FALSE;
1215 LISTVIEW_GetOrigin(infoPtr, &Origin);
1217 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1218 OffsetRect(&frame, -Origin.x, -Origin.y);
1220 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1224 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1226 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1227 if (IntersectRect(&rcTemp, &rcItem, lprc))
1228 i->nSpecial = infoPtr->nFocusedItem;
1230 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1231 /* to do better here, we need to have PosX, and PosY sorted */
1232 TRACE("building icon ranges:\n");
1233 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1235 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1236 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1237 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1238 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1239 if (IntersectRect(&rcTemp, &rcItem, &frame))
1240 ranges_additem(i->ranges, nItem);
1244 else if (uView == LVS_REPORT)
1248 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1249 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1251 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1252 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1253 if (range.upper <= range.lower) return TRUE;
1254 if (!iterator_rangeitems(i, range)) return FALSE;
1255 TRACE(" report=%s\n", debugrange(&i->range));
1259 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1260 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1261 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1262 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1263 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1264 INT lower = nFirstCol * nPerCol + nFirstRow;
1268 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1269 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1271 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1273 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1274 TRACE("building list ranges:\n");
1275 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1277 item_range.lower = nCol * nPerCol + nFirstRow;
1278 if(item_range.lower >= infoPtr->nItemCount) break;
1279 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1280 TRACE(" list=%s\n", debugrange(&item_range));
1281 ranges_add(i->ranges, item_range);
1289 * Creates an iterator over the items which intersect the visible region of hdc.
1291 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1293 POINT Origin, Position;
1294 RECT rcItem, rcClip;
1297 rgntype = GetClipBox(hdc, &rcClip);
1298 if (rgntype == NULLREGION) return iterator_empty(i);
1299 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1300 if (rgntype == SIMPLEREGION) return TRUE;
1302 /* first deal with the special item */
1303 if (i->nSpecial != -1)
1305 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1306 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1309 /* if we can't deal with the region, we'll just go with the simple range */
1310 LISTVIEW_GetOrigin(infoPtr, &Origin);
1311 TRACE("building visible range:\n");
1312 if (!i->ranges && i->range.lower < i->range.upper)
1314 if (!(i->ranges = ranges_create(50))) return TRUE;
1315 if (!ranges_add(i->ranges, i->range))
1317 ranges_destroy(i->ranges);
1323 /* now delete the invisible items from the list */
1324 while(iterator_next(i))
1326 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1327 rcItem.left = Position.x + Origin.x;
1328 rcItem.top = Position.y + Origin.y;
1329 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1330 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1331 if (!RectVisible(hdc, &rcItem))
1332 ranges_delitem(i->ranges, i->nItem);
1334 /* the iterator should restart on the next iterator_next */
1340 /******** Misc helper functions ************************************/
1342 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1343 WPARAM wParam, LPARAM lParam, BOOL isW)
1345 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1346 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1349 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1351 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1353 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1354 (uView == LVS_ICON || uView == LVS_SMALLICON);
1357 /******** Internal API functions ************************************/
1359 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1361 static COLUMN_INFO mainItem;
1363 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1364 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1365 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1368 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1370 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1373 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1375 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1378 /* Listview invalidation functions: use _only_ these functions to invalidate */
1380 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1382 return infoPtr->bRedraw;
1385 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1387 if(!is_redrawing(infoPtr)) return;
1388 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1389 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1392 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1396 if(!is_redrawing(infoPtr)) return;
1397 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1398 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1401 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1403 POINT Origin, Position;
1406 if(!is_redrawing(infoPtr)) return;
1407 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1408 LISTVIEW_GetOrigin(infoPtr, &Origin);
1409 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1410 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1412 rcBox.bottom = infoPtr->nItemHeight;
1413 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1414 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1417 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1419 LISTVIEW_InvalidateRect(infoPtr, NULL);
1422 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1426 if(!is_redrawing(infoPtr)) return;
1427 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1428 rcCol.top = infoPtr->rcList.top;
1429 rcCol.bottom = infoPtr->rcList.bottom;
1430 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1435 * Retrieves the number of items that can fit vertically in the client area.
1438 * [I] infoPtr : valid pointer to the listview structure
1441 * Number of items per row.
1443 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1445 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1447 return max(nListWidth/infoPtr->nItemWidth, 1);
1452 * Retrieves the number of items that can fit horizontally in the client
1456 * [I] infoPtr : valid pointer to the listview structure
1459 * Number of items per column.
1461 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1463 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1465 return max(nListHeight / infoPtr->nItemHeight, 1);
1469 /*************************************************************************
1470 * LISTVIEW_ProcessLetterKeys
1472 * Processes keyboard messages generated by pressing the letter keys
1474 * What this does is perform a case insensitive search from the
1475 * current position with the following quirks:
1476 * - If two chars or more are pressed in quick succession we search
1477 * for the corresponding string (e.g. 'abc').
1478 * - If there is a delay we wipe away the current search string and
1479 * restart with just that char.
1480 * - If the user keeps pressing the same character, whether slowly or
1481 * fast, so that the search string is entirely composed of this
1482 * character ('aaaaa' for instance), then we search for first item
1483 * that starting with that character.
1484 * - If the user types the above character in quick succession, then
1485 * we must also search for the corresponding string ('aaaaa'), and
1486 * go to that string if there is a match.
1489 * [I] hwnd : handle to the window
1490 * [I] charCode : the character code, the actual character
1491 * [I] keyData : key data
1499 * - The current implementation has a list of characters it will
1500 * accept and it ignores averything else. In particular it will
1501 * ignore accentuated characters which seems to match what
1502 * Windows does. But I'm not sure it makes sense to follow
1504 * - We don't sound a beep when the search fails.
1508 * TREEVIEW_ProcessLetterKeys
1510 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1515 WCHAR buffer[MAX_PATH];
1516 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1518 /* simple parameter checking */
1519 if (!charCode || !keyData) return 0;
1521 /* only allow the valid WM_CHARs through */
1522 if (!isalnum(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 != ']' &&
1529 charCode != '}' && charCode != '[' && charCode != '{' &&
1530 charCode != '/' && charCode != '?' && charCode != '>' &&
1531 charCode != '<' && charCode != ',' && charCode != '~')
1534 /* if there's one item or less, there is no where to go */
1535 if (infoPtr->nItemCount <= 1) return 0;
1537 /* update the search parameters */
1538 infoPtr->lastKeyPressTimestamp = GetTickCount();
1539 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1540 if (infoPtr->nSearchParamLength < MAX_PATH)
1541 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1542 if (infoPtr->charCode != charCode)
1543 infoPtr->charCode = charCode = 0;
1545 infoPtr->charCode=charCode;
1546 infoPtr->szSearchParam[0]=charCode;
1547 infoPtr->nSearchParamLength=1;
1548 /* Redundant with the 1 char string */
1552 /* and search from the current position */
1554 if (infoPtr->nFocusedItem >= 0) {
1555 endidx=infoPtr->nFocusedItem;
1557 /* if looking for single character match,
1558 * then we must always move forward
1560 if (infoPtr->nSearchParamLength == 1)
1563 endidx=infoPtr->nItemCount;
1567 if (idx == infoPtr->nItemCount) {
1568 if (endidx == infoPtr->nItemCount || endidx == 0)
1574 item.mask = LVIF_TEXT;
1577 item.pszText = buffer;
1578 item.cchTextMax = MAX_PATH;
1579 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1581 /* check for a match */
1582 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1585 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1586 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1587 /* This would work but we must keep looking for a longer match */
1591 } while (idx != endidx);
1594 LISTVIEW_KeySelection(infoPtr, nItem);
1599 /*************************************************************************
1600 * LISTVIEW_UpdateHeaderSize [Internal]
1602 * Function to resize the header control
1605 * [I] hwnd : handle to a window
1606 * [I] nNewScrollPos : scroll pos to set
1611 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1616 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1618 GetWindowRect(infoPtr->hwndHeader, &winRect);
1619 point[0].x = winRect.left;
1620 point[0].y = winRect.top;
1621 point[1].x = winRect.right;
1622 point[1].y = winRect.bottom;
1624 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1625 point[0].x = -nNewScrollPos;
1626 point[1].x += nNewScrollPos;
1628 SetWindowPos(infoPtr->hwndHeader,0,
1629 point[0].x,point[0].y,point[1].x,point[1].y,
1630 SWP_NOZORDER | SWP_NOACTIVATE);
1635 * Update the scrollbars. This functions should be called whenever
1636 * the content, size or view changes.
1639 * [I] infoPtr : valid pointer to the listview structure
1644 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1646 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1647 SCROLLINFO horzInfo, vertInfo;
1650 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1652 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1653 horzInfo.cbSize = sizeof(SCROLLINFO);
1654 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1656 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1657 if (uView == LVS_LIST)
1659 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1660 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1662 /* scroll by at least one column per page */
1663 if(horzInfo.nPage < infoPtr->nItemWidth)
1664 horzInfo.nPage = infoPtr->nItemWidth;
1666 horzInfo.nPage /= infoPtr->nItemWidth;
1668 else if (uView == LVS_REPORT)
1670 horzInfo.nMax = infoPtr->nItemWidth;
1672 else /* LVS_ICON, or LVS_SMALLICON */
1676 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1679 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1680 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1681 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1682 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1683 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1685 /* Setting the horizontal scroll can change the listview size
1686 * (and potentially everything else) so we need to recompute
1687 * everything again for the vertical scroll
1690 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1691 vertInfo.cbSize = sizeof(SCROLLINFO);
1692 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1694 if (uView == LVS_REPORT)
1696 vertInfo.nMax = infoPtr->nItemCount;
1698 /* scroll by at least one page */
1699 if(vertInfo.nPage < infoPtr->nItemHeight)
1700 vertInfo.nPage = infoPtr->nItemHeight;
1702 vertInfo.nPage /= infoPtr->nItemHeight;
1704 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1708 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1711 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1712 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1713 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1714 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1715 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1717 /* Change of the range may have changed the scroll pos. If so move the content */
1718 if (dx != 0 || dy != 0)
1721 listRect = infoPtr->rcList;
1722 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1723 SW_ERASE | SW_INVALIDATE);
1726 /* Update the Header Control */
1727 if (uView == LVS_REPORT)
1729 horzInfo.fMask = SIF_POS;
1730 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1731 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1738 * Shows/hides the focus rectangle.
1741 * [I] infoPtr : valid pointer to the listview structure
1742 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1747 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
1749 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1752 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1754 if (infoPtr->nFocusedItem < 0) return;
1756 /* we need some gymnastics in ICON mode to handle large items */
1757 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1761 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1762 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1764 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1769 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1771 /* for some reason, owner draw should work only in report mode */
1772 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1777 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1778 HFONT hOldFont = SelectObject(hdc, hFont);
1780 item.iItem = infoPtr->nFocusedItem;
1782 item.mask = LVIF_PARAM;
1783 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1785 ZeroMemory(&dis, sizeof(dis));
1786 dis.CtlType = ODT_LISTVIEW;
1787 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1788 dis.itemID = item.iItem;
1789 dis.itemAction = ODA_FOCUS;
1790 if (fShow) dis.itemState |= ODS_FOCUS;
1791 dis.hwndItem = infoPtr->hwndSelf;
1793 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1794 dis.itemData = item.lParam;
1796 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1798 SelectObject(hdc, hOldFont);
1802 DrawFocusRect(hdc, &infoPtr->rcFocus);
1805 ReleaseDC(infoPtr->hwndSelf, hdc);
1809 * Invalidates all visible selected items.
1811 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
1815 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1816 while(iterator_next(&i))
1818 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1819 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1821 iterator_destroy(&i);
1826 * DESCRIPTION: [INTERNAL]
1827 * Computes an item's (left,top) corner, relative to rcView.
1828 * That is, the position has NOT been made relative to the Origin.
1829 * This is deliberate, to avoid computing the Origin over, and
1830 * over again, when this function is call in a loop. Instead,
1831 * one ca factor the computation of the Origin before the loop,
1832 * and offset the value retured by this function, on every iteration.
1835 * [I] infoPtr : valid pointer to the listview structure
1836 * [I] nItem : item number
1837 * [O] lpptOrig : item top, left corner
1842 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1844 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1846 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1848 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1850 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1851 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1853 else if (uView == LVS_LIST)
1855 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1856 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1857 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1859 else /* LVS_REPORT */
1861 lpptPosition->x = 0;
1862 lpptPosition->y = nItem * infoPtr->nItemHeight;
1867 * DESCRIPTION: [INTERNAL]
1868 * Compute the rectangles of an item. This is to localize all
1869 * the computations in one place. If you are not interested in some
1870 * of these values, simply pass in a NULL -- the fucntion is smart
1871 * enough to compute only what's necessary. The function computes
1872 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1873 * one, the BOX rectangle. This rectangle is very cheap to compute,
1874 * and is guaranteed to contain all the other rectangles. Computing
1875 * the ICON rect is also cheap, but all the others are potentaily
1876 * expensive. This gives an easy and effective optimization when
1877 * searching (like point inclusion, or rectangle intersection):
1878 * first test against the BOX, and if TRUE, test agains the desired
1880 * If the function does not have all the necessary information
1881 * to computed the requested rectangles, will crash with a
1882 * failed assertion. This is done so we catch all programming
1883 * errors, given that the function is called only from our code.
1885 * We have the following 'special' meanings for a few fields:
1886 * * If LVIS_FOCUSED is set, we assume the item has the focus
1887 * This is important in ICON mode, where it might get a larger
1888 * then usual rectange
1890 * Please note that subitem support works only in REPORT mode.
1893 * [I] infoPtr : valid pointer to the listview structure
1894 * [I] lpLVItem : item to compute the measures for
1895 * [O] lprcBox : ptr to Box rectangle
1896 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1897 * [0] lprcSelectBox : ptr to select box rectangle
1898 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1899 * [O] lprcIcon : ptr to Icon rectangle
1900 * Same as LVM_GETITEMRECT with LVIR_ICON
1901 * [O] lprcStateIcon: ptr to State Icon rectangle
1902 * [O] lprcLabel : ptr to Label rectangle
1903 * Same as LVM_GETITEMRECT with LVIR_LABEL
1908 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1909 LPRECT lprcBox, LPRECT lprcSelectBox,
1910 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
1912 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1913 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1914 RECT Box, SelectBox, Icon, Label;
1915 COLUMN_INFO *lpColumnInfo = NULL;
1916 SIZE labelSize = { 0, 0 };
1918 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1920 /* Be smart and try to figure out the minimum we have to do */
1921 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1922 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1924 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1925 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1927 if (lprcSelectBox) doSelectBox = TRUE;
1928 if (lprcLabel) doLabel = TRUE;
1929 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
1936 /************************************************************/
1937 /* compute the box rectangle (it should be cheap to do) */
1938 /************************************************************/
1939 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1940 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1942 if (lpLVItem->iSubItem)
1944 Box = lpColumnInfo->rcHeader;
1949 Box.right = infoPtr->nItemWidth;
1952 Box.bottom = infoPtr->nItemHeight;
1954 /******************************************************************/
1955 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
1956 /******************************************************************/
1959 LONG state_width = 0;
1961 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1962 state_width = infoPtr->iconStateSize.cx;
1964 if (uView == LVS_ICON)
1966 Icon.left = Box.left + state_width;
1967 if (infoPtr->himlNormal)
1968 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
1969 Icon.top = Box.top + ICON_TOP_PADDING;
1970 Icon.right = Icon.left;
1971 Icon.bottom = Icon.top;
1972 if (infoPtr->himlNormal)
1974 Icon.right += infoPtr->iconSize.cx;
1975 Icon.bottom += infoPtr->iconSize.cy;
1978 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1980 Icon.left = Box.left + state_width;
1982 if (uView == LVS_REPORT)
1983 Icon.left += REPORT_MARGINX;
1986 Icon.right = Icon.left;
1987 if (infoPtr->himlSmall &&
1988 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1989 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1990 Icon.right += infoPtr->iconSize.cx;
1991 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
1993 if(lprcIcon) *lprcIcon = Icon;
1994 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
1996 /* TODO: is this correct? */
1999 lprcStateIcon->left = Icon.left - state_width;
2000 lprcStateIcon->right = Icon.left;
2001 lprcStateIcon->top = Icon.top;
2002 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2003 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2006 else Icon.right = 0;
2008 /************************************************************/
2009 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2010 /************************************************************/
2013 /* calculate how far to the right can the label strech */
2014 Label.right = Box.right;
2015 if (uView == LVS_REPORT)
2017 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2020 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2022 labelSize.cx = infoPtr->nItemWidth;
2023 labelSize.cy = infoPtr->nItemHeight;
2027 /* we need the text in non owner draw mode */
2028 assert(lpLVItem->mask & LVIF_TEXT);
2029 if (is_textT(lpLVItem->pszText, TRUE))
2031 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2032 HDC hdc = GetDC(infoPtr->hwndSelf);
2033 HFONT hOldFont = SelectObject(hdc, hFont);
2037 /* compute rough rectangle where the label will go */
2038 SetRectEmpty(&rcText);
2039 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2040 rcText.bottom = infoPtr->nItemHeight;
2041 if (uView == LVS_ICON)
2042 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2044 /* now figure out the flags */
2045 if (uView == LVS_ICON)
2046 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2048 uFormat = LV_SL_DT_FLAGS;
2050 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2052 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2053 labelSize.cy = rcText.bottom - rcText.top;
2055 SelectObject(hdc, hOldFont);
2056 ReleaseDC(infoPtr->hwndSelf, hdc);
2060 if (uView == LVS_ICON)
2062 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2063 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2064 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2065 Label.right = Label.left + labelSize.cx;
2066 Label.bottom = Label.top + infoPtr->nItemHeight;
2067 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2069 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2070 labelSize.cy /= infoPtr->ntmHeight;
2071 labelSize.cy = max(labelSize.cy, 1);
2072 labelSize.cy *= infoPtr->ntmHeight;
2074 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2076 else if (uView == LVS_REPORT)
2078 Label.left = Icon.right;
2079 Label.top = Box.top;
2080 Label.right = lpColumnInfo->rcHeader.right;
2081 Label.bottom = Label.top + infoPtr->nItemHeight;
2083 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2085 Label.left = Icon.right;
2086 Label.top = Box.top;
2087 Label.right = min(Label.left + labelSize.cx, Label.right);
2088 Label.bottom = Label.top + infoPtr->nItemHeight;
2091 if (lprcLabel) *lprcLabel = Label;
2092 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2095 /************************************************************/
2096 /* compute STATEICON bounding box */
2097 /************************************************************/
2100 if (uView == LVS_REPORT)
2102 SelectBox.left = Icon.right; /* FIXME: should be Icon.left */
2103 SelectBox.top = Box.top;
2104 SelectBox.bottom = Box.bottom;
2105 if (lpLVItem->iSubItem == 0)
2107 /* we need the indent in report mode */
2108 assert(lpLVItem->mask & LVIF_INDENT);
2109 SelectBox.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
2111 SelectBox.right = min(SelectBox.left + labelSize.cx, Label.right);
2115 UnionRect(&SelectBox, &Icon, &Label);
2117 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2118 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2121 /* Fix the Box if necessary */
2124 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2125 else *lprcBox = Box;
2127 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2131 * DESCRIPTION: [INTERNAL]
2134 * [I] infoPtr : valid pointer to the listview structure
2135 * [I] nItem : item number
2136 * [O] lprcBox : ptr to Box rectangle
2141 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2143 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2144 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2145 POINT Position, Origin;
2148 LISTVIEW_GetOrigin(infoPtr, &Origin);
2149 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2151 /* Be smart and try to figure out the minimum we have to do */
2153 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2154 lvItem.mask |= LVIF_TEXT;
2155 lvItem.iItem = nItem;
2156 lvItem.iSubItem = 0;
2157 lvItem.pszText = szDispText;
2158 lvItem.cchTextMax = DISP_TEXT_SIZE;
2159 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2160 if (uView == LVS_ICON)
2162 lvItem.mask |= LVIF_STATE;
2163 lvItem.stateMask = LVIS_FOCUSED;
2164 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2166 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2168 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2174 * Returns the current icon position, and advances it along the top.
2175 * The returned position is not offset by Origin.
2178 * [I] infoPtr : valid pointer to the listview structure
2179 * [O] lpPos : will get the current icon position
2184 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2186 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2188 *lpPos = infoPtr->currIconPos;
2190 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2191 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2193 infoPtr->currIconPos.x = 0;
2194 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2200 * Returns the current icon position, and advances it down the left edge.
2201 * The returned position is not offset by Origin.
2204 * [I] infoPtr : valid pointer to the listview structure
2205 * [O] lpPos : will get the current icon position
2210 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2212 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2214 *lpPos = infoPtr->currIconPos;
2216 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2217 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2219 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2220 infoPtr->currIconPos.y = 0;
2226 * Moves an icon to the specified position.
2227 * It takes care of invalidating the item, etc.
2230 * [I] infoPtr : valid pointer to the listview structure
2231 * [I] nItem : the item to move
2232 * [I] lpPos : the new icon position
2233 * [I] isNew : flags the item as being new
2239 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2245 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2246 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2248 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2249 LISTVIEW_InvalidateItem(infoPtr, nItem);
2252 /* Allocating a POINTER for every item is too resource intensive,
2253 * so we'll keep the (x,y) in different arrays */
2254 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2255 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2257 LISTVIEW_InvalidateItem(infoPtr, nItem);
2264 * Arranges listview items in icon display mode.
2267 * [I] infoPtr : valid pointer to the listview structure
2268 * [I] nAlignCode : alignment code
2274 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2276 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2277 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2281 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2283 TRACE("nAlignCode=%d\n", nAlignCode);
2285 if (nAlignCode == LVA_DEFAULT)
2287 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2288 else nAlignCode = LVA_ALIGNTOP;
2293 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2294 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2295 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2296 default: return FALSE;
2299 infoPtr->bAutoarrange = TRUE;
2300 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2301 for (i = 0; i < infoPtr->nItemCount; i++)
2303 next_pos(infoPtr, &pos);
2304 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2312 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2315 * [I] infoPtr : valid pointer to the listview structure
2316 * [O] lprcView : bounding rectangle
2322 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2326 SetRectEmpty(lprcView);
2328 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2332 for (i = 0; i < infoPtr->nItemCount; i++)
2334 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2335 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2336 lprcView->right = max(lprcView->right, x);
2337 lprcView->bottom = max(lprcView->bottom, y);
2339 if (infoPtr->nItemCount > 0)
2341 lprcView->right += infoPtr->nItemWidth;
2342 lprcView->bottom += infoPtr->nItemHeight;
2347 y = LISTVIEW_GetCountPerColumn(infoPtr);
2348 x = infoPtr->nItemCount / y;
2349 if (infoPtr->nItemCount % y) x++;
2350 lprcView->right = x * infoPtr->nItemWidth;
2351 lprcView->bottom = y * infoPtr->nItemHeight;
2355 lprcView->right = infoPtr->nItemWidth;
2356 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2363 * Retrieves the bounding rectangle of all the items.
2366 * [I] infoPtr : valid pointer to the listview structure
2367 * [O] lprcView : bounding rectangle
2373 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2377 TRACE("(lprcView=%p)\n", lprcView);
2379 if (!lprcView) return FALSE;
2381 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2382 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2383 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2385 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2392 * Retrieves the subitem pointer associated with the subitem index.
2395 * [I] hdpaSubItems : DPA handle for a specific item
2396 * [I] nSubItem : index of subitem
2399 * SUCCESS : subitem pointer
2402 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2404 SUBITEM_INFO *lpSubItem;
2407 /* we should binary search here if need be */
2408 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2410 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2411 if (lpSubItem->iSubItem == nSubItem)
2421 * Calculates the desired item width.
2424 * [I] infoPtr : valid pointer to the listview structure
2427 * The desired item width.
2429 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2431 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2434 TRACE("uView=%d\n", uView);
2436 if (uView == LVS_ICON)
2437 nItemWidth = infoPtr->iconSpacing.cx;
2438 else if (uView == LVS_REPORT)
2442 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2444 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2445 nItemWidth = rcHeader.right;
2448 else /* LVS_SMALLICON, or LVS_LIST */
2452 for (i = 0; i < infoPtr->nItemCount; i++)
2453 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2455 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2456 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2458 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2461 return max(nItemWidth, 1);
2466 * Calculates the desired item height.
2469 * [I] infoPtr : valid pointer to the listview structure
2472 * The desired item height.
2474 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2476 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2479 TRACE("uView=%d\n", uView);
2481 if (uView == LVS_ICON)
2482 nItemHeight = infoPtr->iconSpacing.cy;
2485 nItemHeight = infoPtr->ntmHeight;
2486 if (infoPtr->himlState)
2487 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2488 if (infoPtr->himlSmall)
2489 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2490 if (infoPtr->himlState || infoPtr->himlSmall)
2491 nItemHeight += HEIGHT_PADDING;
2492 if (infoPtr->nMeasureItemHeight > 0)
2493 nItemHeight = infoPtr->nMeasureItemHeight;
2496 return max(nItemHeight, 1);
2501 * Updates the width, and height of an item.
2504 * [I] infoPtr : valid pointer to the listview structure
2509 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2511 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2512 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2518 * Retrieves and saves important text metrics info for the current
2522 * [I] infoPtr : valid pointer to the listview structure
2525 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2527 HDC hdc = GetDC(infoPtr->hwndSelf);
2528 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2529 HFONT hOldFont = SelectObject(hdc, hFont);
2533 if (GetTextMetricsW(hdc, &tm))
2535 infoPtr->ntmHeight = tm.tmHeight;
2536 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2539 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2540 infoPtr->nEllipsisWidth = sz.cx;
2542 SelectObject(hdc, hOldFont);
2543 ReleaseDC(infoPtr->hwndSelf, hdc);
2545 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2550 * A compare function for ranges
2553 * [I] range1 : pointer to range 1;
2554 * [I] range2 : pointer to range 2;
2558 * > 0 : if range 1 > range 2
2559 * < 0 : if range 2 > range 1
2560 * = 0 : if range intersects range 2
2562 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2566 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2568 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2573 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2579 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2581 #define ranges_check(ranges, desc) do { } while(0)
2584 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2589 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2591 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2592 ranges_dump(ranges);
2593 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2594 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2595 assert (prev->lower >= 0 && prev->lower < prev->upper);
2596 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2598 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2599 assert (prev->upper <= curr->lower);
2600 assert (curr->lower < curr->upper);
2603 TRACE("--- Done checking---\n");
2606 static RANGES ranges_create(int count)
2608 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2609 if (!ranges) return NULL;
2610 ranges->hdpa = DPA_Create(count);
2611 if (ranges->hdpa) return ranges;
2616 static void ranges_clear(RANGES ranges)
2620 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2621 Free(DPA_GetPtr(ranges->hdpa, i));
2622 DPA_DeleteAllPtrs(ranges->hdpa);
2626 static void ranges_destroy(RANGES ranges)
2628 if (!ranges) return;
2629 ranges_clear(ranges);
2630 DPA_Destroy(ranges->hdpa);
2634 static RANGES ranges_clone(RANGES ranges)
2639 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2641 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2643 RANGE *newrng = Alloc(sizeof(RANGE));
2644 if (!newrng) goto fail;
2645 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2646 DPA_SetPtr(clone->hdpa, i, newrng);
2651 TRACE ("clone failed\n");
2652 ranges_destroy(clone);
2656 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2660 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2661 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2666 static void ranges_dump(RANGES ranges)
2670 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2671 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2674 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2676 RANGE srchrng = { nItem, nItem + 1 };
2678 TRACE("(nItem=%d)\n", nItem);
2679 ranges_check(ranges, "before contain");
2680 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2683 static INT ranges_itemcount(RANGES ranges)
2687 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2689 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2690 count += sel->upper - sel->lower;
2696 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2698 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2701 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2702 if (index == -1) return TRUE;
2704 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2706 chkrng = DPA_GetPtr(ranges->hdpa, index);
2707 if (chkrng->lower >= nItem)
2708 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2709 if (chkrng->upper > nItem)
2710 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2715 static BOOL ranges_add(RANGES ranges, RANGE range)
2720 TRACE("(%s)\n", debugrange(&range));
2721 ranges_check(ranges, "before add");
2723 /* try find overlapping regions first */
2724 srchrgn.lower = range.lower - 1;
2725 srchrgn.upper = range.upper + 1;
2726 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2732 TRACE("Adding new range\n");
2734 /* create the brand new range to insert */
2735 newrgn = Alloc(sizeof(RANGE));
2736 if(!newrgn) goto fail;
2739 /* figure out where to insert it */
2740 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2741 TRACE("index=%d\n", index);
2742 if (index == -1) index = 0;
2744 /* and get it over with */
2745 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2753 RANGE *chkrgn, *mrgrgn;
2754 INT fromindex, mergeindex;
2756 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2757 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2759 chkrgn->lower = min(range.lower, chkrgn->lower);
2760 chkrgn->upper = max(range.upper, chkrgn->upper);
2762 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2764 /* merge now common anges */
2766 srchrgn.lower = chkrgn->lower - 1;
2767 srchrgn.upper = chkrgn->upper + 1;
2771 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2772 if (mergeindex == -1) break;
2773 if (mergeindex == index)
2775 fromindex = index + 1;
2779 TRACE("Merge with index %i\n", mergeindex);
2781 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2782 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2783 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2785 DPA_DeletePtr(ranges->hdpa, mergeindex);
2786 if (mergeindex < index) index --;
2790 ranges_check(ranges, "after add");
2794 ranges_check(ranges, "failed add");
2798 static BOOL ranges_del(RANGES ranges, RANGE range)
2803 TRACE("(%s)\n", debugrange(&range));
2804 ranges_check(ranges, "before del");
2806 /* we don't use DPAS_SORTED here, since we need *
2807 * to find the first overlapping range */
2808 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2811 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2813 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2815 /* case 1: Same range */
2816 if ( (chkrgn->upper == range.upper) &&
2817 (chkrgn->lower == range.lower) )
2819 DPA_DeletePtr(ranges->hdpa, index);
2822 /* case 2: engulf */
2823 else if ( (chkrgn->upper <= range.upper) &&
2824 (chkrgn->lower >= range.lower) )
2826 DPA_DeletePtr(ranges->hdpa, index);
2828 /* case 3: overlap upper */
2829 else if ( (chkrgn->upper <= range.upper) &&
2830 (chkrgn->lower < range.lower) )
2832 chkrgn->upper = range.lower;
2834 /* case 4: overlap lower */
2835 else if ( (chkrgn->upper > range.upper) &&
2836 (chkrgn->lower >= range.lower) )
2838 chkrgn->lower = range.upper;
2841 /* case 5: fully internal */
2844 RANGE tmprgn = *chkrgn, *newrgn;
2846 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
2847 newrgn->lower = chkrgn->lower;
2848 newrgn->upper = range.lower;
2849 chkrgn->lower = range.upper;
2850 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2859 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2862 ranges_check(ranges, "after del");
2866 ranges_check(ranges, "failed del");
2872 * Removes all selection ranges
2875 * [I] infoPtr : valid pointer to the listview structure
2876 * [I] toSkip : item range to skip removing the selection
2882 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2891 lvItem.stateMask = LVIS_SELECTED;
2893 /* need to clone the DPA because callbacks can change it */
2894 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2895 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2896 while(iterator_next(&i))
2897 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2898 /* note that the iterator destructor will free the cloned range */
2899 iterator_destroy(&i);
2904 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2908 if (!(toSkip = ranges_create(1))) return FALSE;
2909 if (nItem != -1) ranges_additem(toSkip, nItem);
2910 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2911 ranges_destroy(toSkip);
2915 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2917 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2922 * Retrieves the number of items that are marked as selected.
2925 * [I] infoPtr : valid pointer to the listview structure
2928 * Number of items selected.
2930 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
2932 INT nSelectedCount = 0;
2934 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2937 for (i = 0; i < infoPtr->nItemCount; i++)
2939 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2944 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2946 TRACE("nSelectedCount=%d\n", nSelectedCount);
2947 return nSelectedCount;
2952 * Manages the item focus.
2955 * [I] infoPtr : valid pointer to the listview structure
2956 * [I] nItem : item index
2959 * TRUE : focused item changed
2960 * FALSE : focused item has NOT changed
2962 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2964 INT oldFocus = infoPtr->nFocusedItem;
2967 if (nItem == infoPtr->nFocusedItem) return FALSE;
2969 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2970 lvItem.stateMask = LVIS_FOCUSED;
2971 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2973 return oldFocus != infoPtr->nFocusedItem;
2976 /* Helper function for LISTVIEW_ShiftIndices *only* */
2977 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2979 if (nShiftItem < nItem) return nShiftItem;
2981 if (nShiftItem > nItem) return nShiftItem + direction;
2983 if (direction > 0) return nShiftItem + direction;
2985 return min(nShiftItem, infoPtr->nItemCount - 1);
2990 * Updates the various indices after an item has been inserted or deleted.
2993 * [I] infoPtr : valid pointer to the listview structure
2994 * [I] nItem : item index
2995 * [I] direction : Direction of shift, +1 or -1.
3000 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3005 /* temporarily disable change notification while shifting items */
3006 bOldChange = infoPtr->bDoChangeNotify;
3007 infoPtr->bDoChangeNotify = FALSE;
3009 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3011 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3013 assert(abs(direction) == 1);
3015 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3017 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3018 if (nNewFocus != infoPtr->nFocusedItem)
3019 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3021 /* But we are not supposed to modify nHotItem! */
3023 infoPtr->bDoChangeNotify = bOldChange;
3029 * Adds a block of selections.
3032 * [I] infoPtr : valid pointer to the listview structure
3033 * [I] nItem : item index
3036 * Whether the window is still valid.
3038 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3040 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3041 INT nLast = max(infoPtr->nSelectionMark, nItem);
3042 HWND hwndSelf = infoPtr->hwndSelf;
3043 NMLVODSTATECHANGE nmlv;
3048 /* Temporarily disable change notification
3049 * If the control is LVS_OWNERDATA, we need to send
3050 * only one LVN_ODSTATECHANGED notification.
3051 * See MSDN documentation for LVN_ITEMCHANGED.
3053 bOldChange = infoPtr->bDoChangeNotify;
3054 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3056 if (nFirst == -1) nFirst = nItem;
3058 item.state = LVIS_SELECTED;
3059 item.stateMask = LVIS_SELECTED;
3061 for (i = nFirst; i <= nLast; i++)
3062 LISTVIEW_SetItemState(infoPtr,i,&item);
3064 ZeroMemory(&nmlv, sizeof(nmlv));
3065 nmlv.iFrom = nFirst;
3068 nmlv.uOldState = item.state;
3070 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3071 if (!IsWindow(hwndSelf))
3073 infoPtr->bDoChangeNotify = bOldChange;
3080 * Sets a single group selection.
3083 * [I] infoPtr : valid pointer to the listview structure
3084 * [I] nItem : item index
3089 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3091 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3097 if (!(selection = ranges_create(100))) return;
3099 item.state = LVIS_SELECTED;
3100 item.stateMask = LVIS_SELECTED;
3102 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3104 if (infoPtr->nSelectionMark == -1)
3106 infoPtr->nSelectionMark = nItem;
3107 ranges_additem(selection, nItem);
3113 sel.lower = min(infoPtr->nSelectionMark, nItem);
3114 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3115 ranges_add(selection, sel);
3120 RECT rcItem, rcSel, rcSelMark;
3123 rcItem.left = LVIR_BOUNDS;
3124 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3125 rcSelMark.left = LVIR_BOUNDS;
3126 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3127 UnionRect(&rcSel, &rcItem, &rcSelMark);
3128 iterator_frameditems(&i, infoPtr, &rcSel);
3129 while(iterator_next(&i))
3131 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3132 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3134 iterator_destroy(&i);
3137 bOldChange = infoPtr->bDoChangeNotify;
3138 infoPtr->bDoChangeNotify = FALSE;
3140 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3143 iterator_rangesitems(&i, selection);
3144 while(iterator_next(&i))
3145 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3146 /* this will also destroy the selection */
3147 iterator_destroy(&i);
3149 infoPtr->bDoChangeNotify = bOldChange;
3151 LISTVIEW_SetItemFocus(infoPtr, nItem);
3156 * Sets a single selection.
3159 * [I] infoPtr : valid pointer to the listview structure
3160 * [I] nItem : item index
3165 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3169 TRACE("nItem=%d\n", nItem);
3171 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3173 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3174 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3175 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3177 infoPtr->nSelectionMark = nItem;
3182 * Set selection(s) with keyboard.
3185 * [I] infoPtr : valid pointer to the listview structure
3186 * [I] nItem : item index
3189 * SUCCESS : TRUE (needs to be repainted)
3190 * FAILURE : FALSE (nothing has changed)
3192 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3194 /* FIXME: pass in the state */
3195 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3196 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3197 BOOL bResult = FALSE;
3199 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3200 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3202 if (infoPtr->dwStyle & LVS_SINGLESEL)
3205 LISTVIEW_SetSelection(infoPtr, nItem);
3212 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3217 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3218 lvItem.stateMask = LVIS_SELECTED;
3219 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3221 if (lvItem.state & LVIS_SELECTED)
3222 infoPtr->nSelectionMark = nItem;
3224 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3229 LISTVIEW_SetSelection(infoPtr, nItem);
3232 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3235 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3239 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3241 LVHITTESTINFO lvHitTestInfo;
3243 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3244 lvHitTestInfo.pt.x = pt.x;
3245 lvHitTestInfo.pt.y = pt.y;
3247 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3249 lpLVItem->mask = LVIF_PARAM;
3250 lpLVItem->iItem = lvHitTestInfo.iItem;
3251 lpLVItem->iSubItem = 0;
3253 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3258 * Called when the mouse is being actively tracked and has hovered for a specified
3262 * [I] infoPtr : valid pointer to the listview structure
3263 * [I] fwKeys : key indicator
3264 * [I] x,y : mouse position
3267 * 0 if the message was processed, non-zero if there was an error
3270 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3271 * over the item for a certain period of time.
3274 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3276 if (infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3284 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3285 LISTVIEW_SetSelection(infoPtr, item.iItem);
3293 * Called whenever WM_MOUSEMOVE is received.
3296 * [I] infoPtr : valid pointer to the listview structure
3297 * [I] fwKeys : key indicator
3298 * [I] x,y : mouse position
3301 * 0 if the message is processed, non-zero if there was an error
3303 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3305 TRACKMOUSEEVENT trackinfo;
3307 if (!(fwKeys & MK_LBUTTON))
3308 infoPtr->bLButtonDown = FALSE;
3310 if (infoPtr->bLButtonDown && DragDetect(infoPtr->hwndSelf, infoPtr->ptClickPos))
3312 LVHITTESTINFO lvHitTestInfo;
3315 lvHitTestInfo.pt = infoPtr->ptClickPos;
3316 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3318 ZeroMemory(&nmlv, sizeof(nmlv));
3319 nmlv.iItem = lvHitTestInfo.iItem;
3320 nmlv.ptAction = infoPtr->ptClickPos;
3322 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3327 infoPtr->bLButtonDown = FALSE;
3329 /* see if we are supposed to be tracking mouse hovering */
3330 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3331 /* fill in the trackinfo struct */
3332 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3333 trackinfo.dwFlags = TME_QUERY;
3334 trackinfo.hwndTrack = infoPtr->hwndSelf;
3335 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3337 /* see if we are already tracking this hwnd */
3338 _TrackMouseEvent(&trackinfo);
3340 if(!(trackinfo.dwFlags & TME_HOVER)) {
3341 trackinfo.dwFlags = TME_HOVER;
3343 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3344 _TrackMouseEvent(&trackinfo);
3353 * Tests wheather the item is assignable to a list with style lStyle
3355 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3357 if ( (lpLVItem->mask & LVIF_TEXT) &&
3358 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3359 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3367 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3370 * [I] infoPtr : valid pointer to the listview structure
3371 * [I] lpLVItem : valid pointer to new item atttributes
3372 * [I] isNew : the item being set is being inserted
3373 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3374 * [O] bChanged : will be set to TRUE if the item really changed
3380 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3382 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3390 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3392 if (lpLVItem->mask == 0) return TRUE;
3394 if (infoPtr->dwStyle & LVS_OWNERDATA)
3396 /* a virtual listview we stores only selection and focus */
3397 if (lpLVItem->mask & ~LVIF_STATE)
3403 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3404 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3408 /* we need to get the lParam and state of the item */
3409 item.iItem = lpLVItem->iItem;
3410 item.iSubItem = lpLVItem->iSubItem;
3411 item.mask = LVIF_STATE | LVIF_PARAM;
3412 item.stateMask = ~0;
3415 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3417 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3418 /* determine what fields will change */
3419 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3420 uChanged |= LVIF_STATE;
3422 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3423 uChanged |= LVIF_IMAGE;
3425 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3426 uChanged |= LVIF_PARAM;
3428 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3429 uChanged |= LVIF_INDENT;
3431 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3432 uChanged |= LVIF_TEXT;
3434 TRACE("uChanged=0x%x\n", uChanged);
3435 if (!uChanged) return TRUE;
3438 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3439 nmlv.iItem = lpLVItem->iItem;
3440 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3441 nmlv.uOldState = item.state;
3442 nmlv.uChanged = uChanged;
3443 nmlv.lParam = item.lParam;
3445 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3446 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3448 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3450 HWND hwndSelf = infoPtr->hwndSelf;
3452 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3454 if (!IsWindow(hwndSelf))
3458 /* copy information */
3459 if (lpLVItem->mask & LVIF_TEXT)
3460 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3462 if (lpLVItem->mask & LVIF_IMAGE)
3463 lpItem->hdr.iImage = lpLVItem->iImage;
3465 if (lpLVItem->mask & LVIF_PARAM)
3466 lpItem->lParam = lpLVItem->lParam;
3468 if (lpLVItem->mask & LVIF_INDENT)
3469 lpItem->iIndent = lpLVItem->iIndent;
3471 if (uChanged & LVIF_STATE)
3473 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3475 lpItem->state &= ~lpLVItem->stateMask;
3476 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3478 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3480 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3481 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3483 else if (lpLVItem->stateMask & LVIS_SELECTED)
3484 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3486 /* if we are asked to change focus, and we manage it, do it */
3487 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3489 if (lpLVItem->state & LVIS_FOCUSED)
3491 LISTVIEW_SetItemFocus(infoPtr, -1);
3492 infoPtr->nFocusedItem = lpLVItem->iItem;
3493 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3495 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3496 infoPtr->nFocusedItem = -1;
3500 /* if we're inserting the item, we're done */
3501 if (isNew) return TRUE;
3503 /* send LVN_ITEMCHANGED notification */
3504 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3505 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3512 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3515 * [I] infoPtr : valid pointer to the listview structure
3516 * [I] lpLVItem : valid pointer to new subitem atttributes
3517 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3518 * [O] bChanged : will be set to TRUE if the item really changed
3524 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3527 SUBITEM_INFO *lpSubItem;
3529 /* we do not support subitems for virtual listviews */
3530 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3532 /* set subitem only if column is present */
3533 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3535 /* First do some sanity checks */
3536 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3537 particularly useful. We currently do not actually do anything with
3538 the flag on subitems.
3540 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3541 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3543 /* get the subitem structure, and create it if not there */
3544 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3545 assert (hdpaSubItems);
3547 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3550 SUBITEM_INFO *tmpSubItem;
3553 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3554 if (!lpSubItem) return FALSE;
3555 /* we could binary search here, if need be...*/
3556 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3558 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3559 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3561 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3566 lpSubItem->iSubItem = lpLVItem->iSubItem;
3567 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3571 if (lpLVItem->mask & LVIF_IMAGE)
3572 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3574 lpSubItem->hdr.iImage = lpLVItem->iImage;
3578 if (lpLVItem->mask & LVIF_TEXT)
3579 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3581 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3590 * Sets item attributes.
3593 * [I] infoPtr : valid pointer to the listview structure
3594 * [I] lpLVItem : new item atttributes
3595 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3601 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3603 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3604 HWND hwndSelf = infoPtr->hwndSelf;
3605 LPWSTR pszText = NULL;
3606 BOOL bResult, bChanged = FALSE;
3608 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3610 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3613 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3614 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3616 pszText = lpLVItem->pszText;
3617 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3620 /* actually set the fields */
3621 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3623 if (lpLVItem->iSubItem)
3624 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3626 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3627 if (!IsWindow(hwndSelf))
3630 /* redraw item, if necessary */
3631 if (bChanged && !infoPtr->bIsDrawing)
3633 /* this little optimization eliminates some nasty flicker */
3634 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3635 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3636 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3637 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3639 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3644 textfreeT(lpLVItem->pszText, isW);
3645 lpLVItem->pszText = pszText;
3653 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3656 * [I] infoPtr : valid pointer to the listview structure
3661 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3663 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3665 SCROLLINFO scrollInfo;
3667 scrollInfo.cbSize = sizeof(SCROLLINFO);
3668 scrollInfo.fMask = SIF_POS;
3670 if (uView == LVS_LIST)
3672 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3673 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3675 else if (uView == LVS_REPORT)
3677 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3678 nItem = scrollInfo.nPos;
3682 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3683 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3686 TRACE("nItem=%d\n", nItem);
3694 * Erases the background of the given rectangle
3697 * [I] infoPtr : valid pointer to the listview structure
3698 * [I] hdc : device context handle
3699 * [I] lprcBox : clipping rectangle
3705 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3707 if (!infoPtr->hBkBrush) return FALSE;
3709 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3711 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3719 * [I] infoPtr : valid pointer to the listview structure
3720 * [I] hdc : device context handle
3721 * [I] nItem : item index
3722 * [I] nSubItem : subitem index
3723 * [I] pos : item position in client coordinates
3724 * [I] cdmode : custom draw mode
3730 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3732 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3733 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3734 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3735 DWORD cdsubitemmode = CDRF_DODEFAULT;
3737 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3738 NMLVCUSTOMDRAW nmlvcd;
3743 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3745 /* get information needed for drawing the item */
3746 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3747 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3748 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3749 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3750 lvItem.iItem = nItem;
3751 lvItem.iSubItem = nSubItem;
3754 lvItem.cchTextMax = DISP_TEXT_SIZE;
3755 lvItem.pszText = szDispText;
3756 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3757 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3758 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3759 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3760 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3762 /* now check if we need to update the focus rectangle */
3763 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3765 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3766 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3767 OffsetRect(&rcBox, pos.x, pos.y);
3768 OffsetRect(&rcSelect, pos.x, pos.y);
3769 OffsetRect(&rcIcon, pos.x, pos.y);
3770 OffsetRect(&rcStateIcon, pos.x, pos.y);
3771 OffsetRect(&rcLabel, pos.x, pos.y);
3772 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3773 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3774 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3776 /* fill in the custom draw structure */
3777 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3779 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3780 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3781 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3782 if (cdmode & CDRF_NOTIFYITEMDRAW)
3783 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3784 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3785 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3786 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3787 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3789 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3790 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3792 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3793 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
3794 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
3795 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
3797 /* in full row select, subitems, will just use main item's colors */
3798 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3799 nmlvcd.clrTextBk = CLR_NONE;
3802 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3804 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3807 TRACE("uStateImage=%d\n", uStateImage);
3808 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3809 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
3814 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3815 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3817 TRACE("iImage=%d\n", lvItem.iImage);
3818 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3819 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3820 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3823 /* Don't bother painting item being edited */
3824 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3826 /* FIXME: temporary hack */
3827 rcSelect.left = rcLabel.left;
3829 /* draw the selection background, if we're drawing the main item */
3832 /* in icon mode, the label rect is really what we want to draw the
3834 if (uView == LVS_ICON)
3837 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3838 rcSelect.right = rcBox.right;
3840 if (nmlvcd.clrTextBk != CLR_NONE)
3841 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3842 if(lprcFocus) *lprcFocus = rcSelect;
3845 /* figure out the text drawing flags */
3846 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3847 if (uView == LVS_ICON)
3848 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3851 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3853 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3854 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3855 default: uFormat |= DT_LEFT;
3858 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3860 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3861 else rcLabel.left += LABEL_HOR_PADDING;
3863 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3864 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3867 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3868 notify_postpaint(infoPtr, &nmlvcd);
3869 if (cdsubitemmode & CDRF_NEWFONT)
3870 SelectObject(hdc, hOldFont);
3876 * Draws listview items when in owner draw mode.
3879 * [I] infoPtr : valid pointer to the listview structure
3880 * [I] hdc : device context handle
3885 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3887 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3888 DWORD cditemmode = CDRF_DODEFAULT;
3889 NMLVCUSTOMDRAW nmlvcd;
3890 POINT Origin, Position;
3896 ZeroMemory(&dis, sizeof(dis));
3898 /* Get scroll info once before loop */
3899 LISTVIEW_GetOrigin(infoPtr, &Origin);
3901 /* iterate through the invalidated rows */
3902 while(iterator_next(i))
3904 item.iItem = i->nItem;
3906 item.mask = LVIF_PARAM | LVIF_STATE;
3907 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3908 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3910 dis.CtlType = ODT_LISTVIEW;
3912 dis.itemID = item.iItem;
3913 dis.itemAction = ODA_DRAWENTIRE;
3915 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3916 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3917 dis.hwndItem = infoPtr->hwndSelf;
3919 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3920 dis.rcItem.left = Position.x + Origin.x;
3921 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3922 dis.rcItem.top = Position.y + Origin.y;
3923 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3924 dis.itemData = item.lParam;
3926 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3929 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3930 * structure for the rest. of the paint cycle
3932 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3933 if (cdmode & CDRF_NOTIFYITEMDRAW)
3934 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3936 if (!(cditemmode & CDRF_SKIPDEFAULT))
3938 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
3939 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3942 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3943 notify_postpaint(infoPtr, &nmlvcd);
3949 * Draws listview items when in report display mode.
3952 * [I] infoPtr : valid pointer to the listview structure
3953 * [I] hdc : device context handle
3954 * [I] cdmode : custom draw mode
3959 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3962 RECT rcClip, rcItem;
3963 POINT Origin, Position;
3969 /* figure out what to draw */
3970 rgntype = GetClipBox(hdc, &rcClip);
3971 if (rgntype == NULLREGION) return;
3973 /* Get scroll info once before loop */
3974 LISTVIEW_GetOrigin(infoPtr, &Origin);
3976 /* narrow down the columns we need to paint */
3977 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3979 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3980 if (rcItem.right + Origin.x >= rcClip.left) break;
3982 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3984 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3985 if (rcItem.left + Origin.x < rcClip.right) break;
3987 iterator_rangeitems(&j, colRange);
3989 /* in full row select, we _have_ to draw the main item */
3990 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3993 /* iterate through the invalidated rows */
3994 while(iterator_next(i))
3996 /* iterate through the invalidated columns */
3997 while(iterator_next(&j))
3999 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4000 Position.x += Origin.x;
4001 Position.y += Origin.y;
4003 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4005 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4007 rcItem.bottom = infoPtr->nItemHeight;
4008 OffsetRect(&rcItem, Position.x, Position.y);
4009 if (!RectVisible(hdc, &rcItem)) continue;
4012 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4015 iterator_destroy(&j);
4020 * Draws listview items when in list display mode.
4023 * [I] infoPtr : valid pointer to the listview structure
4024 * [I] hdc : device context handle
4025 * [I] cdmode : custom draw mode
4030 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4032 POINT Origin, Position;
4034 /* Get scroll info once before loop */
4035 LISTVIEW_GetOrigin(infoPtr, &Origin);
4037 while(iterator_prev(i))
4039 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4040 Position.x += Origin.x;
4041 Position.y += Origin.y;
4043 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4050 * Draws listview items.
4053 * [I] infoPtr : valid pointer to the listview structure
4054 * [I] hdc : device context handle
4055 * [I] prcErase : rect to be erased before refresh (may be NULL)
4060 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4062 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4063 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4064 NMLVCUSTOMDRAW nmlvcd;
4071 HBITMAP hbmp = NULL;
4073 LISTVIEW_DUMP(infoPtr);
4075 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4076 TRACE("double buffering\n");
4078 hdc = CreateCompatibleDC(hdcOrig);
4080 ERR("Failed to create DC for backbuffer\n");
4083 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4084 infoPtr->rcList.bottom);
4086 ERR("Failed to create bitmap for backbuffer\n");
4091 SelectObject(hdc, hbmp);
4092 SelectObject(hdc, infoPtr->hFont);
4094 /* Save dc values we're gonna trash while drawing
4095 * FIXME: Should be done in LISTVIEW_DrawItem() */
4096 hOldFont = SelectObject(hdc, infoPtr->hFont);
4097 oldBkMode = GetBkMode(hdc);
4098 oldBkColor = GetBkColor(hdc);
4099 oldTextColor = GetTextColor(hdc);
4102 infoPtr->bIsDrawing = TRUE;
4105 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4106 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4107 /* If no erasing was done (usually because RedrawWindow was called
4108 * with RDW_INVALIDATE only) we need to copy the old contents into
4109 * the backbuffer before continuing. */
4110 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4111 infoPtr->rcList.right - infoPtr->rcList.left,
4112 infoPtr->rcList.bottom - infoPtr->rcList.top,
4113 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4116 /* FIXME: Shouldn't need to do this */
4117 oldClrTextBk = infoPtr->clrTextBk;
4118 oldClrText = infoPtr->clrText;
4120 infoPtr->cditemmode = CDRF_DODEFAULT;
4122 GetClientRect(infoPtr->hwndSelf, &rcClient);
4123 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4124 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4125 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4126 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4128 /* Use these colors to draw the items */
4129 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4130 infoPtr->clrText = nmlvcd.clrText;
4132 /* nothing to draw */
4133 if(infoPtr->nItemCount == 0) goto enddraw;
4135 /* figure out what we need to draw */
4136 iterator_visibleitems(&i, infoPtr, hdc);
4138 /* send cache hint notification */
4139 if (infoPtr->dwStyle & LVS_OWNERDATA)
4141 RANGE range = iterator_range(&i);
4144 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4145 nmlv.iFrom = range.lower;
4146 nmlv.iTo = range.upper - 1;
4147 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4150 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4151 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4154 if (uView == LVS_REPORT)
4155 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4156 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4157 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4159 /* if we have a focus rect, draw it */
4160 if (infoPtr->bFocus)
4161 DrawFocusRect(hdc, &infoPtr->rcFocus);
4163 iterator_destroy(&i);
4166 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4167 notify_postpaint(infoPtr, &nmlvcd);
4169 infoPtr->clrTextBk = oldClrTextBk;
4170 infoPtr->clrText = oldClrText;
4173 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4174 infoPtr->rcList.right - infoPtr->rcList.left,
4175 infoPtr->rcList.bottom - infoPtr->rcList.top,
4176 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4181 SelectObject(hdc, hOldFont);
4182 SetBkMode(hdc, oldBkMode);
4183 SetBkColor(hdc, oldBkColor);
4184 SetTextColor(hdc, oldTextColor);
4187 infoPtr->bIsDrawing = FALSE;
4193 * Calculates the approximate width and height of a given number of items.
4196 * [I] infoPtr : valid pointer to the listview structure
4197 * [I] nItemCount : number of items
4198 * [I] wWidth : width
4199 * [I] wHeight : height
4202 * Returns a DWORD. The width in the low word and the height in high word.
4204 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4205 WORD wWidth, WORD wHeight)
4207 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4208 INT nItemCountPerColumn = 1;
4209 INT nColumnCount = 0;
4210 DWORD dwViewRect = 0;
4212 if (nItemCount == -1)
4213 nItemCount = infoPtr->nItemCount;
4215 if (uView == LVS_LIST)
4217 if (wHeight == 0xFFFF)
4219 /* use current height */
4220 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4223 if (wHeight < infoPtr->nItemHeight)
4224 wHeight = infoPtr->nItemHeight;
4228 if (infoPtr->nItemHeight > 0)
4230 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4231 if (nItemCountPerColumn == 0)
4232 nItemCountPerColumn = 1;
4234 if (nItemCount % nItemCountPerColumn != 0)
4235 nColumnCount = nItemCount / nItemCountPerColumn;
4237 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4241 /* Microsoft padding magic */
4242 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4243 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4245 dwViewRect = MAKELONG(wWidth, wHeight);
4247 else if (uView == LVS_REPORT)
4251 if (infoPtr->nItemCount > 0)
4253 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4254 wWidth = rcBox.right - rcBox.left;
4255 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4259 /* use current height and width */
4260 if (wHeight == 0xffff)
4261 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4262 if (wWidth == 0xffff)
4263 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4266 dwViewRect = MAKELONG(wWidth, wHeight);
4268 else if (uView == LVS_SMALLICON)
4269 FIXME("uView == LVS_SMALLICON: not implemented\n");
4270 else if (uView == LVS_ICON)
4271 FIXME("uView == LVS_ICON: not implemented\n");
4279 * Create a drag image list for the specified item.
4282 * [I] infoPtr : valid pointer to the listview structure
4283 * [I] iItem : index of item
4284 * [O] lppt : Upperr-left corner of the image
4287 * Returns a handle to the image list if successful, NULL otherwise.
4289 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4295 HBITMAP hbmp, hOldbmp;
4296 HIMAGELIST dragList = 0;
4297 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4299 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4302 rcItem.left = LVIR_BOUNDS;
4303 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4306 lppt->x = rcItem.left;
4307 lppt->y = rcItem.top;
4309 size.cx = rcItem.right - rcItem.left;
4310 size.cy = rcItem.bottom - rcItem.top;
4312 hdcOrig = GetDC(infoPtr->hwndSelf);
4313 hdc = CreateCompatibleDC(hdcOrig);
4314 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4315 hOldbmp = SelectObject(hdc, hbmp);
4317 rcItem.left = rcItem.top = 0;
4318 rcItem.right = size.cx;
4319 rcItem.bottom = size.cy;
4320 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4323 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4325 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4326 SelectObject(hdc, hOldbmp);
4327 ImageList_Add(dragList, hbmp, 0);
4330 SelectObject(hdc, hOldbmp);
4334 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4336 TRACE("ret=%p\n", dragList);
4344 * Removes all listview items and subitems.
4347 * [I] infoPtr : valid pointer to the listview structure
4353 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4356 HDPA hdpaSubItems = NULL;
4363 /* we do it directly, to avoid notifications */
4364 ranges_clear(infoPtr->selectionRanges);
4365 infoPtr->nSelectionMark = -1;
4366 infoPtr->nFocusedItem = -1;
4367 SetRectEmpty(&infoPtr->rcFocus);
4368 /* But we are supposed to leave nHotItem as is! */
4371 /* send LVN_DELETEALLITEMS notification */
4372 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4374 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4376 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4378 /* send LVN_DELETEITEM notification, if not suppressed */
4379 if (!bSuppress) notify_deleteitem(infoPtr, i);
4380 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4382 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4383 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4385 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4386 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4389 DPA_Destroy(hdpaSubItems);
4390 DPA_DeletePtr(infoPtr->hdpaItems, i);
4392 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4393 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4394 infoPtr->nItemCount --;
4397 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4398 LISTVIEW_UpdateScroll(infoPtr);
4399 LISTVIEW_InvalidateList(infoPtr);
4406 * Scrolls, and updates the columns, when a column is changing width.
4409 * [I] infoPtr : valid pointer to the listview structure
4410 * [I] nColumn : column to scroll
4411 * [I] dx : amount of scroll, in pixels
4416 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4418 COLUMN_INFO *lpColumnInfo;
4423 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4424 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4425 rcCol = lpColumnInfo->rcHeader;
4426 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4427 rcCol.left = rcCol.right;
4429 /* ajust the other columns */
4430 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4432 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4433 lpColumnInfo->rcHeader.left += dx;
4434 lpColumnInfo->rcHeader.right += dx;
4437 /* do not update screen if not in report mode */
4438 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4440 /* if we have a focus, must first erase the focus rect */
4441 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4443 /* Need to reset the item width when inserting a new column */
4444 infoPtr->nItemWidth += dx;
4446 LISTVIEW_UpdateScroll(infoPtr);
4447 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4449 /* scroll to cover the deleted column, and invalidate for redraw */
4450 rcOld = infoPtr->rcList;
4451 rcOld.left = ptOrigin.x + rcCol.left + dx;
4452 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4454 /* we can restore focus now */
4455 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4460 * Removes a column from the listview control.
4463 * [I] infoPtr : valid pointer to the listview structure
4464 * [I] nColumn : column index
4470 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4474 TRACE("nColumn=%d\n", nColumn);
4476 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4477 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4479 /* While the MSDN specifically says that column zero should not be deleted,
4480 what actually happens is that the column itself is deleted but no items or subitems
4484 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4486 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4489 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4490 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4492 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4494 SUBITEM_INFO *lpSubItem, *lpDelItem;
4496 INT nItem, nSubItem, i;
4498 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4500 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4503 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4505 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4506 if (lpSubItem->iSubItem == nColumn)
4509 lpDelItem = lpSubItem;
4511 else if (lpSubItem->iSubItem > nColumn)
4513 lpSubItem->iSubItem--;
4517 /* if we found our subitem, zapp it */
4521 if (is_textW(lpDelItem->hdr.pszText))
4522 Free(lpDelItem->hdr.pszText);
4527 /* free dpa memory */
4528 DPA_DeletePtr(hdpaSubItems, nSubItem);
4533 /* update the other column info */
4534 LISTVIEW_UpdateItemSize(infoPtr);
4535 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4536 LISTVIEW_InvalidateList(infoPtr);
4538 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4545 * Invalidates the listview after an item's insertion or deletion.
4548 * [I] infoPtr : valid pointer to the listview structure
4549 * [I] nItem : item index
4550 * [I] dir : -1 if deleting, 1 if inserting
4555 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4557 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4558 INT nPerCol, nItemCol, nItemRow;
4562 /* if we don't refresh, what's the point of scrolling? */
4563 if (!is_redrawing(infoPtr)) return;
4565 assert (abs(dir) == 1);
4567 /* arrange icons if autoarrange is on */
4568 if (is_autoarrange(infoPtr))
4570 BOOL arrange = TRUE;
4571 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4572 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4573 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4576 /* scrollbars need updating */
4577 LISTVIEW_UpdateScroll(infoPtr);
4579 /* figure out the item's position */
4580 if (uView == LVS_REPORT)
4581 nPerCol = infoPtr->nItemCount + 1;
4582 else if (uView == LVS_LIST)
4583 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4584 else /* LVS_ICON, or LVS_SMALLICON */
4587 nItemCol = nItem / nPerCol;
4588 nItemRow = nItem % nPerCol;
4589 LISTVIEW_GetOrigin(infoPtr, &Origin);
4591 /* move the items below up a slot */
4592 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4593 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4594 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4595 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4596 OffsetRect(&rcScroll, Origin.x, Origin.y);
4597 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4598 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4600 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4601 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4602 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4605 /* report has only that column, so we're done */
4606 if (uView == LVS_REPORT) return;
4608 /* now for LISTs, we have to deal with the columns to the right */
4609 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4611 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4612 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4613 OffsetRect(&rcScroll, Origin.x, Origin.y);
4614 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4615 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4616 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4621 * Removes an item from the listview control.
4624 * [I] infoPtr : valid pointer to the listview structure
4625 * [I] nItem : item index
4631 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4633 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4636 TRACE("(nItem=%d)\n", nItem);
4638 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4640 /* remove selection, and focus */
4642 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4643 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4645 /* send LVN_DELETEITEM notification. */
4646 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4648 /* we need to do this here, because we'll be deleting stuff */
4649 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4650 LISTVIEW_InvalidateItem(infoPtr, nItem);
4652 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4658 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4659 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4661 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4662 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4665 DPA_Destroy(hdpaSubItems);
4668 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4670 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4671 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4674 infoPtr->nItemCount--;
4675 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4677 /* now is the invalidation fun */
4678 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4685 * Callback implementation for editlabel control
4688 * [I] infoPtr : valid pointer to the listview structure
4689 * [I] pszText : modified text
4690 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4696 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4698 HWND hwndSelf = infoPtr->hwndSelf;
4699 NMLVDISPINFOW dispInfo;
4701 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4703 ZeroMemory(&dispInfo, sizeof(dispInfo));
4704 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4705 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4706 dispInfo.item.iSubItem = 0;
4707 dispInfo.item.stateMask = ~0;
4708 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4709 /* add the text from the edit in */
4710 dispInfo.item.mask |= LVIF_TEXT;
4711 dispInfo.item.pszText = pszText;
4712 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4714 /* Do we need to update the Item Text */
4715 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4716 if (!IsWindow(hwndSelf))
4718 if (!pszText) return TRUE;
4720 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4722 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4723 ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4724 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4726 LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4731 ZeroMemory(&dispInfo, sizeof(dispInfo));
4732 dispInfo.item.mask = LVIF_TEXT;
4733 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4734 dispInfo.item.iSubItem = 0;
4735 dispInfo.item.pszText = pszText;
4736 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4737 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4742 * Begin in place editing of specified list view item
4745 * [I] infoPtr : valid pointer to the listview structure
4746 * [I] nItem : item index
4747 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4753 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4755 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4756 NMLVDISPINFOW dispInfo;
4758 HWND hwndSelf = infoPtr->hwndSelf;
4760 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4762 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4763 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4765 infoPtr->nEditLabelItem = nItem;
4767 /* Is the EditBox still there, if so remove it */
4768 if(infoPtr->hwndEdit != 0)
4770 SetFocus(infoPtr->hwndSelf);
4771 infoPtr->hwndEdit = 0;
4774 LISTVIEW_SetSelection(infoPtr, nItem);
4775 LISTVIEW_SetItemFocus(infoPtr, nItem);
4776 LISTVIEW_InvalidateItem(infoPtr, nItem);
4778 rect.left = LVIR_LABEL;
4779 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4781 ZeroMemory(&dispInfo, sizeof(dispInfo));
4782 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4783 dispInfo.item.iItem = nItem;
4784 dispInfo.item.iSubItem = 0;
4785 dispInfo.item.stateMask = ~0;
4786 dispInfo.item.pszText = szDispText;
4787 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4788 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4790 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4791 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4792 if (!infoPtr->hwndEdit) return 0;
4794 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4796 if (!IsWindow(hwndSelf))
4798 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4799 infoPtr->hwndEdit = 0;
4803 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4804 SetFocus(infoPtr->hwndEdit);
4805 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4806 return infoPtr->hwndEdit;
4812 * Ensures the specified item is visible, scrolling into view if necessary.
4815 * [I] infoPtr : valid pointer to the listview structure
4816 * [I] nItem : item index
4817 * [I] bPartial : partially or entirely visible
4823 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4825 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4826 INT nScrollPosHeight = 0;
4827 INT nScrollPosWidth = 0;
4828 INT nHorzAdjust = 0;
4829 INT nVertAdjust = 0;
4832 RECT rcItem, rcTemp;
4834 rcItem.left = LVIR_BOUNDS;
4835 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4837 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4839 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4841 /* scroll left/right, but in LVS_REPORT mode */
4842 if (uView == LVS_LIST)
4843 nScrollPosWidth = infoPtr->nItemWidth;
4844 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4845 nScrollPosWidth = 1;
4847 if (rcItem.left < infoPtr->rcList.left)
4850 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4855 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4859 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4861 /* scroll up/down, but not in LVS_LIST mode */
4862 if (uView == LVS_REPORT)
4863 nScrollPosHeight = infoPtr->nItemHeight;
4864 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4865 nScrollPosHeight = 1;
4867 if (rcItem.top < infoPtr->rcList.top)
4870 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4875 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4879 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4881 if (nScrollPosWidth)
4883 INT diff = nHorzDiff / nScrollPosWidth;
4884 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4885 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4888 if (nScrollPosHeight)
4890 INT diff = nVertDiff / nScrollPosHeight;
4891 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4892 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4900 * Searches for an item with specific characteristics.
4903 * [I] hwnd : window handle
4904 * [I] nStart : base item index
4905 * [I] lpFindInfo : item information to look for
4908 * SUCCESS : index of item
4911 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
4912 const LVFINDINFOW *lpFindInfo)
4914 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4915 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4916 BOOL bWrap = FALSE, bNearest = FALSE;
4917 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4918 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4919 POINT Position, Destination;
4922 if (!lpFindInfo || nItem < 0) return -1;
4925 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4927 lvItem.mask |= LVIF_TEXT;
4928 lvItem.pszText = szDispText;
4929 lvItem.cchTextMax = DISP_TEXT_SIZE;
4932 if (lpFindInfo->flags & LVFI_WRAP)
4935 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4936 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4941 LISTVIEW_GetOrigin(infoPtr, &Origin);
4942 Destination.x = lpFindInfo->pt.x - Origin.x;
4943 Destination.y = lpFindInfo->pt.y - Origin.y;
4944 switch(lpFindInfo->vkDirection)
4946 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4947 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4948 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4949 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4950 case VK_HOME: Destination.x = Destination.y = 0; break;
4951 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4952 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4954 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4955 Destination.x = rcArea.right;
4956 Destination.y = rcArea.bottom;
4958 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4962 else Destination.x = Destination.y = 0;
4964 /* if LVFI_PARAM is specified, all other flags are ignored */
4965 if (lpFindInfo->flags & LVFI_PARAM)
4967 lvItem.mask |= LVIF_PARAM;
4969 lvItem.mask &= ~LVIF_TEXT;
4973 for (; nItem < nLast; nItem++)
4975 lvItem.iItem = nItem;
4976 lvItem.iSubItem = 0;
4977 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4979 if (lvItem.mask & LVIF_PARAM)
4981 if (lpFindInfo->lParam == lvItem.lParam)
4987 if (lvItem.mask & LVIF_TEXT)
4989 if (lpFindInfo->flags & LVFI_PARTIAL)
4991 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4995 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4999 if (!bNearest) return nItem;
5001 /* This is very inefficient. To do a good job here,
5002 * we need a sorted array of (x,y) item positions */
5003 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5005 /* compute the distance^2 to the destination */
5006 xdist = Destination.x - Position.x;
5007 ydist = Destination.y - Position.y;
5008 dist = xdist * xdist + ydist * ydist;
5010 /* remember the distance, and item if it's closer */
5014 nNearestItem = nItem;
5021 nLast = min(nStart + 1, infoPtr->nItemCount);
5026 return nNearestItem;
5031 * Searches for an item with specific characteristics.
5034 * [I] hwnd : window handle
5035 * [I] nStart : base item index
5036 * [I] lpFindInfo : item information to look for
5039 * SUCCESS : index of item
5042 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5043 const LVFINDINFOA *lpFindInfo)
5045 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5050 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5051 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5052 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5053 textfreeT(strW, FALSE);
5059 * Retrieves the background image of the listview control.
5062 * [I] infoPtr : valid pointer to the listview structure
5063 * [O] lpBkImage : background image attributes
5069 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5071 /* FIXME (listview, "empty stub!\n"); */
5077 * Retrieves column attributes.
5080 * [I] infoPtr : valid pointer to the listview structure
5081 * [I] nColumn : column index
5082 * [IO] lpColumn : column information
5083 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5084 * otherwise it is in fact a LPLVCOLUMNA
5090 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5092 COLUMN_INFO *lpColumnInfo;
5095 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5096 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5098 /* initialize memory */
5099 ZeroMemory(&hdi, sizeof(hdi));
5101 if (lpColumn->mask & LVCF_TEXT)
5103 hdi.mask |= HDI_TEXT;
5104 hdi.pszText = lpColumn->pszText;
5105 hdi.cchTextMax = lpColumn->cchTextMax;
5108 if (lpColumn->mask & LVCF_IMAGE)
5109 hdi.mask |= HDI_IMAGE;
5111 if (lpColumn->mask & LVCF_ORDER)
5112 hdi.mask |= HDI_ORDER;
5114 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5116 if (lpColumn->mask & LVCF_FMT)
5117 lpColumn->fmt = lpColumnInfo->fmt;
5119 if (lpColumn->mask & LVCF_WIDTH)
5120 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5122 if (lpColumn->mask & LVCF_IMAGE)
5123 lpColumn->iImage = hdi.iImage;
5125 if (lpColumn->mask & LVCF_ORDER)
5126 lpColumn->iOrder = hdi.iOrder;
5132 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5139 /* FIXME: little hack */
5140 for (i = 0; i < iCount; i++)
5148 * Retrieves the column width.
5151 * [I] infoPtr : valid pointer to the listview structure
5152 * [I] int : column index
5155 * SUCCESS : column width
5158 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5160 INT nColumnWidth = 0;
5163 TRACE("nColumn=%d\n", nColumn);
5165 /* we have a 'column' in LIST and REPORT mode only */
5166 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5169 nColumnWidth = infoPtr->nItemWidth;
5172 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5173 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5174 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5176 * TODO: should we do the same in LVM_GETCOLUMN?
5178 hdItem.mask = HDI_WIDTH;
5179 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5181 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5184 nColumnWidth = hdItem.cxy;
5188 TRACE("nColumnWidth=%d\n", nColumnWidth);
5189 return nColumnWidth;
5194 * In list or report display mode, retrieves the number of items that can fit
5195 * vertically in the visible area. In icon or small icon display mode,
5196 * retrieves the total number of visible items.
5199 * [I] infoPtr : valid pointer to the listview structure
5202 * Number of fully visible items.
5204 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5206 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5210 return infoPtr->nItemCount;
5212 return LISTVIEW_GetCountPerColumn(infoPtr);
5214 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5222 * Retrieves an image list handle.
5225 * [I] infoPtr : valid pointer to the listview structure
5226 * [I] nImageList : image list identifier
5229 * SUCCESS : image list handle
5232 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5236 case LVSIL_NORMAL: return infoPtr->himlNormal;
5237 case LVSIL_SMALL: return infoPtr->himlSmall;
5238 case LVSIL_STATE: return infoPtr->himlState;
5243 /* LISTVIEW_GetISearchString */
5247 * Retrieves item attributes.
5250 * [I] hwnd : window handle
5251 * [IO] lpLVItem : item info
5252 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5253 * if FALSE, then lpLVItem is a LPLVITEMA.
5256 * This is the internal 'GetItem' interface -- it tries to
5257 * be smart and avoid text copies, if possible, by modifying
5258 * lpLVItem->pszText to point to the text string. Please note
5259 * that this is not always possible (e.g. OWNERDATA), so on
5260 * entry you *must* supply valid values for pszText, and cchTextMax.
5261 * The only difference to the documented interface is that upon
5262 * return, you should use *only* the lpLVItem->pszText, rather than
5263 * the buffer pointer you provided on input. Most code already does
5264 * that, so it's not a problem.
5265 * For the two cases when the text must be copied (that is,
5266 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5272 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5274 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5275 NMLVDISPINFOW dispInfo;
5281 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5283 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5286 if (lpLVItem->mask == 0) return TRUE;
5288 /* make a local copy */
5289 isubitem = lpLVItem->iSubItem;
5291 /* a quick optimization if all we're asked is the focus state
5292 * these queries are worth optimising since they are common,
5293 * and can be answered in constant time, without the heavy accesses */
5294 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5295 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5297 lpLVItem->state = 0;
5298 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5299 lpLVItem->state |= LVIS_FOCUSED;
5303 ZeroMemory(&dispInfo, sizeof(dispInfo));
5305 /* if the app stores all the data, handle it separately */
5306 if (infoPtr->dwStyle & LVS_OWNERDATA)
5308 dispInfo.item.state = 0;
5310 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5311 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5313 /* NOTE: copy only fields which we _know_ are initialized, some apps
5314 * depend on the uninitialized fields being 0 */
5315 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5316 dispInfo.item.iItem = lpLVItem->iItem;
5317 dispInfo.item.iSubItem = isubitem;
5318 if (lpLVItem->mask & LVIF_TEXT)
5320 dispInfo.item.pszText = lpLVItem->pszText;
5321 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5323 if (lpLVItem->mask & LVIF_STATE)
5324 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5325 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5326 dispInfo.item.stateMask = lpLVItem->stateMask;
5327 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5329 /* full size structure expected - _WIN32IE >= 0x560 */
5330 *lpLVItem = dispInfo.item;
5332 else if (lpLVItem->mask & LVIF_INDENT)
5334 /* indent member expected - _WIN32IE >= 0x300 */
5335 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5339 /* minimal structure expected */
5340 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5342 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5345 /* make sure lParam is zeroed out */
5346 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5348 /* we store only a little state, so if we're not asked, we're done */
5349 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5351 /* if focus is handled by us, report it */
5352 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5354 lpLVItem->state &= ~LVIS_FOCUSED;
5355 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5356 lpLVItem->state |= LVIS_FOCUSED;
5359 /* and do the same for selection, if we handle it */
5360 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5362 lpLVItem->state &= ~LVIS_SELECTED;
5363 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5364 lpLVItem->state |= LVIS_SELECTED;
5370 /* find the item and subitem structures before we proceed */
5371 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5372 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5377 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5378 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5381 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5386 pItemHdr = &lpItem->hdr;
5388 /* Do we need to query the state from the app? */
5389 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5391 dispInfo.item.mask |= LVIF_STATE;
5392 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5395 /* Do we need to enquire about the image? */
5396 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5397 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5399 dispInfo.item.mask |= LVIF_IMAGE;
5400 dispInfo.item.iImage = I_IMAGECALLBACK;
5403 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5404 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5406 dispInfo.item.mask |= LVIF_TEXT;
5407 dispInfo.item.pszText = lpLVItem->pszText;
5408 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5409 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5410 *dispInfo.item.pszText = '\0';
5413 /* If we don't have all the requested info, query the application */
5414 if (dispInfo.item.mask != 0)
5416 dispInfo.item.iItem = lpLVItem->iItem;
5417 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5418 dispInfo.item.lParam = lpItem->lParam;
5419 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5420 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5423 /* we should not store values for subitems */
5424 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5426 /* Now, handle the iImage field */
5427 if (dispInfo.item.mask & LVIF_IMAGE)
5429 lpLVItem->iImage = dispInfo.item.iImage;
5430 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5431 pItemHdr->iImage = dispInfo.item.iImage;
5433 else if (lpLVItem->mask & LVIF_IMAGE)
5435 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5436 lpLVItem->iImage = pItemHdr->iImage;
5438 lpLVItem->iImage = 0;
5441 /* The pszText field */
5442 if (dispInfo.item.mask & LVIF_TEXT)
5444 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5445 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5447 lpLVItem->pszText = dispInfo.item.pszText;
5449 else if (lpLVItem->mask & LVIF_TEXT)
5451 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5452 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5455 /* Next is the lParam field */
5456 if (dispInfo.item.mask & LVIF_PARAM)
5458 lpLVItem->lParam = dispInfo.item.lParam;
5459 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5460 lpItem->lParam = dispInfo.item.lParam;
5462 else if (lpLVItem->mask & LVIF_PARAM)
5463 lpLVItem->lParam = lpItem->lParam;
5465 /* if this is a subitem, we're done */
5466 if (isubitem) return TRUE;
5468 /* ... the state field (this one is different due to uCallbackmask) */
5469 if (lpLVItem->mask & LVIF_STATE)
5471 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5472 if (dispInfo.item.mask & LVIF_STATE)
5474 lpLVItem->state &= ~dispInfo.item.stateMask;
5475 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5477 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5479 lpLVItem->state &= ~LVIS_FOCUSED;
5480 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5481 lpLVItem->state |= LVIS_FOCUSED;
5483 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5485 lpLVItem->state &= ~LVIS_SELECTED;
5486 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5487 lpLVItem->state |= LVIS_SELECTED;
5491 /* and last, but not least, the indent field */
5492 if (lpLVItem->mask & LVIF_INDENT)
5493 lpLVItem->iIndent = lpItem->iIndent;
5500 * Retrieves item attributes.
5503 * [I] hwnd : window handle
5504 * [IO] lpLVItem : item info
5505 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5506 * if FALSE, then lpLVItem is a LPLVITEMA.
5509 * This is the external 'GetItem' interface -- it properly copies
5510 * the text in the provided buffer.
5516 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5521 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5524 pszText = lpLVItem->pszText;
5525 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5526 if (bResult && lpLVItem->pszText != pszText)
5527 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5528 lpLVItem->pszText = pszText;
5536 * Retrieves the position (upper-left) of the listview control item.
5537 * Note that for LVS_ICON style, the upper-left is that of the icon
5538 * and not the bounding box.
5541 * [I] infoPtr : valid pointer to the listview structure
5542 * [I] nItem : item index
5543 * [O] lpptPosition : coordinate information
5549 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5551 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5554 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5556 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5558 LISTVIEW_GetOrigin(infoPtr, &Origin);
5559 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5561 if (uView == LVS_ICON)
5563 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5564 lpptPosition->y += ICON_TOP_PADDING;
5566 lpptPosition->x += Origin.x;
5567 lpptPosition->y += Origin.y;
5569 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5576 * Retrieves the bounding rectangle for a listview control item.
5579 * [I] infoPtr : valid pointer to the listview structure
5580 * [I] nItem : item index
5581 * [IO] lprc : bounding rectangle coordinates
5582 * lprc->left specifies the portion of the item for which the bounding
5583 * rectangle will be retrieved.
5585 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5586 * including the icon and label.
5589 * * Experiment shows that native control returns:
5590 * * width = min (48, length of text line)
5591 * * .left = position.x - (width - iconsize.cx)/2
5592 * * .right = .left + width
5593 * * height = #lines of text * ntmHeight + icon height + 8
5594 * * .top = position.y - 2
5595 * * .bottom = .top + height
5596 * * separation between items .y = itemSpacing.cy - height
5597 * * .x = itemSpacing.cx - width
5598 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5601 * * Experiment shows that native control returns:
5602 * * width = iconSize.cx + 16
5603 * * .left = position.x - (width - iconsize.cx)/2
5604 * * .right = .left + width
5605 * * height = iconSize.cy + 4
5606 * * .top = position.y - 2
5607 * * .bottom = .top + height
5608 * * separation between items .y = itemSpacing.cy - height
5609 * * .x = itemSpacing.cx - width
5610 * LVIR_LABEL Returns the bounding rectangle of the item text.
5613 * * Experiment shows that native control returns:
5614 * * width = text length
5615 * * .left = position.x - width/2
5616 * * .right = .left + width
5617 * * height = ntmH * linecount + 2
5618 * * .top = position.y + iconSize.cy + 6
5619 * * .bottom = .top + height
5620 * * separation between items .y = itemSpacing.cy - height
5621 * * .x = itemSpacing.cx - width
5622 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5623 * rectangles, but excludes columns in report view.
5630 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5631 * upon whether the window has the focus currently and on whether the item
5632 * is the one with the focus. Ensure that the control's record of which
5633 * item has the focus agrees with the items' records.
5635 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5637 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5638 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5639 BOOL doLabel = TRUE, oversizedBox = FALSE;
5640 POINT Position, Origin;
5643 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5645 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5647 LISTVIEW_GetOrigin(infoPtr, &Origin);
5648 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5650 /* Be smart and try to figure out the minimum we have to do */
5651 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5652 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5653 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5654 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5655 oversizedBox = TRUE;
5657 /* get what we need from the item before hand, so we make
5658 * only one request. This can speed up things, if data
5659 * is stored on the app side */
5661 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5662 if (doLabel) lvItem.mask |= LVIF_TEXT;
5663 lvItem.iItem = nItem;
5664 lvItem.iSubItem = 0;
5665 lvItem.pszText = szDispText;
5666 lvItem.cchTextMax = DISP_TEXT_SIZE;
5667 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5668 /* we got the state already up, simulate it here, to avoid a reget */
5669 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5671 lvItem.mask |= LVIF_STATE;
5672 lvItem.stateMask = LVIS_FOCUSED;
5673 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5676 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5677 lprc->left = LVIR_BOUNDS;
5681 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5685 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
5689 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5692 case LVIR_SELECTBOUNDS:
5693 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
5697 WARN("Unknown value: %d\n", lprc->left);
5701 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5703 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5710 * Retrieves the spacing between listview control items.
5713 * [I] infoPtr : valid pointer to the listview structure
5714 * [IO] lprc : rectangle to receive the output
5715 * on input, lprc->top = nSubItem
5716 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5718 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5719 * not only those of the first column.
5720 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5726 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5732 if (!lprc) return FALSE;
5734 nColumn = lprc->top;
5736 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5737 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5739 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5741 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5743 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5745 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5748 lvItem.iItem = nItem;
5749 lvItem.iSubItem = nColumn;
5751 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5755 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5760 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5764 ERR("Unknown bounds=%d\n", lprc->left);
5768 OffsetRect(lprc, Position.x, Position.y);
5775 * Retrieves the width of a label.
5778 * [I] infoPtr : valid pointer to the listview structure
5781 * SUCCESS : string width (in pixels)
5784 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
5786 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5789 TRACE("(nItem=%d)\n", nItem);
5791 lvItem.mask = LVIF_TEXT;
5792 lvItem.iItem = nItem;
5793 lvItem.iSubItem = 0;
5794 lvItem.pszText = szDispText;
5795 lvItem.cchTextMax = DISP_TEXT_SIZE;
5796 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5798 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5803 * Retrieves the spacing between listview control items.
5806 * [I] infoPtr : valid pointer to the listview structure
5807 * [I] bSmall : flag for small or large icon
5810 * Horizontal + vertical spacing
5812 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
5818 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5822 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5823 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5825 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5832 * Retrieves the state of a listview control item.
5835 * [I] infoPtr : valid pointer to the listview structure
5836 * [I] nItem : item index
5837 * [I] uMask : state mask
5840 * State specified by the mask.
5842 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5846 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5848 lvItem.iItem = nItem;
5849 lvItem.iSubItem = 0;
5850 lvItem.mask = LVIF_STATE;
5851 lvItem.stateMask = uMask;
5852 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5854 return lvItem.state & uMask;
5859 * Retrieves the text of a listview control item or subitem.
5862 * [I] hwnd : window handle
5863 * [I] nItem : item index
5864 * [IO] lpLVItem : item information
5865 * [I] isW : TRUE if lpLVItem is Unicode
5868 * SUCCESS : string length
5871 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5873 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5875 lpLVItem->mask = LVIF_TEXT;
5876 lpLVItem->iItem = nItem;
5877 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5879 return textlenT(lpLVItem->pszText, isW);
5884 * Searches for an item based on properties + relationships.
5887 * [I] infoPtr : valid pointer to the listview structure
5888 * [I] nItem : item index
5889 * [I] uFlags : relationship flag
5892 * SUCCESS : item index
5895 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5897 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5899 LVFINDINFOW lvFindInfo;
5900 INT nCountPerColumn;
5904 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5905 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5907 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5909 if (uFlags & LVNI_CUT)
5912 if (uFlags & LVNI_DROPHILITED)
5913 uMask |= LVIS_DROPHILITED;
5915 if (uFlags & LVNI_FOCUSED)
5916 uMask |= LVIS_FOCUSED;
5918 if (uFlags & LVNI_SELECTED)
5919 uMask |= LVIS_SELECTED;
5921 /* if we're asked for the focused item, that's only one,
5922 * so it's worth optimizing */
5923 if (uFlags & LVNI_FOCUSED)
5925 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5926 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5929 if (uFlags & LVNI_ABOVE)
5931 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5936 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5942 /* Special case for autoarrange - move 'til the top of a list */
5943 if (is_autoarrange(infoPtr))
5945 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5946 while (nItem - nCountPerRow >= 0)
5948 nItem -= nCountPerRow;
5949 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5954 lvFindInfo.flags = LVFI_NEARESTXY;
5955 lvFindInfo.vkDirection = VK_UP;
5956 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5957 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5959 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5964 else if (uFlags & LVNI_BELOW)
5966 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5968 while (nItem < infoPtr->nItemCount)
5971 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5977 /* Special case for autoarrange - move 'til the bottom of a list */
5978 if (is_autoarrange(infoPtr))
5980 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5981 while (nItem + nCountPerRow < infoPtr->nItemCount )
5983 nItem += nCountPerRow;
5984 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5989 lvFindInfo.flags = LVFI_NEARESTXY;
5990 lvFindInfo.vkDirection = VK_DOWN;
5991 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5992 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5994 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5999 else if (uFlags & LVNI_TOLEFT)
6001 if (uView == LVS_LIST)
6003 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6004 while (nItem - nCountPerColumn >= 0)
6006 nItem -= nCountPerColumn;
6007 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6011 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6013 /* Special case for autoarrange - move 'ti the beginning of a row */
6014 if (is_autoarrange(infoPtr))
6016 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6017 while (nItem % nCountPerRow > 0)
6020 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6025 lvFindInfo.flags = LVFI_NEARESTXY;
6026 lvFindInfo.vkDirection = VK_LEFT;
6027 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6028 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6030 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6035 else if (uFlags & LVNI_TORIGHT)
6037 if (uView == LVS_LIST)
6039 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6040 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6042 nItem += nCountPerColumn;
6043 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6047 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6049 /* Special case for autoarrange - move 'til the end of a row */
6050 if (is_autoarrange(infoPtr))
6052 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6053 while (nItem % nCountPerRow < nCountPerRow - 1 )
6056 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6061 lvFindInfo.flags = LVFI_NEARESTXY;
6062 lvFindInfo.vkDirection = VK_RIGHT;
6063 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6064 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6066 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6075 /* search by index */
6076 for (i = nItem; i < infoPtr->nItemCount; i++)
6078 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6086 /* LISTVIEW_GetNumberOfWorkAreas */
6090 * Retrieves the origin coordinates when in icon or small icon display mode.
6093 * [I] infoPtr : valid pointer to the listview structure
6094 * [O] lpptOrigin : coordinate information
6099 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6101 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6102 INT nHorzPos = 0, nVertPos = 0;
6103 SCROLLINFO scrollInfo;
6105 scrollInfo.cbSize = sizeof(SCROLLINFO);
6106 scrollInfo.fMask = SIF_POS;
6108 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6109 nHorzPos = scrollInfo.nPos;
6110 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6111 nVertPos = scrollInfo.nPos;
6113 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6115 lpptOrigin->x = infoPtr->rcList.left;
6116 lpptOrigin->y = infoPtr->rcList.top;
6117 if (uView == LVS_LIST)
6118 nHorzPos *= infoPtr->nItemWidth;
6119 else if (uView == LVS_REPORT)
6120 nVertPos *= infoPtr->nItemHeight;
6122 lpptOrigin->x -= nHorzPos;
6123 lpptOrigin->y -= nVertPos;
6125 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6130 * Retrieves the width of a string.
6133 * [I] hwnd : window handle
6134 * [I] lpszText : text string to process
6135 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6138 * SUCCESS : string width (in pixels)
6141 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6146 if (is_textT(lpszText, isW))
6148 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6149 HDC hdc = GetDC(infoPtr->hwndSelf);
6150 HFONT hOldFont = SelectObject(hdc, hFont);
6153 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6155 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6156 SelectObject(hdc, hOldFont);
6157 ReleaseDC(infoPtr->hwndSelf, hdc);
6159 return stringSize.cx;
6164 * Determines which listview item is located at the specified position.
6167 * [I] infoPtr : valid pointer to the listview structure
6168 * [IO] lpht : hit test information
6169 * [I] subitem : fill out iSubItem.
6170 * [I] select : return the index only if the hit selects the item
6173 * (mm 20001022): We must not allow iSubItem to be touched, for
6174 * an app might pass only a structure with space up to iItem!
6175 * (MS Office 97 does that for instance in the file open dialog)
6178 * SUCCESS : item index
6181 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6183 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6184 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6185 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6186 POINT Origin, Position, opt;
6191 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6195 if (subitem) lpht->iSubItem = 0;
6197 if (infoPtr->rcList.left > lpht->pt.x)
6198 lpht->flags |= LVHT_TOLEFT;
6199 else if (infoPtr->rcList.right < lpht->pt.x)
6200 lpht->flags |= LVHT_TORIGHT;
6202 if (infoPtr->rcList.top > lpht->pt.y)
6203 lpht->flags |= LVHT_ABOVE;
6204 else if (infoPtr->rcList.bottom < lpht->pt.y)
6205 lpht->flags |= LVHT_BELOW;
6207 TRACE("lpht->flags=0x%x\n", lpht->flags);
6208 if (lpht->flags) return -1;
6210 lpht->flags |= LVHT_NOWHERE;
6212 LISTVIEW_GetOrigin(infoPtr, &Origin);
6214 /* first deal with the large items */
6215 rcSearch.left = lpht->pt.x;
6216 rcSearch.top = lpht->pt.y;
6217 rcSearch.right = rcSearch.left + 1;
6218 rcSearch.bottom = rcSearch.top + 1;
6220 iterator_frameditems(&i, infoPtr, &rcSearch);
6221 iterator_next(&i); /* go to first item in the sequence */
6223 iterator_destroy(&i);
6225 TRACE("lpht->iItem=%d\n", iItem);
6226 if (iItem == -1) return -1;
6228 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6229 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6230 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6231 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6232 lvItem.iItem = iItem;
6233 lvItem.iSubItem = 0;
6234 lvItem.pszText = szDispText;
6235 lvItem.cchTextMax = DISP_TEXT_SIZE;
6236 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6237 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6239 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6240 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6241 opt.x = lpht->pt.x - Position.x - Origin.x;
6242 opt.y = lpht->pt.y - Position.y - Origin.y;
6244 if (uView == LVS_REPORT)
6247 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6248 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6249 if (!PtInRect(&rcBounds, opt)) return -1;
6251 if (PtInRect(&rcIcon, opt))
6252 lpht->flags |= LVHT_ONITEMICON;
6253 else if (PtInRect(&rcLabel, opt))
6254 lpht->flags |= LVHT_ONITEMLABEL;
6255 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6256 lpht->flags |= LVHT_ONITEMSTATEICON;
6257 if (lpht->flags & LVHT_ONITEM)
6258 lpht->flags &= ~LVHT_NOWHERE;
6260 TRACE("lpht->flags=0x%x\n", lpht->flags);
6261 if (uView == LVS_REPORT && subitem)
6265 rcBounds.right = rcBounds.left;
6266 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6268 rcBounds.left = rcBounds.right;
6269 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6270 if (PtInRect(&rcBounds, opt))
6278 if (select && !(uView == LVS_REPORT &&
6279 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6280 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6282 if (uView == LVS_REPORT)
6284 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6285 UnionRect(&rcBounds, &rcBounds, &rcState);
6287 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6289 return lpht->iItem = iItem;
6293 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6294 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6295 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6296 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6297 their own sort proc. when sending LVM_SORTITEMS.
6300 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6302 LVS_SORTXXX must be specified,
6303 LVS_OWNERDRAW is not set,
6304 <item>.pszText is not LPSTR_TEXTCALLBACK.
6306 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6307 are sorted based on item text..."
6309 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6311 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6312 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6313 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6315 /* if we're sorting descending, negate the return value */
6316 return (((const LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6321 * Inserts a new item in the listview control.
6324 * [I] infoPtr : valid pointer to the listview structure
6325 * [I] lpLVItem : item information
6326 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6329 * SUCCESS : new item index
6332 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6334 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6339 BOOL is_sorted, has_changed;
6341 HWND hwndSelf = infoPtr->hwndSelf;
6343 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6345 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6347 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6348 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6350 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6352 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6354 /* insert item in listview control data structure */
6355 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6356 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6358 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6359 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6361 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6363 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6364 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6365 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6366 if (nItem == -1) goto fail;
6367 infoPtr->nItemCount++;
6369 /* shift indices first so they don't get tangled */
6370 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6372 /* set the item attributes */
6373 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6375 /* full size structure expected - _WIN32IE >= 0x560 */
6378 else if (lpLVItem->mask & LVIF_INDENT)
6380 /* indent member expected - _WIN32IE >= 0x300 */
6381 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6385 /* minimal structure expected */
6386 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6389 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6391 item.mask |= LVIF_STATE;
6392 item.stateMask |= LVIS_STATEIMAGEMASK;
6393 item.state &= ~LVIS_STATEIMAGEMASK;
6394 item.state |= INDEXTOSTATEIMAGEMASK(1);
6396 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6398 /* if we're sorted, sort the list, and update the index */
6401 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6402 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6403 assert(nItem != -1);
6406 /* make room for the position, if we are in the right mode */
6407 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6409 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6411 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6413 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6418 /* send LVN_INSERTITEM notification */
6419 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6421 nmlv.lParam = lpItem->lParam;
6422 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6423 if (!IsWindow(hwndSelf))
6426 /* align items (set position of each item) */
6427 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6431 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6432 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6434 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6436 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6439 /* now is the invalidation fun */
6440 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6444 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6445 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6446 infoPtr->nItemCount--;
6448 DPA_DeletePtr(hdpaSubItems, 0);
6449 DPA_Destroy (hdpaSubItems);
6456 * Redraws a range of items.
6459 * [I] infoPtr : valid pointer to the listview structure
6460 * [I] nFirst : first item
6461 * [I] nLast : last item
6467 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6471 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6472 max(nFirst, nLast) >= infoPtr->nItemCount)
6475 for (i = nFirst; i <= nLast; i++)
6476 LISTVIEW_InvalidateItem(infoPtr, i);
6483 * Scroll the content of a listview.
6486 * [I] infoPtr : valid pointer to the listview structure
6487 * [I] dx : horizontal scroll amount in pixels
6488 * [I] dy : vertical scroll amount in pixels
6495 * If the control is in report mode (LVS_REPORT) the control can
6496 * be scrolled only in line increments. "dy" will be rounded to the
6497 * nearest number of pixels that are a whole line. Ex: if line height
6498 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6499 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6501 * For: (per experimentaion with native control and CSpy ListView)
6502 * LVS_ICON dy=1 = 1 pixel (vertical only)
6504 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6506 * LVS_LIST dx=1 = 1 column (horizontal only)
6507 * but will only scroll 1 column per message
6508 * no matter what the value.
6509 * dy must be 0 or FALSE returned.
6510 * LVS_REPORT dx=1 = 1 pixel
6514 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6516 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6518 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6519 dy /= infoPtr->nItemHeight;
6522 if (dy != 0) return FALSE;
6529 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6530 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6537 * Sets the background color.
6540 * [I] infoPtr : valid pointer to the listview structure
6541 * [I] clrBk : background color
6547 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6549 TRACE("(clrBk=%x)\n", clrBk);
6551 if(infoPtr->clrBk != clrBk) {
6552 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6553 infoPtr->clrBk = clrBk;
6554 if (clrBk == CLR_NONE)
6555 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6557 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6558 LISTVIEW_InvalidateList(infoPtr);
6564 /* LISTVIEW_SetBkImage */
6566 /*** Helper for {Insert,Set}ColumnT *only* */
6567 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6568 const LVCOLUMNW *lpColumn, BOOL isW)
6570 if (lpColumn->mask & LVCF_FMT)
6572 /* format member is valid */
6573 lphdi->mask |= HDI_FORMAT;
6575 /* set text alignment (leftmost column must be left-aligned) */
6576 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6577 lphdi->fmt |= HDF_LEFT;
6578 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6579 lphdi->fmt |= HDF_RIGHT;
6580 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6581 lphdi->fmt |= HDF_CENTER;
6583 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6584 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6586 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6588 lphdi->fmt |= HDF_IMAGE;
6589 lphdi->iImage = I_IMAGECALLBACK;
6593 if (lpColumn->mask & LVCF_WIDTH)
6595 lphdi->mask |= HDI_WIDTH;
6596 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6598 /* make it fill the remainder of the controls width */
6602 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6604 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6605 lphdi->cxy += rcHeader.right - rcHeader.left;
6608 /* retrieve the layout of the header */
6609 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6610 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6612 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6615 lphdi->cxy = lpColumn->cx;
6618 if (lpColumn->mask & LVCF_TEXT)
6620 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6621 lphdi->fmt |= HDF_STRING;
6622 lphdi->pszText = lpColumn->pszText;
6623 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6626 if (lpColumn->mask & LVCF_IMAGE)
6628 lphdi->mask |= HDI_IMAGE;
6629 lphdi->iImage = lpColumn->iImage;
6632 if (lpColumn->mask & LVCF_ORDER)
6634 lphdi->mask |= HDI_ORDER;
6635 lphdi->iOrder = lpColumn->iOrder;
6642 * Inserts a new column.
6645 * [I] infoPtr : valid pointer to the listview structure
6646 * [I] nColumn : column index
6647 * [I] lpColumn : column information
6648 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6651 * SUCCESS : new column index
6654 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6655 const LVCOLUMNW *lpColumn, BOOL isW)
6657 COLUMN_INFO *lpColumnInfo;
6661 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6663 if (!lpColumn || nColumn < 0) return -1;
6664 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6666 ZeroMemory(&hdi, sizeof(HDITEMW));
6667 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6670 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6671 * (can be seen in SPY) otherwise column never gets added.
6673 if (!(lpColumn->mask & LVCF_WIDTH)) {
6674 hdi.mask |= HDI_WIDTH;
6679 * when the iSubItem is available Windows copies it to the header lParam. It seems
6680 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6682 if (lpColumn->mask & LVCF_SUBITEM)
6684 hdi.mask |= HDI_LPARAM;
6685 hdi.lParam = lpColumn->iSubItem;
6688 /* insert item in header control */
6689 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6690 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6691 (WPARAM)nColumn, (LPARAM)&hdi);
6692 if (nNewColumn == -1) return -1;
6693 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6695 /* create our own column info */
6696 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6697 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6699 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6700 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6702 /* now we have to actually adjust the data */
6703 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6705 SUBITEM_INFO *lpSubItem;
6709 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6711 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6712 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6714 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6715 if (lpSubItem->iSubItem >= nNewColumn)
6716 lpSubItem->iSubItem++;
6721 /* make space for the new column */
6722 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6723 LISTVIEW_UpdateItemSize(infoPtr);
6728 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6731 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6739 * Sets the attributes of a header item.
6742 * [I] infoPtr : valid pointer to the listview structure
6743 * [I] nColumn : column index
6744 * [I] lpColumn : column attributes
6745 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6751 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
6752 const LVCOLUMNW *lpColumn, BOOL isW)
6754 HDITEMW hdi, hdiget;
6757 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6759 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6761 ZeroMemory(&hdi, sizeof(HDITEMW));
6762 if (lpColumn->mask & LVCF_FMT)
6764 hdi.mask |= HDI_FORMAT;
6765 hdiget.mask = HDI_FORMAT;
6766 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6767 hdi.fmt = hdiget.fmt & HDF_STRING;
6769 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6771 /* set header item attributes */
6772 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6773 if (!bResult) return FALSE;
6775 if (lpColumn->mask & LVCF_FMT)
6777 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6778 int oldFmt = lpColumnInfo->fmt;
6780 lpColumnInfo->fmt = lpColumn->fmt;
6781 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6783 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6784 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6793 * Sets the column order array
6796 * [I] infoPtr : valid pointer to the listview structure
6797 * [I] iCount : number of elements in column order array
6798 * [I] lpiArray : pointer to column order array
6804 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6806 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6817 * Sets the width of a column
6820 * [I] infoPtr : valid pointer to the listview structure
6821 * [I] nColumn : column index
6822 * [I] cx : column width
6828 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6830 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6831 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6835 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6837 /* set column width only if in report or list mode */
6838 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6840 /* take care of invalid cx values */
6841 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6842 else if (uView == LVS_LIST && cx < 1) return FALSE;
6844 /* resize all columns if in LVS_LIST mode */
6845 if(uView == LVS_LIST)
6847 infoPtr->nItemWidth = cx;
6848 LISTVIEW_InvalidateList(infoPtr);
6852 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6854 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6859 lvItem.mask = LVIF_TEXT;
6861 lvItem.iSubItem = nColumn;
6862 lvItem.pszText = szDispText;
6863 lvItem.cchTextMax = DISP_TEXT_SIZE;
6864 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6866 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6867 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6868 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6870 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6871 max_cx += infoPtr->iconSize.cx;
6872 max_cx += TRAILING_LABEL_PADDING;
6875 /* autosize based on listview items width */
6876 if(cx == LVSCW_AUTOSIZE)
6878 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6880 /* if iCol is the last column make it fill the remainder of the controls width */
6881 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6886 LISTVIEW_GetOrigin(infoPtr, &Origin);
6887 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6889 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6893 /* Despite what the MS docs say, if this is not the last
6894 column, then MS resizes the column to the width of the
6895 largest text string in the column, including headers
6896 and items. This is different from LVSCW_AUTOSIZE in that
6897 LVSCW_AUTOSIZE ignores the header string length. */
6900 /* retrieve header text */
6901 hdi.mask = HDI_TEXT;
6902 hdi.cchTextMax = DISP_TEXT_SIZE;
6903 hdi.pszText = szDispText;
6904 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
6906 HDC hdc = GetDC(infoPtr->hwndSelf);
6907 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6910 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6911 cx = size.cx + TRAILING_HEADER_PADDING;
6912 /* FIXME: Take into account the header image, if one is present */
6913 SelectObject(hdc, old_font);
6914 ReleaseDC(infoPtr->hwndSelf, hdc);
6916 cx = max (cx, max_cx);
6920 if (cx < 0) return FALSE;
6922 /* call header to update the column change */
6923 hdi.mask = HDI_WIDTH;
6925 TRACE("hdi.cxy=%d\n", hdi.cxy);
6926 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6930 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6933 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
6936 HBITMAP hbm_im, hbm_mask, hbm_orig;
6938 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6939 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6942 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6943 ILC_COLOR | ILC_MASK, 2, 2);
6944 hdc_wnd = GetDC(infoPtr->hwndSelf);
6945 hdc = CreateCompatibleDC(hdc_wnd);
6946 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6947 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6948 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6950 rc.left = rc.top = 0;
6951 rc.right = GetSystemMetrics(SM_CXSMICON);
6952 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6954 hbm_orig = SelectObject(hdc, hbm_mask);
6955 FillRect(hdc, &rc, hbr_white);
6956 InflateRect(&rc, -3, -3);
6957 FillRect(hdc, &rc, hbr_black);
6959 SelectObject(hdc, hbm_im);
6960 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6961 SelectObject(hdc, hbm_orig);
6962 ImageList_Add(himl, hbm_im, hbm_mask);
6964 SelectObject(hdc, hbm_im);
6965 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6966 SelectObject(hdc, hbm_orig);
6967 ImageList_Add(himl, hbm_im, hbm_mask);
6969 DeleteObject(hbm_mask);
6970 DeleteObject(hbm_im);
6978 * Sets the extended listview style.
6981 * [I] infoPtr : valid pointer to the listview structure
6983 * [I] dwStyle : style
6986 * SUCCESS : previous style
6989 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6991 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6995 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6997 infoPtr->dwLvExStyle = dwStyle;
6999 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
7001 HIMAGELIST himl = 0;
7002 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7005 item.mask = LVIF_STATE;
7006 item.stateMask = LVIS_STATEIMAGEMASK;
7007 item.state = INDEXTOSTATEIMAGEMASK(1);
7008 LISTVIEW_SetItemState(infoPtr, -1, &item);
7010 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7012 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7015 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_HEADERDRAGDROP)
7017 DWORD dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7018 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7019 dwStyle |= HDS_DRAGDROP;
7021 dwStyle &= ~HDS_DRAGDROP;
7022 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7030 * Sets the new hot cursor used during hot tracking and hover selection.
7033 * [I] infoPtr : valid pointer to the listview structure
7034 * [I] hCursor : the new hot cursor handle
7037 * Returns the previous hot cursor
7039 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7041 HCURSOR oldCursor = infoPtr->hHotCursor;
7043 infoPtr->hHotCursor = hCursor;
7051 * Sets the hot item index.
7054 * [I] infoPtr : valid pointer to the listview structure
7055 * [I] iIndex : index
7058 * SUCCESS : previous hot item index
7059 * FAILURE : -1 (no hot item)
7061 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7063 INT iOldIndex = infoPtr->nHotItem;
7065 infoPtr->nHotItem = iIndex;
7073 * Sets the amount of time the cursor must hover over an item before it is selected.
7076 * [I] infoPtr : valid pointer to the listview structure
7077 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7080 * Returns the previous hover time
7082 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7084 DWORD oldHoverTime = infoPtr->dwHoverTime;
7086 infoPtr->dwHoverTime = dwHoverTime;
7088 return oldHoverTime;
7093 * Sets spacing for icons of LVS_ICON style.
7096 * [I] infoPtr : valid pointer to the listview structure
7097 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7098 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7101 * MAKELONG(oldcx, oldcy)
7103 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7105 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7106 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7108 TRACE("requested=(%d,%d)\n", cx, cy);
7110 /* this is supported only for LVS_ICON style */
7111 if (uView != LVS_ICON) return oldspacing;
7113 /* set to defaults, if instructed to */
7114 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7115 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7117 /* if 0 then compute width
7118 * FIXME: Should scan each item and determine max width of
7119 * icon or label, then make that the width */
7121 cx = infoPtr->iconSpacing.cx;
7123 /* if 0 then compute height */
7125 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7126 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7129 infoPtr->iconSpacing.cx = cx;
7130 infoPtr->iconSpacing.cy = cy;
7132 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7133 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7134 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7135 infoPtr->ntmHeight);
7137 /* these depend on the iconSpacing */
7138 LISTVIEW_UpdateItemSize(infoPtr);
7143 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7147 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7154 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7155 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7164 * [I] infoPtr : valid pointer to the listview structure
7165 * [I] nType : image list type
7166 * [I] himl : image list handle
7169 * SUCCESS : old image list
7172 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7174 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7175 INT oldHeight = infoPtr->nItemHeight;
7176 HIMAGELIST himlOld = 0;
7178 TRACE("(nType=%d, himl=%p\n", nType, himl);
7183 himlOld = infoPtr->himlNormal;
7184 infoPtr->himlNormal = himl;
7185 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7186 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7190 himlOld = infoPtr->himlSmall;
7191 infoPtr->himlSmall = himl;
7192 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7196 himlOld = infoPtr->himlState;
7197 infoPtr->himlState = himl;
7198 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7199 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7203 ERR("Unknown icon type=%d\n", nType);
7207 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7208 if (infoPtr->nItemHeight != oldHeight)
7209 LISTVIEW_UpdateScroll(infoPtr);
7216 * Preallocates memory (does *not* set the actual count of items !)
7219 * [I] infoPtr : valid pointer to the listview structure
7220 * [I] nItems : item count (projected number of items to allocate)
7221 * [I] dwFlags : update flags
7227 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7229 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7231 if (infoPtr->dwStyle & LVS_OWNERDATA)
7233 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7234 INT nOldCount = infoPtr->nItemCount;
7236 if (nItems < nOldCount)
7238 RANGE range = { nItems, nOldCount };
7239 ranges_del(infoPtr->selectionRanges, range);
7240 if (infoPtr->nFocusedItem >= nItems)
7242 infoPtr->nFocusedItem = -1;
7243 SetRectEmpty(&infoPtr->rcFocus);
7247 infoPtr->nItemCount = nItems;
7248 LISTVIEW_UpdateScroll(infoPtr);
7250 /* the flags are valid only in ownerdata report and list modes */
7251 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7253 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7254 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7256 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7257 LISTVIEW_InvalidateList(infoPtr);
7264 LISTVIEW_GetOrigin(infoPtr, &Origin);
7265 nFrom = min(nOldCount, nItems);
7266 nTo = max(nOldCount, nItems);
7268 if (uView == LVS_REPORT)
7271 rcErase.top = nFrom * infoPtr->nItemHeight;
7272 rcErase.right = infoPtr->nItemWidth;
7273 rcErase.bottom = nTo * infoPtr->nItemHeight;
7274 OffsetRect(&rcErase, Origin.x, Origin.y);
7275 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7276 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7280 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7282 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7283 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7284 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7285 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7286 OffsetRect(&rcErase, Origin.x, Origin.y);
7287 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7288 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7290 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7292 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7293 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7294 OffsetRect(&rcErase, Origin.x, Origin.y);
7295 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7296 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7302 /* According to MSDN for non-LVS_OWNERDATA this is just
7303 * a performance issue. The control allocates its internal
7304 * data structures for the number of items specified. It
7305 * cuts down on the number of memory allocations. Therefore
7306 * we will just issue a WARN here
7308 WARN("for non-ownerdata performance option not implemented.\n");
7316 * Sets the position of an item.
7319 * [I] infoPtr : valid pointer to the listview structure
7320 * [I] nItem : item index
7321 * [I] pt : coordinate
7327 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7329 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7332 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7334 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7335 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7337 LISTVIEW_GetOrigin(infoPtr, &Origin);
7339 /* This point value seems to be an undocumented feature.
7340 * The best guess is that it means either at the origin,
7341 * or at true beginning of the list. I will assume the origin. */
7342 if ((pt.x == -1) && (pt.y == -1))
7345 if (uView == LVS_ICON)
7347 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7348 pt.y -= ICON_TOP_PADDING;
7353 infoPtr->bAutoarrange = FALSE;
7355 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7360 * Sets the state of one or many items.
7363 * [I] infoPtr : valid pointer to the listview structure
7364 * [I] nItem : item index
7365 * [I] lpLVItem : item or subitem info
7371 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7373 BOOL bResult = TRUE;
7376 lvItem.iItem = nItem;
7377 lvItem.iSubItem = 0;
7378 lvItem.mask = LVIF_STATE;
7379 lvItem.state = lpLVItem->state;
7380 lvItem.stateMask = lpLVItem->stateMask;
7381 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7385 /* apply to all items */
7386 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7387 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7390 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7393 * Update selection mark
7395 * Investigation on windows 2k showed that selection mark was updated
7396 * whenever a new selection was made, but if the selected item was
7397 * unselected it was not updated.
7399 * we are probably still not 100% accurate, but this at least sets the
7400 * proper selection mark when it is needed
7403 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7404 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7407 infoPtr->nSelectionMark = -1;
7408 for (i = 0; i < infoPtr->nItemCount; i++)
7410 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7412 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7414 infoPtr->nSelectionMark = i;
7418 else if (ranges_contain(infoPtr->selectionRanges, i))
7420 infoPtr->nSelectionMark = i;
7431 * Sets the text of an item or subitem.
7434 * [I] hwnd : window handle
7435 * [I] nItem : item index
7436 * [I] lpLVItem : item or subitem info
7437 * [I] isW : TRUE if input is Unicode
7443 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7447 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7449 lvItem.iItem = nItem;
7450 lvItem.iSubItem = lpLVItem->iSubItem;
7451 lvItem.mask = LVIF_TEXT;
7452 lvItem.pszText = lpLVItem->pszText;
7453 lvItem.cchTextMax = lpLVItem->cchTextMax;
7455 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7457 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7462 * Set item index that marks the start of a multiple selection.
7465 * [I] infoPtr : valid pointer to the listview structure
7466 * [I] nIndex : index
7469 * Index number or -1 if there is no selection mark.
7471 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7473 INT nOldIndex = infoPtr->nSelectionMark;
7475 TRACE("(nIndex=%d)\n", nIndex);
7477 infoPtr->nSelectionMark = nIndex;
7484 * Sets the text background color.
7487 * [I] infoPtr : valid pointer to the listview structure
7488 * [I] clrTextBk : text background color
7494 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7496 TRACE("(clrTextBk=%x)\n", clrTextBk);
7498 if (infoPtr->clrTextBk != clrTextBk)
7500 infoPtr->clrTextBk = clrTextBk;
7501 LISTVIEW_InvalidateList(infoPtr);
7509 * Sets the text foreground color.
7512 * [I] infoPtr : valid pointer to the listview structure
7513 * [I] clrText : text color
7519 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7521 TRACE("(clrText=%x)\n", clrText);
7523 if (infoPtr->clrText != clrText)
7525 infoPtr->clrText = clrText;
7526 LISTVIEW_InvalidateList(infoPtr);
7534 * Determines which listview item is located at the specified position.
7537 * [I] infoPtr : valid pointer to the listview structure
7538 * [I] hwndNewToolTip : handle to new ToolTip
7543 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7545 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7546 infoPtr->hwndToolTip = hwndNewToolTip;
7547 return hwndOldToolTip;
7552 * sets the Unicode character format flag for the control
7554 * [I] infoPtr :valid pointer to the listview structure
7555 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7558 * Old Unicode Format
7560 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7562 BOOL rc = infoPtr->notifyFormat;
7563 infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7567 /* LISTVIEW_SetWorkAreas */
7571 * Callback internally used by LISTVIEW_SortItems()
7574 * [I] first : pointer to first ITEM_INFO to compare
7575 * [I] second : pointer to second ITEM_INFO to compare
7576 * [I] lParam : HWND of control
7579 * if first comes before second : negative
7580 * if first comes after second : positive
7581 * if first and second are equivalent : zero
7583 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7585 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7586 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7587 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7589 /* Forward the call to the client defined callback */
7590 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7595 * Sorts the listview items.
7598 * [I] infoPtr : valid pointer to the listview structure
7599 * [I] pfnCompare : application-defined value
7600 * [I] lParamSort : pointer to comparision callback
7606 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7608 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7611 LPVOID selectionMarkItem;
7615 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7617 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7619 if (!pfnCompare) return FALSE;
7620 if (!infoPtr->hdpaItems) return FALSE;
7622 /* if there are 0 or 1 items, there is no need to sort */
7623 if (infoPtr->nItemCount < 2) return TRUE;
7625 if (infoPtr->nFocusedItem >= 0)
7627 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7628 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7629 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7631 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7632 /* clear the lpItem->state for non-selected ones */
7633 /* remove the selection ranges */
7635 infoPtr->pfnCompare = pfnCompare;
7636 infoPtr->lParamSort = lParamSort;
7637 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7639 /* Adjust selections and indices so that they are the way they should
7640 * be after the sort (otherwise, the list items move around, but
7641 * whatever is at the item's previous original position will be
7644 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7645 for (i=0; i < infoPtr->nItemCount; i++)
7647 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7648 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7650 if (lpItem->state & LVIS_SELECTED)
7652 item.state = LVIS_SELECTED;
7653 item.stateMask = LVIS_SELECTED;
7654 LISTVIEW_SetItemState(infoPtr, i, &item);
7656 if (lpItem->state & LVIS_FOCUSED)
7658 infoPtr->nFocusedItem = i;
7659 lpItem->state &= ~LVIS_FOCUSED;
7662 if (selectionMarkItem != NULL)
7663 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7664 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7666 /* refresh the display */
7667 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7668 LISTVIEW_InvalidateList(infoPtr);
7675 * Update theme handle after a theme change.
7678 * [I] infoPtr : valid pointer to the listview structure
7682 * FAILURE : something else
7684 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
7686 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7687 CloseThemeData(theme);
7688 OpenThemeData(infoPtr->hwndSelf, themeClass);
7694 * Updates an items or rearranges the listview control.
7697 * [I] infoPtr : valid pointer to the listview structure
7698 * [I] nItem : item index
7704 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7706 TRACE("(nItem=%d)\n", nItem);
7708 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7710 /* rearrange with default alignment style */
7711 if (is_autoarrange(infoPtr))
7712 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7714 LISTVIEW_InvalidateItem(infoPtr, nItem);
7721 * Draw the track line at the place defined in the infoPtr structure.
7722 * The line is drawn with a XOR pen so drawing the line for the second time
7723 * in the same place erases the line.
7726 * [I] infoPtr : valid pointer to the listview structure
7732 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
7738 if (infoPtr->xTrackLine == -1)
7741 if (!(hdc = GetDC(infoPtr->hwndSelf)))
7743 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7744 oldROP = SetROP2(hdc, R2_XORPEN);
7745 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7746 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7747 SetROP2(hdc, oldROP);
7748 SelectObject(hdc, hOldPen);
7749 ReleaseDC(infoPtr->hwndSelf, hdc);
7755 * Called when an edit control should be displayed. This function is called after
7756 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7759 * [I] hwnd : Handle to the listview
7760 * [I] uMsg : WM_TIMER (ignored)
7761 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7762 * [I] dwTimer : The elapsed time (ignored)
7767 static CALLBACK VOID LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
7769 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
7770 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7772 KillTimer(hwnd, idEvent);
7773 editItem->fEnabled = FALSE;
7774 /* check if the item is still selected */
7775 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
7776 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
7781 * Creates the listview control - the WM_NCCREATE phase.
7784 * [I] hwnd : window handle
7785 * [I] lpcs : the create parameters
7791 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
7793 LISTVIEW_INFO *infoPtr;
7796 TRACE("(lpcs=%p)\n", lpcs);
7798 /* initialize info pointer */
7799 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
7800 if (!infoPtr) return FALSE;
7802 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7804 infoPtr->hwndSelf = hwnd;
7805 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
7806 /* determine the type of structures to use */
7807 infoPtr->hwndNotify = lpcs->hwndParent;
7808 /* infoPtr->notifyFormat will be filled in WM_CREATE */
7810 /* initialize color information */
7811 infoPtr->clrBk = CLR_NONE;
7812 infoPtr->clrText = CLR_DEFAULT;
7813 infoPtr->clrTextBk = CLR_DEFAULT;
7814 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7816 /* set default values */
7817 infoPtr->nFocusedItem = -1;
7818 infoPtr->nSelectionMark = -1;
7819 infoPtr->nHotItem = -1;
7820 infoPtr->bRedraw = TRUE;
7821 infoPtr->bNoItemMetrics = TRUE;
7822 infoPtr->bDoChangeNotify = TRUE;
7823 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7824 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7825 infoPtr->nEditLabelItem = -1;
7826 infoPtr->dwHoverTime = -1; /* default system hover time */
7827 infoPtr->nMeasureItemHeight = 0;
7828 infoPtr->xTrackLine = -1; /* no track line */
7829 infoPtr->itemEdit.fEnabled = FALSE;
7831 /* get default font (icon title) */
7832 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7833 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7834 infoPtr->hFont = infoPtr->hDefaultFont;
7835 LISTVIEW_SaveTextMetrics(infoPtr);
7837 /* allocate memory for the data structure */
7838 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7839 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7840 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7841 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7842 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7846 DestroyWindow(infoPtr->hwndHeader);
7847 ranges_destroy(infoPtr->selectionRanges);
7848 DPA_Destroy(infoPtr->hdpaItems);
7849 DPA_Destroy(infoPtr->hdpaPosX);
7850 DPA_Destroy(infoPtr->hdpaPosY);
7851 DPA_Destroy(infoPtr->hdpaColumns);
7858 * Creates the listview control - the WM_CREATE phase. Most of the data is
7859 * already set up in LISTVIEW_NCCreate
7862 * [I] hwnd : window handle
7863 * [I] lpcs : the create parameters
7869 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7871 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7872 UINT uView = lpcs->style & LVS_TYPEMASK;
7874 TRACE("(lpcs=%p)\n", lpcs);
7876 infoPtr->dwStyle = lpcs->style;
7877 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7878 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7881 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7882 WS_CHILD | HDS_HORZ | HDS_FULLDRAG | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7883 0, 0, 0, 0, hwnd, NULL,
7884 lpcs->hInstance, NULL);
7885 if (!infoPtr->hwndHeader) return -1;
7887 /* set header unicode format */
7888 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7890 /* set header font */
7891 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7893 /* init item size to avoid division by 0 */
7894 LISTVIEW_UpdateItemSize (infoPtr);
7896 if (uView == LVS_REPORT)
7898 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7900 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7904 /* set HDS_HIDDEN flag to hide the header bar */
7905 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7906 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7910 OpenThemeData(hwnd, themeClass);
7912 /* initialize the icon sizes */
7913 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7914 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7920 * Destroys the listview control.
7923 * [I] infoPtr : valid pointer to the listview structure
7929 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
7931 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7932 CloseThemeData(theme);
7938 * Enables the listview control.
7941 * [I] infoPtr : valid pointer to the listview structure
7942 * [I] bEnable : specifies whether to enable or disable the window
7948 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
7950 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7951 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7957 * Erases the background of the listview control.
7960 * [I] infoPtr : valid pointer to the listview structure
7961 * [I] hdc : device context handle
7967 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
7971 TRACE("(hdc=%p)\n", hdc);
7973 if (!GetClipBox(hdc, &rc)) return FALSE;
7975 /* for double buffered controls we need to do this during refresh */
7976 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
7978 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7984 * Helper function for LISTVIEW_[HV]Scroll *only*.
7985 * Performs vertical/horizontal scrolling by a give amount.
7988 * [I] infoPtr : valid pointer to the listview structure
7989 * [I] dx : amount of horizontal scroll
7990 * [I] dy : amount of vertical scroll
7992 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7994 /* now we can scroll the list */
7995 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7996 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7997 /* if we have focus, adjust rect */
7998 OffsetRect(&infoPtr->rcFocus, dx, dy);
7999 UpdateWindow(infoPtr->hwndSelf);
8004 * Performs vertical scrolling.
8007 * [I] infoPtr : valid pointer to the listview structure
8008 * [I] nScrollCode : scroll code
8009 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8010 * [I] hScrollWnd : scrollbar control window handle
8016 * SB_LINEUP/SB_LINEDOWN:
8017 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8018 * for LVS_REPORT is 1 line
8019 * for LVS_LIST cannot occur
8022 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8023 INT nScrollDiff, HWND hScrollWnd)
8025 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8026 INT nOldScrollPos, nNewScrollPos;
8027 SCROLLINFO scrollInfo;
8030 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8031 debugscrollcode(nScrollCode), nScrollDiff);
8033 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8035 scrollInfo.cbSize = sizeof(SCROLLINFO);
8036 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8038 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8040 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8042 nOldScrollPos = scrollInfo.nPos;
8043 switch (nScrollCode)
8049 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8053 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8057 nScrollDiff = -scrollInfo.nPage;
8061 nScrollDiff = scrollInfo.nPage;
8064 case SB_THUMBPOSITION:
8066 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8073 /* quit right away if pos isn't changing */
8074 if (nScrollDiff == 0) return 0;
8076 /* calculate new position, and handle overflows */
8077 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8078 if (nScrollDiff > 0) {
8079 if (nNewScrollPos < nOldScrollPos ||
8080 nNewScrollPos > scrollInfo.nMax)
8081 nNewScrollPos = scrollInfo.nMax;
8083 if (nNewScrollPos > nOldScrollPos ||
8084 nNewScrollPos < scrollInfo.nMin)
8085 nNewScrollPos = scrollInfo.nMin;
8088 /* set the new position, and reread in case it changed */
8089 scrollInfo.fMask = SIF_POS;
8090 scrollInfo.nPos = nNewScrollPos;
8091 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8093 /* carry on only if it really changed */
8094 if (nNewScrollPos == nOldScrollPos) return 0;
8096 /* now adjust to client coordinates */
8097 nScrollDiff = nOldScrollPos - nNewScrollPos;
8098 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8100 /* and scroll the window */
8101 scroll_list(infoPtr, 0, nScrollDiff);
8108 * Performs horizontal scrolling.
8111 * [I] infoPtr : valid pointer to the listview structure
8112 * [I] nScrollCode : scroll code
8113 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8114 * [I] hScrollWnd : scrollbar control window handle
8120 * SB_LINELEFT/SB_LINERIGHT:
8121 * for LVS_ICON, LVS_SMALLICON 1 pixel
8122 * for LVS_REPORT is 1 pixel
8123 * for LVS_LIST is 1 column --> which is a 1 because the
8124 * scroll is based on columns not pixels
8127 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8128 INT nScrollDiff, HWND hScrollWnd)
8130 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8131 INT nOldScrollPos, nNewScrollPos;
8132 SCROLLINFO scrollInfo;
8134 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8135 debugscrollcode(nScrollCode), nScrollDiff);
8137 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8139 scrollInfo.cbSize = sizeof(SCROLLINFO);
8140 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8142 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8144 nOldScrollPos = scrollInfo.nPos;
8146 switch (nScrollCode)
8160 nScrollDiff = -scrollInfo.nPage;
8164 nScrollDiff = scrollInfo.nPage;
8167 case SB_THUMBPOSITION:
8169 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8176 /* quit right away if pos isn't changing */
8177 if (nScrollDiff == 0) return 0;
8179 /* calculate new position, and handle overflows */
8180 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8181 if (nScrollDiff > 0) {
8182 if (nNewScrollPos < nOldScrollPos ||
8183 nNewScrollPos > scrollInfo.nMax)
8184 nNewScrollPos = scrollInfo.nMax;
8186 if (nNewScrollPos > nOldScrollPos ||
8187 nNewScrollPos < scrollInfo.nMin)
8188 nNewScrollPos = scrollInfo.nMin;
8191 /* set the new position, and reread in case it changed */
8192 scrollInfo.fMask = SIF_POS;
8193 scrollInfo.nPos = nNewScrollPos;
8194 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8196 /* carry on only if it really changed */
8197 if (nNewScrollPos == nOldScrollPos) return 0;
8199 if(uView == LVS_REPORT)
8200 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8202 /* now adjust to client coordinates */
8203 nScrollDiff = nOldScrollPos - nNewScrollPos;
8204 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8206 /* and scroll the window */
8207 scroll_list(infoPtr, nScrollDiff, 0);
8212 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8214 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8215 INT gcWheelDelta = 0;
8216 INT pulScrollLines = 3;
8217 SCROLLINFO scrollInfo;
8219 TRACE("(wheelDelta=%d)\n", wheelDelta);
8221 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8222 gcWheelDelta -= wheelDelta;
8224 scrollInfo.cbSize = sizeof(SCROLLINFO);
8225 scrollInfo.fMask = SIF_POS;
8232 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8233 * should be fixed in the future.
8235 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8236 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8240 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8242 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8243 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8244 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8249 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8260 * [I] infoPtr : valid pointer to the listview structure
8261 * [I] nVirtualKey : virtual key
8262 * [I] lKeyData : key data
8267 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8269 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8270 HWND hwndSelf = infoPtr->hwndSelf;
8272 NMLVKEYDOWN nmKeyDown;
8274 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8276 /* send LVN_KEYDOWN notification */
8277 nmKeyDown.wVKey = nVirtualKey;
8278 nmKeyDown.flags = 0;
8279 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8280 if (!IsWindow(hwndSelf))
8283 switch (nVirtualKey)
8286 nItem = infoPtr->nFocusedItem;
8290 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8292 if (!notify(infoPtr, NM_RETURN)) return 0;
8293 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8298 if (infoPtr->nItemCount > 0)
8303 if (infoPtr->nItemCount > 0)
8304 nItem = infoPtr->nItemCount - 1;
8308 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8312 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8316 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8320 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8324 if (uView == LVS_REPORT)
8326 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8327 if (infoPtr->nFocusedItem == topidx)
8328 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8333 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8334 * LISTVIEW_GetCountPerRow(infoPtr);
8335 if(nItem < 0) nItem = 0;
8339 if (uView == LVS_REPORT)
8341 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8342 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8343 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8344 nItem = infoPtr->nFocusedItem + cnt - 1;
8346 nItem = topidx + cnt - 1;
8349 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8350 * LISTVIEW_GetCountPerRow(infoPtr);
8351 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8355 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8356 LISTVIEW_KeySelection(infoPtr, nItem);
8366 * [I] infoPtr : valid pointer to the listview structure
8371 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8375 /* if we did not have the focus, there's nothing to do */
8376 if (!infoPtr->bFocus) return 0;
8378 /* send NM_KILLFOCUS notification */
8379 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8381 /* if we have a focus rectagle, get rid of it */
8382 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8384 /* set window focus flag */
8385 infoPtr->bFocus = FALSE;
8387 /* invalidate the selected items before reseting focus flag */
8388 LISTVIEW_InvalidateSelectedItems(infoPtr);
8395 * Processes double click messages (left mouse button).
8398 * [I] infoPtr : valid pointer to the listview structure
8399 * [I] wKey : key flag
8400 * [I] x,y : mouse coordinate
8405 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8407 LVHITTESTINFO htInfo;
8409 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8411 /* Cancel the item edition if any */
8412 if (infoPtr->itemEdit.fEnabled)
8414 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8415 infoPtr->itemEdit.fEnabled = FALSE;
8418 /* send NM_RELEASEDCAPTURE notification */
8419 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8424 /* send NM_DBLCLK notification */
8425 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8426 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8428 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8429 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8436 * Processes mouse down messages (left mouse button).
8439 * infoPtr [I ] valid pointer to the listview structure
8440 * wKey [I ] key flag
8441 * x,y [I ] mouse coordinate
8446 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8448 LVHITTESTINFO lvHitTestInfo;
8449 static BOOL bGroupSelect = TRUE;
8450 BOOL bReceivedFocus = FALSE;
8451 POINT pt = { x, y };
8454 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8456 /* send NM_RELEASEDCAPTURE notification */
8457 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8459 if (!infoPtr->bFocus)
8461 bReceivedFocus = TRUE;
8462 SetFocus(infoPtr->hwndSelf);
8465 /* set left button down flag and record the click position */
8466 infoPtr->bLButtonDown = TRUE;
8467 infoPtr->ptClickPos = pt;
8469 lvHitTestInfo.pt.x = x;
8470 lvHitTestInfo.pt.y = y;
8472 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8473 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8474 infoPtr->nEditLabelItem = -1;
8475 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8477 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8479 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8480 if(state == 1 || state == 2)
8484 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8485 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8486 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8491 if (infoPtr->dwStyle & LVS_SINGLESEL)
8493 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8494 infoPtr->nEditLabelItem = nItem;
8496 LISTVIEW_SetSelection(infoPtr, nItem);
8500 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8504 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8505 LISTVIEW_SetItemFocus(infoPtr, nItem);
8506 infoPtr->nSelectionMark = nItem;
8512 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8513 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8515 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8516 infoPtr->nSelectionMark = nItem;
8519 else if (wKey & MK_CONTROL)
8523 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8525 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8526 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8527 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8528 infoPtr->nSelectionMark = nItem;
8530 else if (wKey & MK_SHIFT)
8532 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8536 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8537 infoPtr->nEditLabelItem = nItem;
8539 /* set selection (clears other pre-existing selections) */
8540 LISTVIEW_SetSelection(infoPtr, nItem);
8546 /* remove all selections */
8547 LISTVIEW_DeselectAll(infoPtr);
8552 infoPtr->nEditLabelItem = -1;
8559 * Processes mouse up messages (left mouse button).
8562 * infoPtr [I ] valid pointer to the listview structure
8563 * wKey [I ] key flag
8564 * x,y [I ] mouse coordinate
8569 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8571 LVHITTESTINFO lvHitTestInfo;
8573 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8575 if (!infoPtr->bLButtonDown) return 0;
8577 lvHitTestInfo.pt.x = x;
8578 lvHitTestInfo.pt.y = y;
8580 /* send NM_CLICK notification */
8581 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8582 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8584 /* set left button flag */
8585 infoPtr->bLButtonDown = FALSE;
8587 /* if we clicked on a selected item, edit the label */
8588 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8590 /* we want to make sure the user doesn't want to do a double click. So we will
8591 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8593 infoPtr->itemEdit.fEnabled = TRUE;
8594 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8595 SetTimer(infoPtr->hwndSelf,
8596 (UINT_PTR)&infoPtr->itemEdit,
8597 GetDoubleClickTime(),
8598 LISTVIEW_DelayedEditItem);
8606 * Destroys the listview control (called after WM_DESTROY).
8609 * [I] infoPtr : valid pointer to the listview structure
8614 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8618 /* delete all items */
8619 LISTVIEW_DeleteAllItems(infoPtr);
8621 /* destroy data structure */
8622 DPA_Destroy(infoPtr->hdpaItems);
8623 DPA_Destroy(infoPtr->hdpaPosX);
8624 DPA_Destroy(infoPtr->hdpaPosY);
8625 DPA_Destroy(infoPtr->hdpaColumns);
8626 ranges_destroy(infoPtr->selectionRanges);
8628 /* destroy image lists */
8629 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8631 if (infoPtr->himlNormal)
8632 ImageList_Destroy(infoPtr->himlNormal);
8633 if (infoPtr->himlSmall)
8634 ImageList_Destroy(infoPtr->himlSmall);
8635 if (infoPtr->himlState)
8636 ImageList_Destroy(infoPtr->himlState);
8639 /* destroy font, bkgnd brush */
8641 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8642 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8644 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8646 /* free listview info pointer*/
8654 * Handles notifications from header.
8657 * [I] infoPtr : valid pointer to the listview structure
8658 * [I] nCtrlId : control identifier
8659 * [I] lpnmh : notification information
8664 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8666 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8667 HWND hwndSelf = infoPtr->hwndSelf;
8669 TRACE("(lpnmh=%p)\n", lpnmh);
8671 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8673 switch (lpnmh->hdr.code)
8678 COLUMN_INFO *lpColumnInfo;
8682 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8685 /* remove the old line (if any) */
8686 LISTVIEW_DrawTrackLine(infoPtr);
8688 /* compute & draw the new line */
8689 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8690 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8691 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8692 infoPtr->xTrackLine = x + ptOrigin.x;
8693 LISTVIEW_DrawTrackLine(infoPtr);
8699 /* remove the track line (if any) */
8700 LISTVIEW_DrawTrackLine(infoPtr);
8701 infoPtr->xTrackLine = -1;
8705 FIXME("Changing column order not implemented\n");
8708 case HDN_ITEMCHANGINGW:
8709 case HDN_ITEMCHANGINGA:
8710 return notify_forward_header(infoPtr, lpnmh);
8712 case HDN_ITEMCHANGEDW:
8713 case HDN_ITEMCHANGEDA:
8715 COLUMN_INFO *lpColumnInfo;
8718 notify_forward_header(infoPtr, lpnmh);
8719 if (!IsWindow(hwndSelf))
8722 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8726 hdi.mask = HDI_WIDTH;
8727 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8731 cxy = lpnmh->pitem->cxy;
8733 /* determine how much we change since the last know position */
8734 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8735 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8738 lpColumnInfo->rcHeader.right += dx;
8739 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
8740 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8743 /* only needs to update the scrolls */
8744 infoPtr->nItemWidth += dx;
8745 LISTVIEW_UpdateScroll(infoPtr);
8747 LISTVIEW_UpdateItemSize(infoPtr);
8748 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8751 RECT rcCol = lpColumnInfo->rcHeader;
8753 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8754 OffsetRect(&rcCol, ptOrigin.x, 0);
8756 rcCol.top = infoPtr->rcList.top;
8757 rcCol.bottom = infoPtr->rcList.bottom;
8759 /* resizing left-aligned columns leaves most of the left side untouched */
8760 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8762 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
8765 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8768 /* when shrinking the last column clear the now unused field */
8769 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1 && dx < 0)
8772 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8778 case HDN_ITEMCLICKW:
8779 case HDN_ITEMCLICKA:
8781 /* Handle sorting by Header Column */
8784 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8786 nmlv.iSubItem = lpnmh->iItem;
8787 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8791 case HDN_DIVIDERDBLCLICKW:
8792 case HDN_DIVIDERDBLCLICKA:
8793 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8802 * Paint non-client area of control.
8805 * [I] infoPtr : valid pointer to the listview structureof the sender
8806 * [I] region : update region
8809 * TRUE - frame was painted
8810 * FALSE - call default window proc
8812 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
8814 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8818 int cxEdge = GetSystemMetrics (SM_CXEDGE),
8819 cyEdge = GetSystemMetrics (SM_CYEDGE);
8821 if (!theme) return FALSE;
8823 GetWindowRect(infoPtr->hwndSelf, &r);
8825 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8826 r.right - cxEdge, r.bottom - cyEdge);
8827 if (region != (HRGN)1)
8828 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8829 OffsetRect(&r, -r.left, -r.top);
8831 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8832 OffsetRect(&r, -r.left, -r.top);
8834 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8835 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8836 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8837 ReleaseDC(infoPtr->hwndSelf, dc);
8839 /* Call default proc to get the scrollbars etc. painted */
8840 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8847 * Determines the type of structure to use.
8850 * [I] infoPtr : valid pointer to the listview structureof the sender
8851 * [I] hwndFrom : listview window handle
8852 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8857 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8859 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8861 if (nCommand != NF_REQUERY) return 0;
8863 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8870 * Paints/Repaints the listview control.
8873 * [I] infoPtr : valid pointer to the listview structure
8874 * [I] hdc : device context handle
8879 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8881 TRACE("(hdc=%p)\n", hdc);
8883 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8885 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8887 infoPtr->bNoItemMetrics = FALSE;
8888 LISTVIEW_UpdateItemSize(infoPtr);
8889 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8890 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8891 LISTVIEW_UpdateScroll(infoPtr);
8894 UpdateWindow(infoPtr->hwndHeader);
8897 LISTVIEW_Refresh(infoPtr, hdc, NULL);
8902 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8904 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
8905 EndPaint(infoPtr->hwndSelf, &ps);
8914 * Paints/Repaints the listview control.
8917 * [I] infoPtr : valid pointer to the listview structure
8918 * [I] hdc : device context handle
8919 * [I] options : drawing options
8924 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8926 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
8928 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8931 if (options & PRF_ERASEBKGND)
8932 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8934 if (options & PRF_CLIENT)
8935 LISTVIEW_Paint(infoPtr, hdc);
8943 * Processes double click messages (right mouse button).
8946 * [I] infoPtr : valid pointer to the listview structure
8947 * [I] wKey : key flag
8948 * [I] x,y : mouse coordinate
8953 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8955 LVHITTESTINFO lvHitTestInfo;
8957 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8959 /* send NM_RELEASEDCAPTURE notification */
8960 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8962 /* send NM_RDBLCLK notification */
8963 lvHitTestInfo.pt.x = x;
8964 lvHitTestInfo.pt.y = y;
8965 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8966 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8973 * Processes mouse down messages (right mouse button).
8976 * [I] infoPtr : valid pointer to the listview structure
8977 * [I] wKey : key flag
8978 * [I] x,y : mouse coordinate
8983 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8985 LVHITTESTINFO lvHitTestInfo;
8988 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8990 /* send NM_RELEASEDCAPTURE notification */
8991 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8993 /* make sure the listview control window has the focus */
8994 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8996 /* set right button down flag */
8997 infoPtr->bRButtonDown = TRUE;
8999 /* determine the index of the selected item */
9000 lvHitTestInfo.pt.x = x;
9001 lvHitTestInfo.pt.y = y;
9002 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9004 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9006 LISTVIEW_SetItemFocus(infoPtr, nItem);
9007 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9008 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9009 LISTVIEW_SetSelection(infoPtr, nItem);
9013 LISTVIEW_DeselectAll(infoPtr);
9021 * Processes mouse up messages (right mouse button).
9024 * [I] infoPtr : valid pointer to the listview structure
9025 * [I] wKey : key flag
9026 * [I] x,y : mouse coordinate
9031 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9033 LVHITTESTINFO lvHitTestInfo;
9036 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9038 if (!infoPtr->bRButtonDown) return 0;
9040 /* set button flag */
9041 infoPtr->bRButtonDown = FALSE;
9043 /* Send NM_RClICK notification */
9044 lvHitTestInfo.pt.x = x;
9045 lvHitTestInfo.pt.y = y;
9046 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9047 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9049 /* Change to screen coordinate for WM_CONTEXTMENU */
9050 pt = lvHitTestInfo.pt;
9051 ClientToScreen(infoPtr->hwndSelf, &pt);
9053 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9054 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9055 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9066 * [I] infoPtr : valid pointer to the listview structure
9067 * [I] hwnd : window handle of window containing the cursor
9068 * [I] nHittest : hit-test code
9069 * [I] wMouseMsg : ideintifier of the mouse message
9072 * TRUE if cursor is set
9075 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9077 LVHITTESTINFO lvHitTestInfo;
9079 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
9081 if(!infoPtr->hHotCursor) return FALSE;
9083 GetCursorPos(&lvHitTestInfo.pt);
9084 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9086 SetCursor(infoPtr->hHotCursor);
9096 * [I] infoPtr : valid pointer to the listview structure
9097 * [I] hwndLoseFocus : handle of previously focused window
9102 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9104 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9106 /* if we have the focus already, there's nothing to do */
9107 if (infoPtr->bFocus) return 0;
9109 /* send NM_SETFOCUS notification */
9110 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9112 /* set window focus flag */
9113 infoPtr->bFocus = TRUE;
9115 /* put the focus rect back on */
9116 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9118 /* redraw all visible selected items */
9119 LISTVIEW_InvalidateSelectedItems(infoPtr);
9129 * [I] infoPtr : valid pointer to the listview structure
9130 * [I] fRedraw : font handle
9131 * [I] fRedraw : redraw flag
9136 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9138 HFONT oldFont = infoPtr->hFont;
9140 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9142 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9143 if (infoPtr->hFont == oldFont) return 0;
9145 LISTVIEW_SaveTextMetrics(infoPtr);
9147 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9148 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9150 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9157 * Message handling for WM_SETREDRAW.
9158 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9161 * [I] infoPtr : valid pointer to the listview structure
9162 * [I] bRedraw: state of redraw flag
9165 * DefWinProc return value
9167 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9169 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9171 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9172 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9174 infoPtr->bRedraw = bRedraw;
9176 if(!bRedraw) return 0;
9178 if (is_autoarrange(infoPtr))
9179 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9180 LISTVIEW_UpdateScroll(infoPtr);
9182 /* despite what the WM_SETREDRAW docs says, apps expect us
9183 * to invalidate the listview here... stupid! */
9184 LISTVIEW_InvalidateList(infoPtr);
9191 * Resizes the listview control. This function processes WM_SIZE
9192 * messages. At this time, the width and height are not used.
9195 * [I] infoPtr : valid pointer to the listview structure
9196 * [I] Width : new width
9197 * [I] Height : new height
9202 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9204 RECT rcOld = infoPtr->rcList;
9206 TRACE("(width=%d, height=%d)\n", Width, Height);
9208 LISTVIEW_UpdateSize(infoPtr);
9209 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9211 /* do not bother with display related stuff if we're not redrawing */
9212 if (!is_redrawing(infoPtr)) return 0;
9214 if (is_autoarrange(infoPtr))
9215 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9217 LISTVIEW_UpdateScroll(infoPtr);
9219 /* refresh all only for lists whose height changed significantly */
9220 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
9221 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9222 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9223 LISTVIEW_InvalidateList(infoPtr);
9230 * Sets the size information.
9233 * [I] infoPtr : valid pointer to the listview structure
9238 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9240 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9242 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9244 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9246 if (uView == LVS_LIST)
9248 /* Apparently the "LIST" style is supposed to have the same
9249 * number of items in a column even if there is no scroll bar.
9250 * Since if a scroll bar already exists then the bottom is already
9251 * reduced, only reduce if the scroll bar does not currently exist.
9252 * The "2" is there to mimic the native control. I think it may be
9253 * related to either padding or edges. (GLA 7/2002)
9255 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9256 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9257 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9259 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
9264 hl.prc = &infoPtr->rcList;
9266 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9268 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9270 infoPtr->rcList.top = max(wp.cy, 0);
9273 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9278 * Processes WM_STYLECHANGED messages.
9281 * [I] infoPtr : valid pointer to the listview structure
9282 * [I] wStyleType : window style type (normal or extended)
9283 * [I] lpss : window style information
9288 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9289 const STYLESTRUCT *lpss)
9291 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9292 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9294 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9295 wStyleType, lpss->styleOld, lpss->styleNew);
9297 if (wStyleType != GWL_STYLE) return 0;
9299 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9300 /* what if LVS_OWNERDATA changed? */
9301 /* or LVS_SINGLESEL */
9302 /* or LVS_SORT{AS,DES}CENDING */
9304 infoPtr->dwStyle = lpss->styleNew;
9306 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9307 ((lpss->styleNew & WS_HSCROLL) == 0))
9308 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9310 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9311 ((lpss->styleNew & WS_VSCROLL) == 0))
9312 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9314 if (uNewView != uOldView)
9316 SIZE oldIconSize = infoPtr->iconSize;
9319 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9320 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9322 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9323 SetRectEmpty(&infoPtr->rcFocus);
9325 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9326 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9328 if (uNewView == LVS_ICON)
9330 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9332 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9333 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9334 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9337 else if (uNewView == LVS_REPORT)
9342 hl.prc = &infoPtr->rcList;
9344 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9345 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9348 LISTVIEW_UpdateItemSize(infoPtr);
9351 if (uNewView == LVS_REPORT)
9352 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
9354 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9355 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9356 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9358 /* update the size of the client area */
9359 LISTVIEW_UpdateSize(infoPtr);
9361 /* add scrollbars if needed */
9362 LISTVIEW_UpdateScroll(infoPtr);
9364 /* invalidate client area + erase background */
9365 LISTVIEW_InvalidateList(infoPtr);
9372 * Window procedure of the listview control.
9375 static LRESULT WINAPI
9376 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9378 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9380 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9382 if (!infoPtr && (uMsg != WM_NCCREATE))
9383 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9387 case LVM_APPROXIMATEVIEWRECT:
9388 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9389 LOWORD(lParam), HIWORD(lParam));
9391 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9393 /* case LVM_CANCELEDITLABEL: */
9395 case LVM_CREATEDRAGIMAGE:
9396 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9398 case LVM_DELETEALLITEMS:
9399 return LISTVIEW_DeleteAllItems(infoPtr);
9401 case LVM_DELETECOLUMN:
9402 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9404 case LVM_DELETEITEM:
9405 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9407 case LVM_EDITLABELW:
9408 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9410 case LVM_EDITLABELA:
9411 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9413 /* case LVM_ENABLEGROUPVIEW: */
9415 case LVM_ENSUREVISIBLE:
9416 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9419 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9422 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9424 case LVM_GETBKCOLOR:
9425 return infoPtr->clrBk;
9427 /* case LVM_GETBKIMAGE: */
9429 case LVM_GETCALLBACKMASK:
9430 return infoPtr->uCallbackMask;
9432 case LVM_GETCOLUMNA:
9433 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9435 case LVM_GETCOLUMNW:
9436 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9438 case LVM_GETCOLUMNORDERARRAY:
9439 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9441 case LVM_GETCOLUMNWIDTH:
9442 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9444 case LVM_GETCOUNTPERPAGE:
9445 return LISTVIEW_GetCountPerPage(infoPtr);
9447 case LVM_GETEDITCONTROL:
9448 return (LRESULT)infoPtr->hwndEdit;
9450 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9451 return infoPtr->dwLvExStyle;
9453 /* case LVM_GETGROUPINFO: */
9455 /* case LVM_GETGROUPMETRICS: */
9458 return (LRESULT)infoPtr->hwndHeader;
9460 case LVM_GETHOTCURSOR:
9461 return (LRESULT)infoPtr->hHotCursor;
9463 case LVM_GETHOTITEM:
9464 return infoPtr->nHotItem;
9466 case LVM_GETHOVERTIME:
9467 return infoPtr->dwHoverTime;
9469 case LVM_GETIMAGELIST:
9470 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9472 /* case LVM_GETINSERTMARK: */
9474 /* case LVM_GETINSERTMARKCOLOR: */
9476 /* case LVM_GETINSERTMARKRECT: */
9478 case LVM_GETISEARCHSTRINGA:
9479 case LVM_GETISEARCHSTRINGW:
9480 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9484 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9487 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9489 case LVM_GETITEMCOUNT:
9490 return infoPtr->nItemCount;
9492 case LVM_GETITEMPOSITION:
9493 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9495 case LVM_GETITEMRECT:
9496 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9498 case LVM_GETITEMSPACING:
9499 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9501 case LVM_GETITEMSTATE:
9502 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9504 case LVM_GETITEMTEXTA:
9505 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9507 case LVM_GETITEMTEXTW:
9508 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9510 case LVM_GETNEXTITEM:
9511 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9513 case LVM_GETNUMBEROFWORKAREAS:
9514 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9518 if (!lParam) return FALSE;
9519 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9522 /* case LVM_GETOUTLINECOLOR: */
9524 /* case LVM_GETSELECTEDCOLUMN: */
9526 case LVM_GETSELECTEDCOUNT:
9527 return LISTVIEW_GetSelectedCount(infoPtr);
9529 case LVM_GETSELECTIONMARK:
9530 return infoPtr->nSelectionMark;
9532 case LVM_GETSTRINGWIDTHA:
9533 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9535 case LVM_GETSTRINGWIDTHW:
9536 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9538 case LVM_GETSUBITEMRECT:
9539 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9541 case LVM_GETTEXTBKCOLOR:
9542 return infoPtr->clrTextBk;
9544 case LVM_GETTEXTCOLOR:
9545 return infoPtr->clrText;
9547 /* case LVM_GETTILEINFO: */
9549 /* case LVM_GETTILEVIEWINFO: */
9551 case LVM_GETTOOLTIPS:
9552 if( !infoPtr->hwndToolTip )
9553 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9554 return (LRESULT)infoPtr->hwndToolTip;
9556 case LVM_GETTOPINDEX:
9557 return LISTVIEW_GetTopIndex(infoPtr);
9559 /*case LVM_GETUNICODEFORMAT:
9560 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9563 /* case LVM_GETVIEW: */
9565 case LVM_GETVIEWRECT:
9566 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9568 case LVM_GETWORKAREAS:
9569 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9572 /* case LVM_HASGROUP: */
9575 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9577 case LVM_INSERTCOLUMNA:
9578 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9580 case LVM_INSERTCOLUMNW:
9581 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9583 /* case LVM_INSERTGROUP: */
9585 /* case LVM_INSERTGROUPSORTED: */
9587 case LVM_INSERTITEMA:
9588 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9590 case LVM_INSERTITEMW:
9591 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9593 /* case LVM_INSERTMARKHITTEST: */
9595 /* case LVM_ISGROUPVIEWENABLED: */
9597 /* case LVM_MAPIDTOINDEX: */
9599 /* case LVM_MAPINDEXTOID: */
9601 /* case LVM_MOVEGROUP: */
9603 /* case LVM_MOVEITEMTOGROUP: */
9605 case LVM_REDRAWITEMS:
9606 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9608 /* case LVM_REMOVEALLGROUPS: */
9610 /* case LVM_REMOVEGROUP: */
9613 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9615 case LVM_SETBKCOLOR:
9616 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9618 /* case LVM_SETBKIMAGE: */
9620 case LVM_SETCALLBACKMASK:
9621 infoPtr->uCallbackMask = (UINT)wParam;
9624 case LVM_SETCOLUMNA:
9625 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9627 case LVM_SETCOLUMNW:
9628 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9630 case LVM_SETCOLUMNORDERARRAY:
9631 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9633 case LVM_SETCOLUMNWIDTH:
9634 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9636 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9637 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9639 /* case LVM_SETGROUPINFO: */
9641 /* case LVM_SETGROUPMETRICS: */
9643 case LVM_SETHOTCURSOR:
9644 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9646 case LVM_SETHOTITEM:
9647 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9649 case LVM_SETHOVERTIME:
9650 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9652 case LVM_SETICONSPACING:
9653 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9655 case LVM_SETIMAGELIST:
9656 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9658 /* case LVM_SETINFOTIP: */
9660 /* case LVM_SETINSERTMARK: */
9662 /* case LVM_SETINSERTMARKCOLOR: */
9665 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9668 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9670 case LVM_SETITEMCOUNT:
9671 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9673 case LVM_SETITEMPOSITION:
9676 pt.x = (short)LOWORD(lParam);
9677 pt.y = (short)HIWORD(lParam);
9678 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9681 case LVM_SETITEMPOSITION32:
9682 if (lParam == 0) return FALSE;
9683 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9685 case LVM_SETITEMSTATE:
9686 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9688 case LVM_SETITEMTEXTA:
9689 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9691 case LVM_SETITEMTEXTW:
9692 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9694 /* case LVM_SETOUTLINECOLOR: */
9696 /* case LVM_SETSELECTEDCOLUMN: */
9698 case LVM_SETSELECTIONMARK:
9699 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9701 case LVM_SETTEXTBKCOLOR:
9702 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9704 case LVM_SETTEXTCOLOR:
9705 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9707 /* case LVM_SETTILEINFO: */
9709 /* case LVM_SETTILEVIEWINFO: */
9711 /* case LVM_SETTILEWIDTH: */
9713 case LVM_SETTOOLTIPS:
9714 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9716 case LVM_SETUNICODEFORMAT:
9717 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
9719 /* case LVM_SETVIEW: */
9721 /* case LVM_SETWORKAREAS: */
9723 /* case LVM_SORTGROUPS: */
9726 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9728 /* LVM_SORTITEMSEX: */
9730 case LVM_SUBITEMHITTEST:
9731 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9734 return LISTVIEW_Update(infoPtr, (INT)wParam);
9737 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9740 return LISTVIEW_Command(infoPtr, wParam, lParam);
9743 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
9746 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9749 return LISTVIEW_Destroy(infoPtr);
9752 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9755 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9758 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9761 return (LRESULT)infoPtr->hFont;
9764 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9767 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9770 return LISTVIEW_KillFocus(infoPtr);
9772 case WM_LBUTTONDBLCLK:
9773 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9775 case WM_LBUTTONDOWN:
9776 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9779 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9782 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9785 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9788 return LISTVIEW_NCDestroy(infoPtr);
9791 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9796 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9797 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9800 case WM_NOTIFYFORMAT:
9801 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9803 case WM_PRINTCLIENT:
9804 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9807 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9809 case WM_RBUTTONDBLCLK:
9810 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9812 case WM_RBUTTONDOWN:
9813 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9816 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9819 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9824 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9827 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9830 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9833 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9835 case WM_STYLECHANGED:
9836 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9838 case WM_SYSCOLORCHANGE:
9839 COMCTL32_RefreshSysColors();
9842 /* case WM_TIMER: */
9843 case WM_THEMECHANGED:
9844 return LISTVIEW_ThemeChanged(infoPtr);
9847 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9850 if (wParam & (MK_SHIFT | MK_CONTROL))
9851 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9852 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9854 case WM_WINDOWPOSCHANGED:
9855 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9857 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9858 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9859 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9861 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9863 MEASUREITEMSTRUCT mis;
9864 mis.CtlType = ODT_LISTVIEW;
9865 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9869 mis.itemHeight= infoPtr->nItemHeight;
9870 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9871 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9872 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9875 LISTVIEW_UpdateSize(infoPtr);
9876 LISTVIEW_UpdateScroll(infoPtr);
9878 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9880 /* case WM_WININICHANGE: */
9883 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9884 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
9887 /* call default window procedure */
9888 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9895 * Registers the window class.
9903 void LISTVIEW_Register(void)
9907 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9908 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9909 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9910 wndClass.cbClsExtra = 0;
9911 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9912 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9913 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9914 wndClass.lpszClassName = WC_LISTVIEWW;
9915 RegisterClassW(&wndClass);
9920 * Unregisters the window class.
9928 void LISTVIEW_Unregister(void)
9930 UnregisterClassW(WC_LISTVIEWW, NULL);
9935 * Handle any WM_COMMAND messages
9938 * [I] infoPtr : valid pointer to the listview structure
9939 * [I] wParam : the first message parameter
9940 * [I] lParam : the second message parameter
9945 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9947 switch (HIWORD(wParam))
9952 * Adjust the edit window size
9955 HDC hdc = GetDC(infoPtr->hwndEdit);
9956 HFONT hFont, hOldFont = 0;
9961 if (!infoPtr->hwndEdit || !hdc) return 0;
9962 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9963 GetWindowRect(infoPtr->hwndEdit, &rect);
9965 /* Select font to get the right dimension of the string */
9966 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9969 hOldFont = SelectObject(hdc, hFont);
9972 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9974 TEXTMETRICW textMetric;
9976 /* Add Extra spacing for the next character */
9977 GetTextMetricsW(hdc, &textMetric);
9978 sz.cx += (textMetric.tmMaxCharWidth * 2);
9986 rect.bottom - rect.top,
9987 SWP_DRAWFRAME|SWP_NOMOVE);
9990 SelectObject(hdc, hOldFont);
9992 ReleaseDC(infoPtr->hwndEdit, hdc);
9998 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10007 * Subclassed edit control windproc function
10010 * [I] hwnd : the edit window handle
10011 * [I] uMsg : the message that is to be processed
10012 * [I] wParam : first message parameter
10013 * [I] lParam : second message parameter
10014 * [I] isW : TRUE if input is Unicode
10019 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10021 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10022 BOOL cancel = FALSE;
10024 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10025 hwnd, uMsg, wParam, lParam, isW);
10029 case WM_GETDLGCODE:
10030 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10037 WNDPROC editProc = infoPtr->EditWndProc;
10038 infoPtr->EditWndProc = 0;
10039 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10040 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10044 if (VK_ESCAPE == (INT)wParam)
10049 else if (VK_RETURN == (INT)wParam)
10053 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10056 /* kill the edit */
10057 if (infoPtr->hwndEdit)
10059 LPWSTR buffer = NULL;
10061 infoPtr->hwndEdit = 0;
10064 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10068 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10070 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10071 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10075 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10080 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10086 * Subclassed edit control Unicode windproc function
10089 * [I] hwnd : the edit window handle
10090 * [I] uMsg : the message that is to be processed
10091 * [I] wParam : first message parameter
10092 * [I] lParam : second message parameter
10096 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10098 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10103 * Subclassed edit control ANSI windproc function
10106 * [I] hwnd : the edit window handle
10107 * [I] uMsg : the message that is to be processed
10108 * [I] wParam : first message parameter
10109 * [I] lParam : second message parameter
10113 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10115 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10120 * Creates a subclassed edit cotrol
10123 * [I] infoPtr : valid pointer to the listview structure
10124 * [I] text : initial text for the edit
10125 * [I] style : the window style
10126 * [I] isW : TRUE if input is Unicode
10130 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10131 INT x, INT y, INT width, INT height, BOOL isW)
10133 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10138 TEXTMETRICW textMetric;
10139 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10141 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10143 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10144 hdc = GetDC(infoPtr->hwndSelf);
10146 /* Select the font to get appropriate metric dimensions */
10147 if(infoPtr->hFont != 0)
10148 hOldFont = SelectObject(hdc, infoPtr->hFont);
10150 /*Get String Length in pixels */
10151 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10153 /*Add Extra spacing for the next character */
10154 GetTextMetricsW(hdc, &textMetric);
10155 sz.cx += (textMetric.tmMaxCharWidth * 2);
10157 if(infoPtr->hFont != 0)
10158 SelectObject(hdc, hOldFont);
10160 ReleaseDC(infoPtr->hwndSelf, hdc);
10162 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10164 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10166 if (!hedit) return 0;
10168 infoPtr->EditWndProc = (WNDPROC)
10169 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10170 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10172 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);