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 between 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",
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, pnmh->idFrom, (LPARAM)pnmh);
740 TRACE(" <= %ld\n", result);
745 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
748 HWND hwnd = infoPtr->hwndSelf;
749 notify_hdr(infoPtr, code, &nmh);
750 return IsWindow(hwnd);
753 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
764 item.mask = LVIF_PARAM|LVIF_STATE;
765 item.iItem = htInfo->iItem;
767 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
768 nmia.lParam = item.lParam;
769 nmia.uOldState = item.state;
770 nmia.uNewState = item.state | LVIS_ACTIVATING;
771 nmia.uChanged = LVIF_STATE;
774 nmia.iItem = htInfo->iItem;
775 nmia.iSubItem = htInfo->iSubItem;
776 nmia.ptAction = htInfo->pt;
778 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
779 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
780 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
782 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
785 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
787 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
788 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
791 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
795 HWND hwnd = infoPtr->hwndSelf;
797 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
798 ZeroMemory(&nmlv, sizeof(nmlv));
799 nmlv.iItem = lvht->iItem;
800 nmlv.iSubItem = lvht->iSubItem;
801 nmlv.ptAction = lvht->pt;
802 item.mask = LVIF_PARAM;
803 item.iItem = lvht->iItem;
805 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
806 notify_listview(infoPtr, code, &nmlv);
807 return IsWindow(hwnd);
810 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
814 HWND hwnd = infoPtr->hwndSelf;
816 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
818 item.mask = LVIF_PARAM;
821 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
822 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
823 return IsWindow(hwnd);
826 static int get_ansi_notification(INT unicodeNotificationCode)
828 switch (unicodeNotificationCode)
830 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
831 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
832 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
833 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
834 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
835 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
837 ERR("unknown notification %x\n", unicodeNotificationCode);
843 Send notification. depends on dispinfoW having same
844 structure as dispinfoA.
845 infoPtr : listview struct
846 notificationCode : *Unicode* notification code
847 pdi : dispinfo structure (can be unicode or ansi)
848 isW : TRUE if dispinfo is Unicode
850 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
852 BOOL bResult = FALSE;
853 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
854 INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
855 LPWSTR pszTempBuf = NULL, savPszText = NULL;
857 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
859 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
860 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
863 if (convertToAnsi || convertToUnicode)
865 if (notificationCode != LVN_GETDISPINFOW)
867 cchTempBufMax = convertToUnicode ?
868 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
869 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
873 cchTempBufMax = pdi->item.cchTextMax;
874 *pdi->item.pszText = 0; /* make sure we don't process garbage */
877 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
878 if (!pszTempBuf) return FALSE;
880 if (convertToUnicode)
881 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
882 pszTempBuf, cchTempBufMax);
884 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
885 cchTempBufMax, NULL, NULL);
887 savCchTextMax = pdi->item.cchTextMax;
888 savPszText = pdi->item.pszText;
889 pdi->item.pszText = pszTempBuf;
890 pdi->item.cchTextMax = cchTempBufMax;
893 if (infoPtr->notifyFormat == NFR_ANSI)
894 realNotifCode = get_ansi_notification(notificationCode);
896 realNotifCode = notificationCode;
897 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
898 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
900 if (convertToUnicode || convertToAnsi)
902 if (convertToUnicode) /* note : pointer can be changed by app ! */
903 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
904 savCchTextMax, NULL, NULL);
906 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
907 savPszText, savCchTextMax);
908 pdi->item.pszText = savPszText; /* restores our buffer */
909 pdi->item.cchTextMax = savCchTextMax;
915 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
916 const RECT *rcBounds, const LVITEMW *lplvItem)
918 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
919 lpnmlvcd->nmcd.hdc = hdc;
920 lpnmlvcd->nmcd.rc = *rcBounds;
921 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
922 lpnmlvcd->clrText = infoPtr->clrText;
923 if (!lplvItem) return;
924 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
925 lpnmlvcd->iSubItem = lplvItem->iSubItem;
926 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
927 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
928 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
929 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
932 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
934 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
937 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
938 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
939 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
940 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
941 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
942 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
946 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
948 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
949 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
950 if (lpnmlvcd->clrText == CLR_DEFAULT)
951 lpnmlvcd->clrText = comctl32_color.clrWindowText;
953 /* apparently, for selected items, we have to override the returned values */
956 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
960 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
961 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
963 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
965 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
966 lpnmlvcd->clrText = comctl32_color.clrBtnText;
971 /* Set the text attributes */
972 if (lpnmlvcd->clrTextBk != CLR_NONE)
974 SetBkMode(hdc, OPAQUE);
975 SetBkColor(hdc,lpnmlvcd->clrTextBk);
978 SetBkMode(hdc, TRANSPARENT);
979 SetTextColor(hdc, lpnmlvcd->clrText);
982 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
984 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
987 /******** Item iterator functions **********************************/
989 static RANGES ranges_create(int count);
990 static void ranges_destroy(RANGES ranges);
991 static BOOL ranges_add(RANGES ranges, RANGE range);
992 static BOOL ranges_del(RANGES ranges, RANGE range);
993 static void ranges_dump(RANGES ranges);
995 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
997 RANGE range = { nItem, nItem + 1 };
999 return ranges_add(ranges, range);
1002 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1004 RANGE range = { nItem, nItem + 1 };
1006 return ranges_del(ranges, range);
1010 * ITERATOR DOCUMENTATION
1012 * The iterator functions allow for easy, and convenient iteration
1013 * over items of interest in the list. Typically, you create a
1014 * iterator, use it, and destroy it, as such:
1017 * iterator_xxxitems(&i, ...);
1018 * while (iterator_{prev,next}(&i)
1020 * //code which uses i.nItem
1022 * iterator_destroy(&i);
1024 * where xxx is either: framed, or visible.
1025 * Note that it is important that the code destroys the iterator
1026 * after it's done with it, as the creation of the iterator may
1027 * allocate memory, which thus needs to be freed.
1029 * You can iterate both forwards, and backwards through the list,
1030 * by using iterator_next or iterator_prev respectively.
1032 * Lower numbered items are draw on top of higher number items in
1033 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1034 * items may overlap). So, to test items, you should use
1036 * which lists the items top to bottom (in Z-order).
1037 * For drawing items, you should use
1039 * which lists the items bottom to top (in Z-order).
1040 * If you keep iterating over the items after the end-of-items
1041 * marker (-1) is returned, the iterator will start from the
1042 * beginning. Typically, you don't need to test for -1,
1043 * because iterator_{next,prev} will return TRUE if more items
1044 * are to be iterated over, or FALSE otherwise.
1046 * Note: the iterator is defined to be bidirectional. That is,
1047 * any number of prev followed by any number of next, or
1048 * five versa, should leave the iterator at the same item:
1049 * prev * n, next * n = next * n, prev * n
1051 * The iterator has a notion of an out-of-order, special item,
1052 * which sits at the start of the list. This is used in
1053 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1054 * which needs to be first, as it may overlap other items.
1056 * The code is a bit messy because we have:
1057 * - a special item to deal with
1058 * - simple range, or composite range
1060 * If you find bugs, or want to add features, please make sure you
1061 * always check/modify *both* iterator_prev, and iterator_next.
1065 * This function iterates through the items in increasing order,
1066 * but prefixed by the special item, then -1. That is:
1067 * special, 1, 2, 3, ..., n, -1.
1068 * Each item is listed only once.
1070 static inline BOOL iterator_next(ITERATOR* i)
1074 i->nItem = i->nSpecial;
1075 if (i->nItem != -1) return TRUE;
1077 if (i->nItem == i->nSpecial)
1079 if (i->ranges) i->index = 0;
1085 if (i->nItem == i->nSpecial) i->nItem++;
1086 if (i->nItem < i->range.upper) return TRUE;
1091 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1092 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1095 else if (i->nItem >= i->range.upper) goto end;
1097 i->nItem = i->range.lower;
1098 if (i->nItem >= 0) goto testitem;
1105 * This function iterates through the items in decreasing order,
1106 * followed by the special item, then -1. That is:
1107 * n, n-1, ..., 3, 2, 1, special, -1.
1108 * Each item is listed only once.
1110 static inline BOOL iterator_prev(ITERATOR* i)
1117 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1120 if (i->nItem == i->nSpecial)
1128 if (i->nItem == i->nSpecial) i->nItem--;
1129 if (i->nItem >= i->range.lower) return TRUE;
1135 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1138 else if (!start && i->nItem < i->range.lower) goto end;
1140 i->nItem = i->range.upper;
1141 if (i->nItem > 0) goto testitem;
1143 return (i->nItem = i->nSpecial) != -1;
1146 static RANGE iterator_range(const ITERATOR *i)
1150 if (!i->ranges) return i->range;
1152 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1154 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1155 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1157 else range.lower = range.upper = 0;
1163 * Releases resources associated with this ierator.
1165 static inline void iterator_destroy(const ITERATOR *i)
1167 ranges_destroy(i->ranges);
1171 * Create an empty iterator.
1173 static inline BOOL iterator_empty(ITERATOR* i)
1175 ZeroMemory(i, sizeof(*i));
1176 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1181 * Create an iterator over a range.
1183 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1191 * Create an iterator over a bunch of ranges.
1192 * Please note that the iterator will take ownership of the ranges,
1193 * and will free them upon destruction.
1195 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1203 * Creates an iterator over the items which intersect lprc.
1205 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1207 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1208 RECT frame = *lprc, rcItem, rcTemp;
1211 /* in case we fail, we want to return an empty iterator */
1212 if (!iterator_empty(i)) return FALSE;
1214 LISTVIEW_GetOrigin(infoPtr, &Origin);
1216 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1217 OffsetRect(&frame, -Origin.x, -Origin.y);
1219 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1223 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1225 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1226 if (IntersectRect(&rcTemp, &rcItem, lprc))
1227 i->nSpecial = infoPtr->nFocusedItem;
1229 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1230 /* to do better here, we need to have PosX, and PosY sorted */
1231 TRACE("building icon ranges:\n");
1232 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1234 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1235 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1236 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1237 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1238 if (IntersectRect(&rcTemp, &rcItem, &frame))
1239 ranges_additem(i->ranges, nItem);
1243 else if (uView == LVS_REPORT)
1247 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1248 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1250 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1251 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1252 if (range.upper <= range.lower) return TRUE;
1253 if (!iterator_rangeitems(i, range)) return FALSE;
1254 TRACE(" report=%s\n", debugrange(&i->range));
1258 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1259 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1260 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1261 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1262 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1263 INT lower = nFirstCol * nPerCol + nFirstRow;
1267 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1268 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1270 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1272 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1273 TRACE("building list ranges:\n");
1274 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1276 item_range.lower = nCol * nPerCol + nFirstRow;
1277 if(item_range.lower >= infoPtr->nItemCount) break;
1278 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1279 TRACE(" list=%s\n", debugrange(&item_range));
1280 ranges_add(i->ranges, item_range);
1288 * Creates an iterator over the items which intersect the visible region of hdc.
1290 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1292 POINT Origin, Position;
1293 RECT rcItem, rcClip;
1296 rgntype = GetClipBox(hdc, &rcClip);
1297 if (rgntype == NULLREGION) return iterator_empty(i);
1298 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1299 if (rgntype == SIMPLEREGION) return TRUE;
1301 /* first deal with the special item */
1302 if (i->nSpecial != -1)
1304 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1305 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1308 /* if we can't deal with the region, we'll just go with the simple range */
1309 LISTVIEW_GetOrigin(infoPtr, &Origin);
1310 TRACE("building visible range:\n");
1311 if (!i->ranges && i->range.lower < i->range.upper)
1313 if (!(i->ranges = ranges_create(50))) return TRUE;
1314 if (!ranges_add(i->ranges, i->range))
1316 ranges_destroy(i->ranges);
1322 /* now delete the invisible items from the list */
1323 while(iterator_next(i))
1325 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1326 rcItem.left = Position.x + Origin.x;
1327 rcItem.top = Position.y + Origin.y;
1328 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1329 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1330 if (!RectVisible(hdc, &rcItem))
1331 ranges_delitem(i->ranges, i->nItem);
1333 /* the iterator should restart on the next iterator_next */
1339 /******** Misc helper functions ************************************/
1341 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1342 WPARAM wParam, LPARAM lParam, BOOL isW)
1344 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1345 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1348 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1350 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1352 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1353 (uView == LVS_ICON || uView == LVS_SMALLICON);
1356 /******** Internal API functions ************************************/
1358 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1360 static COLUMN_INFO mainItem;
1362 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1363 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1364 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1367 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1369 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1372 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1374 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1377 /* Listview invalidation functions: use _only_ these functions to invalidate */
1379 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1381 return infoPtr->bRedraw;
1384 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1386 if(!is_redrawing(infoPtr)) return;
1387 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1388 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1391 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1395 if(!is_redrawing(infoPtr)) return;
1396 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1397 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1400 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1402 POINT Origin, Position;
1405 if(!is_redrawing(infoPtr)) return;
1406 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1407 LISTVIEW_GetOrigin(infoPtr, &Origin);
1408 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1409 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1411 rcBox.bottom = infoPtr->nItemHeight;
1412 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1413 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1416 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1418 LISTVIEW_InvalidateRect(infoPtr, NULL);
1421 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1425 if(!is_redrawing(infoPtr)) return;
1426 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1427 rcCol.top = infoPtr->rcList.top;
1428 rcCol.bottom = infoPtr->rcList.bottom;
1429 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1434 * Retrieves the number of items that can fit vertically in the client area.
1437 * [I] infoPtr : valid pointer to the listview structure
1440 * Number of items per row.
1442 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1444 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1446 return max(nListWidth/infoPtr->nItemWidth, 1);
1451 * Retrieves the number of items that can fit horizontally in the client
1455 * [I] infoPtr : valid pointer to the listview structure
1458 * Number of items per column.
1460 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1462 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1464 return max(nListHeight / infoPtr->nItemHeight, 1);
1468 /*************************************************************************
1469 * LISTVIEW_ProcessLetterKeys
1471 * Processes keyboard messages generated by pressing the letter keys
1473 * What this does is perform a case insensitive search from the
1474 * current position with the following quirks:
1475 * - If two chars or more are pressed in quick succession we search
1476 * for the corresponding string (e.g. 'abc').
1477 * - If there is a delay we wipe away the current search string and
1478 * restart with just that char.
1479 * - If the user keeps pressing the same character, whether slowly or
1480 * fast, so that the search string is entirely composed of this
1481 * character ('aaaaa' for instance), then we search for first item
1482 * that starting with that character.
1483 * - If the user types the above character in quick succession, then
1484 * we must also search for the corresponding string ('aaaaa'), and
1485 * go to that string if there is a match.
1488 * [I] hwnd : handle to the window
1489 * [I] charCode : the character code, the actual character
1490 * [I] keyData : key data
1498 * - The current implementation has a list of characters it will
1499 * accept and it ignores everything else. In particular it will
1500 * ignore accentuated characters which seems to match what
1501 * Windows does. But I'm not sure it makes sense to follow
1503 * - We don't sound a beep when the search fails.
1507 * TREEVIEW_ProcessLetterKeys
1509 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1514 WCHAR buffer[MAX_PATH];
1515 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1517 /* simple parameter checking */
1518 if (!charCode || !keyData) return 0;
1520 /* only allow the valid WM_CHARs through */
1521 if (!isalnum(charCode) &&
1522 charCode != '.' && charCode != '`' && charCode != '!' &&
1523 charCode != '@' && charCode != '#' && charCode != '$' &&
1524 charCode != '%' && charCode != '^' && charCode != '&' &&
1525 charCode != '*' && charCode != '(' && charCode != ')' &&
1526 charCode != '-' && charCode != '_' && charCode != '+' &&
1527 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1528 charCode != '}' && charCode != '[' && charCode != '{' &&
1529 charCode != '/' && charCode != '?' && charCode != '>' &&
1530 charCode != '<' && charCode != ',' && charCode != '~')
1533 /* if there's one item or less, there is no where to go */
1534 if (infoPtr->nItemCount <= 1) return 0;
1536 /* update the search parameters */
1537 infoPtr->lastKeyPressTimestamp = GetTickCount();
1538 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1539 if (infoPtr->nSearchParamLength < MAX_PATH)
1540 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1541 if (infoPtr->charCode != charCode)
1542 infoPtr->charCode = charCode = 0;
1544 infoPtr->charCode=charCode;
1545 infoPtr->szSearchParam[0]=charCode;
1546 infoPtr->nSearchParamLength=1;
1547 /* Redundant with the 1 char string */
1551 /* and search from the current position */
1553 if (infoPtr->nFocusedItem >= 0) {
1554 endidx=infoPtr->nFocusedItem;
1556 /* if looking for single character match,
1557 * then we must always move forward
1559 if (infoPtr->nSearchParamLength == 1)
1562 endidx=infoPtr->nItemCount;
1566 if (idx == infoPtr->nItemCount) {
1567 if (endidx == infoPtr->nItemCount || endidx == 0)
1573 item.mask = LVIF_TEXT;
1576 item.pszText = buffer;
1577 item.cchTextMax = MAX_PATH;
1578 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1580 /* check for a match */
1581 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1584 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1585 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1586 /* This would work but we must keep looking for a longer match */
1590 } while (idx != endidx);
1593 LISTVIEW_KeySelection(infoPtr, nItem);
1598 /*************************************************************************
1599 * LISTVIEW_UpdateHeaderSize [Internal]
1601 * Function to resize the header control
1604 * [I] hwnd : handle to a window
1605 * [I] nNewScrollPos : scroll pos to set
1610 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1615 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1617 GetWindowRect(infoPtr->hwndHeader, &winRect);
1618 point[0].x = winRect.left;
1619 point[0].y = winRect.top;
1620 point[1].x = winRect.right;
1621 point[1].y = winRect.bottom;
1623 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1624 point[0].x = -nNewScrollPos;
1625 point[1].x += nNewScrollPos;
1627 SetWindowPos(infoPtr->hwndHeader,0,
1628 point[0].x,point[0].y,point[1].x,point[1].y,
1629 SWP_NOZORDER | SWP_NOACTIVATE);
1634 * Update the scrollbars. This functions should be called whenever
1635 * the content, size or view changes.
1638 * [I] infoPtr : valid pointer to the listview structure
1643 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1645 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1646 SCROLLINFO horzInfo, vertInfo;
1649 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1651 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1652 horzInfo.cbSize = sizeof(SCROLLINFO);
1653 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1655 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1656 if (uView == LVS_LIST)
1658 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1659 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1661 /* scroll by at least one column per page */
1662 if(horzInfo.nPage < infoPtr->nItemWidth)
1663 horzInfo.nPage = infoPtr->nItemWidth;
1665 horzInfo.nPage /= infoPtr->nItemWidth;
1667 else if (uView == LVS_REPORT)
1669 horzInfo.nMax = infoPtr->nItemWidth;
1671 else /* LVS_ICON, or LVS_SMALLICON */
1675 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1678 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1679 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1680 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1681 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1682 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1684 /* Setting the horizontal scroll can change the listview size
1685 * (and potentially everything else) so we need to recompute
1686 * everything again for the vertical scroll
1689 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1690 vertInfo.cbSize = sizeof(SCROLLINFO);
1691 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1693 if (uView == LVS_REPORT)
1695 vertInfo.nMax = infoPtr->nItemCount;
1697 /* scroll by at least one page */
1698 if(vertInfo.nPage < infoPtr->nItemHeight)
1699 vertInfo.nPage = infoPtr->nItemHeight;
1701 vertInfo.nPage /= infoPtr->nItemHeight;
1703 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1707 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1710 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1711 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1712 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1713 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1714 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1716 /* Change of the range may have changed the scroll pos. If so move the content */
1717 if (dx != 0 || dy != 0)
1720 listRect = infoPtr->rcList;
1721 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1722 SW_ERASE | SW_INVALIDATE);
1725 /* Update the Header Control */
1726 if (uView == LVS_REPORT)
1728 horzInfo.fMask = SIF_POS;
1729 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1730 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1737 * Shows/hides the focus rectangle.
1740 * [I] infoPtr : valid pointer to the listview structure
1741 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1746 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
1748 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1751 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1753 if (infoPtr->nFocusedItem < 0) return;
1755 /* we need some gymnastics in ICON mode to handle large items */
1756 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1760 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1761 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1763 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1768 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1770 /* for some reason, owner draw should work only in report mode */
1771 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1776 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1777 HFONT hOldFont = SelectObject(hdc, hFont);
1779 item.iItem = infoPtr->nFocusedItem;
1781 item.mask = LVIF_PARAM;
1782 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1784 ZeroMemory(&dis, sizeof(dis));
1785 dis.CtlType = ODT_LISTVIEW;
1786 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1787 dis.itemID = item.iItem;
1788 dis.itemAction = ODA_FOCUS;
1789 if (fShow) dis.itemState |= ODS_FOCUS;
1790 dis.hwndItem = infoPtr->hwndSelf;
1792 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1793 dis.itemData = item.lParam;
1795 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1797 SelectObject(hdc, hOldFont);
1801 DrawFocusRect(hdc, &infoPtr->rcFocus);
1804 ReleaseDC(infoPtr->hwndSelf, hdc);
1808 * Invalidates all visible selected items.
1810 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
1814 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1815 while(iterator_next(&i))
1817 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1818 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1820 iterator_destroy(&i);
1825 * DESCRIPTION: [INTERNAL]
1826 * Computes an item's (left,top) corner, relative to rcView.
1827 * That is, the position has NOT been made relative to the Origin.
1828 * This is deliberate, to avoid computing the Origin over, and
1829 * over again, when this function is called in a loop. Instead,
1830 * one can factor the computation of the Origin before the loop,
1831 * and offset the value returned by this function, on every iteration.
1834 * [I] infoPtr : valid pointer to the listview structure
1835 * [I] nItem : item number
1836 * [O] lpptOrig : item top, left corner
1841 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1843 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1845 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1847 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1849 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1850 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1852 else if (uView == LVS_LIST)
1854 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1855 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1856 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1858 else /* LVS_REPORT */
1860 lpptPosition->x = 0;
1861 lpptPosition->y = nItem * infoPtr->nItemHeight;
1866 * DESCRIPTION: [INTERNAL]
1867 * Compute the rectangles of an item. This is to localize all
1868 * the computations in one place. If you are not interested in some
1869 * of these values, simply pass in a NULL -- the function is smart
1870 * enough to compute only what's necessary. The function computes
1871 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1872 * one, the BOX rectangle. This rectangle is very cheap to compute,
1873 * and is guaranteed to contain all the other rectangles. Computing
1874 * the ICON rect is also cheap, but all the others are potentially
1875 * expensive. This gives an easy and effective optimization when
1876 * searching (like point inclusion, or rectangle intersection):
1877 * first test against the BOX, and if TRUE, test against the desired
1879 * If the function does not have all the necessary information
1880 * to computed the requested rectangles, will crash with a
1881 * failed assertion. This is done so we catch all programming
1882 * errors, given that the function is called only from our code.
1884 * We have the following 'special' meanings for a few fields:
1885 * * If LVIS_FOCUSED is set, we assume the item has the focus
1886 * This is important in ICON mode, where it might get a larger
1887 * then usual rectangle
1889 * Please note that subitem support works only in REPORT mode.
1892 * [I] infoPtr : valid pointer to the listview structure
1893 * [I] lpLVItem : item to compute the measures for
1894 * [O] lprcBox : ptr to Box rectangle
1895 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1896 * [0] lprcSelectBox : ptr to select box rectangle
1897 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1898 * [O] lprcIcon : ptr to Icon rectangle
1899 * Same as LVM_GETITEMRECT with LVIR_ICON
1900 * [O] lprcStateIcon: ptr to State Icon rectangle
1901 * [O] lprcLabel : ptr to Label rectangle
1902 * Same as LVM_GETITEMRECT with LVIR_LABEL
1907 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1908 LPRECT lprcBox, LPRECT lprcSelectBox,
1909 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
1911 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1912 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1913 RECT Box, SelectBox, Icon, Label;
1914 COLUMN_INFO *lpColumnInfo = NULL;
1915 SIZE labelSize = { 0, 0 };
1917 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1919 /* Be smart and try to figure out the minimum we have to do */
1920 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1921 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1923 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1924 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1926 if (lprcSelectBox) doSelectBox = TRUE;
1927 if (lprcLabel) doLabel = TRUE;
1928 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
1935 /************************************************************/
1936 /* compute the box rectangle (it should be cheap to do) */
1937 /************************************************************/
1938 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1939 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1941 if (lpLVItem->iSubItem)
1943 Box = lpColumnInfo->rcHeader;
1948 Box.right = infoPtr->nItemWidth;
1951 Box.bottom = infoPtr->nItemHeight;
1953 /******************************************************************/
1954 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
1955 /******************************************************************/
1958 LONG state_width = 0;
1960 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1961 state_width = infoPtr->iconStateSize.cx;
1963 if (uView == LVS_ICON)
1965 Icon.left = Box.left + state_width;
1966 if (infoPtr->himlNormal)
1967 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
1968 Icon.top = Box.top + ICON_TOP_PADDING;
1969 Icon.right = Icon.left;
1970 Icon.bottom = Icon.top;
1971 if (infoPtr->himlNormal)
1973 Icon.right += infoPtr->iconSize.cx;
1974 Icon.bottom += infoPtr->iconSize.cy;
1977 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1979 Icon.left = Box.left + state_width;
1981 if (uView == LVS_REPORT)
1982 Icon.left += REPORT_MARGINX;
1985 Icon.right = Icon.left;
1986 if (infoPtr->himlSmall &&
1987 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1988 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1989 Icon.right += infoPtr->iconSize.cx;
1990 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
1992 if(lprcIcon) *lprcIcon = Icon;
1993 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
1995 /* TODO: is this correct? */
1998 lprcStateIcon->left = Icon.left - state_width;
1999 lprcStateIcon->right = Icon.left;
2000 lprcStateIcon->top = Icon.top;
2001 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2002 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2005 else Icon.right = 0;
2007 /************************************************************/
2008 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2009 /************************************************************/
2012 /* calculate how far to the right can the label stretch */
2013 Label.right = Box.right;
2014 if (uView == LVS_REPORT)
2016 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2019 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2021 labelSize.cx = infoPtr->nItemWidth;
2022 labelSize.cy = infoPtr->nItemHeight;
2026 /* we need the text in non owner draw mode */
2027 assert(lpLVItem->mask & LVIF_TEXT);
2028 if (is_textT(lpLVItem->pszText, TRUE))
2030 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2031 HDC hdc = GetDC(infoPtr->hwndSelf);
2032 HFONT hOldFont = SelectObject(hdc, hFont);
2036 /* compute rough rectangle where the label will go */
2037 SetRectEmpty(&rcText);
2038 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2039 rcText.bottom = infoPtr->nItemHeight;
2040 if (uView == LVS_ICON)
2041 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2043 /* now figure out the flags */
2044 if (uView == LVS_ICON)
2045 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2047 uFormat = LV_SL_DT_FLAGS;
2049 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2051 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2052 labelSize.cy = rcText.bottom - rcText.top;
2054 SelectObject(hdc, hOldFont);
2055 ReleaseDC(infoPtr->hwndSelf, hdc);
2059 if (uView == LVS_ICON)
2061 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2062 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2063 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2064 Label.right = Label.left + labelSize.cx;
2065 Label.bottom = Label.top + infoPtr->nItemHeight;
2066 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2068 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2069 labelSize.cy /= infoPtr->ntmHeight;
2070 labelSize.cy = max(labelSize.cy, 1);
2071 labelSize.cy *= infoPtr->ntmHeight;
2073 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2075 else if (uView == LVS_REPORT)
2077 Label.left = Icon.right;
2078 Label.top = Box.top;
2079 Label.right = lpColumnInfo->rcHeader.right;
2080 Label.bottom = Label.top + infoPtr->nItemHeight;
2082 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2084 Label.left = Icon.right;
2085 Label.top = Box.top;
2086 Label.right = min(Label.left + labelSize.cx, Label.right);
2087 Label.bottom = Label.top + infoPtr->nItemHeight;
2090 if (lprcLabel) *lprcLabel = Label;
2091 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2094 /************************************************************/
2095 /* compute STATEICON bounding box */
2096 /************************************************************/
2099 if (uView == LVS_REPORT)
2101 SelectBox.left = Icon.right; /* FIXME: should be Icon.left */
2102 SelectBox.top = Box.top;
2103 SelectBox.bottom = Box.bottom;
2104 if (lpLVItem->iSubItem == 0)
2106 /* we need the indent in report mode */
2107 assert(lpLVItem->mask & LVIF_INDENT);
2108 SelectBox.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
2110 SelectBox.right = min(SelectBox.left + labelSize.cx, Label.right);
2114 UnionRect(&SelectBox, &Icon, &Label);
2116 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2117 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2120 /* Fix the Box if necessary */
2123 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2124 else *lprcBox = Box;
2126 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2130 * DESCRIPTION: [INTERNAL]
2133 * [I] infoPtr : valid pointer to the listview structure
2134 * [I] nItem : item number
2135 * [O] lprcBox : ptr to Box rectangle
2140 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2142 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2143 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2144 POINT Position, Origin;
2147 LISTVIEW_GetOrigin(infoPtr, &Origin);
2148 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2150 /* Be smart and try to figure out the minimum we have to do */
2152 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2153 lvItem.mask |= LVIF_TEXT;
2154 lvItem.iItem = nItem;
2155 lvItem.iSubItem = 0;
2156 lvItem.pszText = szDispText;
2157 lvItem.cchTextMax = DISP_TEXT_SIZE;
2158 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2159 if (uView == LVS_ICON)
2161 lvItem.mask |= LVIF_STATE;
2162 lvItem.stateMask = LVIS_FOCUSED;
2163 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2165 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2167 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2173 * Returns the current icon position, and advances it along the top.
2174 * The returned position is not offset by Origin.
2177 * [I] infoPtr : valid pointer to the listview structure
2178 * [O] lpPos : will get the current icon position
2183 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2185 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2187 *lpPos = infoPtr->currIconPos;
2189 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2190 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2192 infoPtr->currIconPos.x = 0;
2193 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2199 * Returns the current icon position, and advances it down the left edge.
2200 * The returned position is not offset by Origin.
2203 * [I] infoPtr : valid pointer to the listview structure
2204 * [O] lpPos : will get the current icon position
2209 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2211 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2213 *lpPos = infoPtr->currIconPos;
2215 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2216 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2218 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2219 infoPtr->currIconPos.y = 0;
2225 * Moves an icon to the specified position.
2226 * It takes care of invalidating the item, etc.
2229 * [I] infoPtr : valid pointer to the listview structure
2230 * [I] nItem : the item to move
2231 * [I] lpPos : the new icon position
2232 * [I] isNew : flags the item as being new
2238 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2244 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2245 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2247 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2248 LISTVIEW_InvalidateItem(infoPtr, nItem);
2251 /* Allocating a POINTER for every item is too resource intensive,
2252 * so we'll keep the (x,y) in different arrays */
2253 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2254 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2256 LISTVIEW_InvalidateItem(infoPtr, nItem);
2263 * Arranges listview items in icon display mode.
2266 * [I] infoPtr : valid pointer to the listview structure
2267 * [I] nAlignCode : alignment code
2273 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2275 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2276 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2280 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2282 TRACE("nAlignCode=%d\n", nAlignCode);
2284 if (nAlignCode == LVA_DEFAULT)
2286 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2287 else nAlignCode = LVA_ALIGNTOP;
2292 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2293 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2294 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2295 default: return FALSE;
2298 infoPtr->bAutoarrange = TRUE;
2299 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2300 for (i = 0; i < infoPtr->nItemCount; i++)
2302 next_pos(infoPtr, &pos);
2303 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2311 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2314 * [I] infoPtr : valid pointer to the listview structure
2315 * [O] lprcView : bounding rectangle
2321 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2325 SetRectEmpty(lprcView);
2327 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2331 for (i = 0; i < infoPtr->nItemCount; i++)
2333 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2334 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2335 lprcView->right = max(lprcView->right, x);
2336 lprcView->bottom = max(lprcView->bottom, y);
2338 if (infoPtr->nItemCount > 0)
2340 lprcView->right += infoPtr->nItemWidth;
2341 lprcView->bottom += infoPtr->nItemHeight;
2346 y = LISTVIEW_GetCountPerColumn(infoPtr);
2347 x = infoPtr->nItemCount / y;
2348 if (infoPtr->nItemCount % y) x++;
2349 lprcView->right = x * infoPtr->nItemWidth;
2350 lprcView->bottom = y * infoPtr->nItemHeight;
2354 lprcView->right = infoPtr->nItemWidth;
2355 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2362 * Retrieves the bounding rectangle of all the items.
2365 * [I] infoPtr : valid pointer to the listview structure
2366 * [O] lprcView : bounding rectangle
2372 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2376 TRACE("(lprcView=%p)\n", lprcView);
2378 if (!lprcView) return FALSE;
2380 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2381 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2382 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2384 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2391 * Retrieves the subitem pointer associated with the subitem index.
2394 * [I] hdpaSubItems : DPA handle for a specific item
2395 * [I] nSubItem : index of subitem
2398 * SUCCESS : subitem pointer
2401 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2403 SUBITEM_INFO *lpSubItem;
2406 /* we should binary search here if need be */
2407 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2409 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2410 if (lpSubItem->iSubItem == nSubItem)
2420 * Calculates the desired item width.
2423 * [I] infoPtr : valid pointer to the listview structure
2426 * The desired item width.
2428 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2430 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2433 TRACE("uView=%d\n", uView);
2435 if (uView == LVS_ICON)
2436 nItemWidth = infoPtr->iconSpacing.cx;
2437 else if (uView == LVS_REPORT)
2441 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2443 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2444 nItemWidth = rcHeader.right;
2447 else /* LVS_SMALLICON, or LVS_LIST */
2451 for (i = 0; i < infoPtr->nItemCount; i++)
2452 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2454 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2455 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2457 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2460 return max(nItemWidth, 1);
2465 * Calculates the desired item height.
2468 * [I] infoPtr : valid pointer to the listview structure
2471 * The desired item height.
2473 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2475 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2478 TRACE("uView=%d\n", uView);
2480 if (uView == LVS_ICON)
2481 nItemHeight = infoPtr->iconSpacing.cy;
2484 nItemHeight = infoPtr->ntmHeight;
2485 if (infoPtr->himlState)
2486 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2487 if (infoPtr->himlSmall)
2488 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2489 if (infoPtr->himlState || infoPtr->himlSmall)
2490 nItemHeight += HEIGHT_PADDING;
2491 if (infoPtr->nMeasureItemHeight > 0)
2492 nItemHeight = infoPtr->nMeasureItemHeight;
2495 return max(nItemHeight, 1);
2500 * Updates the width, and height of an item.
2503 * [I] infoPtr : valid pointer to the listview structure
2508 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2510 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2511 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2517 * Retrieves and saves important text metrics info for the current
2521 * [I] infoPtr : valid pointer to the listview structure
2524 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2526 HDC hdc = GetDC(infoPtr->hwndSelf);
2527 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2528 HFONT hOldFont = SelectObject(hdc, hFont);
2532 if (GetTextMetricsW(hdc, &tm))
2534 infoPtr->ntmHeight = tm.tmHeight;
2535 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2538 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2539 infoPtr->nEllipsisWidth = sz.cx;
2541 SelectObject(hdc, hOldFont);
2542 ReleaseDC(infoPtr->hwndSelf, hdc);
2544 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2549 * A compare function for ranges
2552 * [I] range1 : pointer to range 1;
2553 * [I] range2 : pointer to range 2;
2557 * > 0 : if range 1 > range 2
2558 * < 0 : if range 2 > range 1
2559 * = 0 : if range intersects range 2
2561 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2565 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2567 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2572 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2578 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2580 #define ranges_check(ranges, desc) do { } while(0)
2583 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2588 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2590 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2591 ranges_dump(ranges);
2592 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2593 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2594 assert (prev->lower >= 0 && prev->lower < prev->upper);
2595 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2597 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2598 assert (prev->upper <= curr->lower);
2599 assert (curr->lower < curr->upper);
2602 TRACE("--- Done checking---\n");
2605 static RANGES ranges_create(int count)
2607 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2608 if (!ranges) return NULL;
2609 ranges->hdpa = DPA_Create(count);
2610 if (ranges->hdpa) return ranges;
2615 static void ranges_clear(RANGES ranges)
2619 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2620 Free(DPA_GetPtr(ranges->hdpa, i));
2621 DPA_DeleteAllPtrs(ranges->hdpa);
2625 static void ranges_destroy(RANGES ranges)
2627 if (!ranges) return;
2628 ranges_clear(ranges);
2629 DPA_Destroy(ranges->hdpa);
2633 static RANGES ranges_clone(RANGES ranges)
2638 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2640 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2642 RANGE *newrng = Alloc(sizeof(RANGE));
2643 if (!newrng) goto fail;
2644 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2645 DPA_SetPtr(clone->hdpa, i, newrng);
2650 TRACE ("clone failed\n");
2651 ranges_destroy(clone);
2655 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2659 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2660 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2665 static void ranges_dump(RANGES ranges)
2669 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2670 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2673 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2675 RANGE srchrng = { nItem, nItem + 1 };
2677 TRACE("(nItem=%d)\n", nItem);
2678 ranges_check(ranges, "before contain");
2679 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2682 static INT ranges_itemcount(RANGES ranges)
2686 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2688 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2689 count += sel->upper - sel->lower;
2695 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2697 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2700 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2701 if (index == -1) return TRUE;
2703 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2705 chkrng = DPA_GetPtr(ranges->hdpa, index);
2706 if (chkrng->lower >= nItem)
2707 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2708 if (chkrng->upper > nItem)
2709 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2714 static BOOL ranges_add(RANGES ranges, RANGE range)
2719 TRACE("(%s)\n", debugrange(&range));
2720 ranges_check(ranges, "before add");
2722 /* try find overlapping regions first */
2723 srchrgn.lower = range.lower - 1;
2724 srchrgn.upper = range.upper + 1;
2725 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2731 TRACE("Adding new range\n");
2733 /* create the brand new range to insert */
2734 newrgn = Alloc(sizeof(RANGE));
2735 if(!newrgn) goto fail;
2738 /* figure out where to insert it */
2739 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2740 TRACE("index=%d\n", index);
2741 if (index == -1) index = 0;
2743 /* and get it over with */
2744 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2752 RANGE *chkrgn, *mrgrgn;
2753 INT fromindex, mergeindex;
2755 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2756 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2758 chkrgn->lower = min(range.lower, chkrgn->lower);
2759 chkrgn->upper = max(range.upper, chkrgn->upper);
2761 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2763 /* merge now common ranges */
2765 srchrgn.lower = chkrgn->lower - 1;
2766 srchrgn.upper = chkrgn->upper + 1;
2770 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2771 if (mergeindex == -1) break;
2772 if (mergeindex == index)
2774 fromindex = index + 1;
2778 TRACE("Merge with index %i\n", mergeindex);
2780 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2781 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2782 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2784 DPA_DeletePtr(ranges->hdpa, mergeindex);
2785 if (mergeindex < index) index --;
2789 ranges_check(ranges, "after add");
2793 ranges_check(ranges, "failed add");
2797 static BOOL ranges_del(RANGES ranges, RANGE range)
2802 TRACE("(%s)\n", debugrange(&range));
2803 ranges_check(ranges, "before del");
2805 /* we don't use DPAS_SORTED here, since we need *
2806 * to find the first overlapping range */
2807 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2810 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2812 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2814 /* case 1: Same range */
2815 if ( (chkrgn->upper == range.upper) &&
2816 (chkrgn->lower == range.lower) )
2818 DPA_DeletePtr(ranges->hdpa, index);
2821 /* case 2: engulf */
2822 else if ( (chkrgn->upper <= range.upper) &&
2823 (chkrgn->lower >= range.lower) )
2825 DPA_DeletePtr(ranges->hdpa, index);
2827 /* case 3: overlap upper */
2828 else if ( (chkrgn->upper <= range.upper) &&
2829 (chkrgn->lower < range.lower) )
2831 chkrgn->upper = range.lower;
2833 /* case 4: overlap lower */
2834 else if ( (chkrgn->upper > range.upper) &&
2835 (chkrgn->lower >= range.lower) )
2837 chkrgn->lower = range.upper;
2840 /* case 5: fully internal */
2843 RANGE tmprgn = *chkrgn, *newrgn;
2845 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
2846 newrgn->lower = chkrgn->lower;
2847 newrgn->upper = range.lower;
2848 chkrgn->lower = range.upper;
2849 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2858 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2861 ranges_check(ranges, "after del");
2865 ranges_check(ranges, "failed del");
2871 * Removes all selection ranges
2874 * [I] infoPtr : valid pointer to the listview structure
2875 * [I] toSkip : item range to skip removing the selection
2881 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2890 lvItem.stateMask = LVIS_SELECTED;
2892 /* need to clone the DPA because callbacks can change it */
2893 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2894 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2895 while(iterator_next(&i))
2896 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2897 /* note that the iterator destructor will free the cloned range */
2898 iterator_destroy(&i);
2903 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2907 if (!(toSkip = ranges_create(1))) return FALSE;
2908 if (nItem != -1) ranges_additem(toSkip, nItem);
2909 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2910 ranges_destroy(toSkip);
2914 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2916 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2921 * Retrieves the number of items that are marked as selected.
2924 * [I] infoPtr : valid pointer to the listview structure
2927 * Number of items selected.
2929 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
2931 INT nSelectedCount = 0;
2933 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2936 for (i = 0; i < infoPtr->nItemCount; i++)
2938 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2943 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2945 TRACE("nSelectedCount=%d\n", nSelectedCount);
2946 return nSelectedCount;
2951 * Manages the item focus.
2954 * [I] infoPtr : valid pointer to the listview structure
2955 * [I] nItem : item index
2958 * TRUE : focused item changed
2959 * FALSE : focused item has NOT changed
2961 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2963 INT oldFocus = infoPtr->nFocusedItem;
2966 if (nItem == infoPtr->nFocusedItem) return FALSE;
2968 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2969 lvItem.stateMask = LVIS_FOCUSED;
2970 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2972 return oldFocus != infoPtr->nFocusedItem;
2975 /* Helper function for LISTVIEW_ShiftIndices *only* */
2976 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2978 if (nShiftItem < nItem) return nShiftItem;
2980 if (nShiftItem > nItem) return nShiftItem + direction;
2982 if (direction > 0) return nShiftItem + direction;
2984 return min(nShiftItem, infoPtr->nItemCount - 1);
2989 * Updates the various indices after an item has been inserted or deleted.
2992 * [I] infoPtr : valid pointer to the listview structure
2993 * [I] nItem : item index
2994 * [I] direction : Direction of shift, +1 or -1.
2999 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3004 /* temporarily disable change notification while shifting items */
3005 bOldChange = infoPtr->bDoChangeNotify;
3006 infoPtr->bDoChangeNotify = FALSE;
3008 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3010 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3012 assert(abs(direction) == 1);
3014 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3016 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3017 if (nNewFocus != infoPtr->nFocusedItem)
3018 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3020 /* But we are not supposed to modify nHotItem! */
3022 infoPtr->bDoChangeNotify = bOldChange;
3028 * Adds a block of selections.
3031 * [I] infoPtr : valid pointer to the listview structure
3032 * [I] nItem : item index
3035 * Whether the window is still valid.
3037 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3039 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3040 INT nLast = max(infoPtr->nSelectionMark, nItem);
3041 HWND hwndSelf = infoPtr->hwndSelf;
3042 NMLVODSTATECHANGE nmlv;
3047 /* Temporarily disable change notification
3048 * If the control is LVS_OWNERDATA, we need to send
3049 * only one LVN_ODSTATECHANGED notification.
3050 * See MSDN documentation for LVN_ITEMCHANGED.
3052 bOldChange = infoPtr->bDoChangeNotify;
3053 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3055 if (nFirst == -1) nFirst = nItem;
3057 item.state = LVIS_SELECTED;
3058 item.stateMask = LVIS_SELECTED;
3060 for (i = nFirst; i <= nLast; i++)
3061 LISTVIEW_SetItemState(infoPtr,i,&item);
3063 ZeroMemory(&nmlv, sizeof(nmlv));
3064 nmlv.iFrom = nFirst;
3067 nmlv.uOldState = item.state;
3069 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3070 if (!IsWindow(hwndSelf))
3072 infoPtr->bDoChangeNotify = bOldChange;
3079 * Sets a single group selection.
3082 * [I] infoPtr : valid pointer to the listview structure
3083 * [I] nItem : item index
3088 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3090 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3096 if (!(selection = ranges_create(100))) return;
3098 item.state = LVIS_SELECTED;
3099 item.stateMask = LVIS_SELECTED;
3101 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3103 if (infoPtr->nSelectionMark == -1)
3105 infoPtr->nSelectionMark = nItem;
3106 ranges_additem(selection, nItem);
3112 sel.lower = min(infoPtr->nSelectionMark, nItem);
3113 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3114 ranges_add(selection, sel);
3119 RECT rcItem, rcSel, rcSelMark;
3122 rcItem.left = LVIR_BOUNDS;
3123 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3124 rcSelMark.left = LVIR_BOUNDS;
3125 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3126 UnionRect(&rcSel, &rcItem, &rcSelMark);
3127 iterator_frameditems(&i, infoPtr, &rcSel);
3128 while(iterator_next(&i))
3130 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3131 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3133 iterator_destroy(&i);
3136 bOldChange = infoPtr->bDoChangeNotify;
3137 infoPtr->bDoChangeNotify = FALSE;
3139 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3142 iterator_rangesitems(&i, selection);
3143 while(iterator_next(&i))
3144 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3145 /* this will also destroy the selection */
3146 iterator_destroy(&i);
3148 infoPtr->bDoChangeNotify = bOldChange;
3150 LISTVIEW_SetItemFocus(infoPtr, nItem);
3155 * Sets a single selection.
3158 * [I] infoPtr : valid pointer to the listview structure
3159 * [I] nItem : item index
3164 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3168 TRACE("nItem=%d\n", nItem);
3170 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3172 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3173 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3174 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3176 infoPtr->nSelectionMark = nItem;
3181 * Set selection(s) with keyboard.
3184 * [I] infoPtr : valid pointer to the listview structure
3185 * [I] nItem : item index
3188 * SUCCESS : TRUE (needs to be repainted)
3189 * FAILURE : FALSE (nothing has changed)
3191 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3193 /* FIXME: pass in the state */
3194 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3195 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3196 BOOL bResult = FALSE;
3198 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3199 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3201 if (infoPtr->dwStyle & LVS_SINGLESEL)
3204 LISTVIEW_SetSelection(infoPtr, nItem);
3211 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3216 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3217 lvItem.stateMask = LVIS_SELECTED;
3218 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3220 if (lvItem.state & LVIS_SELECTED)
3221 infoPtr->nSelectionMark = nItem;
3223 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3228 LISTVIEW_SetSelection(infoPtr, nItem);
3231 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3234 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3238 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3240 LVHITTESTINFO lvHitTestInfo;
3242 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3243 lvHitTestInfo.pt.x = pt.x;
3244 lvHitTestInfo.pt.y = pt.y;
3246 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3248 lpLVItem->mask = LVIF_PARAM;
3249 lpLVItem->iItem = lvHitTestInfo.iItem;
3250 lpLVItem->iSubItem = 0;
3252 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3257 * Called when the mouse is being actively tracked and has hovered for a specified
3261 * [I] infoPtr : valid pointer to the listview structure
3262 * [I] fwKeys : key indicator
3263 * [I] x,y : mouse position
3266 * 0 if the message was processed, non-zero if there was an error
3269 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3270 * over the item for a certain period of time.
3273 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3275 if (infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3283 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3284 LISTVIEW_SetSelection(infoPtr, item.iItem);
3292 * Called whenever WM_MOUSEMOVE is received.
3295 * [I] infoPtr : valid pointer to the listview structure
3296 * [I] fwKeys : key indicator
3297 * [I] x,y : mouse position
3300 * 0 if the message is processed, non-zero if there was an error
3302 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3304 TRACKMOUSEEVENT trackinfo;
3306 if (!(fwKeys & MK_LBUTTON))
3307 infoPtr->bLButtonDown = FALSE;
3309 if (infoPtr->bLButtonDown && DragDetect(infoPtr->hwndSelf, infoPtr->ptClickPos))
3311 LVHITTESTINFO lvHitTestInfo;
3314 lvHitTestInfo.pt = infoPtr->ptClickPos;
3315 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3317 ZeroMemory(&nmlv, sizeof(nmlv));
3318 nmlv.iItem = lvHitTestInfo.iItem;
3319 nmlv.ptAction = infoPtr->ptClickPos;
3321 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3326 infoPtr->bLButtonDown = FALSE;
3328 /* see if we are supposed to be tracking mouse hovering */
3329 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3330 /* fill in the trackinfo struct */
3331 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3332 trackinfo.dwFlags = TME_QUERY;
3333 trackinfo.hwndTrack = infoPtr->hwndSelf;
3334 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3336 /* see if we are already tracking this hwnd */
3337 _TrackMouseEvent(&trackinfo);
3339 if(!(trackinfo.dwFlags & TME_HOVER)) {
3340 trackinfo.dwFlags = TME_HOVER;
3342 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3343 _TrackMouseEvent(&trackinfo);
3352 * Tests whether the item is assignable to a list with style lStyle
3354 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3356 if ( (lpLVItem->mask & LVIF_TEXT) &&
3357 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3358 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3366 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3369 * [I] infoPtr : valid pointer to the listview structure
3370 * [I] lpLVItem : valid pointer to new item attributes
3371 * [I] isNew : the item being set is being inserted
3372 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3373 * [O] bChanged : will be set to TRUE if the item really changed
3379 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3381 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3389 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3391 if (lpLVItem->mask == 0) return TRUE;
3393 if (infoPtr->dwStyle & LVS_OWNERDATA)
3395 /* a virtual listview only stores selection and focus */
3396 if (lpLVItem->mask & ~LVIF_STATE)
3402 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3403 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3407 /* we need to get the lParam and state of the item */
3408 item.iItem = lpLVItem->iItem;
3409 item.iSubItem = lpLVItem->iSubItem;
3410 item.mask = LVIF_STATE | LVIF_PARAM;
3411 item.stateMask = ~0;
3414 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3416 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3417 /* determine what fields will change */
3418 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3419 uChanged |= LVIF_STATE;
3421 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3422 uChanged |= LVIF_IMAGE;
3424 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3425 uChanged |= LVIF_PARAM;
3427 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3428 uChanged |= LVIF_INDENT;
3430 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3431 uChanged |= LVIF_TEXT;
3433 TRACE("uChanged=0x%x\n", uChanged);
3434 if (!uChanged) return TRUE;
3437 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3438 nmlv.iItem = lpLVItem->iItem;
3439 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3440 nmlv.uOldState = item.state;
3441 nmlv.uChanged = uChanged;
3442 nmlv.lParam = item.lParam;
3444 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3445 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3447 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3449 HWND hwndSelf = infoPtr->hwndSelf;
3451 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3453 if (!IsWindow(hwndSelf))
3457 /* copy information */
3458 if (lpLVItem->mask & LVIF_TEXT)
3459 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3461 if (lpLVItem->mask & LVIF_IMAGE)
3462 lpItem->hdr.iImage = lpLVItem->iImage;
3464 if (lpLVItem->mask & LVIF_PARAM)
3465 lpItem->lParam = lpLVItem->lParam;
3467 if (lpLVItem->mask & LVIF_INDENT)
3468 lpItem->iIndent = lpLVItem->iIndent;
3470 if (uChanged & LVIF_STATE)
3472 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3474 lpItem->state &= ~lpLVItem->stateMask;
3475 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3477 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3479 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3480 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3482 else if (lpLVItem->stateMask & LVIS_SELECTED)
3483 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3485 /* if we are asked to change focus, and we manage it, do it */
3486 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3488 if (lpLVItem->state & LVIS_FOCUSED)
3490 LISTVIEW_SetItemFocus(infoPtr, -1);
3491 infoPtr->nFocusedItem = lpLVItem->iItem;
3492 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3494 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3495 infoPtr->nFocusedItem = -1;
3499 /* if we're inserting the item, we're done */
3500 if (isNew) return TRUE;
3502 /* send LVN_ITEMCHANGED notification */
3503 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3504 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3511 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3514 * [I] infoPtr : valid pointer to the listview structure
3515 * [I] lpLVItem : valid pointer to new subitem attributes
3516 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3517 * [O] bChanged : will be set to TRUE if the item really changed
3523 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3526 SUBITEM_INFO *lpSubItem;
3528 /* we do not support subitems for virtual listviews */
3529 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3531 /* set subitem only if column is present */
3532 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3534 /* First do some sanity checks */
3535 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3536 particularly useful. We currently do not actually do anything with
3537 the flag on subitems.
3539 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3540 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3542 /* get the subitem structure, and create it if not there */
3543 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3544 assert (hdpaSubItems);
3546 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3549 SUBITEM_INFO *tmpSubItem;
3552 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3553 if (!lpSubItem) return FALSE;
3554 /* we could binary search here, if need be...*/
3555 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3557 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3558 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3560 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3565 lpSubItem->iSubItem = lpLVItem->iSubItem;
3566 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3570 if (lpLVItem->mask & LVIF_IMAGE)
3571 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3573 lpSubItem->hdr.iImage = lpLVItem->iImage;
3577 if (lpLVItem->mask & LVIF_TEXT)
3578 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3580 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3589 * Sets item attributes.
3592 * [I] infoPtr : valid pointer to the listview structure
3593 * [I] lpLVItem : new item attributes
3594 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3600 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3602 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3603 HWND hwndSelf = infoPtr->hwndSelf;
3604 LPWSTR pszText = NULL;
3605 BOOL bResult, bChanged = FALSE;
3607 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3609 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3612 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3613 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3615 pszText = lpLVItem->pszText;
3616 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3619 /* actually set the fields */
3620 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3622 if (lpLVItem->iSubItem)
3623 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3625 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3626 if (!IsWindow(hwndSelf))
3629 /* redraw item, if necessary */
3630 if (bChanged && !infoPtr->bIsDrawing)
3632 /* this little optimization eliminates some nasty flicker */
3633 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3634 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3635 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3636 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3638 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3643 textfreeT(lpLVItem->pszText, isW);
3644 lpLVItem->pszText = pszText;
3652 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3655 * [I] infoPtr : valid pointer to the listview structure
3660 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3662 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3664 SCROLLINFO scrollInfo;
3666 scrollInfo.cbSize = sizeof(SCROLLINFO);
3667 scrollInfo.fMask = SIF_POS;
3669 if (uView == LVS_LIST)
3671 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3672 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3674 else if (uView == LVS_REPORT)
3676 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3677 nItem = scrollInfo.nPos;
3681 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3682 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3685 TRACE("nItem=%d\n", nItem);
3693 * Erases the background of the given rectangle
3696 * [I] infoPtr : valid pointer to the listview structure
3697 * [I] hdc : device context handle
3698 * [I] lprcBox : clipping rectangle
3704 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3706 if (!infoPtr->hBkBrush) return FALSE;
3708 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3710 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3718 * [I] infoPtr : valid pointer to the listview structure
3719 * [I] hdc : device context handle
3720 * [I] nItem : item index
3721 * [I] nSubItem : subitem index
3722 * [I] pos : item position in client coordinates
3723 * [I] cdmode : custom draw mode
3729 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3731 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3732 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3733 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3734 DWORD cdsubitemmode = CDRF_DODEFAULT;
3736 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3737 NMLVCUSTOMDRAW nmlvcd;
3742 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3744 /* get information needed for drawing the item */
3745 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3746 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3747 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3748 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3749 lvItem.iItem = nItem;
3750 lvItem.iSubItem = nSubItem;
3753 lvItem.cchTextMax = DISP_TEXT_SIZE;
3754 lvItem.pszText = szDispText;
3755 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3756 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3757 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3758 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3759 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3761 /* now check if we need to update the focus rectangle */
3762 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3764 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3765 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3766 OffsetRect(&rcBox, pos.x, pos.y);
3767 OffsetRect(&rcSelect, pos.x, pos.y);
3768 OffsetRect(&rcIcon, pos.x, pos.y);
3769 OffsetRect(&rcStateIcon, pos.x, pos.y);
3770 OffsetRect(&rcLabel, pos.x, pos.y);
3771 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3772 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3773 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3775 /* fill in the custom draw structure */
3776 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3778 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3779 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3780 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3781 if (cdmode & CDRF_NOTIFYITEMDRAW)
3782 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3783 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3784 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3785 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3786 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3788 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3789 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3791 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3792 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
3793 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
3794 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
3796 /* in full row select, subitems, will just use main item's colors */
3797 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3798 nmlvcd.clrTextBk = CLR_NONE;
3801 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3803 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3806 TRACE("uStateImage=%d\n", uStateImage);
3807 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3808 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
3813 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3814 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3816 TRACE("iImage=%d\n", lvItem.iImage);
3817 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3818 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3819 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3822 /* Don't bother painting item being edited */
3823 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3825 /* FIXME: temporary hack */
3826 rcSelect.left = rcLabel.left;
3828 /* draw the selection background, if we're drawing the main item */
3831 /* in icon mode, the label rect is really what we want to draw the
3833 if (uView == LVS_ICON)
3836 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3837 rcSelect.right = rcBox.right;
3839 if (nmlvcd.clrTextBk != CLR_NONE)
3840 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3841 if(lprcFocus) *lprcFocus = rcSelect;
3844 /* figure out the text drawing flags */
3845 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3846 if (uView == LVS_ICON)
3847 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3850 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3852 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3853 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3854 default: uFormat |= DT_LEFT;
3857 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3859 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3860 else rcLabel.left += LABEL_HOR_PADDING;
3862 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3863 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3866 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3867 notify_postpaint(infoPtr, &nmlvcd);
3868 if (cdsubitemmode & CDRF_NEWFONT)
3869 SelectObject(hdc, hOldFont);
3875 * Draws listview items when in owner draw mode.
3878 * [I] infoPtr : valid pointer to the listview structure
3879 * [I] hdc : device context handle
3884 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3886 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3887 DWORD cditemmode = CDRF_DODEFAULT;
3888 NMLVCUSTOMDRAW nmlvcd;
3889 POINT Origin, Position;
3895 ZeroMemory(&dis, sizeof(dis));
3897 /* Get scroll info once before loop */
3898 LISTVIEW_GetOrigin(infoPtr, &Origin);
3900 /* iterate through the invalidated rows */
3901 while(iterator_next(i))
3903 item.iItem = i->nItem;
3905 item.mask = LVIF_PARAM | LVIF_STATE;
3906 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3907 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3909 dis.CtlType = ODT_LISTVIEW;
3911 dis.itemID = item.iItem;
3912 dis.itemAction = ODA_DRAWENTIRE;
3914 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3915 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3916 dis.hwndItem = infoPtr->hwndSelf;
3918 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3919 dis.rcItem.left = Position.x + Origin.x;
3920 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3921 dis.rcItem.top = Position.y + Origin.y;
3922 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3923 dis.itemData = item.lParam;
3925 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3928 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3929 * structure for the rest. of the paint cycle
3931 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3932 if (cdmode & CDRF_NOTIFYITEMDRAW)
3933 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3935 if (!(cditemmode & CDRF_SKIPDEFAULT))
3937 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
3938 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3941 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3942 notify_postpaint(infoPtr, &nmlvcd);
3948 * Draws listview items when in report display mode.
3951 * [I] infoPtr : valid pointer to the listview structure
3952 * [I] hdc : device context handle
3953 * [I] cdmode : custom draw mode
3958 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3961 RECT rcClip, rcItem;
3962 POINT Origin, Position;
3968 /* figure out what to draw */
3969 rgntype = GetClipBox(hdc, &rcClip);
3970 if (rgntype == NULLREGION) return;
3972 /* Get scroll info once before loop */
3973 LISTVIEW_GetOrigin(infoPtr, &Origin);
3975 /* narrow down the columns we need to paint */
3976 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3978 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3979 if (rcItem.right + Origin.x >= rcClip.left) break;
3981 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3983 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3984 if (rcItem.left + Origin.x < rcClip.right) break;
3986 iterator_rangeitems(&j, colRange);
3988 /* in full row select, we _have_ to draw the main item */
3989 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3992 /* iterate through the invalidated rows */
3993 while(iterator_next(i))
3995 /* iterate through the invalidated columns */
3996 while(iterator_next(&j))
3998 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3999 Position.x += Origin.x;
4000 Position.y += Origin.y;
4002 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4004 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4006 rcItem.bottom = infoPtr->nItemHeight;
4007 OffsetRect(&rcItem, Position.x, Position.y);
4008 if (!RectVisible(hdc, &rcItem)) continue;
4011 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4014 iterator_destroy(&j);
4019 * Draws listview items when in list display mode.
4022 * [I] infoPtr : valid pointer to the listview structure
4023 * [I] hdc : device context handle
4024 * [I] cdmode : custom draw mode
4029 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4031 POINT Origin, Position;
4033 /* Get scroll info once before loop */
4034 LISTVIEW_GetOrigin(infoPtr, &Origin);
4036 while(iterator_prev(i))
4038 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4039 Position.x += Origin.x;
4040 Position.y += Origin.y;
4042 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4049 * Draws listview items.
4052 * [I] infoPtr : valid pointer to the listview structure
4053 * [I] hdc : device context handle
4054 * [I] prcErase : rect to be erased before refresh (may be NULL)
4059 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4061 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4062 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4063 NMLVCUSTOMDRAW nmlvcd;
4070 HBITMAP hbmp = NULL;
4072 LISTVIEW_DUMP(infoPtr);
4074 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4075 TRACE("double buffering\n");
4077 hdc = CreateCompatibleDC(hdcOrig);
4079 ERR("Failed to create DC for backbuffer\n");
4082 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4083 infoPtr->rcList.bottom);
4085 ERR("Failed to create bitmap for backbuffer\n");
4090 SelectObject(hdc, hbmp);
4091 SelectObject(hdc, infoPtr->hFont);
4093 /* Save dc values we're gonna trash while drawing
4094 * FIXME: Should be done in LISTVIEW_DrawItem() */
4095 hOldFont = SelectObject(hdc, infoPtr->hFont);
4096 oldBkMode = GetBkMode(hdc);
4097 oldBkColor = GetBkColor(hdc);
4098 oldTextColor = GetTextColor(hdc);
4101 infoPtr->bIsDrawing = TRUE;
4104 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4105 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4106 /* If no erasing was done (usually because RedrawWindow was called
4107 * with RDW_INVALIDATE only) we need to copy the old contents into
4108 * the backbuffer before continuing. */
4109 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4110 infoPtr->rcList.right - infoPtr->rcList.left,
4111 infoPtr->rcList.bottom - infoPtr->rcList.top,
4112 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4115 /* FIXME: Shouldn't need to do this */
4116 oldClrTextBk = infoPtr->clrTextBk;
4117 oldClrText = infoPtr->clrText;
4119 infoPtr->cditemmode = CDRF_DODEFAULT;
4121 GetClientRect(infoPtr->hwndSelf, &rcClient);
4122 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4123 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4124 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4125 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4127 /* Use these colors to draw the items */
4128 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4129 infoPtr->clrText = nmlvcd.clrText;
4131 /* nothing to draw */
4132 if(infoPtr->nItemCount == 0) goto enddraw;
4134 /* figure out what we need to draw */
4135 iterator_visibleitems(&i, infoPtr, hdc);
4137 /* send cache hint notification */
4138 if (infoPtr->dwStyle & LVS_OWNERDATA)
4140 RANGE range = iterator_range(&i);
4143 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4144 nmlv.iFrom = range.lower;
4145 nmlv.iTo = range.upper - 1;
4146 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4149 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4150 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4153 if (uView == LVS_REPORT)
4154 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4155 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4156 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4158 /* if we have a focus rect, draw it */
4159 if (infoPtr->bFocus)
4160 DrawFocusRect(hdc, &infoPtr->rcFocus);
4162 iterator_destroy(&i);
4165 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4166 notify_postpaint(infoPtr, &nmlvcd);
4168 infoPtr->clrTextBk = oldClrTextBk;
4169 infoPtr->clrText = oldClrText;
4172 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4173 infoPtr->rcList.right - infoPtr->rcList.left,
4174 infoPtr->rcList.bottom - infoPtr->rcList.top,
4175 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4180 SelectObject(hdc, hOldFont);
4181 SetBkMode(hdc, oldBkMode);
4182 SetBkColor(hdc, oldBkColor);
4183 SetTextColor(hdc, oldTextColor);
4186 infoPtr->bIsDrawing = FALSE;
4192 * Calculates the approximate width and height of a given number of items.
4195 * [I] infoPtr : valid pointer to the listview structure
4196 * [I] nItemCount : number of items
4197 * [I] wWidth : width
4198 * [I] wHeight : height
4201 * Returns a DWORD. The width in the low word and the height in high word.
4203 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4204 WORD wWidth, WORD wHeight)
4206 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4207 INT nItemCountPerColumn = 1;
4208 INT nColumnCount = 0;
4209 DWORD dwViewRect = 0;
4211 if (nItemCount == -1)
4212 nItemCount = infoPtr->nItemCount;
4214 if (uView == LVS_LIST)
4216 if (wHeight == 0xFFFF)
4218 /* use current height */
4219 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4222 if (wHeight < infoPtr->nItemHeight)
4223 wHeight = infoPtr->nItemHeight;
4227 if (infoPtr->nItemHeight > 0)
4229 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4230 if (nItemCountPerColumn == 0)
4231 nItemCountPerColumn = 1;
4233 if (nItemCount % nItemCountPerColumn != 0)
4234 nColumnCount = nItemCount / nItemCountPerColumn;
4236 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4240 /* Microsoft padding magic */
4241 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4242 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4244 dwViewRect = MAKELONG(wWidth, wHeight);
4246 else if (uView == LVS_REPORT)
4250 if (infoPtr->nItemCount > 0)
4252 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4253 wWidth = rcBox.right - rcBox.left;
4254 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4258 /* use current height and width */
4259 if (wHeight == 0xffff)
4260 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4261 if (wWidth == 0xffff)
4262 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4265 dwViewRect = MAKELONG(wWidth, wHeight);
4267 else if (uView == LVS_SMALLICON)
4268 FIXME("uView == LVS_SMALLICON: not implemented\n");
4269 else if (uView == LVS_ICON)
4270 FIXME("uView == LVS_ICON: not implemented\n");
4278 * Create a drag image list for the specified item.
4281 * [I] infoPtr : valid pointer to the listview structure
4282 * [I] iItem : index of item
4283 * [O] lppt : Upperr-left corner of the image
4286 * Returns a handle to the image list if successful, NULL otherwise.
4288 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4294 HBITMAP hbmp, hOldbmp;
4295 HIMAGELIST dragList = 0;
4296 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4298 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4301 rcItem.left = LVIR_BOUNDS;
4302 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4305 lppt->x = rcItem.left;
4306 lppt->y = rcItem.top;
4308 size.cx = rcItem.right - rcItem.left;
4309 size.cy = rcItem.bottom - rcItem.top;
4311 hdcOrig = GetDC(infoPtr->hwndSelf);
4312 hdc = CreateCompatibleDC(hdcOrig);
4313 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4314 hOldbmp = SelectObject(hdc, hbmp);
4316 rcItem.left = rcItem.top = 0;
4317 rcItem.right = size.cx;
4318 rcItem.bottom = size.cy;
4319 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4322 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4324 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4325 SelectObject(hdc, hOldbmp);
4326 ImageList_Add(dragList, hbmp, 0);
4329 SelectObject(hdc, hOldbmp);
4333 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4335 TRACE("ret=%p\n", dragList);
4343 * Removes all listview items and subitems.
4346 * [I] infoPtr : valid pointer to the listview structure
4352 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4355 HDPA hdpaSubItems = NULL;
4362 /* we do it directly, to avoid notifications */
4363 ranges_clear(infoPtr->selectionRanges);
4364 infoPtr->nSelectionMark = -1;
4365 infoPtr->nFocusedItem = -1;
4366 SetRectEmpty(&infoPtr->rcFocus);
4367 /* But we are supposed to leave nHotItem as is! */
4370 /* send LVN_DELETEALLITEMS notification */
4371 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4373 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4375 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4377 /* send LVN_DELETEITEM notification, if not suppressed */
4378 if (!bSuppress) notify_deleteitem(infoPtr, i);
4379 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4381 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4382 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4384 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4385 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4388 DPA_Destroy(hdpaSubItems);
4389 DPA_DeletePtr(infoPtr->hdpaItems, i);
4391 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4392 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4393 infoPtr->nItemCount --;
4396 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4397 LISTVIEW_UpdateScroll(infoPtr);
4398 LISTVIEW_InvalidateList(infoPtr);
4405 * Scrolls, and updates the columns, when a column is changing width.
4408 * [I] infoPtr : valid pointer to the listview structure
4409 * [I] nColumn : column to scroll
4410 * [I] dx : amount of scroll, in pixels
4415 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4417 COLUMN_INFO *lpColumnInfo;
4422 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4423 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4424 rcCol = lpColumnInfo->rcHeader;
4425 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4426 rcCol.left = rcCol.right;
4428 /* adjust the other columns */
4429 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4431 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4432 lpColumnInfo->rcHeader.left += dx;
4433 lpColumnInfo->rcHeader.right += dx;
4436 /* do not update screen if not in report mode */
4437 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4439 /* if we have a focus, we must first erase the focus rect */
4440 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4442 /* Need to reset the item width when inserting a new column */
4443 infoPtr->nItemWidth += dx;
4445 LISTVIEW_UpdateScroll(infoPtr);
4446 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4448 /* scroll to cover the deleted column, and invalidate for redraw */
4449 rcOld = infoPtr->rcList;
4450 rcOld.left = ptOrigin.x + rcCol.left + dx;
4451 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4453 /* we can restore focus now */
4454 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4459 * Removes a column from the listview control.
4462 * [I] infoPtr : valid pointer to the listview structure
4463 * [I] nColumn : column index
4469 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4473 TRACE("nColumn=%d\n", nColumn);
4475 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4476 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4478 /* While the MSDN specifically says that column zero should not be deleted,
4479 what actually happens is that the column itself is deleted but no items or subitems
4483 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4485 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4488 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4489 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4491 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4493 SUBITEM_INFO *lpSubItem, *lpDelItem;
4495 INT nItem, nSubItem, i;
4497 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4499 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4502 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4504 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4505 if (lpSubItem->iSubItem == nColumn)
4508 lpDelItem = lpSubItem;
4510 else if (lpSubItem->iSubItem > nColumn)
4512 lpSubItem->iSubItem--;
4516 /* if we found our subitem, zapp it */
4520 if (is_textW(lpDelItem->hdr.pszText))
4521 Free(lpDelItem->hdr.pszText);
4526 /* free dpa memory */
4527 DPA_DeletePtr(hdpaSubItems, nSubItem);
4532 /* update the other column info */
4533 LISTVIEW_UpdateItemSize(infoPtr);
4534 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4535 LISTVIEW_InvalidateList(infoPtr);
4537 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4544 * Invalidates the listview after an item's insertion or deletion.
4547 * [I] infoPtr : valid pointer to the listview structure
4548 * [I] nItem : item index
4549 * [I] dir : -1 if deleting, 1 if inserting
4554 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4556 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4557 INT nPerCol, nItemCol, nItemRow;
4561 /* if we don't refresh, what's the point of scrolling? */
4562 if (!is_redrawing(infoPtr)) return;
4564 assert (abs(dir) == 1);
4566 /* arrange icons if autoarrange is on */
4567 if (is_autoarrange(infoPtr))
4569 BOOL arrange = TRUE;
4570 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4571 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4572 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4575 /* scrollbars need updating */
4576 LISTVIEW_UpdateScroll(infoPtr);
4578 /* figure out the item's position */
4579 if (uView == LVS_REPORT)
4580 nPerCol = infoPtr->nItemCount + 1;
4581 else if (uView == LVS_LIST)
4582 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4583 else /* LVS_ICON, or LVS_SMALLICON */
4586 nItemCol = nItem / nPerCol;
4587 nItemRow = nItem % nPerCol;
4588 LISTVIEW_GetOrigin(infoPtr, &Origin);
4590 /* move the items below up a slot */
4591 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4592 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4593 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4594 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4595 OffsetRect(&rcScroll, Origin.x, Origin.y);
4596 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4597 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4599 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4600 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4601 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4604 /* report has only that column, so we're done */
4605 if (uView == LVS_REPORT) return;
4607 /* now for LISTs, we have to deal with the columns to the right */
4608 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4610 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4611 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4612 OffsetRect(&rcScroll, Origin.x, Origin.y);
4613 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4614 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4615 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4620 * Removes an item from the listview control.
4623 * [I] infoPtr : valid pointer to the listview structure
4624 * [I] nItem : item index
4630 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4632 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4635 TRACE("(nItem=%d)\n", nItem);
4637 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4639 /* remove selection, and focus */
4641 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4642 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4644 /* send LVN_DELETEITEM notification. */
4645 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4647 /* we need to do this here, because we'll be deleting stuff */
4648 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4649 LISTVIEW_InvalidateItem(infoPtr, nItem);
4651 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4657 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4658 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4660 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4661 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4664 DPA_Destroy(hdpaSubItems);
4667 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4669 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4670 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4673 infoPtr->nItemCount--;
4674 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4676 /* now is the invalidation fun */
4677 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4684 * Callback implementation for editlabel control
4687 * [I] infoPtr : valid pointer to the listview structure
4688 * [I] pszText : modified text
4689 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4695 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4697 HWND hwndSelf = infoPtr->hwndSelf;
4698 NMLVDISPINFOW dispInfo;
4700 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4702 ZeroMemory(&dispInfo, sizeof(dispInfo));
4703 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4704 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4705 dispInfo.item.iSubItem = 0;
4706 dispInfo.item.stateMask = ~0;
4707 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4708 /* add the text from the edit in */
4709 dispInfo.item.mask |= LVIF_TEXT;
4710 dispInfo.item.pszText = pszText;
4711 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4713 /* Do we need to update the Item Text */
4714 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4715 if (!IsWindow(hwndSelf))
4717 if (!pszText) return TRUE;
4719 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4721 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4722 ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4723 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4725 LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4730 ZeroMemory(&dispInfo, sizeof(dispInfo));
4731 dispInfo.item.mask = LVIF_TEXT;
4732 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4733 dispInfo.item.iSubItem = 0;
4734 dispInfo.item.pszText = pszText;
4735 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4736 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4741 * Begin in place editing of specified list view item
4744 * [I] infoPtr : valid pointer to the listview structure
4745 * [I] nItem : item index
4746 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4752 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4754 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4755 NMLVDISPINFOW dispInfo;
4757 HWND hwndSelf = infoPtr->hwndSelf;
4759 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4761 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4762 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4764 infoPtr->nEditLabelItem = nItem;
4766 /* Is the EditBox still there, if so remove it */
4767 if(infoPtr->hwndEdit != 0)
4769 SetFocus(infoPtr->hwndSelf);
4770 infoPtr->hwndEdit = 0;
4773 LISTVIEW_SetSelection(infoPtr, nItem);
4774 LISTVIEW_SetItemFocus(infoPtr, nItem);
4775 LISTVIEW_InvalidateItem(infoPtr, nItem);
4777 rect.left = LVIR_LABEL;
4778 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4780 ZeroMemory(&dispInfo, sizeof(dispInfo));
4781 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4782 dispInfo.item.iItem = nItem;
4783 dispInfo.item.iSubItem = 0;
4784 dispInfo.item.stateMask = ~0;
4785 dispInfo.item.pszText = szDispText;
4786 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4787 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4789 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4790 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4791 if (!infoPtr->hwndEdit) return 0;
4793 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4795 if (!IsWindow(hwndSelf))
4797 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4798 infoPtr->hwndEdit = 0;
4802 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4803 SetFocus(infoPtr->hwndEdit);
4804 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4805 return infoPtr->hwndEdit;
4811 * Ensures the specified item is visible, scrolling into view if necessary.
4814 * [I] infoPtr : valid pointer to the listview structure
4815 * [I] nItem : item index
4816 * [I] bPartial : partially or entirely visible
4822 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4824 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4825 INT nScrollPosHeight = 0;
4826 INT nScrollPosWidth = 0;
4827 INT nHorzAdjust = 0;
4828 INT nVertAdjust = 0;
4831 RECT rcItem, rcTemp;
4833 rcItem.left = LVIR_BOUNDS;
4834 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4836 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4838 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4840 /* scroll left/right, but in LVS_REPORT mode */
4841 if (uView == LVS_LIST)
4842 nScrollPosWidth = infoPtr->nItemWidth;
4843 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4844 nScrollPosWidth = 1;
4846 if (rcItem.left < infoPtr->rcList.left)
4849 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4854 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4858 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4860 /* scroll up/down, but not in LVS_LIST mode */
4861 if (uView == LVS_REPORT)
4862 nScrollPosHeight = infoPtr->nItemHeight;
4863 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4864 nScrollPosHeight = 1;
4866 if (rcItem.top < infoPtr->rcList.top)
4869 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4874 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4878 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4880 if (nScrollPosWidth)
4882 INT diff = nHorzDiff / nScrollPosWidth;
4883 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4884 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4887 if (nScrollPosHeight)
4889 INT diff = nVertDiff / nScrollPosHeight;
4890 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4891 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4899 * Searches for an item with specific characteristics.
4902 * [I] hwnd : window handle
4903 * [I] nStart : base item index
4904 * [I] lpFindInfo : item information to look for
4907 * SUCCESS : index of item
4910 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
4911 const LVFINDINFOW *lpFindInfo)
4913 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4914 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4915 BOOL bWrap = FALSE, bNearest = FALSE;
4916 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4917 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4918 POINT Position, Destination;
4921 if (!lpFindInfo || nItem < 0) return -1;
4924 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4926 lvItem.mask |= LVIF_TEXT;
4927 lvItem.pszText = szDispText;
4928 lvItem.cchTextMax = DISP_TEXT_SIZE;
4931 if (lpFindInfo->flags & LVFI_WRAP)
4934 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4935 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4940 LISTVIEW_GetOrigin(infoPtr, &Origin);
4941 Destination.x = lpFindInfo->pt.x - Origin.x;
4942 Destination.y = lpFindInfo->pt.y - Origin.y;
4943 switch(lpFindInfo->vkDirection)
4945 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4946 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4947 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4948 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4949 case VK_HOME: Destination.x = Destination.y = 0; break;
4950 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4951 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4953 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4954 Destination.x = rcArea.right;
4955 Destination.y = rcArea.bottom;
4957 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4961 else Destination.x = Destination.y = 0;
4963 /* if LVFI_PARAM is specified, all other flags are ignored */
4964 if (lpFindInfo->flags & LVFI_PARAM)
4966 lvItem.mask |= LVIF_PARAM;
4968 lvItem.mask &= ~LVIF_TEXT;
4972 for (; nItem < nLast; nItem++)
4974 lvItem.iItem = nItem;
4975 lvItem.iSubItem = 0;
4976 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4978 if (lvItem.mask & LVIF_PARAM)
4980 if (lpFindInfo->lParam == lvItem.lParam)
4986 if (lvItem.mask & LVIF_TEXT)
4988 if (lpFindInfo->flags & LVFI_PARTIAL)
4990 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4994 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4998 if (!bNearest) return nItem;
5000 /* This is very inefficient. To do a good job here,
5001 * we need a sorted array of (x,y) item positions */
5002 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5004 /* compute the distance^2 to the destination */
5005 xdist = Destination.x - Position.x;
5006 ydist = Destination.y - Position.y;
5007 dist = xdist * xdist + ydist * ydist;
5009 /* remember the distance, and item if it's closer */
5013 nNearestItem = nItem;
5020 nLast = min(nStart + 1, infoPtr->nItemCount);
5025 return nNearestItem;
5030 * Searches for an item with specific characteristics.
5033 * [I] hwnd : window handle
5034 * [I] nStart : base item index
5035 * [I] lpFindInfo : item information to look for
5038 * SUCCESS : index of item
5041 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5042 const LVFINDINFOA *lpFindInfo)
5044 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5049 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5050 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5051 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5052 textfreeT(strW, FALSE);
5058 * Retrieves the background image of the listview control.
5061 * [I] infoPtr : valid pointer to the listview structure
5062 * [O] lpBkImage : background image attributes
5068 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5070 /* FIXME (listview, "empty stub!\n"); */
5076 * Retrieves column attributes.
5079 * [I] infoPtr : valid pointer to the listview structure
5080 * [I] nColumn : column index
5081 * [IO] lpColumn : column information
5082 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5083 * otherwise it is in fact a LPLVCOLUMNA
5089 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5091 COLUMN_INFO *lpColumnInfo;
5094 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5095 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5097 /* initialize memory */
5098 ZeroMemory(&hdi, sizeof(hdi));
5100 if (lpColumn->mask & LVCF_TEXT)
5102 hdi.mask |= HDI_TEXT;
5103 hdi.pszText = lpColumn->pszText;
5104 hdi.cchTextMax = lpColumn->cchTextMax;
5107 if (lpColumn->mask & LVCF_IMAGE)
5108 hdi.mask |= HDI_IMAGE;
5110 if (lpColumn->mask & LVCF_ORDER)
5111 hdi.mask |= HDI_ORDER;
5113 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5115 if (lpColumn->mask & LVCF_FMT)
5116 lpColumn->fmt = lpColumnInfo->fmt;
5118 if (lpColumn->mask & LVCF_WIDTH)
5119 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5121 if (lpColumn->mask & LVCF_IMAGE)
5122 lpColumn->iImage = hdi.iImage;
5124 if (lpColumn->mask & LVCF_ORDER)
5125 lpColumn->iOrder = hdi.iOrder;
5131 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5138 /* FIXME: little hack */
5139 for (i = 0; i < iCount; i++)
5147 * Retrieves the column width.
5150 * [I] infoPtr : valid pointer to the listview structure
5151 * [I] int : column index
5154 * SUCCESS : column width
5157 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5159 INT nColumnWidth = 0;
5162 TRACE("nColumn=%d\n", nColumn);
5164 /* we have a 'column' in LIST and REPORT mode only */
5165 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5168 nColumnWidth = infoPtr->nItemWidth;
5171 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5172 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5173 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5175 * TODO: should we do the same in LVM_GETCOLUMN?
5177 hdItem.mask = HDI_WIDTH;
5178 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5180 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5183 nColumnWidth = hdItem.cxy;
5187 TRACE("nColumnWidth=%d\n", nColumnWidth);
5188 return nColumnWidth;
5193 * In list or report display mode, retrieves the number of items that can fit
5194 * vertically in the visible area. In icon or small icon display mode,
5195 * retrieves the total number of visible items.
5198 * [I] infoPtr : valid pointer to the listview structure
5201 * Number of fully visible items.
5203 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5205 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5209 return infoPtr->nItemCount;
5211 return LISTVIEW_GetCountPerColumn(infoPtr);
5213 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5221 * Retrieves an image list handle.
5224 * [I] infoPtr : valid pointer to the listview structure
5225 * [I] nImageList : image list identifier
5228 * SUCCESS : image list handle
5231 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5235 case LVSIL_NORMAL: return infoPtr->himlNormal;
5236 case LVSIL_SMALL: return infoPtr->himlSmall;
5237 case LVSIL_STATE: return infoPtr->himlState;
5242 /* LISTVIEW_GetISearchString */
5246 * Retrieves item attributes.
5249 * [I] hwnd : window handle
5250 * [IO] lpLVItem : item info
5251 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5252 * if FALSE, then lpLVItem is a LPLVITEMA.
5255 * This is the internal 'GetItem' interface -- it tries to
5256 * be smart and avoid text copies, if possible, by modifying
5257 * lpLVItem->pszText to point to the text string. Please note
5258 * that this is not always possible (e.g. OWNERDATA), so on
5259 * entry you *must* supply valid values for pszText, and cchTextMax.
5260 * The only difference to the documented interface is that upon
5261 * return, you should use *only* the lpLVItem->pszText, rather than
5262 * the buffer pointer you provided on input. Most code already does
5263 * that, so it's not a problem.
5264 * For the two cases when the text must be copied (that is,
5265 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5271 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5273 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5274 NMLVDISPINFOW dispInfo;
5280 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5282 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5285 if (lpLVItem->mask == 0) return TRUE;
5287 /* make a local copy */
5288 isubitem = lpLVItem->iSubItem;
5290 /* a quick optimization if all we're asked is the focus state
5291 * these queries are worth optimising since they are common,
5292 * and can be answered in constant time, without the heavy accesses */
5293 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5294 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5296 lpLVItem->state = 0;
5297 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5298 lpLVItem->state |= LVIS_FOCUSED;
5302 ZeroMemory(&dispInfo, sizeof(dispInfo));
5304 /* if the app stores all the data, handle it separately */
5305 if (infoPtr->dwStyle & LVS_OWNERDATA)
5307 dispInfo.item.state = 0;
5309 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5310 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5312 /* NOTE: copy only fields which we _know_ are initialized, some apps
5313 * depend on the uninitialized fields being 0 */
5314 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5315 dispInfo.item.iItem = lpLVItem->iItem;
5316 dispInfo.item.iSubItem = isubitem;
5317 if (lpLVItem->mask & LVIF_TEXT)
5319 dispInfo.item.pszText = lpLVItem->pszText;
5320 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5322 if (lpLVItem->mask & LVIF_STATE)
5323 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5324 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5325 dispInfo.item.stateMask = lpLVItem->stateMask;
5326 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5328 /* full size structure expected - _WIN32IE >= 0x560 */
5329 *lpLVItem = dispInfo.item;
5331 else if (lpLVItem->mask & LVIF_INDENT)
5333 /* indent member expected - _WIN32IE >= 0x300 */
5334 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5338 /* minimal structure expected */
5339 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5341 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5344 /* make sure lParam is zeroed out */
5345 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5347 /* we store only a little state, so if we're not asked, we're done */
5348 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5350 /* if focus is handled by us, report it */
5351 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5353 lpLVItem->state &= ~LVIS_FOCUSED;
5354 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5355 lpLVItem->state |= LVIS_FOCUSED;
5358 /* and do the same for selection, if we handle it */
5359 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5361 lpLVItem->state &= ~LVIS_SELECTED;
5362 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5363 lpLVItem->state |= LVIS_SELECTED;
5369 /* find the item and subitem structures before we proceed */
5370 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5371 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5376 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5377 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5380 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5385 pItemHdr = &lpItem->hdr;
5387 /* Do we need to query the state from the app? */
5388 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5390 dispInfo.item.mask |= LVIF_STATE;
5391 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5394 /* Do we need to enquire about the image? */
5395 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5396 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5398 dispInfo.item.mask |= LVIF_IMAGE;
5399 dispInfo.item.iImage = I_IMAGECALLBACK;
5402 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5403 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5405 dispInfo.item.mask |= LVIF_TEXT;
5406 dispInfo.item.pszText = lpLVItem->pszText;
5407 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5408 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5409 *dispInfo.item.pszText = '\0';
5412 /* If we don't have all the requested info, query the application */
5413 if (dispInfo.item.mask != 0)
5415 dispInfo.item.iItem = lpLVItem->iItem;
5416 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5417 dispInfo.item.lParam = lpItem->lParam;
5418 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5419 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5422 /* we should not store values for subitems */
5423 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5425 /* Now, handle the iImage field */
5426 if (dispInfo.item.mask & LVIF_IMAGE)
5428 lpLVItem->iImage = dispInfo.item.iImage;
5429 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5430 pItemHdr->iImage = dispInfo.item.iImage;
5432 else if (lpLVItem->mask & LVIF_IMAGE)
5434 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5435 lpLVItem->iImage = pItemHdr->iImage;
5437 lpLVItem->iImage = 0;
5440 /* The pszText field */
5441 if (dispInfo.item.mask & LVIF_TEXT)
5443 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5444 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5446 lpLVItem->pszText = dispInfo.item.pszText;
5448 else if (lpLVItem->mask & LVIF_TEXT)
5450 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5451 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5454 /* Next is the lParam field */
5455 if (dispInfo.item.mask & LVIF_PARAM)
5457 lpLVItem->lParam = dispInfo.item.lParam;
5458 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5459 lpItem->lParam = dispInfo.item.lParam;
5461 else if (lpLVItem->mask & LVIF_PARAM)
5462 lpLVItem->lParam = lpItem->lParam;
5464 /* if this is a subitem, we're done */
5465 if (isubitem) return TRUE;
5467 /* ... the state field (this one is different due to uCallbackmask) */
5468 if (lpLVItem->mask & LVIF_STATE)
5470 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5471 if (dispInfo.item.mask & LVIF_STATE)
5473 lpLVItem->state &= ~dispInfo.item.stateMask;
5474 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5476 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5478 lpLVItem->state &= ~LVIS_FOCUSED;
5479 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5480 lpLVItem->state |= LVIS_FOCUSED;
5482 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5484 lpLVItem->state &= ~LVIS_SELECTED;
5485 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5486 lpLVItem->state |= LVIS_SELECTED;
5490 /* and last, but not least, the indent field */
5491 if (lpLVItem->mask & LVIF_INDENT)
5492 lpLVItem->iIndent = lpItem->iIndent;
5499 * Retrieves item attributes.
5502 * [I] hwnd : window handle
5503 * [IO] lpLVItem : item info
5504 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5505 * if FALSE, then lpLVItem is a LPLVITEMA.
5508 * This is the external 'GetItem' interface -- it properly copies
5509 * the text in the provided buffer.
5515 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5520 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5523 pszText = lpLVItem->pszText;
5524 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5525 if (bResult && lpLVItem->pszText != pszText)
5526 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5527 lpLVItem->pszText = pszText;
5535 * Retrieves the position (upper-left) of the listview control item.
5536 * Note that for LVS_ICON style, the upper-left is that of the icon
5537 * and not the bounding box.
5540 * [I] infoPtr : valid pointer to the listview structure
5541 * [I] nItem : item index
5542 * [O] lpptPosition : coordinate information
5548 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5550 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5553 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5555 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5557 LISTVIEW_GetOrigin(infoPtr, &Origin);
5558 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5560 if (uView == LVS_ICON)
5562 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5563 lpptPosition->y += ICON_TOP_PADDING;
5565 lpptPosition->x += Origin.x;
5566 lpptPosition->y += Origin.y;
5568 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5575 * Retrieves the bounding rectangle for a listview control item.
5578 * [I] infoPtr : valid pointer to the listview structure
5579 * [I] nItem : item index
5580 * [IO] lprc : bounding rectangle coordinates
5581 * lprc->left specifies the portion of the item for which the bounding
5582 * rectangle will be retrieved.
5584 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5585 * including the icon and label.
5588 * * Experiment shows that native control returns:
5589 * * width = min (48, length of text line)
5590 * * .left = position.x - (width - iconsize.cx)/2
5591 * * .right = .left + width
5592 * * height = #lines of text * ntmHeight + icon height + 8
5593 * * .top = position.y - 2
5594 * * .bottom = .top + height
5595 * * separation between items .y = itemSpacing.cy - height
5596 * * .x = itemSpacing.cx - width
5597 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5600 * * Experiment shows that native control returns:
5601 * * width = iconSize.cx + 16
5602 * * .left = position.x - (width - iconsize.cx)/2
5603 * * .right = .left + width
5604 * * height = iconSize.cy + 4
5605 * * .top = position.y - 2
5606 * * .bottom = .top + height
5607 * * separation between items .y = itemSpacing.cy - height
5608 * * .x = itemSpacing.cx - width
5609 * LVIR_LABEL Returns the bounding rectangle of the item text.
5612 * * Experiment shows that native control returns:
5613 * * width = text length
5614 * * .left = position.x - width/2
5615 * * .right = .left + width
5616 * * height = ntmH * linecount + 2
5617 * * .top = position.y + iconSize.cy + 6
5618 * * .bottom = .top + height
5619 * * separation between items .y = itemSpacing.cy - height
5620 * * .x = itemSpacing.cx - width
5621 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5622 * rectangles, but excludes columns in report view.
5629 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5630 * upon whether the window has the focus currently and on whether the item
5631 * is the one with the focus. Ensure that the control's record of which
5632 * item has the focus agrees with the items' records.
5634 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5636 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5637 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5638 BOOL doLabel = TRUE, oversizedBox = FALSE;
5639 POINT Position, Origin;
5642 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5644 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5646 LISTVIEW_GetOrigin(infoPtr, &Origin);
5647 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5649 /* Be smart and try to figure out the minimum we have to do */
5650 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5651 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5652 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5653 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5654 oversizedBox = TRUE;
5656 /* get what we need from the item before hand, so we make
5657 * only one request. This can speed up things, if data
5658 * is stored on the app side */
5660 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5661 if (doLabel) lvItem.mask |= LVIF_TEXT;
5662 lvItem.iItem = nItem;
5663 lvItem.iSubItem = 0;
5664 lvItem.pszText = szDispText;
5665 lvItem.cchTextMax = DISP_TEXT_SIZE;
5666 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5667 /* we got the state already up, simulate it here, to avoid a reget */
5668 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5670 lvItem.mask |= LVIF_STATE;
5671 lvItem.stateMask = LVIS_FOCUSED;
5672 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5675 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5676 lprc->left = LVIR_BOUNDS;
5680 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5684 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
5688 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5691 case LVIR_SELECTBOUNDS:
5692 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
5696 WARN("Unknown value: %d\n", lprc->left);
5700 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5702 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5709 * Retrieves the spacing between listview control items.
5712 * [I] infoPtr : valid pointer to the listview structure
5713 * [IO] lprc : rectangle to receive the output
5714 * on input, lprc->top = nSubItem
5715 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5717 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5718 * not only those of the first column.
5719 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5725 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5731 if (!lprc) return FALSE;
5733 nColumn = lprc->top;
5735 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5736 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5738 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5740 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5742 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5744 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5747 lvItem.iItem = nItem;
5748 lvItem.iSubItem = nColumn;
5750 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5754 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5759 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5763 ERR("Unknown bounds=%d\n", lprc->left);
5767 OffsetRect(lprc, Position.x, Position.y);
5774 * Retrieves the width of a label.
5777 * [I] infoPtr : valid pointer to the listview structure
5780 * SUCCESS : string width (in pixels)
5783 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
5785 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5788 TRACE("(nItem=%d)\n", nItem);
5790 lvItem.mask = LVIF_TEXT;
5791 lvItem.iItem = nItem;
5792 lvItem.iSubItem = 0;
5793 lvItem.pszText = szDispText;
5794 lvItem.cchTextMax = DISP_TEXT_SIZE;
5795 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5797 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5802 * Retrieves the spacing between listview control items.
5805 * [I] infoPtr : valid pointer to the listview structure
5806 * [I] bSmall : flag for small or large icon
5809 * Horizontal + vertical spacing
5811 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
5817 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5821 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5822 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5824 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5831 * Retrieves the state of a listview control item.
5834 * [I] infoPtr : valid pointer to the listview structure
5835 * [I] nItem : item index
5836 * [I] uMask : state mask
5839 * State specified by the mask.
5841 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5845 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5847 lvItem.iItem = nItem;
5848 lvItem.iSubItem = 0;
5849 lvItem.mask = LVIF_STATE;
5850 lvItem.stateMask = uMask;
5851 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5853 return lvItem.state & uMask;
5858 * Retrieves the text of a listview control item or subitem.
5861 * [I] hwnd : window handle
5862 * [I] nItem : item index
5863 * [IO] lpLVItem : item information
5864 * [I] isW : TRUE if lpLVItem is Unicode
5867 * SUCCESS : string length
5870 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5872 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5874 lpLVItem->mask = LVIF_TEXT;
5875 lpLVItem->iItem = nItem;
5876 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5878 return textlenT(lpLVItem->pszText, isW);
5883 * Searches for an item based on properties + relationships.
5886 * [I] infoPtr : valid pointer to the listview structure
5887 * [I] nItem : item index
5888 * [I] uFlags : relationship flag
5891 * SUCCESS : item index
5894 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5896 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5898 LVFINDINFOW lvFindInfo;
5899 INT nCountPerColumn;
5903 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5904 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5906 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5908 if (uFlags & LVNI_CUT)
5911 if (uFlags & LVNI_DROPHILITED)
5912 uMask |= LVIS_DROPHILITED;
5914 if (uFlags & LVNI_FOCUSED)
5915 uMask |= LVIS_FOCUSED;
5917 if (uFlags & LVNI_SELECTED)
5918 uMask |= LVIS_SELECTED;
5920 /* if we're asked for the focused item, that's only one,
5921 * so it's worth optimizing */
5922 if (uFlags & LVNI_FOCUSED)
5924 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5925 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5928 if (uFlags & LVNI_ABOVE)
5930 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5935 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5941 /* Special case for autoarrange - move 'til the top of a list */
5942 if (is_autoarrange(infoPtr))
5944 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5945 while (nItem - nCountPerRow >= 0)
5947 nItem -= nCountPerRow;
5948 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5953 lvFindInfo.flags = LVFI_NEARESTXY;
5954 lvFindInfo.vkDirection = VK_UP;
5955 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5956 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5958 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5963 else if (uFlags & LVNI_BELOW)
5965 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5967 while (nItem < infoPtr->nItemCount)
5970 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5976 /* Special case for autoarrange - move 'til the bottom of a list */
5977 if (is_autoarrange(infoPtr))
5979 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5980 while (nItem + nCountPerRow < infoPtr->nItemCount )
5982 nItem += nCountPerRow;
5983 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5988 lvFindInfo.flags = LVFI_NEARESTXY;
5989 lvFindInfo.vkDirection = VK_DOWN;
5990 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5991 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5993 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5998 else if (uFlags & LVNI_TOLEFT)
6000 if (uView == LVS_LIST)
6002 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6003 while (nItem - nCountPerColumn >= 0)
6005 nItem -= nCountPerColumn;
6006 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6010 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6012 /* Special case for autoarrange - move 'ti the beginning of a row */
6013 if (is_autoarrange(infoPtr))
6015 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6016 while (nItem % nCountPerRow > 0)
6019 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6024 lvFindInfo.flags = LVFI_NEARESTXY;
6025 lvFindInfo.vkDirection = VK_LEFT;
6026 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6027 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6029 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6034 else if (uFlags & LVNI_TORIGHT)
6036 if (uView == LVS_LIST)
6038 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6039 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6041 nItem += nCountPerColumn;
6042 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6046 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6048 /* Special case for autoarrange - move 'til the end of a row */
6049 if (is_autoarrange(infoPtr))
6051 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6052 while (nItem % nCountPerRow < nCountPerRow - 1 )
6055 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6060 lvFindInfo.flags = LVFI_NEARESTXY;
6061 lvFindInfo.vkDirection = VK_RIGHT;
6062 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6063 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6065 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6074 /* search by index */
6075 for (i = nItem; i < infoPtr->nItemCount; i++)
6077 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6085 /* LISTVIEW_GetNumberOfWorkAreas */
6089 * Retrieves the origin coordinates when in icon or small icon display mode.
6092 * [I] infoPtr : valid pointer to the listview structure
6093 * [O] lpptOrigin : coordinate information
6098 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6100 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6101 INT nHorzPos = 0, nVertPos = 0;
6102 SCROLLINFO scrollInfo;
6104 scrollInfo.cbSize = sizeof(SCROLLINFO);
6105 scrollInfo.fMask = SIF_POS;
6107 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6108 nHorzPos = scrollInfo.nPos;
6109 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6110 nVertPos = scrollInfo.nPos;
6112 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6114 lpptOrigin->x = infoPtr->rcList.left;
6115 lpptOrigin->y = infoPtr->rcList.top;
6116 if (uView == LVS_LIST)
6117 nHorzPos *= infoPtr->nItemWidth;
6118 else if (uView == LVS_REPORT)
6119 nVertPos *= infoPtr->nItemHeight;
6121 lpptOrigin->x -= nHorzPos;
6122 lpptOrigin->y -= nVertPos;
6124 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6129 * Retrieves the width of a string.
6132 * [I] hwnd : window handle
6133 * [I] lpszText : text string to process
6134 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6137 * SUCCESS : string width (in pixels)
6140 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6145 if (is_textT(lpszText, isW))
6147 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6148 HDC hdc = GetDC(infoPtr->hwndSelf);
6149 HFONT hOldFont = SelectObject(hdc, hFont);
6152 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6154 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6155 SelectObject(hdc, hOldFont);
6156 ReleaseDC(infoPtr->hwndSelf, hdc);
6158 return stringSize.cx;
6163 * Determines which listview item is located at the specified position.
6166 * [I] infoPtr : valid pointer to the listview structure
6167 * [IO] lpht : hit test information
6168 * [I] subitem : fill out iSubItem.
6169 * [I] select : return the index only if the hit selects the item
6172 * (mm 20001022): We must not allow iSubItem to be touched, for
6173 * an app might pass only a structure with space up to iItem!
6174 * (MS Office 97 does that for instance in the file open dialog)
6177 * SUCCESS : item index
6180 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6182 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6183 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6184 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6185 POINT Origin, Position, opt;
6190 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6194 if (subitem) lpht->iSubItem = 0;
6196 if (infoPtr->rcList.left > lpht->pt.x)
6197 lpht->flags |= LVHT_TOLEFT;
6198 else if (infoPtr->rcList.right < lpht->pt.x)
6199 lpht->flags |= LVHT_TORIGHT;
6201 if (infoPtr->rcList.top > lpht->pt.y)
6202 lpht->flags |= LVHT_ABOVE;
6203 else if (infoPtr->rcList.bottom < lpht->pt.y)
6204 lpht->flags |= LVHT_BELOW;
6206 TRACE("lpht->flags=0x%x\n", lpht->flags);
6207 if (lpht->flags) return -1;
6209 lpht->flags |= LVHT_NOWHERE;
6211 LISTVIEW_GetOrigin(infoPtr, &Origin);
6213 /* first deal with the large items */
6214 rcSearch.left = lpht->pt.x;
6215 rcSearch.top = lpht->pt.y;
6216 rcSearch.right = rcSearch.left + 1;
6217 rcSearch.bottom = rcSearch.top + 1;
6219 iterator_frameditems(&i, infoPtr, &rcSearch);
6220 iterator_next(&i); /* go to first item in the sequence */
6222 iterator_destroy(&i);
6224 TRACE("lpht->iItem=%d\n", iItem);
6225 if (iItem == -1) return -1;
6227 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6228 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6229 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6230 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6231 lvItem.iItem = iItem;
6232 lvItem.iSubItem = 0;
6233 lvItem.pszText = szDispText;
6234 lvItem.cchTextMax = DISP_TEXT_SIZE;
6235 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6236 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6238 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6239 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6240 opt.x = lpht->pt.x - Position.x - Origin.x;
6241 opt.y = lpht->pt.y - Position.y - Origin.y;
6243 if (uView == LVS_REPORT)
6246 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6247 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6248 if (!PtInRect(&rcBounds, opt)) return -1;
6250 if (PtInRect(&rcIcon, opt))
6251 lpht->flags |= LVHT_ONITEMICON;
6252 else if (PtInRect(&rcLabel, opt))
6253 lpht->flags |= LVHT_ONITEMLABEL;
6254 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6255 lpht->flags |= LVHT_ONITEMSTATEICON;
6256 if (lpht->flags & LVHT_ONITEM)
6257 lpht->flags &= ~LVHT_NOWHERE;
6259 TRACE("lpht->flags=0x%x\n", lpht->flags);
6260 if (uView == LVS_REPORT && subitem)
6264 rcBounds.right = rcBounds.left;
6265 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6267 rcBounds.left = rcBounds.right;
6268 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6269 if (PtInRect(&rcBounds, opt))
6277 if (select && !(uView == LVS_REPORT &&
6278 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6279 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6281 if (uView == LVS_REPORT)
6283 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6284 UnionRect(&rcBounds, &rcBounds, &rcState);
6286 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6288 return lpht->iItem = iItem;
6292 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6293 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6294 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6295 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6296 their own sort proc. when sending LVM_SORTITEMS.
6299 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6301 LVS_SORTXXX must be specified,
6302 LVS_OWNERDRAW is not set,
6303 <item>.pszText is not LPSTR_TEXTCALLBACK.
6305 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6306 are sorted based on item text..."
6308 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6310 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6311 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6312 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6314 /* if we're sorting descending, negate the return value */
6315 return (((const LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6320 * Inserts a new item in the listview control.
6323 * [I] infoPtr : valid pointer to the listview structure
6324 * [I] lpLVItem : item information
6325 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6328 * SUCCESS : new item index
6331 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6333 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6338 BOOL is_sorted, has_changed;
6340 HWND hwndSelf = infoPtr->hwndSelf;
6342 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6344 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6346 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6347 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6349 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6351 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6353 /* insert item in listview control data structure */
6354 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6355 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6357 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6358 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6360 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6362 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6363 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6364 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6365 if (nItem == -1) goto fail;
6366 infoPtr->nItemCount++;
6368 /* shift indices first so they don't get tangled */
6369 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6371 /* set the item attributes */
6372 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6374 /* full size structure expected - _WIN32IE >= 0x560 */
6377 else if (lpLVItem->mask & LVIF_INDENT)
6379 /* indent member expected - _WIN32IE >= 0x300 */
6380 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6384 /* minimal structure expected */
6385 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6388 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6390 item.mask |= LVIF_STATE;
6391 item.stateMask |= LVIS_STATEIMAGEMASK;
6392 item.state &= ~LVIS_STATEIMAGEMASK;
6393 item.state |= INDEXTOSTATEIMAGEMASK(1);
6395 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6397 /* if we're sorted, sort the list, and update the index */
6400 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6401 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6402 assert(nItem != -1);
6405 /* make room for the position, if we are in the right mode */
6406 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6408 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6410 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6412 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6417 /* send LVN_INSERTITEM notification */
6418 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6420 nmlv.lParam = lpItem->lParam;
6421 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6422 if (!IsWindow(hwndSelf))
6425 /* align items (set position of each item) */
6426 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6430 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6431 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6433 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6435 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6438 /* now is the invalidation fun */
6439 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6443 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6444 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6445 infoPtr->nItemCount--;
6447 DPA_DeletePtr(hdpaSubItems, 0);
6448 DPA_Destroy (hdpaSubItems);
6455 * Redraws a range of items.
6458 * [I] infoPtr : valid pointer to the listview structure
6459 * [I] nFirst : first item
6460 * [I] nLast : last item
6466 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6470 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6471 max(nFirst, nLast) >= infoPtr->nItemCount)
6474 for (i = nFirst; i <= nLast; i++)
6475 LISTVIEW_InvalidateItem(infoPtr, i);
6482 * Scroll the content of a listview.
6485 * [I] infoPtr : valid pointer to the listview structure
6486 * [I] dx : horizontal scroll amount in pixels
6487 * [I] dy : vertical scroll amount in pixels
6494 * If the control is in report mode (LVS_REPORT) the control can
6495 * be scrolled only in line increments. "dy" will be rounded to the
6496 * nearest number of pixels that are a whole line. Ex: if line height
6497 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6498 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6500 * For: (per experimentation with native control and CSpy ListView)
6501 * LVS_ICON dy=1 = 1 pixel (vertical only)
6503 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6505 * LVS_LIST dx=1 = 1 column (horizontal only)
6506 * but will only scroll 1 column per message
6507 * no matter what the value.
6508 * dy must be 0 or FALSE returned.
6509 * LVS_REPORT dx=1 = 1 pixel
6513 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6515 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6517 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6518 dy /= infoPtr->nItemHeight;
6521 if (dy != 0) return FALSE;
6528 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6529 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6536 * Sets the background color.
6539 * [I] infoPtr : valid pointer to the listview structure
6540 * [I] clrBk : background color
6546 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6548 TRACE("(clrBk=%x)\n", clrBk);
6550 if(infoPtr->clrBk != clrBk) {
6551 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6552 infoPtr->clrBk = clrBk;
6553 if (clrBk == CLR_NONE)
6554 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6556 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6557 LISTVIEW_InvalidateList(infoPtr);
6563 /* LISTVIEW_SetBkImage */
6565 /*** Helper for {Insert,Set}ColumnT *only* */
6566 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6567 const LVCOLUMNW *lpColumn, BOOL isW)
6569 if (lpColumn->mask & LVCF_FMT)
6571 /* format member is valid */
6572 lphdi->mask |= HDI_FORMAT;
6574 /* set text alignment (leftmost column must be left-aligned) */
6575 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6576 lphdi->fmt |= HDF_LEFT;
6577 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6578 lphdi->fmt |= HDF_RIGHT;
6579 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6580 lphdi->fmt |= HDF_CENTER;
6582 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6583 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6585 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6587 lphdi->fmt |= HDF_IMAGE;
6588 lphdi->iImage = I_IMAGECALLBACK;
6592 if (lpColumn->mask & LVCF_WIDTH)
6594 lphdi->mask |= HDI_WIDTH;
6595 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6597 /* make it fill the remainder of the controls width */
6601 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6603 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6604 lphdi->cxy += rcHeader.right - rcHeader.left;
6607 /* retrieve the layout of the header */
6608 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6609 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6611 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6614 lphdi->cxy = lpColumn->cx;
6617 if (lpColumn->mask & LVCF_TEXT)
6619 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6620 lphdi->fmt |= HDF_STRING;
6621 lphdi->pszText = lpColumn->pszText;
6622 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6625 if (lpColumn->mask & LVCF_IMAGE)
6627 lphdi->mask |= HDI_IMAGE;
6628 lphdi->iImage = lpColumn->iImage;
6631 if (lpColumn->mask & LVCF_ORDER)
6633 lphdi->mask |= HDI_ORDER;
6634 lphdi->iOrder = lpColumn->iOrder;
6641 * Inserts a new column.
6644 * [I] infoPtr : valid pointer to the listview structure
6645 * [I] nColumn : column index
6646 * [I] lpColumn : column information
6647 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6650 * SUCCESS : new column index
6653 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6654 const LVCOLUMNW *lpColumn, BOOL isW)
6656 COLUMN_INFO *lpColumnInfo;
6660 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6662 if (!lpColumn || nColumn < 0) return -1;
6663 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6665 ZeroMemory(&hdi, sizeof(HDITEMW));
6666 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6669 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6670 * (can be seen in SPY) otherwise column never gets added.
6672 if (!(lpColumn->mask & LVCF_WIDTH)) {
6673 hdi.mask |= HDI_WIDTH;
6678 * when the iSubItem is available Windows copies it to the header lParam. It seems
6679 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6681 if (lpColumn->mask & LVCF_SUBITEM)
6683 hdi.mask |= HDI_LPARAM;
6684 hdi.lParam = lpColumn->iSubItem;
6687 /* insert item in header control */
6688 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6689 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6690 (WPARAM)nColumn, (LPARAM)&hdi);
6691 if (nNewColumn == -1) return -1;
6692 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6694 /* create our own column info */
6695 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6696 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6698 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6699 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6701 /* now we have to actually adjust the data */
6702 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6704 SUBITEM_INFO *lpSubItem;
6708 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6710 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6711 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6713 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6714 if (lpSubItem->iSubItem >= nNewColumn)
6715 lpSubItem->iSubItem++;
6720 /* make space for the new column */
6721 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6722 LISTVIEW_UpdateItemSize(infoPtr);
6727 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6730 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6738 * Sets the attributes of a header item.
6741 * [I] infoPtr : valid pointer to the listview structure
6742 * [I] nColumn : column index
6743 * [I] lpColumn : column attributes
6744 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6750 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
6751 const LVCOLUMNW *lpColumn, BOOL isW)
6753 HDITEMW hdi, hdiget;
6756 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6758 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6760 ZeroMemory(&hdi, sizeof(HDITEMW));
6761 if (lpColumn->mask & LVCF_FMT)
6763 hdi.mask |= HDI_FORMAT;
6764 hdiget.mask = HDI_FORMAT;
6765 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6766 hdi.fmt = hdiget.fmt & HDF_STRING;
6768 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6770 /* set header item attributes */
6771 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6772 if (!bResult) return FALSE;
6774 if (lpColumn->mask & LVCF_FMT)
6776 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6777 int oldFmt = lpColumnInfo->fmt;
6779 lpColumnInfo->fmt = lpColumn->fmt;
6780 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6782 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6783 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6792 * Sets the column order array
6795 * [I] infoPtr : valid pointer to the listview structure
6796 * [I] iCount : number of elements in column order array
6797 * [I] lpiArray : pointer to column order array
6803 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6805 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6816 * Sets the width of a column
6819 * [I] infoPtr : valid pointer to the listview structure
6820 * [I] nColumn : column index
6821 * [I] cx : column width
6827 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6829 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6830 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6834 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6836 /* set column width only if in report or list mode */
6837 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6839 /* take care of invalid cx values */
6840 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6841 else if (uView == LVS_LIST && cx < 1) return FALSE;
6843 /* resize all columns if in LVS_LIST mode */
6844 if(uView == LVS_LIST)
6846 infoPtr->nItemWidth = cx;
6847 LISTVIEW_InvalidateList(infoPtr);
6851 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6853 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6858 lvItem.mask = LVIF_TEXT;
6860 lvItem.iSubItem = nColumn;
6861 lvItem.pszText = szDispText;
6862 lvItem.cchTextMax = DISP_TEXT_SIZE;
6863 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6865 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6866 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6867 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6869 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6870 max_cx += infoPtr->iconSize.cx;
6871 max_cx += TRAILING_LABEL_PADDING;
6874 /* autosize based on listview items width */
6875 if(cx == LVSCW_AUTOSIZE)
6877 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6879 /* if iCol is the last column make it fill the remainder of the controls width */
6880 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6885 LISTVIEW_GetOrigin(infoPtr, &Origin);
6886 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6888 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6892 /* Despite what the MS docs say, if this is not the last
6893 column, then MS resizes the column to the width of the
6894 largest text string in the column, including headers
6895 and items. This is different from LVSCW_AUTOSIZE in that
6896 LVSCW_AUTOSIZE ignores the header string length. */
6899 /* retrieve header text */
6900 hdi.mask = HDI_TEXT;
6901 hdi.cchTextMax = DISP_TEXT_SIZE;
6902 hdi.pszText = szDispText;
6903 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
6905 HDC hdc = GetDC(infoPtr->hwndSelf);
6906 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6909 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6910 cx = size.cx + TRAILING_HEADER_PADDING;
6911 /* FIXME: Take into account the header image, if one is present */
6912 SelectObject(hdc, old_font);
6913 ReleaseDC(infoPtr->hwndSelf, hdc);
6915 cx = max (cx, max_cx);
6919 if (cx < 0) return FALSE;
6921 /* call header to update the column change */
6922 hdi.mask = HDI_WIDTH;
6924 TRACE("hdi.cxy=%d\n", hdi.cxy);
6925 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6929 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6932 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
6935 HBITMAP hbm_im, hbm_mask, hbm_orig;
6937 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6938 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6941 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6942 ILC_COLOR | ILC_MASK, 2, 2);
6943 hdc_wnd = GetDC(infoPtr->hwndSelf);
6944 hdc = CreateCompatibleDC(hdc_wnd);
6945 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6946 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6947 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6949 rc.left = rc.top = 0;
6950 rc.right = GetSystemMetrics(SM_CXSMICON);
6951 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6953 hbm_orig = SelectObject(hdc, hbm_mask);
6954 FillRect(hdc, &rc, hbr_white);
6955 InflateRect(&rc, -3, -3);
6956 FillRect(hdc, &rc, hbr_black);
6958 SelectObject(hdc, hbm_im);
6959 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6960 SelectObject(hdc, hbm_orig);
6961 ImageList_Add(himl, hbm_im, hbm_mask);
6963 SelectObject(hdc, hbm_im);
6964 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6965 SelectObject(hdc, hbm_orig);
6966 ImageList_Add(himl, hbm_im, hbm_mask);
6968 DeleteObject(hbm_mask);
6969 DeleteObject(hbm_im);
6977 * Sets the extended listview style.
6980 * [I] infoPtr : valid pointer to the listview structure
6982 * [I] dwStyle : style
6985 * SUCCESS : previous style
6988 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
6990 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
6994 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
6996 infoPtr->dwLvExStyle = dwExStyle;
6998 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7000 HIMAGELIST himl = 0;
7001 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7004 item.mask = LVIF_STATE;
7005 item.stateMask = LVIS_STATEIMAGEMASK;
7006 item.state = INDEXTOSTATEIMAGEMASK(1);
7007 LISTVIEW_SetItemState(infoPtr, -1, &item);
7009 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7011 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7014 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7016 DWORD dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7017 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7018 dwStyle |= HDS_DRAGDROP;
7020 dwStyle &= ~HDS_DRAGDROP;
7021 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7024 return dwOldExStyle;
7029 * Sets the new hot cursor used during hot tracking and hover selection.
7032 * [I] infoPtr : valid pointer to the listview structure
7033 * [I] hCursor : the new hot cursor handle
7036 * Returns the previous hot cursor
7038 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7040 HCURSOR oldCursor = infoPtr->hHotCursor;
7042 infoPtr->hHotCursor = hCursor;
7050 * Sets the hot item index.
7053 * [I] infoPtr : valid pointer to the listview structure
7054 * [I] iIndex : index
7057 * SUCCESS : previous hot item index
7058 * FAILURE : -1 (no hot item)
7060 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7062 INT iOldIndex = infoPtr->nHotItem;
7064 infoPtr->nHotItem = iIndex;
7072 * Sets the amount of time the cursor must hover over an item before it is selected.
7075 * [I] infoPtr : valid pointer to the listview structure
7076 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7079 * Returns the previous hover time
7081 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7083 DWORD oldHoverTime = infoPtr->dwHoverTime;
7085 infoPtr->dwHoverTime = dwHoverTime;
7087 return oldHoverTime;
7092 * Sets spacing for icons of LVS_ICON style.
7095 * [I] infoPtr : valid pointer to the listview structure
7096 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7097 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7100 * MAKELONG(oldcx, oldcy)
7102 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7104 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7105 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7107 TRACE("requested=(%d,%d)\n", cx, cy);
7109 /* this is supported only for LVS_ICON style */
7110 if (uView != LVS_ICON) return oldspacing;
7112 /* set to defaults, if instructed to */
7113 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7114 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7116 /* if 0 then compute width
7117 * FIXME: Should scan each item and determine max width of
7118 * icon or label, then make that the width */
7120 cx = infoPtr->iconSpacing.cx;
7122 /* if 0 then compute height */
7124 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7125 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7128 infoPtr->iconSpacing.cx = cx;
7129 infoPtr->iconSpacing.cy = cy;
7131 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7132 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7133 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7134 infoPtr->ntmHeight);
7136 /* these depend on the iconSpacing */
7137 LISTVIEW_UpdateItemSize(infoPtr);
7142 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7146 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7153 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7154 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7163 * [I] infoPtr : valid pointer to the listview structure
7164 * [I] nType : image list type
7165 * [I] himl : image list handle
7168 * SUCCESS : old image list
7171 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7173 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7174 INT oldHeight = infoPtr->nItemHeight;
7175 HIMAGELIST himlOld = 0;
7177 TRACE("(nType=%d, himl=%p\n", nType, himl);
7182 himlOld = infoPtr->himlNormal;
7183 infoPtr->himlNormal = himl;
7184 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7185 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7189 himlOld = infoPtr->himlSmall;
7190 infoPtr->himlSmall = himl;
7191 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7195 himlOld = infoPtr->himlState;
7196 infoPtr->himlState = himl;
7197 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7198 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7202 ERR("Unknown icon type=%d\n", nType);
7206 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7207 if (infoPtr->nItemHeight != oldHeight)
7208 LISTVIEW_UpdateScroll(infoPtr);
7215 * Preallocates memory (does *not* set the actual count of items !)
7218 * [I] infoPtr : valid pointer to the listview structure
7219 * [I] nItems : item count (projected number of items to allocate)
7220 * [I] dwFlags : update flags
7226 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7228 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7230 if (infoPtr->dwStyle & LVS_OWNERDATA)
7232 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7233 INT nOldCount = infoPtr->nItemCount;
7235 if (nItems < nOldCount)
7237 RANGE range = { nItems, nOldCount };
7238 ranges_del(infoPtr->selectionRanges, range);
7239 if (infoPtr->nFocusedItem >= nItems)
7241 infoPtr->nFocusedItem = -1;
7242 SetRectEmpty(&infoPtr->rcFocus);
7246 infoPtr->nItemCount = nItems;
7247 LISTVIEW_UpdateScroll(infoPtr);
7249 /* the flags are valid only in ownerdata report and list modes */
7250 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7252 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7253 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7255 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7256 LISTVIEW_InvalidateList(infoPtr);
7263 LISTVIEW_GetOrigin(infoPtr, &Origin);
7264 nFrom = min(nOldCount, nItems);
7265 nTo = max(nOldCount, nItems);
7267 if (uView == LVS_REPORT)
7270 rcErase.top = nFrom * infoPtr->nItemHeight;
7271 rcErase.right = infoPtr->nItemWidth;
7272 rcErase.bottom = nTo * infoPtr->nItemHeight;
7273 OffsetRect(&rcErase, Origin.x, Origin.y);
7274 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7275 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7279 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7281 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7282 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7283 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7284 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7285 OffsetRect(&rcErase, Origin.x, Origin.y);
7286 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7287 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7289 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7291 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7292 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7293 OffsetRect(&rcErase, Origin.x, Origin.y);
7294 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7295 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7301 /* According to MSDN for non-LVS_OWNERDATA this is just
7302 * a performance issue. The control allocates its internal
7303 * data structures for the number of items specified. It
7304 * cuts down on the number of memory allocations. Therefore
7305 * we will just issue a WARN here
7307 WARN("for non-ownerdata performance option not implemented.\n");
7315 * Sets the position of an item.
7318 * [I] infoPtr : valid pointer to the listview structure
7319 * [I] nItem : item index
7320 * [I] pt : coordinate
7326 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7328 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7331 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7333 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7334 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7336 LISTVIEW_GetOrigin(infoPtr, &Origin);
7338 /* This point value seems to be an undocumented feature.
7339 * The best guess is that it means either at the origin,
7340 * or at true beginning of the list. I will assume the origin. */
7341 if ((pt.x == -1) && (pt.y == -1))
7344 if (uView == LVS_ICON)
7346 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7347 pt.y -= ICON_TOP_PADDING;
7352 infoPtr->bAutoarrange = FALSE;
7354 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7359 * Sets the state of one or many items.
7362 * [I] infoPtr : valid pointer to the listview structure
7363 * [I] nItem : item index
7364 * [I] lpLVItem : item or subitem info
7370 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7372 BOOL bResult = TRUE;
7375 lvItem.iItem = nItem;
7376 lvItem.iSubItem = 0;
7377 lvItem.mask = LVIF_STATE;
7378 lvItem.state = lpLVItem->state;
7379 lvItem.stateMask = lpLVItem->stateMask;
7380 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7384 /* apply to all items */
7385 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7386 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7389 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7392 * Update selection mark
7394 * Investigation on windows 2k showed that selection mark was updated
7395 * whenever a new selection was made, but if the selected item was
7396 * unselected it was not updated.
7398 * we are probably still not 100% accurate, but this at least sets the
7399 * proper selection mark when it is needed
7402 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7403 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7406 infoPtr->nSelectionMark = -1;
7407 for (i = 0; i < infoPtr->nItemCount; i++)
7409 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7411 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7413 infoPtr->nSelectionMark = i;
7417 else if (ranges_contain(infoPtr->selectionRanges, i))
7419 infoPtr->nSelectionMark = i;
7430 * Sets the text of an item or subitem.
7433 * [I] hwnd : window handle
7434 * [I] nItem : item index
7435 * [I] lpLVItem : item or subitem info
7436 * [I] isW : TRUE if input is Unicode
7442 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7446 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7448 lvItem.iItem = nItem;
7449 lvItem.iSubItem = lpLVItem->iSubItem;
7450 lvItem.mask = LVIF_TEXT;
7451 lvItem.pszText = lpLVItem->pszText;
7452 lvItem.cchTextMax = lpLVItem->cchTextMax;
7454 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7456 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7461 * Set item index that marks the start of a multiple selection.
7464 * [I] infoPtr : valid pointer to the listview structure
7465 * [I] nIndex : index
7468 * Index number or -1 if there is no selection mark.
7470 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7472 INT nOldIndex = infoPtr->nSelectionMark;
7474 TRACE("(nIndex=%d)\n", nIndex);
7476 infoPtr->nSelectionMark = nIndex;
7483 * Sets the text background color.
7486 * [I] infoPtr : valid pointer to the listview structure
7487 * [I] clrTextBk : text background color
7493 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7495 TRACE("(clrTextBk=%x)\n", clrTextBk);
7497 if (infoPtr->clrTextBk != clrTextBk)
7499 infoPtr->clrTextBk = clrTextBk;
7500 LISTVIEW_InvalidateList(infoPtr);
7508 * Sets the text foreground color.
7511 * [I] infoPtr : valid pointer to the listview structure
7512 * [I] clrText : text color
7518 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7520 TRACE("(clrText=%x)\n", clrText);
7522 if (infoPtr->clrText != clrText)
7524 infoPtr->clrText = clrText;
7525 LISTVIEW_InvalidateList(infoPtr);
7533 * Determines which listview item is located at the specified position.
7536 * [I] infoPtr : valid pointer to the listview structure
7537 * [I] hwndNewToolTip : handle to new ToolTip
7542 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7544 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7545 infoPtr->hwndToolTip = hwndNewToolTip;
7546 return hwndOldToolTip;
7551 * sets the Unicode character format flag for the control
7553 * [I] infoPtr :valid pointer to the listview structure
7554 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7557 * Old Unicode Format
7559 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7561 BOOL rc = infoPtr->notifyFormat;
7562 infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7566 /* LISTVIEW_SetWorkAreas */
7570 * Callback internally used by LISTVIEW_SortItems()
7573 * [I] first : pointer to first ITEM_INFO to compare
7574 * [I] second : pointer to second ITEM_INFO to compare
7575 * [I] lParam : HWND of control
7578 * if first comes before second : negative
7579 * if first comes after second : positive
7580 * if first and second are equivalent : zero
7582 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7584 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7585 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7586 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7588 /* Forward the call to the client defined callback */
7589 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7594 * Sorts the listview items.
7597 * [I] infoPtr : valid pointer to the listview structure
7598 * [I] pfnCompare : application-defined value
7599 * [I] lParamSort : pointer to comparison callback
7605 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7607 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7610 LPVOID selectionMarkItem;
7614 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7616 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7618 if (!pfnCompare) return FALSE;
7619 if (!infoPtr->hdpaItems) return FALSE;
7621 /* if there are 0 or 1 items, there is no need to sort */
7622 if (infoPtr->nItemCount < 2) return TRUE;
7624 if (infoPtr->nFocusedItem >= 0)
7626 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7627 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7628 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7630 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7631 /* clear the lpItem->state for non-selected ones */
7632 /* remove the selection ranges */
7634 infoPtr->pfnCompare = pfnCompare;
7635 infoPtr->lParamSort = lParamSort;
7636 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7638 /* Adjust selections and indices so that they are the way they should
7639 * be after the sort (otherwise, the list items move around, but
7640 * whatever is at the item's previous original position will be
7643 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7644 for (i=0; i < infoPtr->nItemCount; i++)
7646 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7647 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7649 if (lpItem->state & LVIS_SELECTED)
7651 item.state = LVIS_SELECTED;
7652 item.stateMask = LVIS_SELECTED;
7653 LISTVIEW_SetItemState(infoPtr, i, &item);
7655 if (lpItem->state & LVIS_FOCUSED)
7657 infoPtr->nFocusedItem = i;
7658 lpItem->state &= ~LVIS_FOCUSED;
7661 if (selectionMarkItem != NULL)
7662 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7663 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7665 /* refresh the display */
7666 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7667 LISTVIEW_InvalidateList(infoPtr);
7674 * Update theme handle after a theme change.
7677 * [I] infoPtr : valid pointer to the listview structure
7681 * FAILURE : something else
7683 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
7685 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7686 CloseThemeData(theme);
7687 OpenThemeData(infoPtr->hwndSelf, themeClass);
7693 * Updates an items or rearranges the listview control.
7696 * [I] infoPtr : valid pointer to the listview structure
7697 * [I] nItem : item index
7703 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7705 TRACE("(nItem=%d)\n", nItem);
7707 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7709 /* rearrange with default alignment style */
7710 if (is_autoarrange(infoPtr))
7711 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7713 LISTVIEW_InvalidateItem(infoPtr, nItem);
7720 * Draw the track line at the place defined in the infoPtr structure.
7721 * The line is drawn with a XOR pen so drawing the line for the second time
7722 * in the same place erases the line.
7725 * [I] infoPtr : valid pointer to the listview structure
7731 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
7737 if (infoPtr->xTrackLine == -1)
7740 if (!(hdc = GetDC(infoPtr->hwndSelf)))
7742 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7743 oldROP = SetROP2(hdc, R2_XORPEN);
7744 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7745 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7746 SetROP2(hdc, oldROP);
7747 SelectObject(hdc, hOldPen);
7748 ReleaseDC(infoPtr->hwndSelf, hdc);
7754 * Called when an edit control should be displayed. This function is called after
7755 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7758 * [I] hwnd : Handle to the listview
7759 * [I] uMsg : WM_TIMER (ignored)
7760 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7761 * [I] dwTimer : The elapsed time (ignored)
7766 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
7768 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
7769 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7771 KillTimer(hwnd, idEvent);
7772 editItem->fEnabled = FALSE;
7773 /* check if the item is still selected */
7774 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
7775 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
7780 * Creates the listview control - the WM_NCCREATE phase.
7783 * [I] hwnd : window handle
7784 * [I] lpcs : the create parameters
7790 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
7792 LISTVIEW_INFO *infoPtr;
7795 TRACE("(lpcs=%p)\n", lpcs);
7797 /* initialize info pointer */
7798 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
7799 if (!infoPtr) return FALSE;
7801 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7803 infoPtr->hwndSelf = hwnd;
7804 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
7805 /* determine the type of structures to use */
7806 infoPtr->hwndNotify = lpcs->hwndParent;
7807 /* infoPtr->notifyFormat will be filled in WM_CREATE */
7809 /* initialize color information */
7810 infoPtr->clrBk = CLR_NONE;
7811 infoPtr->clrText = CLR_DEFAULT;
7812 infoPtr->clrTextBk = CLR_DEFAULT;
7813 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7815 /* set default values */
7816 infoPtr->nFocusedItem = -1;
7817 infoPtr->nSelectionMark = -1;
7818 infoPtr->nHotItem = -1;
7819 infoPtr->bRedraw = TRUE;
7820 infoPtr->bNoItemMetrics = TRUE;
7821 infoPtr->bDoChangeNotify = TRUE;
7822 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7823 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7824 infoPtr->nEditLabelItem = -1;
7825 infoPtr->dwHoverTime = -1; /* default system hover time */
7826 infoPtr->nMeasureItemHeight = 0;
7827 infoPtr->xTrackLine = -1; /* no track line */
7828 infoPtr->itemEdit.fEnabled = FALSE;
7830 /* get default font (icon title) */
7831 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7832 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7833 infoPtr->hFont = infoPtr->hDefaultFont;
7834 LISTVIEW_SaveTextMetrics(infoPtr);
7836 /* allocate memory for the data structure */
7837 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7838 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7839 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7840 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7841 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7845 DestroyWindow(infoPtr->hwndHeader);
7846 ranges_destroy(infoPtr->selectionRanges);
7847 DPA_Destroy(infoPtr->hdpaItems);
7848 DPA_Destroy(infoPtr->hdpaPosX);
7849 DPA_Destroy(infoPtr->hdpaPosY);
7850 DPA_Destroy(infoPtr->hdpaColumns);
7857 * Creates the listview control - the WM_CREATE phase. Most of the data is
7858 * already set up in LISTVIEW_NCCreate
7861 * [I] hwnd : window handle
7862 * [I] lpcs : the create parameters
7868 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7870 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7871 UINT uView = lpcs->style & LVS_TYPEMASK;
7873 TRACE("(lpcs=%p)\n", lpcs);
7875 infoPtr->dwStyle = lpcs->style;
7876 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7877 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7880 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7881 WS_CHILD | HDS_HORZ | HDS_FULLDRAG | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7882 0, 0, 0, 0, hwnd, NULL,
7883 lpcs->hInstance, NULL);
7884 if (!infoPtr->hwndHeader) return -1;
7886 /* set header unicode format */
7887 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7889 /* set header font */
7890 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7892 /* init item size to avoid division by 0 */
7893 LISTVIEW_UpdateItemSize (infoPtr);
7895 if (uView == LVS_REPORT)
7897 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7899 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7903 /* set HDS_HIDDEN flag to hide the header bar */
7904 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7905 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7909 OpenThemeData(hwnd, themeClass);
7911 /* initialize the icon sizes */
7912 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7913 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7919 * Destroys the listview control.
7922 * [I] infoPtr : valid pointer to the listview structure
7928 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
7930 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7931 CloseThemeData(theme);
7937 * Enables the listview control.
7940 * [I] infoPtr : valid pointer to the listview structure
7941 * [I] bEnable : specifies whether to enable or disable the window
7947 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
7949 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7950 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7956 * Erases the background of the listview control.
7959 * [I] infoPtr : valid pointer to the listview structure
7960 * [I] hdc : device context handle
7966 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
7970 TRACE("(hdc=%p)\n", hdc);
7972 if (!GetClipBox(hdc, &rc)) return FALSE;
7974 /* for double buffered controls we need to do this during refresh */
7975 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
7977 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7983 * Helper function for LISTVIEW_[HV]Scroll *only*.
7984 * Performs vertical/horizontal scrolling by a give amount.
7987 * [I] infoPtr : valid pointer to the listview structure
7988 * [I] dx : amount of horizontal scroll
7989 * [I] dy : amount of vertical scroll
7991 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7993 /* now we can scroll the list */
7994 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7995 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7996 /* if we have focus, adjust rect */
7997 OffsetRect(&infoPtr->rcFocus, dx, dy);
7998 UpdateWindow(infoPtr->hwndSelf);
8003 * Performs vertical scrolling.
8006 * [I] infoPtr : valid pointer to the listview structure
8007 * [I] nScrollCode : scroll code
8008 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8009 * [I] hScrollWnd : scrollbar control window handle
8015 * SB_LINEUP/SB_LINEDOWN:
8016 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8017 * for LVS_REPORT is 1 line
8018 * for LVS_LIST cannot occur
8021 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8022 INT nScrollDiff, HWND hScrollWnd)
8024 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8025 INT nOldScrollPos, nNewScrollPos;
8026 SCROLLINFO scrollInfo;
8029 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8030 debugscrollcode(nScrollCode), nScrollDiff);
8032 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8034 scrollInfo.cbSize = sizeof(SCROLLINFO);
8035 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8037 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8039 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8041 nOldScrollPos = scrollInfo.nPos;
8042 switch (nScrollCode)
8048 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8052 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8056 nScrollDiff = -scrollInfo.nPage;
8060 nScrollDiff = scrollInfo.nPage;
8063 case SB_THUMBPOSITION:
8065 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8072 /* quit right away if pos isn't changing */
8073 if (nScrollDiff == 0) return 0;
8075 /* calculate new position, and handle overflows */
8076 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8077 if (nScrollDiff > 0) {
8078 if (nNewScrollPos < nOldScrollPos ||
8079 nNewScrollPos > scrollInfo.nMax)
8080 nNewScrollPos = scrollInfo.nMax;
8082 if (nNewScrollPos > nOldScrollPos ||
8083 nNewScrollPos < scrollInfo.nMin)
8084 nNewScrollPos = scrollInfo.nMin;
8087 /* set the new position, and reread in case it changed */
8088 scrollInfo.fMask = SIF_POS;
8089 scrollInfo.nPos = nNewScrollPos;
8090 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8092 /* carry on only if it really changed */
8093 if (nNewScrollPos == nOldScrollPos) return 0;
8095 /* now adjust to client coordinates */
8096 nScrollDiff = nOldScrollPos - nNewScrollPos;
8097 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8099 /* and scroll the window */
8100 scroll_list(infoPtr, 0, nScrollDiff);
8107 * Performs horizontal scrolling.
8110 * [I] infoPtr : valid pointer to the listview structure
8111 * [I] nScrollCode : scroll code
8112 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8113 * [I] hScrollWnd : scrollbar control window handle
8119 * SB_LINELEFT/SB_LINERIGHT:
8120 * for LVS_ICON, LVS_SMALLICON 1 pixel
8121 * for LVS_REPORT is 1 pixel
8122 * for LVS_LIST is 1 column --> which is a 1 because the
8123 * scroll is based on columns not pixels
8126 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8127 INT nScrollDiff, HWND hScrollWnd)
8129 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8130 INT nOldScrollPos, nNewScrollPos;
8131 SCROLLINFO scrollInfo;
8133 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8134 debugscrollcode(nScrollCode), nScrollDiff);
8136 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8138 scrollInfo.cbSize = sizeof(SCROLLINFO);
8139 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8141 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8143 nOldScrollPos = scrollInfo.nPos;
8145 switch (nScrollCode)
8159 nScrollDiff = -scrollInfo.nPage;
8163 nScrollDiff = scrollInfo.nPage;
8166 case SB_THUMBPOSITION:
8168 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8175 /* quit right away if pos isn't changing */
8176 if (nScrollDiff == 0) return 0;
8178 /* calculate new position, and handle overflows */
8179 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8180 if (nScrollDiff > 0) {
8181 if (nNewScrollPos < nOldScrollPos ||
8182 nNewScrollPos > scrollInfo.nMax)
8183 nNewScrollPos = scrollInfo.nMax;
8185 if (nNewScrollPos > nOldScrollPos ||
8186 nNewScrollPos < scrollInfo.nMin)
8187 nNewScrollPos = scrollInfo.nMin;
8190 /* set the new position, and reread in case it changed */
8191 scrollInfo.fMask = SIF_POS;
8192 scrollInfo.nPos = nNewScrollPos;
8193 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8195 /* carry on only if it really changed */
8196 if (nNewScrollPos == nOldScrollPos) return 0;
8198 if(uView == LVS_REPORT)
8199 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8201 /* now adjust to client coordinates */
8202 nScrollDiff = nOldScrollPos - nNewScrollPos;
8203 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8205 /* and scroll the window */
8206 scroll_list(infoPtr, nScrollDiff, 0);
8211 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8213 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8214 INT gcWheelDelta = 0;
8215 INT pulScrollLines = 3;
8216 SCROLLINFO scrollInfo;
8218 TRACE("(wheelDelta=%d)\n", wheelDelta);
8220 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8221 gcWheelDelta -= wheelDelta;
8223 scrollInfo.cbSize = sizeof(SCROLLINFO);
8224 scrollInfo.fMask = SIF_POS;
8231 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8232 * should be fixed in the future.
8234 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8235 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8239 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8241 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8242 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8243 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8248 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8259 * [I] infoPtr : valid pointer to the listview structure
8260 * [I] nVirtualKey : virtual key
8261 * [I] lKeyData : key data
8266 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8268 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8269 HWND hwndSelf = infoPtr->hwndSelf;
8271 NMLVKEYDOWN nmKeyDown;
8273 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8275 /* send LVN_KEYDOWN notification */
8276 nmKeyDown.wVKey = nVirtualKey;
8277 nmKeyDown.flags = 0;
8278 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8279 if (!IsWindow(hwndSelf))
8282 switch (nVirtualKey)
8285 nItem = infoPtr->nFocusedItem;
8289 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8291 if (!notify(infoPtr, NM_RETURN)) return 0;
8292 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8297 if (infoPtr->nItemCount > 0)
8302 if (infoPtr->nItemCount > 0)
8303 nItem = infoPtr->nItemCount - 1;
8307 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8311 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8315 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8319 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8323 if (uView == LVS_REPORT)
8325 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8326 if (infoPtr->nFocusedItem == topidx)
8327 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8332 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8333 * LISTVIEW_GetCountPerRow(infoPtr);
8334 if(nItem < 0) nItem = 0;
8338 if (uView == LVS_REPORT)
8340 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8341 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8342 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8343 nItem = infoPtr->nFocusedItem + cnt - 1;
8345 nItem = topidx + cnt - 1;
8348 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8349 * LISTVIEW_GetCountPerRow(infoPtr);
8350 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8354 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8355 LISTVIEW_KeySelection(infoPtr, nItem);
8365 * [I] infoPtr : valid pointer to the listview structure
8370 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8374 /* if we did not have the focus, there's nothing to do */
8375 if (!infoPtr->bFocus) return 0;
8377 /* send NM_KILLFOCUS notification */
8378 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8380 /* if we have a focus rectagle, get rid of it */
8381 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8383 /* set window focus flag */
8384 infoPtr->bFocus = FALSE;
8386 /* invalidate the selected items before reseting focus flag */
8387 LISTVIEW_InvalidateSelectedItems(infoPtr);
8394 * Processes double click messages (left mouse button).
8397 * [I] infoPtr : valid pointer to the listview structure
8398 * [I] wKey : key flag
8399 * [I] x,y : mouse coordinate
8404 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8406 LVHITTESTINFO htInfo;
8408 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8410 /* Cancel the item edition if any */
8411 if (infoPtr->itemEdit.fEnabled)
8413 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8414 infoPtr->itemEdit.fEnabled = FALSE;
8417 /* send NM_RELEASEDCAPTURE notification */
8418 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8423 /* send NM_DBLCLK notification */
8424 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8425 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8427 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8428 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8435 * Processes mouse down messages (left mouse button).
8438 * infoPtr [I ] valid pointer to the listview structure
8439 * wKey [I ] key flag
8440 * x,y [I ] mouse coordinate
8445 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8447 LVHITTESTINFO lvHitTestInfo;
8448 static BOOL bGroupSelect = TRUE;
8449 BOOL bReceivedFocus = FALSE;
8450 POINT pt = { x, y };
8453 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8455 /* send NM_RELEASEDCAPTURE notification */
8456 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8458 if (!infoPtr->bFocus)
8460 bReceivedFocus = TRUE;
8461 SetFocus(infoPtr->hwndSelf);
8464 /* set left button down flag and record the click position */
8465 infoPtr->bLButtonDown = TRUE;
8466 infoPtr->ptClickPos = pt;
8468 lvHitTestInfo.pt.x = x;
8469 lvHitTestInfo.pt.y = y;
8471 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8472 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8473 infoPtr->nEditLabelItem = -1;
8474 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8476 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8478 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8479 if(state == 1 || state == 2)
8483 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8484 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8485 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8490 if (infoPtr->dwStyle & LVS_SINGLESEL)
8492 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8493 infoPtr->nEditLabelItem = nItem;
8495 LISTVIEW_SetSelection(infoPtr, nItem);
8499 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8503 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8504 LISTVIEW_SetItemFocus(infoPtr, nItem);
8505 infoPtr->nSelectionMark = nItem;
8511 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8512 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8514 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8515 infoPtr->nSelectionMark = nItem;
8518 else if (wKey & MK_CONTROL)
8522 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8524 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8525 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8526 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8527 infoPtr->nSelectionMark = nItem;
8529 else if (wKey & MK_SHIFT)
8531 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8535 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8536 infoPtr->nEditLabelItem = nItem;
8538 /* set selection (clears other pre-existing selections) */
8539 LISTVIEW_SetSelection(infoPtr, nItem);
8545 /* remove all selections */
8546 LISTVIEW_DeselectAll(infoPtr);
8551 infoPtr->nEditLabelItem = -1;
8558 * Processes mouse up messages (left mouse button).
8561 * infoPtr [I ] valid pointer to the listview structure
8562 * wKey [I ] key flag
8563 * x,y [I ] mouse coordinate
8568 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8570 LVHITTESTINFO lvHitTestInfo;
8572 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8574 if (!infoPtr->bLButtonDown) return 0;
8576 lvHitTestInfo.pt.x = x;
8577 lvHitTestInfo.pt.y = y;
8579 /* send NM_CLICK notification */
8580 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8581 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8583 /* set left button flag */
8584 infoPtr->bLButtonDown = FALSE;
8586 /* if we clicked on a selected item, edit the label */
8587 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8589 /* we want to make sure the user doesn't want to do a double click. So we will
8590 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8592 infoPtr->itemEdit.fEnabled = TRUE;
8593 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8594 SetTimer(infoPtr->hwndSelf,
8595 (UINT_PTR)&infoPtr->itemEdit,
8596 GetDoubleClickTime(),
8597 LISTVIEW_DelayedEditItem);
8605 * Destroys the listview control (called after WM_DESTROY).
8608 * [I] infoPtr : valid pointer to the listview structure
8613 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8617 /* delete all items */
8618 LISTVIEW_DeleteAllItems(infoPtr);
8620 /* destroy data structure */
8621 DPA_Destroy(infoPtr->hdpaItems);
8622 DPA_Destroy(infoPtr->hdpaPosX);
8623 DPA_Destroy(infoPtr->hdpaPosY);
8624 DPA_Destroy(infoPtr->hdpaColumns);
8625 ranges_destroy(infoPtr->selectionRanges);
8627 /* destroy image lists */
8628 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8630 if (infoPtr->himlNormal)
8631 ImageList_Destroy(infoPtr->himlNormal);
8632 if (infoPtr->himlSmall)
8633 ImageList_Destroy(infoPtr->himlSmall);
8634 if (infoPtr->himlState)
8635 ImageList_Destroy(infoPtr->himlState);
8638 /* destroy font, bkgnd brush */
8640 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8641 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8643 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8645 /* free listview info pointer*/
8653 * Handles notifications from header.
8656 * [I] infoPtr : valid pointer to the listview structure
8657 * [I] nCtrlId : control identifier
8658 * [I] lpnmh : notification information
8663 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8665 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8666 HWND hwndSelf = infoPtr->hwndSelf;
8668 TRACE("(lpnmh=%p)\n", lpnmh);
8670 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8672 switch (lpnmh->hdr.code)
8677 COLUMN_INFO *lpColumnInfo;
8681 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8684 /* remove the old line (if any) */
8685 LISTVIEW_DrawTrackLine(infoPtr);
8687 /* compute & draw the new line */
8688 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8689 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8690 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8691 infoPtr->xTrackLine = x + ptOrigin.x;
8692 LISTVIEW_DrawTrackLine(infoPtr);
8698 /* remove the track line (if any) */
8699 LISTVIEW_DrawTrackLine(infoPtr);
8700 infoPtr->xTrackLine = -1;
8704 FIXME("Changing column order not implemented\n");
8707 case HDN_ITEMCHANGINGW:
8708 case HDN_ITEMCHANGINGA:
8709 return notify_forward_header(infoPtr, lpnmh);
8711 case HDN_ITEMCHANGEDW:
8712 case HDN_ITEMCHANGEDA:
8714 COLUMN_INFO *lpColumnInfo;
8717 notify_forward_header(infoPtr, lpnmh);
8718 if (!IsWindow(hwndSelf))
8721 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8725 hdi.mask = HDI_WIDTH;
8726 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8730 cxy = lpnmh->pitem->cxy;
8732 /* determine how much we change since the last know position */
8733 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8734 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8737 lpColumnInfo->rcHeader.right += dx;
8738 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
8739 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8742 /* only needs to update the scrolls */
8743 infoPtr->nItemWidth += dx;
8744 LISTVIEW_UpdateScroll(infoPtr);
8746 LISTVIEW_UpdateItemSize(infoPtr);
8747 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8750 RECT rcCol = lpColumnInfo->rcHeader;
8752 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8753 OffsetRect(&rcCol, ptOrigin.x, 0);
8755 rcCol.top = infoPtr->rcList.top;
8756 rcCol.bottom = infoPtr->rcList.bottom;
8758 /* resizing left-aligned columns leaves most of the left side untouched */
8759 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8761 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
8764 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8767 /* when shrinking the last column clear the now unused field */
8768 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1 && dx < 0)
8771 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8777 case HDN_ITEMCLICKW:
8778 case HDN_ITEMCLICKA:
8780 /* Handle sorting by Header Column */
8783 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8785 nmlv.iSubItem = lpnmh->iItem;
8786 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8790 case HDN_DIVIDERDBLCLICKW:
8791 case HDN_DIVIDERDBLCLICKA:
8792 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8801 * Paint non-client area of control.
8804 * [I] infoPtr : valid pointer to the listview structureof the sender
8805 * [I] region : update region
8808 * TRUE - frame was painted
8809 * FALSE - call default window proc
8811 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
8813 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8817 int cxEdge = GetSystemMetrics (SM_CXEDGE),
8818 cyEdge = GetSystemMetrics (SM_CYEDGE);
8820 if (!theme) return FALSE;
8822 GetWindowRect(infoPtr->hwndSelf, &r);
8824 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8825 r.right - cxEdge, r.bottom - cyEdge);
8826 if (region != (HRGN)1)
8827 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8828 OffsetRect(&r, -r.left, -r.top);
8830 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8831 OffsetRect(&r, -r.left, -r.top);
8833 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8834 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8835 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8836 ReleaseDC(infoPtr->hwndSelf, dc);
8838 /* Call default proc to get the scrollbars etc. painted */
8839 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8846 * Determines the type of structure to use.
8849 * [I] infoPtr : valid pointer to the listview structureof the sender
8850 * [I] hwndFrom : listview window handle
8851 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8856 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8858 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8860 if (nCommand != NF_REQUERY) return 0;
8862 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8869 * Paints/Repaints the listview control.
8872 * [I] infoPtr : valid pointer to the listview structure
8873 * [I] hdc : device context handle
8878 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8880 TRACE("(hdc=%p)\n", hdc);
8882 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8884 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8886 infoPtr->bNoItemMetrics = FALSE;
8887 LISTVIEW_UpdateItemSize(infoPtr);
8888 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8889 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8890 LISTVIEW_UpdateScroll(infoPtr);
8893 UpdateWindow(infoPtr->hwndHeader);
8896 LISTVIEW_Refresh(infoPtr, hdc, NULL);
8901 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8903 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
8904 EndPaint(infoPtr->hwndSelf, &ps);
8913 * Paints/Repaints the listview control.
8916 * [I] infoPtr : valid pointer to the listview structure
8917 * [I] hdc : device context handle
8918 * [I] options : drawing options
8923 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8925 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
8927 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8930 if (options & PRF_ERASEBKGND)
8931 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8933 if (options & PRF_CLIENT)
8934 LISTVIEW_Paint(infoPtr, hdc);
8942 * Processes double click messages (right mouse button).
8945 * [I] infoPtr : valid pointer to the listview structure
8946 * [I] wKey : key flag
8947 * [I] x,y : mouse coordinate
8952 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8954 LVHITTESTINFO lvHitTestInfo;
8956 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8958 /* send NM_RELEASEDCAPTURE notification */
8959 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8961 /* send NM_RDBLCLK notification */
8962 lvHitTestInfo.pt.x = x;
8963 lvHitTestInfo.pt.y = y;
8964 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8965 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8972 * Processes mouse down messages (right mouse button).
8975 * [I] infoPtr : valid pointer to the listview structure
8976 * [I] wKey : key flag
8977 * [I] x,y : mouse coordinate
8982 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8984 LVHITTESTINFO lvHitTestInfo;
8987 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8989 /* send NM_RELEASEDCAPTURE notification */
8990 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8992 /* make sure the listview control window has the focus */
8993 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8995 /* set right button down flag */
8996 infoPtr->bRButtonDown = TRUE;
8998 /* determine the index of the selected item */
8999 lvHitTestInfo.pt.x = x;
9000 lvHitTestInfo.pt.y = y;
9001 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9003 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9005 LISTVIEW_SetItemFocus(infoPtr, nItem);
9006 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9007 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9008 LISTVIEW_SetSelection(infoPtr, nItem);
9012 LISTVIEW_DeselectAll(infoPtr);
9020 * Processes mouse up messages (right mouse button).
9023 * [I] infoPtr : valid pointer to the listview structure
9024 * [I] wKey : key flag
9025 * [I] x,y : mouse coordinate
9030 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9032 LVHITTESTINFO lvHitTestInfo;
9035 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9037 if (!infoPtr->bRButtonDown) return 0;
9039 /* set button flag */
9040 infoPtr->bRButtonDown = FALSE;
9042 /* Send NM_RClICK notification */
9043 lvHitTestInfo.pt.x = x;
9044 lvHitTestInfo.pt.y = y;
9045 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9046 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9048 /* Change to screen coordinate for WM_CONTEXTMENU */
9049 pt = lvHitTestInfo.pt;
9050 ClientToScreen(infoPtr->hwndSelf, &pt);
9052 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9053 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9054 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9065 * [I] infoPtr : valid pointer to the listview structure
9066 * [I] hwnd : window handle of window containing the cursor
9067 * [I] nHittest : hit-test code
9068 * [I] wMouseMsg : ideintifier of the mouse message
9071 * TRUE if cursor is set
9074 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9076 LVHITTESTINFO lvHitTestInfo;
9078 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
9080 if(!infoPtr->hHotCursor) return FALSE;
9082 GetCursorPos(&lvHitTestInfo.pt);
9083 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9085 SetCursor(infoPtr->hHotCursor);
9095 * [I] infoPtr : valid pointer to the listview structure
9096 * [I] hwndLoseFocus : handle of previously focused window
9101 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9103 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9105 /* if we have the focus already, there's nothing to do */
9106 if (infoPtr->bFocus) return 0;
9108 /* send NM_SETFOCUS notification */
9109 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9111 /* set window focus flag */
9112 infoPtr->bFocus = TRUE;
9114 /* put the focus rect back on */
9115 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9117 /* redraw all visible selected items */
9118 LISTVIEW_InvalidateSelectedItems(infoPtr);
9128 * [I] infoPtr : valid pointer to the listview structure
9129 * [I] fRedraw : font handle
9130 * [I] fRedraw : redraw flag
9135 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9137 HFONT oldFont = infoPtr->hFont;
9139 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9141 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9142 if (infoPtr->hFont == oldFont) return 0;
9144 LISTVIEW_SaveTextMetrics(infoPtr);
9146 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9147 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9149 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9156 * Message handling for WM_SETREDRAW.
9157 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9160 * [I] infoPtr : valid pointer to the listview structure
9161 * [I] bRedraw: state of redraw flag
9164 * DefWinProc return value
9166 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9168 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9170 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9171 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9173 infoPtr->bRedraw = bRedraw;
9175 if(!bRedraw) return 0;
9177 if (is_autoarrange(infoPtr))
9178 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9179 LISTVIEW_UpdateScroll(infoPtr);
9181 /* despite what the WM_SETREDRAW docs says, apps expect us
9182 * to invalidate the listview here... stupid! */
9183 LISTVIEW_InvalidateList(infoPtr);
9190 * Resizes the listview control. This function processes WM_SIZE
9191 * messages. At this time, the width and height are not used.
9194 * [I] infoPtr : valid pointer to the listview structure
9195 * [I] Width : new width
9196 * [I] Height : new height
9201 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9203 RECT rcOld = infoPtr->rcList;
9205 TRACE("(width=%d, height=%d)\n", Width, Height);
9207 LISTVIEW_UpdateSize(infoPtr);
9208 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9210 /* do not bother with display related stuff if we're not redrawing */
9211 if (!is_redrawing(infoPtr)) return 0;
9213 if (is_autoarrange(infoPtr))
9214 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9216 LISTVIEW_UpdateScroll(infoPtr);
9218 /* refresh all only for lists whose height changed significantly */
9219 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
9220 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9221 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9222 LISTVIEW_InvalidateList(infoPtr);
9229 * Sets the size information.
9232 * [I] infoPtr : valid pointer to the listview structure
9237 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9239 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9241 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9243 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9245 if (uView == LVS_LIST)
9247 /* Apparently the "LIST" style is supposed to have the same
9248 * number of items in a column even if there is no scroll bar.
9249 * Since if a scroll bar already exists then the bottom is already
9250 * reduced, only reduce if the scroll bar does not currently exist.
9251 * The "2" is there to mimic the native control. I think it may be
9252 * related to either padding or edges. (GLA 7/2002)
9254 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9255 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9256 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9258 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
9263 hl.prc = &infoPtr->rcList;
9265 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9267 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9269 infoPtr->rcList.top = max(wp.cy, 0);
9272 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9277 * Processes WM_STYLECHANGED messages.
9280 * [I] infoPtr : valid pointer to the listview structure
9281 * [I] wStyleType : window style type (normal or extended)
9282 * [I] lpss : window style information
9287 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9288 const STYLESTRUCT *lpss)
9290 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9291 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9293 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9294 wStyleType, lpss->styleOld, lpss->styleNew);
9296 if (wStyleType != GWL_STYLE) return 0;
9298 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9299 /* what if LVS_OWNERDATA changed? */
9300 /* or LVS_SINGLESEL */
9301 /* or LVS_SORT{AS,DES}CENDING */
9303 infoPtr->dwStyle = lpss->styleNew;
9305 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9306 ((lpss->styleNew & WS_HSCROLL) == 0))
9307 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9309 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9310 ((lpss->styleNew & WS_VSCROLL) == 0))
9311 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9313 if (uNewView != uOldView)
9315 SIZE oldIconSize = infoPtr->iconSize;
9318 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9319 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9321 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9322 SetRectEmpty(&infoPtr->rcFocus);
9324 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9325 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9327 if (uNewView == LVS_ICON)
9329 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9331 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9332 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9333 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9336 else if (uNewView == LVS_REPORT)
9341 hl.prc = &infoPtr->rcList;
9343 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9344 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9347 LISTVIEW_UpdateItemSize(infoPtr);
9350 if (uNewView == LVS_REPORT)
9351 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
9353 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9354 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9355 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9357 /* update the size of the client area */
9358 LISTVIEW_UpdateSize(infoPtr);
9360 /* add scrollbars if needed */
9361 LISTVIEW_UpdateScroll(infoPtr);
9363 /* invalidate client area + erase background */
9364 LISTVIEW_InvalidateList(infoPtr);
9371 * Window procedure of the listview control.
9374 static LRESULT WINAPI
9375 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9377 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9379 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9381 if (!infoPtr && (uMsg != WM_NCCREATE))
9382 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9386 case LVM_APPROXIMATEVIEWRECT:
9387 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9388 LOWORD(lParam), HIWORD(lParam));
9390 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9392 /* case LVM_CANCELEDITLABEL: */
9394 case LVM_CREATEDRAGIMAGE:
9395 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9397 case LVM_DELETEALLITEMS:
9398 return LISTVIEW_DeleteAllItems(infoPtr);
9400 case LVM_DELETECOLUMN:
9401 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9403 case LVM_DELETEITEM:
9404 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9406 case LVM_EDITLABELW:
9407 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9409 case LVM_EDITLABELA:
9410 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9412 /* case LVM_ENABLEGROUPVIEW: */
9414 case LVM_ENSUREVISIBLE:
9415 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9418 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9421 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9423 case LVM_GETBKCOLOR:
9424 return infoPtr->clrBk;
9426 /* case LVM_GETBKIMAGE: */
9428 case LVM_GETCALLBACKMASK:
9429 return infoPtr->uCallbackMask;
9431 case LVM_GETCOLUMNA:
9432 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9434 case LVM_GETCOLUMNW:
9435 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9437 case LVM_GETCOLUMNORDERARRAY:
9438 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9440 case LVM_GETCOLUMNWIDTH:
9441 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9443 case LVM_GETCOUNTPERPAGE:
9444 return LISTVIEW_GetCountPerPage(infoPtr);
9446 case LVM_GETEDITCONTROL:
9447 return (LRESULT)infoPtr->hwndEdit;
9449 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9450 return infoPtr->dwLvExStyle;
9452 /* case LVM_GETGROUPINFO: */
9454 /* case LVM_GETGROUPMETRICS: */
9457 return (LRESULT)infoPtr->hwndHeader;
9459 case LVM_GETHOTCURSOR:
9460 return (LRESULT)infoPtr->hHotCursor;
9462 case LVM_GETHOTITEM:
9463 return infoPtr->nHotItem;
9465 case LVM_GETHOVERTIME:
9466 return infoPtr->dwHoverTime;
9468 case LVM_GETIMAGELIST:
9469 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9471 /* case LVM_GETINSERTMARK: */
9473 /* case LVM_GETINSERTMARKCOLOR: */
9475 /* case LVM_GETINSERTMARKRECT: */
9477 case LVM_GETISEARCHSTRINGA:
9478 case LVM_GETISEARCHSTRINGW:
9479 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9483 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9486 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9488 case LVM_GETITEMCOUNT:
9489 return infoPtr->nItemCount;
9491 case LVM_GETITEMPOSITION:
9492 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9494 case LVM_GETITEMRECT:
9495 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9497 case LVM_GETITEMSPACING:
9498 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9500 case LVM_GETITEMSTATE:
9501 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9503 case LVM_GETITEMTEXTA:
9504 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9506 case LVM_GETITEMTEXTW:
9507 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9509 case LVM_GETNEXTITEM:
9510 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9512 case LVM_GETNUMBEROFWORKAREAS:
9513 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9517 if (!lParam) return FALSE;
9518 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9521 /* case LVM_GETOUTLINECOLOR: */
9523 /* case LVM_GETSELECTEDCOLUMN: */
9525 case LVM_GETSELECTEDCOUNT:
9526 return LISTVIEW_GetSelectedCount(infoPtr);
9528 case LVM_GETSELECTIONMARK:
9529 return infoPtr->nSelectionMark;
9531 case LVM_GETSTRINGWIDTHA:
9532 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9534 case LVM_GETSTRINGWIDTHW:
9535 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9537 case LVM_GETSUBITEMRECT:
9538 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9540 case LVM_GETTEXTBKCOLOR:
9541 return infoPtr->clrTextBk;
9543 case LVM_GETTEXTCOLOR:
9544 return infoPtr->clrText;
9546 /* case LVM_GETTILEINFO: */
9548 /* case LVM_GETTILEVIEWINFO: */
9550 case LVM_GETTOOLTIPS:
9551 if( !infoPtr->hwndToolTip )
9552 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9553 return (LRESULT)infoPtr->hwndToolTip;
9555 case LVM_GETTOPINDEX:
9556 return LISTVIEW_GetTopIndex(infoPtr);
9558 /*case LVM_GETUNICODEFORMAT:
9559 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9562 /* case LVM_GETVIEW: */
9564 case LVM_GETVIEWRECT:
9565 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9567 case LVM_GETWORKAREAS:
9568 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9571 /* case LVM_HASGROUP: */
9574 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9576 case LVM_INSERTCOLUMNA:
9577 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9579 case LVM_INSERTCOLUMNW:
9580 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9582 /* case LVM_INSERTGROUP: */
9584 /* case LVM_INSERTGROUPSORTED: */
9586 case LVM_INSERTITEMA:
9587 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9589 case LVM_INSERTITEMW:
9590 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9592 /* case LVM_INSERTMARKHITTEST: */
9594 /* case LVM_ISGROUPVIEWENABLED: */
9596 /* case LVM_MAPIDTOINDEX: */
9598 /* case LVM_MAPINDEXTOID: */
9600 /* case LVM_MOVEGROUP: */
9602 /* case LVM_MOVEITEMTOGROUP: */
9604 case LVM_REDRAWITEMS:
9605 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9607 /* case LVM_REMOVEALLGROUPS: */
9609 /* case LVM_REMOVEGROUP: */
9612 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9614 case LVM_SETBKCOLOR:
9615 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9617 /* case LVM_SETBKIMAGE: */
9619 case LVM_SETCALLBACKMASK:
9620 infoPtr->uCallbackMask = (UINT)wParam;
9623 case LVM_SETCOLUMNA:
9624 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9626 case LVM_SETCOLUMNW:
9627 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9629 case LVM_SETCOLUMNORDERARRAY:
9630 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9632 case LVM_SETCOLUMNWIDTH:
9633 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9635 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9636 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9638 /* case LVM_SETGROUPINFO: */
9640 /* case LVM_SETGROUPMETRICS: */
9642 case LVM_SETHOTCURSOR:
9643 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9645 case LVM_SETHOTITEM:
9646 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9648 case LVM_SETHOVERTIME:
9649 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9651 case LVM_SETICONSPACING:
9652 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9654 case LVM_SETIMAGELIST:
9655 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9657 /* case LVM_SETINFOTIP: */
9659 /* case LVM_SETINSERTMARK: */
9661 /* case LVM_SETINSERTMARKCOLOR: */
9664 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9667 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9669 case LVM_SETITEMCOUNT:
9670 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9672 case LVM_SETITEMPOSITION:
9675 pt.x = (short)LOWORD(lParam);
9676 pt.y = (short)HIWORD(lParam);
9677 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9680 case LVM_SETITEMPOSITION32:
9681 if (lParam == 0) return FALSE;
9682 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9684 case LVM_SETITEMSTATE:
9685 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9687 case LVM_SETITEMTEXTA:
9688 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9690 case LVM_SETITEMTEXTW:
9691 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9693 /* case LVM_SETOUTLINECOLOR: */
9695 /* case LVM_SETSELECTEDCOLUMN: */
9697 case LVM_SETSELECTIONMARK:
9698 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9700 case LVM_SETTEXTBKCOLOR:
9701 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9703 case LVM_SETTEXTCOLOR:
9704 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9706 /* case LVM_SETTILEINFO: */
9708 /* case LVM_SETTILEVIEWINFO: */
9710 /* case LVM_SETTILEWIDTH: */
9712 case LVM_SETTOOLTIPS:
9713 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9715 case LVM_SETUNICODEFORMAT:
9716 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
9718 /* case LVM_SETVIEW: */
9720 /* case LVM_SETWORKAREAS: */
9722 /* case LVM_SORTGROUPS: */
9725 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9727 /* LVM_SORTITEMSEX: */
9729 case LVM_SUBITEMHITTEST:
9730 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9733 return LISTVIEW_Update(infoPtr, (INT)wParam);
9736 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9739 return LISTVIEW_Command(infoPtr, wParam, lParam);
9742 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
9745 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9748 return LISTVIEW_Destroy(infoPtr);
9751 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9754 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9757 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9760 return (LRESULT)infoPtr->hFont;
9763 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9766 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9769 return LISTVIEW_KillFocus(infoPtr);
9771 case WM_LBUTTONDBLCLK:
9772 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9774 case WM_LBUTTONDOWN:
9775 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9778 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9781 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9784 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9787 return LISTVIEW_NCDestroy(infoPtr);
9790 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9795 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9796 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9799 case WM_NOTIFYFORMAT:
9800 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9802 case WM_PRINTCLIENT:
9803 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9806 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9808 case WM_RBUTTONDBLCLK:
9809 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9811 case WM_RBUTTONDOWN:
9812 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9815 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9818 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9823 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9826 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9829 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9832 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9834 case WM_STYLECHANGED:
9835 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9837 case WM_SYSCOLORCHANGE:
9838 COMCTL32_RefreshSysColors();
9841 /* case WM_TIMER: */
9842 case WM_THEMECHANGED:
9843 return LISTVIEW_ThemeChanged(infoPtr);
9846 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9849 if (wParam & (MK_SHIFT | MK_CONTROL))
9850 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9851 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9853 case WM_WINDOWPOSCHANGED:
9854 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9856 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9857 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9858 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9860 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9862 MEASUREITEMSTRUCT mis;
9863 mis.CtlType = ODT_LISTVIEW;
9864 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9868 mis.itemHeight= infoPtr->nItemHeight;
9869 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9870 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9871 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9874 LISTVIEW_UpdateSize(infoPtr);
9875 LISTVIEW_UpdateScroll(infoPtr);
9877 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9879 /* case WM_WININICHANGE: */
9882 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9883 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
9886 /* call default window procedure */
9887 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9894 * Registers the window class.
9902 void LISTVIEW_Register(void)
9906 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9907 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9908 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9909 wndClass.cbClsExtra = 0;
9910 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9911 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9912 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9913 wndClass.lpszClassName = WC_LISTVIEWW;
9914 RegisterClassW(&wndClass);
9919 * Unregisters the window class.
9927 void LISTVIEW_Unregister(void)
9929 UnregisterClassW(WC_LISTVIEWW, NULL);
9934 * Handle any WM_COMMAND messages
9937 * [I] infoPtr : valid pointer to the listview structure
9938 * [I] wParam : the first message parameter
9939 * [I] lParam : the second message parameter
9944 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9946 switch (HIWORD(wParam))
9951 * Adjust the edit window size
9954 HDC hdc = GetDC(infoPtr->hwndEdit);
9955 HFONT hFont, hOldFont = 0;
9960 if (!infoPtr->hwndEdit || !hdc) return 0;
9961 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9962 GetWindowRect(infoPtr->hwndEdit, &rect);
9964 /* Select font to get the right dimension of the string */
9965 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9968 hOldFont = SelectObject(hdc, hFont);
9971 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9973 TEXTMETRICW textMetric;
9975 /* Add Extra spacing for the next character */
9976 GetTextMetricsW(hdc, &textMetric);
9977 sz.cx += (textMetric.tmMaxCharWidth * 2);
9985 rect.bottom - rect.top,
9986 SWP_DRAWFRAME|SWP_NOMOVE);
9989 SelectObject(hdc, hOldFont);
9991 ReleaseDC(infoPtr->hwndEdit, hdc);
9997 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10006 * Subclassed edit control windproc function
10009 * [I] hwnd : the edit window handle
10010 * [I] uMsg : the message that is to be processed
10011 * [I] wParam : first message parameter
10012 * [I] lParam : second message parameter
10013 * [I] isW : TRUE if input is Unicode
10018 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10020 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10021 BOOL cancel = FALSE;
10023 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10024 hwnd, uMsg, wParam, lParam, isW);
10028 case WM_GETDLGCODE:
10029 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10036 WNDPROC editProc = infoPtr->EditWndProc;
10037 infoPtr->EditWndProc = 0;
10038 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10039 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10043 if (VK_ESCAPE == (INT)wParam)
10048 else if (VK_RETURN == (INT)wParam)
10052 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10055 /* kill the edit */
10056 if (infoPtr->hwndEdit)
10058 LPWSTR buffer = NULL;
10060 infoPtr->hwndEdit = 0;
10063 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10067 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10069 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10070 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10074 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10079 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10085 * Subclassed edit control Unicode windproc function
10088 * [I] hwnd : the edit window handle
10089 * [I] uMsg : the message that is to be processed
10090 * [I] wParam : first message parameter
10091 * [I] lParam : second message parameter
10095 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10097 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10102 * Subclassed edit control ANSI windproc function
10105 * [I] hwnd : the edit window handle
10106 * [I] uMsg : the message that is to be processed
10107 * [I] wParam : first message parameter
10108 * [I] lParam : second message parameter
10112 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10114 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10119 * Creates a subclassed edit control
10122 * [I] infoPtr : valid pointer to the listview structure
10123 * [I] text : initial text for the edit
10124 * [I] style : the window style
10125 * [I] isW : TRUE if input is Unicode
10129 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10130 INT x, INT y, INT width, INT height, BOOL isW)
10132 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10137 TEXTMETRICW textMetric;
10138 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10140 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10142 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10143 hdc = GetDC(infoPtr->hwndSelf);
10145 /* Select the font to get appropriate metric dimensions */
10146 if(infoPtr->hFont != 0)
10147 hOldFont = SelectObject(hdc, infoPtr->hFont);
10149 /*Get String Length in pixels */
10150 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10152 /*Add Extra spacing for the next character */
10153 GetTextMetricsW(hdc, &textMetric);
10154 sz.cx += (textMetric.tmMaxCharWidth * 2);
10156 if(infoPtr->hFont != 0)
10157 SelectObject(hdc, hOldFont);
10159 ReleaseDC(infoPtr->hwndSelf, hdc);
10161 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10163 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10165 if (!hedit) return 0;
10167 infoPtr->EditWndProc = (WNDPROC)
10168 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10169 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10171 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);