4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
35 * Default Message Processing
36 * -- EN_KILLFOCUS should be handled in WM_COMMAND
37 * -- WM_CREATE: create the icon and small icon image lists at this point only if
38 * the LVS_SHAREIMAGELISTS style is not specified.
39 * -- WM_ERASEBKGND: forward this message to the parent window if the bkgnd
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_[GS]etColumnOrderArray stubs
58 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
59 * -- LISTVIEW_SetIconSpacing is incomplete
60 * -- LISTVIEW_SortItems is broken
61 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
64 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
65 * linear in the number of items in the list, and this is
66 * unacceptable for large lists.
67 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
68 * instead of inserting in the right spot
69 * -- we should keep an ordered array of coordinates in iconic mode
70 * this would allow to frame items (iterator_frameditems),
71 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
79 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
86 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
89 * -- LVS_TYPESTYLEMASK
92 * -- LVS_EX_BORDERSELECT
95 * -- LVS_EX_HEADERDRAGDROP
98 * -- LVS_EX_MULTIWORKAREAS
99 * -- LVS_EX_ONECLICKACTIVATE
101 * -- LVS_EX_SIMPLESELECT
102 * -- LVS_EX_TRACKSELECT
103 * -- LVS_EX_TWOCLICKACTIVATE
104 * -- LVS_EX_UNDERLINECOLD
105 * -- LVS_EX_UNDERLINEHOT
108 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
111 * -- LVN_MARQUEEBEGIN
118 * -- LVM_CANCELEDITLABEL
119 * -- LVM_ENABLEGROUPVIEW
120 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
121 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
122 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
123 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
124 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
125 * -- LVM_GETINSERTMARKRECT
126 * -- LVM_GETNUMBEROFWORKAREAS
127 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
128 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
129 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
130 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
131 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
132 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
133 * -- LVM_GETVIEW, LVM_SETVIEW
134 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
135 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
136 * -- LVM_INSERTGROUPSORTED
137 * -- LVM_INSERTMARKHITTEST
138 * -- LVM_ISGROUPVIEWENABLED
139 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
141 * -- LVM_MOVEITEMTOGROUP
143 * -- LVM_SETTILEWIDTH
148 * -- ListView_GetCheckSate, ListView_SetCheckState
149 * -- ListView_GetHoverTime, ListView_SetHoverTime
150 * -- ListView_GetISearchString
151 * -- ListView_GetNumberOfWorkAreas
152 * -- ListView_GetOrigin
153 * -- ListView_GetTextBkColor
154 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
155 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
156 * -- ListView_SortItemsEx
161 * Known differences in message stream from native control (not known if
162 * these differences cause problems):
163 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
164 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
165 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
166 * processing for "USEDOUBLECLICKTIME".
170 #include "wine/port.h"
185 #include "commctrl.h"
186 #include "comctl32.h"
189 #include "wine/debug.h"
190 #include "wine/unicode.h"
192 WINE_DEFAULT_DEBUG_CHANNEL(listview);
194 /* make sure you set this to 0 for production use! */
195 #define DEBUG_RANGES 1
197 typedef struct tagCOLUMN_INFO
199 RECT rcHeader; /* tracks the header's rectangle */
200 int fmt; /* same as LVCOLUMN.fmt */
203 typedef struct tagITEMHDR
207 } ITEMHDR, *LPITEMHDR;
209 typedef struct tagSUBITEM_INFO
215 typedef struct tagITEM_INFO
223 typedef struct tagRANGE
229 typedef struct tagRANGES
234 typedef struct tagITERATOR
243 typedef struct tagDELAYED_ITEM_EDIT
249 typedef struct tagLISTVIEW_INFO
256 HIMAGELIST himlNormal;
257 HIMAGELIST himlSmall;
258 HIMAGELIST himlState;
261 POINT ptClickPos; /* point where the user clicked */
262 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
265 RANGES selectionRanges;
270 RECT rcList; /* This rectangle is really the window
271 * client rectangle possibly reduced by the
272 * horizontal scroll bar and/or header - see
273 * LISTVIEW_UpdateSize. This rectangle offset
274 * by the LISTVIEW_GetOrigin value is in
275 * client coordinates */
284 INT ntmHeight; /* Some cached metrics of the font used */
285 INT ntmMaxCharWidth; /* by the listview to draw items */
287 BOOL bRedraw; /* Turns on/off repaints & invalidations */
288 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
290 BOOL bDoChangeNotify; /* send change notification messages? */
293 DWORD dwStyle; /* the cached window GWL_STYLE */
294 DWORD dwLvExStyle; /* extended listview style */
295 INT nItemCount; /* the number of items in the list */
296 HDPA hdpaItems; /* array ITEM_INFO pointers */
297 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
298 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
299 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
300 POINT currIconPos; /* this is the position next icon will be placed */
301 PFNLVCOMPARE pfnCompare;
309 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
311 DWORD lastKeyPressTimestamp;
313 INT nSearchParamLength;
314 WCHAR szSearchParam[ MAX_PATH ];
316 INT nMeasureItemHeight;
317 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
318 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
324 /* How many we debug buffer to allocate */
325 #define DEBUG_BUFFERS 20
326 /* The size of a single debug bbuffer */
327 #define DEBUG_BUFFER_SIZE 256
329 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
330 #define SB_INTERNAL -1
332 /* maximum size of a label */
333 #define DISP_TEXT_SIZE 512
335 /* padding for items in list and small icon display modes */
336 #define WIDTH_PADDING 12
338 /* padding for items in list, report and small icon display modes */
339 #define HEIGHT_PADDING 1
341 /* offset of items in report display mode */
342 #define REPORT_MARGINX 2
344 /* padding for icon in large icon display mode
345 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
346 * that HITTEST will see.
347 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
348 * ICON_TOP_PADDING - sum of the two above.
349 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
350 * LABEL_HOR_PADDING - between text and sides of box
351 * LABEL_VERT_PADDING - between bottom of text and end of box
353 * ICON_LR_PADDING - additional width above icon size.
354 * ICON_LR_HALF - half of the above value
356 #define ICON_TOP_PADDING_NOTHITABLE 2
357 #define ICON_TOP_PADDING_HITABLE 2
358 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
359 #define ICON_BOTTOM_PADDING 4
360 #define LABEL_HOR_PADDING 5
361 #define LABEL_VERT_PADDING 7
362 #define ICON_LR_PADDING 16
363 #define ICON_LR_HALF (ICON_LR_PADDING/2)
365 /* default label width for items in list and small icon display modes */
366 #define DEFAULT_LABEL_WIDTH 40
368 /* default column width for items in list display mode */
369 #define DEFAULT_COLUMN_WIDTH 128
371 /* Size of "line" scroll for V & H scrolls */
372 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
374 /* Padding betwen image and label */
375 #define IMAGE_PADDING 2
377 /* Padding behind the label */
378 #define TRAILING_LABEL_PADDING 12
379 #define TRAILING_HEADER_PADDING 11
381 /* Border for the icon caption */
382 #define CAPTION_BORDER 2
384 /* Standard DrawText flags */
385 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
386 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
387 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
389 /* Image index from state */
390 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
392 /* The time in milliseconds to reset the search in the list */
393 #define KEY_DELAY 450
395 /* Dump the LISTVIEW_INFO structure to the debug channel */
396 #define LISTVIEW_DUMP(iP) do { \
397 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
398 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
399 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
400 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
401 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
402 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
403 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
404 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
405 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
406 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
409 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
412 * forward declarations
414 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
415 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
416 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
417 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
418 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
419 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
420 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
421 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
422 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
423 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
424 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
425 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
426 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
427 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
428 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
429 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
430 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
431 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
432 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
433 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
434 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
435 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
436 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
437 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
438 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
439 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
440 static INT LISTVIEW_HitTest(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
442 /******** Text handling functions *************************************/
444 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
445 * text string. The string may be ANSI or Unicode, in which case
446 * the boolean isW tells us the type of the string.
448 * The name of the function tell what type of strings it expects:
449 * W: Unicode, T: ANSI/Unicode - function of isW
452 static inline BOOL is_textW(LPCWSTR text)
454 return text != NULL && text != LPSTR_TEXTCALLBACKW;
457 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
459 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
460 return is_textW(text);
463 static inline int textlenT(LPCWSTR text, BOOL isW)
465 return !is_textT(text, isW) ? 0 :
466 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
469 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
472 if (isSrcW) lstrcpynW(dest, src, max);
473 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
475 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
476 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
479 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
481 LPWSTR wstr = (LPWSTR)text;
483 if (!isW && is_textT(text, isW))
485 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
486 wstr = Alloc(len * sizeof(WCHAR));
487 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
489 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
493 static inline void textfreeT(LPWSTR wstr, BOOL isW)
495 if (!isW && is_textT(wstr, isW)) Free (wstr);
499 * dest is a pointer to a Unicode string
500 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
502 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
506 if (src == LPSTR_TEXTCALLBACKW)
508 if (is_textW(*dest)) Free(*dest);
509 *dest = LPSTR_TEXTCALLBACKW;
513 LPWSTR pszText = textdupTtoW(src, isW);
514 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
515 bResult = Str_SetPtrW(dest, pszText);
516 textfreeT(pszText, isW);
522 * compares a Unicode to a Unicode/ANSI text string
524 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
526 if (!aw) return bt ? -1 : 0;
527 if (!bt) return aw ? 1 : 0;
528 if (aw == LPSTR_TEXTCALLBACKW)
529 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
530 if (bt != LPSTR_TEXTCALLBACKW)
532 LPWSTR bw = textdupTtoW(bt, isW);
533 int r = bw ? lstrcmpW(aw, bw) : 1;
541 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
545 n = min(min(n, strlenW(s1)), strlenW(s2));
546 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
547 return res ? res - sizeof(WCHAR) : res;
550 /******** Debugging functions *****************************************/
552 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
554 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
555 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
558 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
560 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
561 n = min(textlenT(text, isW), n);
562 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
565 static char* debug_getbuf(void)
567 static int index = 0;
568 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
569 return buffers[index++ % DEBUG_BUFFERS];
572 static inline const char* debugrange(const RANGE *lprng)
574 if (!lprng) return "(null)";
575 return wine_dbg_sprintf("[%d, %d)", lprng->lower, lprng->upper);
578 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
580 char* buf = debug_getbuf(), *text = buf;
581 int len, size = DEBUG_BUFFER_SIZE;
583 if (pScrollInfo == NULL) return "(null)";
584 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
585 if (len == -1) goto end; buf += len; size -= len;
586 if (pScrollInfo->fMask & SIF_RANGE)
587 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
589 if (len == -1) goto end; buf += len; size -= len;
590 if (pScrollInfo->fMask & SIF_PAGE)
591 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
593 if (len == -1) goto end; buf += len; size -= len;
594 if (pScrollInfo->fMask & SIF_POS)
595 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
597 if (len == -1) goto end; buf += len; size -= len;
598 if (pScrollInfo->fMask & SIF_TRACKPOS)
599 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
601 if (len == -1) goto end; buf += len; size -= len;
604 buf = text + strlen(text);
606 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
610 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
612 if (!plvnm) return "(null)";
613 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
614 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
615 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
616 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
619 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
621 char* buf = debug_getbuf(), *text = buf;
622 int len, size = DEBUG_BUFFER_SIZE;
624 if (lpLVItem == NULL) return "(null)";
625 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
626 if (len == -1) goto end; buf += len; size -= len;
627 if (lpLVItem->mask & LVIF_STATE)
628 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
630 if (len == -1) goto end; buf += len; size -= len;
631 if (lpLVItem->mask & LVIF_TEXT)
632 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
634 if (len == -1) goto end; buf += len; size -= len;
635 if (lpLVItem->mask & LVIF_IMAGE)
636 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
638 if (len == -1) goto end; buf += len; size -= len;
639 if (lpLVItem->mask & LVIF_PARAM)
640 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
642 if (len == -1) goto end; buf += len; size -= len;
643 if (lpLVItem->mask & LVIF_INDENT)
644 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
646 if (len == -1) goto end; buf += len; size -= len;
649 buf = text + strlen(text);
651 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
655 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
657 char* buf = debug_getbuf(), *text = buf;
658 int len, size = DEBUG_BUFFER_SIZE;
660 if (lpColumn == NULL) return "(null)";
661 len = snprintf(buf, size, "{");
662 if (len == -1) goto end; buf += len; size -= len;
663 if (lpColumn->mask & LVCF_SUBITEM)
664 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
666 if (len == -1) goto end; buf += len; size -= len;
667 if (lpColumn->mask & LVCF_FMT)
668 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
670 if (len == -1) goto end; buf += len; size -= len;
671 if (lpColumn->mask & LVCF_WIDTH)
672 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
674 if (len == -1) goto end; buf += len; size -= len;
675 if (lpColumn->mask & LVCF_TEXT)
676 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
678 if (len == -1) goto end; buf += len; size -= len;
679 if (lpColumn->mask & LVCF_IMAGE)
680 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
682 if (len == -1) goto end; buf += len; size -= len;
683 if (lpColumn->mask & LVCF_ORDER)
684 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
686 if (len == -1) goto end; buf += len; size -= len;
689 buf = text + strlen(text);
691 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
695 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
697 if (!lpht) return "(null)";
699 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
700 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
703 /* Return the corresponding text for a given scroll value */
704 static inline LPCSTR debugscrollcode(int nScrollCode)
708 case SB_LINELEFT: return "SB_LINELEFT";
709 case SB_LINERIGHT: return "SB_LINERIGHT";
710 case SB_PAGELEFT: return "SB_PAGELEFT";
711 case SB_PAGERIGHT: return "SB_PAGERIGHT";
712 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
713 case SB_THUMBTRACK: return "SB_THUMBTRACK";
714 case SB_ENDSCROLL: return "SB_ENDSCROLL";
715 case SB_INTERNAL: return "SB_INTERNAL";
716 default: return "unknown";
721 /******** Notification functions i************************************/
723 static LRESULT notify_forward_header(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
725 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
726 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
729 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
733 TRACE("(code=%d)\n", code);
735 pnmh->hwndFrom = infoPtr->hwndSelf;
736 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
738 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
739 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
741 TRACE(" <= %ld\n", result);
746 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code)
749 HWND hwnd = infoPtr->hwndSelf;
750 notify_hdr(infoPtr, code, &nmh);
751 return IsWindow(hwnd);
754 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
765 item.mask = LVIF_PARAM|LVIF_STATE;
766 item.iItem = htInfo->iItem;
768 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
769 nmia.lParam = item.lParam;
770 nmia.uOldState = item.state;
771 nmia.uNewState = item.state | LVIS_ACTIVATING;
772 nmia.uChanged = LVIF_STATE;
775 nmia.iItem = htInfo->iItem;
776 nmia.iSubItem = htInfo->iSubItem;
777 nmia.ptAction = htInfo->pt;
779 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
780 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
781 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
783 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
786 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
788 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
789 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
792 static BOOL notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
796 HWND hwnd = infoPtr->hwndSelf;
798 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
799 ZeroMemory(&nmlv, sizeof(nmlv));
800 nmlv.iItem = lvht->iItem;
801 nmlv.iSubItem = lvht->iSubItem;
802 nmlv.ptAction = lvht->pt;
803 item.mask = LVIF_PARAM;
804 item.iItem = lvht->iItem;
806 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
807 notify_listview(infoPtr, code, &nmlv);
808 return IsWindow(hwnd);
811 static BOOL notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
815 HWND hwnd = infoPtr->hwndSelf;
817 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
819 item.mask = LVIF_PARAM;
822 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
823 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
824 return IsWindow(hwnd);
827 static int get_ansi_notification(INT unicodeNotificationCode)
829 switch (unicodeNotificationCode)
831 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
832 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
833 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
834 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
835 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
836 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
838 ERR("unknown notification %x\n", unicodeNotificationCode);
844 Send notification. depends on dispinfoW having same
845 structure as dispinfoA.
846 infoPtr : listview struct
847 notificationCode : *Unicode* notification code
848 pdi : dispinfo structure (can be unicode or ansi)
849 isW : TRUE if dispinfo is Unicode
851 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
853 BOOL bResult = FALSE;
854 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
855 INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
856 LPWSTR pszTempBuf = NULL, savPszText = NULL;
858 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
860 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
861 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
864 if (convertToAnsi || convertToUnicode)
866 if (notificationCode != LVN_GETDISPINFOW)
868 cchTempBufMax = convertToUnicode ?
869 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
870 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
874 cchTempBufMax = pdi->item.cchTextMax;
875 *pdi->item.pszText = 0; /* make sure we don't process garbage */
878 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
879 if (!pszTempBuf) return FALSE;
881 if (convertToUnicode)
882 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
883 pszTempBuf, cchTempBufMax);
885 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
886 cchTempBufMax, NULL, NULL);
888 savCchTextMax = pdi->item.cchTextMax;
889 savPszText = pdi->item.pszText;
890 pdi->item.pszText = pszTempBuf;
891 pdi->item.cchTextMax = cchTempBufMax;
894 if (infoPtr->notifyFormat == NFR_ANSI)
895 realNotifCode = get_ansi_notification(notificationCode);
897 realNotifCode = notificationCode;
898 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
899 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
901 if (convertToUnicode || convertToAnsi)
903 if (convertToUnicode) /* note : pointer can be changed by app ! */
904 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
905 savCchTextMax, NULL, NULL);
907 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
908 savPszText, savCchTextMax);
909 pdi->item.pszText = savPszText; /* restores our buffer */
910 pdi->item.cchTextMax = savCchTextMax;
916 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
917 const RECT *rcBounds, const LVITEMW *lplvItem)
919 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
920 lpnmlvcd->nmcd.hdc = hdc;
921 lpnmlvcd->nmcd.rc = *rcBounds;
922 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
923 lpnmlvcd->clrText = infoPtr->clrText;
924 if (!lplvItem) return;
925 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
926 lpnmlvcd->iSubItem = lplvItem->iSubItem;
927 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
928 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
929 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
930 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
933 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
935 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
938 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
939 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
940 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
941 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
942 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
943 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
947 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
949 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
950 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
951 if (lpnmlvcd->clrText == CLR_DEFAULT)
952 lpnmlvcd->clrText = comctl32_color.clrWindowText;
954 /* apprently, for selected items, we have to override the returned values */
955 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
959 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
960 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
962 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
964 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
965 lpnmlvcd->clrText = comctl32_color.clrBtnText;
969 /* Set the text attributes */
970 if (lpnmlvcd->clrTextBk != CLR_NONE)
972 SetBkMode(hdc, OPAQUE);
973 SetBkColor(hdc,lpnmlvcd->clrTextBk);
976 SetBkMode(hdc, TRANSPARENT);
977 SetTextColor(hdc, lpnmlvcd->clrText);
980 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
982 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
985 /******** Item iterator functions **********************************/
987 static RANGES ranges_create(int count);
988 static void ranges_destroy(RANGES ranges);
989 static BOOL ranges_add(RANGES ranges, RANGE range);
990 static BOOL ranges_del(RANGES ranges, RANGE range);
991 static void ranges_dump(RANGES ranges);
993 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
995 RANGE range = { nItem, nItem + 1 };
997 return ranges_add(ranges, range);
1000 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1002 RANGE range = { nItem, nItem + 1 };
1004 return ranges_del(ranges, range);
1008 * ITERATOR DOCUMENTATION
1010 * The iterator functions allow for easy, and convenient iteration
1011 * over items of iterest in the list. Typically, you create a
1012 * iterator, use it, and destroy it, as such:
1015 * iterator_xxxitems(&i, ...);
1016 * while (iterator_{prev,next}(&i)
1018 * //code which uses i.nItem
1020 * iterator_destroy(&i);
1022 * where xxx is either: framed, or visible.
1023 * Note that it is important that the code destroys the iterator
1024 * after it's done with it, as the creation of the iterator may
1025 * allocate memory, which thus needs to be freed.
1027 * You can iterate both forwards, and backwards through the list,
1028 * by using iterator_next or iterator_prev respectively.
1030 * Lower numbered items are draw on top of higher number items in
1031 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1032 * items may overlap). So, to test items, you should use
1034 * which lists the items top to bottom (in Z-order).
1035 * For drawing items, you should use
1037 * which lists the items bottom to top (in Z-order).
1038 * If you keep iterating over the items after the end-of-items
1039 * marker (-1) is returned, the iterator will start from the
1040 * beginning. Typically, you don't need to test for -1,
1041 * because iterator_{next,prev} will return TRUE if more items
1042 * are to be iterated over, or FALSE otherwise.
1044 * Note: the iterator is defined to be bidirectional. That is,
1045 * any number of prev followed by any number of next, or
1046 * five versa, should leave the iterator at the same item:
1047 * prev * n, next * n = next * n, prev * n
1049 * The iterator has a notion of an out-of-order, special item,
1050 * which sits at the start of the list. This is used in
1051 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1052 * which needs to be first, as it may overlap other items.
1054 * The code is a bit messy because we have:
1055 * - a special item to deal with
1056 * - simple range, or composite range
1058 * If you find bugs, or want to add features, please make sure you
1059 * always check/modify *both* iterator_prev, and iterator_next.
1063 * This function iterates through the items in increasing order,
1064 * but prefixed by the special item, then -1. That is:
1065 * special, 1, 2, 3, ..., n, -1.
1066 * Each item is listed only once.
1068 static inline BOOL iterator_next(ITERATOR* i)
1072 i->nItem = i->nSpecial;
1073 if (i->nItem != -1) return TRUE;
1075 if (i->nItem == i->nSpecial)
1077 if (i->ranges) i->index = 0;
1083 if (i->nItem == i->nSpecial) i->nItem++;
1084 if (i->nItem < i->range.upper) return TRUE;
1089 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1090 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1093 else if (i->nItem >= i->range.upper) goto end;
1095 i->nItem = i->range.lower;
1096 if (i->nItem >= 0) goto testitem;
1103 * This function iterates through the items in decreasing order,
1104 * followed by the special item, then -1. That is:
1105 * n, n-1, ..., 3, 2, 1, special, -1.
1106 * Each item is listed only once.
1108 static inline BOOL iterator_prev(ITERATOR* i)
1115 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1118 if (i->nItem == i->nSpecial)
1126 if (i->nItem == i->nSpecial) i->nItem--;
1127 if (i->nItem >= i->range.lower) return TRUE;
1133 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1136 else if (!start && i->nItem < i->range.lower) goto end;
1138 i->nItem = i->range.upper;
1139 if (i->nItem > 0) goto testitem;
1141 return (i->nItem = i->nSpecial) != -1;
1144 static RANGE iterator_range(ITERATOR* i)
1148 if (!i->ranges) return i->range;
1150 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1152 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1153 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1155 else range.lower = range.upper = 0;
1161 * Releases resources associated with this ierator.
1163 static inline void iterator_destroy(ITERATOR* i)
1165 ranges_destroy(i->ranges);
1169 * Create an empty iterator.
1171 static inline BOOL iterator_empty(ITERATOR* i)
1173 ZeroMemory(i, sizeof(*i));
1174 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1179 * Create an iterator over a range.
1181 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1189 * Create an iterator over a bunch of ranges.
1190 * Please note that the iterator will take ownership of the ranges,
1191 * and will free them upon destruction.
1193 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1201 * Creates an iterator over the items which intersect lprc.
1203 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1205 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1206 RECT frame = *lprc, rcItem, rcTemp;
1209 /* in case we fail, we want to return an empty iterator */
1210 if (!iterator_empty(i)) return FALSE;
1212 LISTVIEW_GetOrigin(infoPtr, &Origin);
1214 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1215 OffsetRect(&frame, -Origin.x, -Origin.y);
1217 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1221 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1223 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1224 if (IntersectRect(&rcTemp, &rcItem, lprc))
1225 i->nSpecial = infoPtr->nFocusedItem;
1227 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1228 /* to do better here, we need to have PosX, and PosY sorted */
1229 TRACE("building icon ranges:\n");
1230 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1232 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1233 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1234 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1235 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1236 if (IntersectRect(&rcTemp, &rcItem, &frame))
1237 ranges_additem(i->ranges, nItem);
1241 else if (uView == LVS_REPORT)
1245 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1246 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1248 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1249 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1250 if (range.upper <= range.lower) return TRUE;
1251 if (!iterator_rangeitems(i, range)) return FALSE;
1252 TRACE(" report=%s\n", debugrange(&i->range));
1256 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1257 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1258 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1259 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1260 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1261 INT lower = nFirstCol * nPerCol + nFirstRow;
1265 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1266 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1268 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1270 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1271 TRACE("building list ranges:\n");
1272 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1274 item_range.lower = nCol * nPerCol + nFirstRow;
1275 if(item_range.lower >= infoPtr->nItemCount) break;
1276 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1277 TRACE(" list=%s\n", debugrange(&item_range));
1278 ranges_add(i->ranges, item_range);
1286 * Creates an iterator over the items which intersect the visible region of hdc.
1288 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1290 POINT Origin, Position;
1291 RECT rcItem, rcClip;
1294 rgntype = GetClipBox(hdc, &rcClip);
1295 if (rgntype == NULLREGION) return iterator_empty(i);
1296 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1297 if (rgntype == SIMPLEREGION) return TRUE;
1299 /* first deal with the special item */
1300 if (i->nSpecial != -1)
1302 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1303 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1306 /* if we can't deal with the region, we'll just go with the simple range */
1307 LISTVIEW_GetOrigin(infoPtr, &Origin);
1308 TRACE("building visible range:\n");
1309 if (!i->ranges && i->range.lower < i->range.upper)
1311 if (!(i->ranges = ranges_create(50))) return TRUE;
1312 if (!ranges_add(i->ranges, i->range))
1314 ranges_destroy(i->ranges);
1320 /* now delete the invisible items from the list */
1321 while(iterator_next(i))
1323 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1324 rcItem.left = Position.x + Origin.x;
1325 rcItem.top = Position.y + Origin.y;
1326 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1327 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1328 if (!RectVisible(hdc, &rcItem))
1329 ranges_delitem(i->ranges, i->nItem);
1331 /* the iterator should restart on the next iterator_next */
1337 /******** Misc helper functions ************************************/
1339 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1340 WPARAM wParam, LPARAM lParam, BOOL isW)
1342 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1343 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1346 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1348 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1350 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1351 (uView == LVS_ICON || uView == LVS_SMALLICON);
1354 /******** Internal API functions ************************************/
1356 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1358 static COLUMN_INFO mainItem;
1360 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1361 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1362 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1365 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1367 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1370 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1372 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1375 /* Listview invalidation functions: use _only_ these functions to invalidate */
1377 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1379 return infoPtr->bRedraw;
1382 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1384 if(!is_redrawing(infoPtr)) return;
1385 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1386 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1389 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1393 if(!is_redrawing(infoPtr)) return;
1394 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1395 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1398 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1400 POINT Origin, Position;
1403 if(!is_redrawing(infoPtr)) return;
1404 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1405 LISTVIEW_GetOrigin(infoPtr, &Origin);
1406 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1407 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1409 rcBox.bottom = infoPtr->nItemHeight;
1410 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1411 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1414 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1416 LISTVIEW_InvalidateRect(infoPtr, NULL);
1419 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1423 if(!is_redrawing(infoPtr)) return;
1424 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1425 rcCol.top = infoPtr->rcList.top;
1426 rcCol.bottom = infoPtr->rcList.bottom;
1427 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1432 * Retrieves the number of items that can fit vertically in the client area.
1435 * [I] infoPtr : valid pointer to the listview structure
1438 * Number of items per row.
1440 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1442 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1444 return max(nListWidth/infoPtr->nItemWidth, 1);
1449 * Retrieves the number of items that can fit horizontally in the client
1453 * [I] infoPtr : valid pointer to the listview structure
1456 * Number of items per column.
1458 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1460 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1462 return max(nListHeight / infoPtr->nItemHeight, 1);
1466 /*************************************************************************
1467 * LISTVIEW_ProcessLetterKeys
1469 * Processes keyboard messages generated by pressing the letter keys
1471 * What this does is perform a case insensitive search from the
1472 * current position with the following quirks:
1473 * - If two chars or more are pressed in quick succession we search
1474 * for the corresponding string (e.g. 'abc').
1475 * - If there is a delay we wipe away the current search string and
1476 * restart with just that char.
1477 * - If the user keeps pressing the same character, whether slowly or
1478 * fast, so that the search string is entirely composed of this
1479 * character ('aaaaa' for instance), then we search for first item
1480 * that starting with that character.
1481 * - If the user types the above character in quick succession, then
1482 * we must also search for the corresponding string ('aaaaa'), and
1483 * go to that string if there is a match.
1486 * [I] hwnd : handle to the window
1487 * [I] charCode : the character code, the actual character
1488 * [I] keyData : key data
1496 * - The current implementation has a list of characters it will
1497 * accept and it ignores averything else. In particular it will
1498 * ignore accentuated characters which seems to match what
1499 * Windows does. But I'm not sure it makes sense to follow
1501 * - We don't sound a beep when the search fails.
1505 * TREEVIEW_ProcessLetterKeys
1507 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1512 WCHAR buffer[MAX_PATH];
1513 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1515 /* simple parameter checking */
1516 if (!charCode || !keyData) return 0;
1518 /* only allow the valid WM_CHARs through */
1519 if (!isalnum(charCode) &&
1520 charCode != '.' && charCode != '`' && charCode != '!' &&
1521 charCode != '@' && charCode != '#' && charCode != '$' &&
1522 charCode != '%' && charCode != '^' && charCode != '&' &&
1523 charCode != '*' && charCode != '(' && charCode != ')' &&
1524 charCode != '-' && charCode != '_' && charCode != '+' &&
1525 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1526 charCode != '}' && charCode != '[' && charCode != '{' &&
1527 charCode != '/' && charCode != '?' && charCode != '>' &&
1528 charCode != '<' && charCode != ',' && charCode != '~')
1531 /* if there's one item or less, there is no where to go */
1532 if (infoPtr->nItemCount <= 1) return 0;
1534 /* update the search parameters */
1535 infoPtr->lastKeyPressTimestamp = GetTickCount();
1536 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1537 if (infoPtr->nSearchParamLength < MAX_PATH)
1538 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1539 if (infoPtr->charCode != charCode)
1540 infoPtr->charCode = charCode = 0;
1542 infoPtr->charCode=charCode;
1543 infoPtr->szSearchParam[0]=charCode;
1544 infoPtr->nSearchParamLength=1;
1545 /* Redundant with the 1 char string */
1549 /* and search from the current position */
1551 if (infoPtr->nFocusedItem >= 0) {
1552 endidx=infoPtr->nFocusedItem;
1554 /* if looking for single character match,
1555 * then we must always move forward
1557 if (infoPtr->nSearchParamLength == 1)
1560 endidx=infoPtr->nItemCount;
1564 if (idx == infoPtr->nItemCount) {
1565 if (endidx == infoPtr->nItemCount || endidx == 0)
1571 item.mask = LVIF_TEXT;
1574 item.pszText = buffer;
1575 item.cchTextMax = MAX_PATH;
1576 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1578 /* check for a match */
1579 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1582 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1583 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1584 /* This would work but we must keep looking for a longer match */
1588 } while (idx != endidx);
1591 LISTVIEW_KeySelection(infoPtr, nItem);
1596 /*************************************************************************
1597 * LISTVIEW_UpdateHeaderSize [Internal]
1599 * Function to resize the header control
1602 * [I] hwnd : handle to a window
1603 * [I] nNewScrollPos : scroll pos to set
1608 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1613 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1615 GetWindowRect(infoPtr->hwndHeader, &winRect);
1616 point[0].x = winRect.left;
1617 point[0].y = winRect.top;
1618 point[1].x = winRect.right;
1619 point[1].y = winRect.bottom;
1621 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1622 point[0].x = -nNewScrollPos;
1623 point[1].x += nNewScrollPos;
1625 SetWindowPos(infoPtr->hwndHeader,0,
1626 point[0].x,point[0].y,point[1].x,point[1].y,
1627 SWP_NOZORDER | SWP_NOACTIVATE);
1632 * Update the scrollbars. This functions should be called whenever
1633 * the content, size or view changes.
1636 * [I] infoPtr : valid pointer to the listview structure
1641 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1643 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1644 SCROLLINFO horzInfo, vertInfo;
1647 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1649 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1650 horzInfo.cbSize = sizeof(SCROLLINFO);
1651 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1653 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1654 if (uView == LVS_LIST)
1656 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1657 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1659 /* scroll by at least one column per page */
1660 if(horzInfo.nPage < infoPtr->nItemWidth)
1661 horzInfo.nPage = infoPtr->nItemWidth;
1663 horzInfo.nPage /= infoPtr->nItemWidth;
1665 else if (uView == LVS_REPORT)
1667 horzInfo.nMax = infoPtr->nItemWidth;
1669 else /* LVS_ICON, or LVS_SMALLICON */
1673 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1676 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1677 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1678 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1679 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1680 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1682 /* Setting the horizontal scroll can change the listview size
1683 * (and potentially everything else) so we need to recompute
1684 * everything again for the vertical scroll
1687 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1688 vertInfo.cbSize = sizeof(SCROLLINFO);
1689 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1691 if (uView == LVS_REPORT)
1693 vertInfo.nMax = infoPtr->nItemCount;
1695 /* scroll by at least one page */
1696 if(vertInfo.nPage < infoPtr->nItemHeight)
1697 vertInfo.nPage = infoPtr->nItemHeight;
1699 vertInfo.nPage /= infoPtr->nItemHeight;
1701 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1705 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1708 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1709 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1710 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1711 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1712 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1714 /* Change of the range may have changed the scroll pos. If so move the content */
1715 if (dx != 0 || dy != 0)
1718 listRect = infoPtr->rcList;
1719 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1720 SW_ERASE | SW_INVALIDATE);
1723 /* Update the Header Control */
1724 if (uView == LVS_REPORT)
1726 horzInfo.fMask = SIF_POS;
1727 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1728 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1735 * Shows/hides the focus rectangle.
1738 * [I] infoPtr : valid pointer to the listview structure
1739 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1744 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1746 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1749 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1751 if (infoPtr->nFocusedItem < 0) return;
1753 /* we need some gymnastics in ICON mode to handle large items */
1754 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1758 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1759 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1761 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1766 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1768 /* for some reason, owner draw should work only in report mode */
1769 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1774 item.iItem = infoPtr->nFocusedItem;
1776 item.mask = LVIF_PARAM;
1777 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1779 ZeroMemory(&dis, sizeof(dis));
1780 dis.CtlType = ODT_LISTVIEW;
1781 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1782 dis.itemID = item.iItem;
1783 dis.itemAction = ODA_FOCUS;
1784 if (fShow) dis.itemState |= ODS_FOCUS;
1785 dis.hwndItem = infoPtr->hwndSelf;
1787 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1788 dis.itemData = item.lParam;
1790 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1794 DrawFocusRect(hdc, &infoPtr->rcFocus);
1797 ReleaseDC(infoPtr->hwndSelf, hdc);
1801 * Invalidates all visible selected items.
1803 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1807 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1808 while(iterator_next(&i))
1810 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1811 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1813 iterator_destroy(&i);
1818 * DESCRIPTION: [INTERNAL]
1819 * Computes an item's (left,top) corner, relative to rcView.
1820 * That is, the position has NOT been made relative to the Origin.
1821 * This is deliberate, to avoid computing the Origin over, and
1822 * over again, when this function is call in a loop. Instead,
1823 * one ca factor the computation of the Origin before the loop,
1824 * and offset the value retured by this function, on every iteration.
1827 * [I] infoPtr : valid pointer to the listview structure
1828 * [I] nItem : item number
1829 * [O] lpptOrig : item top, left corner
1834 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1836 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1838 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1840 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1842 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1843 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1845 else if (uView == LVS_LIST)
1847 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1848 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1849 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1851 else /* LVS_REPORT */
1853 lpptPosition->x = 0;
1854 lpptPosition->y = nItem * infoPtr->nItemHeight;
1859 * DESCRIPTION: [INTERNAL]
1860 * Compute the rectangles of an item. This is to localize all
1861 * the computations in one place. If you are not interested in some
1862 * of these values, simply pass in a NULL -- the fucntion is smart
1863 * enough to compute only what's necessary. The function computes
1864 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1865 * one, the BOX rectangle. This rectangle is very cheap to compute,
1866 * and is guaranteed to contain all the other rectangles. Computing
1867 * the ICON rect is also cheap, but all the others are potentaily
1868 * expensive. This gives an easy and effective optimization when
1869 * searching (like point inclusion, or rectangle intersection):
1870 * first test against the BOX, and if TRUE, test agains the desired
1872 * If the function does not have all the necessary information
1873 * to computed the requested rectangles, will crash with a
1874 * failed assertion. This is done so we catch all programming
1875 * errors, given that the function is called only from our code.
1877 * We have the following 'special' meanings for a few fields:
1878 * * If LVIS_FOCUSED is set, we assume the item has the focus
1879 * This is important in ICON mode, where it might get a larger
1880 * then usual rectange
1882 * Please note that subitem support works only in REPORT mode.
1885 * [I] infoPtr : valid pointer to the listview structure
1886 * [I] lpLVItem : item to compute the measures for
1887 * [O] lprcBox : ptr to Box rectangle
1888 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1889 * [0] lprcSelectBox : ptr to State icon rectangle
1890 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1891 * [O] lprcIcon : ptr to Icon rectangle
1892 * Same as LVM_GETITEMRECT with LVIR_ICON
1893 * [O] lprcLabel : ptr to Label rectangle
1894 * Same as LVM_GETITEMRECT with LVIR_LABEL
1899 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1900 LPRECT lprcBox, LPRECT lprcSelectBox,
1901 LPRECT lprcIcon, LPRECT lprcLabel)
1903 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1904 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1905 RECT Box, SelectBox, Icon, Label;
1906 COLUMN_INFO *lpColumnInfo = NULL;
1907 SIZE labelSize = { 0, 0 };
1909 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1911 /* Be smart and try to figure out the minimum we have to do */
1912 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1913 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1915 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1916 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1918 if (lprcSelectBox) doSelectBox = TRUE;
1919 if (lprcLabel) doLabel = TRUE;
1920 if (doLabel || lprcIcon) doIcon = TRUE;
1927 /************************************************************/
1928 /* compute the box rectangle (it should be cheap to do) */
1929 /************************************************************/
1930 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1931 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1933 if (lpLVItem->iSubItem)
1935 Box = lpColumnInfo->rcHeader;
1940 Box.right = infoPtr->nItemWidth;
1943 Box.bottom = infoPtr->nItemHeight;
1945 /************************************************************/
1946 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1947 /************************************************************/
1950 LONG state_width = 0;
1952 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1953 state_width = infoPtr->iconStateSize.cx;
1955 if (uView == LVS_ICON)
1957 Icon.left = Box.left + state_width;
1958 if (infoPtr->himlNormal)
1959 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
1960 Icon.top = Box.top + ICON_TOP_PADDING;
1961 Icon.right = Icon.left;
1962 Icon.bottom = Icon.top;
1963 if (infoPtr->himlNormal)
1965 Icon.right += infoPtr->iconSize.cx;
1966 Icon.bottom += infoPtr->iconSize.cy;
1969 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1971 Icon.left = Box.left + state_width;
1973 if (uView == LVS_REPORT)
1974 Icon.left += REPORT_MARGINX;
1977 Icon.right = Icon.left;
1978 if (infoPtr->himlSmall &&
1979 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1980 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1981 Icon.right += infoPtr->iconSize.cx;
1982 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1984 if(lprcIcon) *lprcIcon = Icon;
1985 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
1987 else Icon.right = 0;
1989 /************************************************************/
1990 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1991 /************************************************************/
1994 /* calculate how far to the right can the label strech */
1995 Label.right = Box.right;
1996 if (uView == LVS_REPORT)
1998 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2001 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2003 labelSize.cx = infoPtr->nItemWidth;
2004 labelSize.cy = infoPtr->nItemHeight;
2008 /* we need the text in non owner draw mode */
2009 assert(lpLVItem->mask & LVIF_TEXT);
2010 if (is_textT(lpLVItem->pszText, TRUE))
2012 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2013 HDC hdc = GetDC(infoPtr->hwndSelf);
2014 HFONT hOldFont = SelectObject(hdc, hFont);
2018 /* compute rough rectangle where the label will go */
2019 SetRectEmpty(&rcText);
2020 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2021 rcText.bottom = infoPtr->nItemHeight;
2022 if (uView == LVS_ICON)
2023 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2025 /* now figure out the flags */
2026 if (uView == LVS_ICON)
2027 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2029 uFormat = LV_SL_DT_FLAGS;
2031 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2033 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2034 labelSize.cy = rcText.bottom - rcText.top;
2036 SelectObject(hdc, hOldFont);
2037 ReleaseDC(infoPtr->hwndSelf, hdc);
2041 if (uView == LVS_ICON)
2043 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2044 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2045 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2046 Label.right = Label.left + labelSize.cx;
2047 Label.bottom = Label.top + infoPtr->nItemHeight;
2048 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2050 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2051 labelSize.cy /= infoPtr->ntmHeight;
2052 labelSize.cy = max(labelSize.cy, 1);
2053 labelSize.cy *= infoPtr->ntmHeight;
2055 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2057 else if (uView == LVS_REPORT)
2059 Label.left = Icon.right;
2060 Label.top = Box.top;
2061 Label.right = lpColumnInfo->rcHeader.right;
2062 Label.bottom = Label.top + infoPtr->nItemHeight;
2064 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2066 Label.left = Icon.right;
2067 Label.top = Box.top;
2068 Label.right = min(Label.left + labelSize.cx, Label.right);
2069 Label.bottom = Label.top + infoPtr->nItemHeight;
2072 if (lprcLabel) *lprcLabel = Label;
2073 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2076 /************************************************************/
2077 /* compute STATEICON bounding box */
2078 /************************************************************/
2081 if (uView == LVS_REPORT)
2083 SelectBox.left = Icon.right; /* FIXME: should be Icon.left */
2084 SelectBox.top = Box.top;
2085 SelectBox.bottom = Box.bottom;
2086 if (lpLVItem->iSubItem == 0)
2088 /* we need the indent in report mode */
2089 assert(lpLVItem->mask & LVIF_INDENT);
2090 SelectBox.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
2092 SelectBox.right = min(SelectBox.left + labelSize.cx, Label.right);
2096 UnionRect(&SelectBox, &Icon, &Label);
2098 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2099 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2102 /* Fix the Box if necessary */
2105 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2106 else *lprcBox = Box;
2108 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2112 * DESCRIPTION: [INTERNAL]
2115 * [I] infoPtr : valid pointer to the listview structure
2116 * [I] nItem : item number
2117 * [O] lprcBox : ptr to Box rectangle
2122 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2124 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2125 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2126 POINT Position, Origin;
2129 LISTVIEW_GetOrigin(infoPtr, &Origin);
2130 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2132 /* Be smart and try to figure out the minimum we have to do */
2134 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2135 lvItem.mask |= LVIF_TEXT;
2136 lvItem.iItem = nItem;
2137 lvItem.iSubItem = 0;
2138 lvItem.pszText = szDispText;
2139 lvItem.cchTextMax = DISP_TEXT_SIZE;
2140 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2141 if (uView == LVS_ICON)
2143 lvItem.mask |= LVIF_STATE;
2144 lvItem.stateMask = LVIS_FOCUSED;
2145 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2147 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2149 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2155 * Returns the current icon position, and advances it along the top.
2156 * The returned position is not offset by Origin.
2159 * [I] infoPtr : valid pointer to the listview structure
2160 * [O] lpPos : will get the current icon position
2165 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2167 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2169 *lpPos = infoPtr->currIconPos;
2171 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2172 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2174 infoPtr->currIconPos.x = 0;
2175 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2181 * Returns the current icon position, and advances it down the left edge.
2182 * The returned position is not offset by Origin.
2185 * [I] infoPtr : valid pointer to the listview structure
2186 * [O] lpPos : will get the current icon position
2191 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2193 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2195 *lpPos = infoPtr->currIconPos;
2197 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2198 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2200 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2201 infoPtr->currIconPos.y = 0;
2207 * Moves an icon to the specified position.
2208 * It takes care of invalidating the item, etc.
2211 * [I] infoPtr : valid pointer to the listview structure
2212 * [I] nItem : the item to move
2213 * [I] lpPos : the new icon position
2214 * [I] isNew : flags the item as being new
2220 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2226 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2227 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2229 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2230 LISTVIEW_InvalidateItem(infoPtr, nItem);
2233 /* Allocating a POINTER for every item is too resource intensive,
2234 * so we'll keep the (x,y) in different arrays */
2235 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2236 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2238 LISTVIEW_InvalidateItem(infoPtr, nItem);
2245 * Arranges listview items in icon display mode.
2248 * [I] infoPtr : valid pointer to the listview structure
2249 * [I] nAlignCode : alignment code
2255 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2257 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2258 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2262 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2264 TRACE("nAlignCode=%d\n", nAlignCode);
2266 if (nAlignCode == LVA_DEFAULT)
2268 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2269 else nAlignCode = LVA_ALIGNTOP;
2274 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2275 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2276 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2277 default: return FALSE;
2280 infoPtr->bAutoarrange = TRUE;
2281 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2282 for (i = 0; i < infoPtr->nItemCount; i++)
2284 next_pos(infoPtr, &pos);
2285 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2293 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2296 * [I] infoPtr : valid pointer to the listview structure
2297 * [O] lprcView : bounding rectangle
2303 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2307 SetRectEmpty(lprcView);
2309 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2313 for (i = 0; i < infoPtr->nItemCount; i++)
2315 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2316 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2317 lprcView->right = max(lprcView->right, x);
2318 lprcView->bottom = max(lprcView->bottom, y);
2320 if (infoPtr->nItemCount > 0)
2322 lprcView->right += infoPtr->nItemWidth;
2323 lprcView->bottom += infoPtr->nItemHeight;
2328 y = LISTVIEW_GetCountPerColumn(infoPtr);
2329 x = infoPtr->nItemCount / y;
2330 if (infoPtr->nItemCount % y) x++;
2331 lprcView->right = x * infoPtr->nItemWidth;
2332 lprcView->bottom = y * infoPtr->nItemHeight;
2336 lprcView->right = infoPtr->nItemWidth;
2337 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2344 * Retrieves the bounding rectangle of all the items.
2347 * [I] infoPtr : valid pointer to the listview structure
2348 * [O] lprcView : bounding rectangle
2354 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2358 TRACE("(lprcView=%p)\n", lprcView);
2360 if (!lprcView) return FALSE;
2362 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2363 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2364 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2366 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2373 * Retrieves the subitem pointer associated with the subitem index.
2376 * [I] hdpaSubItems : DPA handle for a specific item
2377 * [I] nSubItem : index of subitem
2380 * SUCCESS : subitem pointer
2383 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2385 SUBITEM_INFO *lpSubItem;
2388 /* we should binary search here if need be */
2389 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2391 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2392 if (lpSubItem->iSubItem == nSubItem)
2402 * Calculates the desired item width.
2405 * [I] infoPtr : valid pointer to the listview structure
2408 * The desired item width.
2410 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2412 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2415 TRACE("uView=%d\n", uView);
2417 if (uView == LVS_ICON)
2418 nItemWidth = infoPtr->iconSpacing.cx;
2419 else if (uView == LVS_REPORT)
2423 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2425 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2426 nItemWidth = rcHeader.right;
2429 else /* LVS_SMALLICON, or LVS_LIST */
2433 for (i = 0; i < infoPtr->nItemCount; i++)
2434 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2436 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2437 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2439 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2442 return max(nItemWidth, 1);
2447 * Calculates the desired item height.
2450 * [I] infoPtr : valid pointer to the listview structure
2453 * The desired item height.
2455 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2457 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2460 TRACE("uView=%d\n", uView);
2462 if (uView == LVS_ICON)
2463 nItemHeight = infoPtr->iconSpacing.cy;
2466 nItemHeight = infoPtr->ntmHeight;
2467 if (infoPtr->himlState)
2468 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2469 if (infoPtr->himlSmall)
2470 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2471 if (infoPtr->himlState || infoPtr->himlSmall)
2472 nItemHeight += HEIGHT_PADDING;
2473 if (infoPtr->nMeasureItemHeight > 0)
2474 nItemHeight = infoPtr->nMeasureItemHeight;
2477 return max(nItemHeight, 1);
2482 * Updates the width, and height of an item.
2485 * [I] infoPtr : valid pointer to the listview structure
2490 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2492 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2493 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2499 * Retrieves and saves important text metrics info for the current
2503 * [I] infoPtr : valid pointer to the listview structure
2506 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2508 HDC hdc = GetDC(infoPtr->hwndSelf);
2509 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2510 HFONT hOldFont = SelectObject(hdc, hFont);
2514 if (GetTextMetricsW(hdc, &tm))
2516 infoPtr->ntmHeight = tm.tmHeight;
2517 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2520 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2521 infoPtr->nEllipsisWidth = sz.cx;
2523 SelectObject(hdc, hOldFont);
2524 ReleaseDC(infoPtr->hwndSelf, hdc);
2526 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2531 * A compare function for ranges
2534 * [I] range1 : pointer to range 1;
2535 * [I] range2 : pointer to range 2;
2539 * > 0 : if range 1 > range 2
2540 * < 0 : if range 2 > range 1
2541 * = 0 : if range intersects range 2
2543 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2547 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2549 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2554 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2560 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2562 #define ranges_check(ranges, desc) do { } while(0)
2565 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2570 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2572 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2573 ranges_dump(ranges);
2574 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2575 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2576 assert (prev->lower >= 0 && prev->lower < prev->upper);
2577 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2579 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2580 assert (prev->upper <= curr->lower);
2581 assert (curr->lower < curr->upper);
2584 TRACE("--- Done checking---\n");
2587 static RANGES ranges_create(int count)
2589 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2590 if (!ranges) return NULL;
2591 ranges->hdpa = DPA_Create(count);
2592 if (ranges->hdpa) return ranges;
2597 static void ranges_clear(RANGES ranges)
2601 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2602 Free(DPA_GetPtr(ranges->hdpa, i));
2603 DPA_DeleteAllPtrs(ranges->hdpa);
2607 static void ranges_destroy(RANGES ranges)
2609 if (!ranges) return;
2610 ranges_clear(ranges);
2611 DPA_Destroy(ranges->hdpa);
2615 static RANGES ranges_clone(RANGES ranges)
2620 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2622 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2624 RANGE *newrng = Alloc(sizeof(RANGE));
2625 if (!newrng) goto fail;
2626 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2627 DPA_SetPtr(clone->hdpa, i, newrng);
2632 TRACE ("clone failed\n");
2633 ranges_destroy(clone);
2637 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2641 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2642 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2647 static void ranges_dump(RANGES ranges)
2651 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2652 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2655 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2657 RANGE srchrng = { nItem, nItem + 1 };
2659 TRACE("(nItem=%d)\n", nItem);
2660 ranges_check(ranges, "before contain");
2661 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2664 static INT ranges_itemcount(RANGES ranges)
2668 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2670 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2671 count += sel->upper - sel->lower;
2677 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2679 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2682 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2683 if (index == -1) return TRUE;
2685 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2687 chkrng = DPA_GetPtr(ranges->hdpa, index);
2688 if (chkrng->lower >= nItem)
2689 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2690 if (chkrng->upper > nItem)
2691 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2696 static BOOL ranges_add(RANGES ranges, RANGE range)
2701 TRACE("(%s)\n", debugrange(&range));
2702 ranges_check(ranges, "before add");
2704 /* try find overlapping regions first */
2705 srchrgn.lower = range.lower - 1;
2706 srchrgn.upper = range.upper + 1;
2707 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2713 TRACE("Adding new range\n");
2715 /* create the brand new range to insert */
2716 newrgn = Alloc(sizeof(RANGE));
2717 if(!newrgn) goto fail;
2720 /* figure out where to insert it */
2721 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2722 TRACE("index=%d\n", index);
2723 if (index == -1) index = 0;
2725 /* and get it over with */
2726 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2734 RANGE *chkrgn, *mrgrgn;
2735 INT fromindex, mergeindex;
2737 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2738 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2740 chkrgn->lower = min(range.lower, chkrgn->lower);
2741 chkrgn->upper = max(range.upper, chkrgn->upper);
2743 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2745 /* merge now common anges */
2747 srchrgn.lower = chkrgn->lower - 1;
2748 srchrgn.upper = chkrgn->upper + 1;
2752 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2753 if (mergeindex == -1) break;
2754 if (mergeindex == index)
2756 fromindex = index + 1;
2760 TRACE("Merge with index %i\n", mergeindex);
2762 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2763 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2764 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2766 DPA_DeletePtr(ranges->hdpa, mergeindex);
2767 if (mergeindex < index) index --;
2771 ranges_check(ranges, "after add");
2775 ranges_check(ranges, "failed add");
2779 static BOOL ranges_del(RANGES ranges, RANGE range)
2784 TRACE("(%s)\n", debugrange(&range));
2785 ranges_check(ranges, "before del");
2787 /* we don't use DPAS_SORTED here, since we need *
2788 * to find the first overlapping range */
2789 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2792 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2794 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2796 /* case 1: Same range */
2797 if ( (chkrgn->upper == range.upper) &&
2798 (chkrgn->lower == range.lower) )
2800 DPA_DeletePtr(ranges->hdpa, index);
2803 /* case 2: engulf */
2804 else if ( (chkrgn->upper <= range.upper) &&
2805 (chkrgn->lower >= range.lower) )
2807 DPA_DeletePtr(ranges->hdpa, index);
2809 /* case 3: overlap upper */
2810 else if ( (chkrgn->upper <= range.upper) &&
2811 (chkrgn->lower < range.lower) )
2813 chkrgn->upper = range.lower;
2815 /* case 4: overlap lower */
2816 else if ( (chkrgn->upper > range.upper) &&
2817 (chkrgn->lower >= range.lower) )
2819 chkrgn->lower = range.upper;
2822 /* case 5: fully internal */
2825 RANGE tmprgn = *chkrgn, *newrgn;
2827 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
2828 newrgn->lower = chkrgn->lower;
2829 newrgn->upper = range.lower;
2830 chkrgn->lower = range.upper;
2831 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2840 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2843 ranges_check(ranges, "after del");
2847 ranges_check(ranges, "failed del");
2853 * Removes all selection ranges
2856 * [I] infoPtr : valid pointer to the listview structure
2857 * [I] toSkip : item range to skip removing the selection
2863 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2872 lvItem.stateMask = LVIS_SELECTED;
2874 /* need to clone the DPA because callbacks can change it */
2875 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2876 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2877 while(iterator_next(&i))
2878 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2879 /* note that the iterator destructor will free the cloned range */
2880 iterator_destroy(&i);
2885 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2889 if (!(toSkip = ranges_create(1))) return FALSE;
2890 if (nItem != -1) ranges_additem(toSkip, nItem);
2891 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2892 ranges_destroy(toSkip);
2896 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2898 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2903 * Retrieves the number of items that are marked as selected.
2906 * [I] infoPtr : valid pointer to the listview structure
2909 * Number of items selected.
2911 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2913 INT nSelectedCount = 0;
2915 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2918 for (i = 0; i < infoPtr->nItemCount; i++)
2920 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2925 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2927 TRACE("nSelectedCount=%d\n", nSelectedCount);
2928 return nSelectedCount;
2933 * Manages the item focus.
2936 * [I] infoPtr : valid pointer to the listview structure
2937 * [I] nItem : item index
2940 * TRUE : focused item changed
2941 * FALSE : focused item has NOT changed
2943 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2945 INT oldFocus = infoPtr->nFocusedItem;
2948 if (nItem == infoPtr->nFocusedItem) return FALSE;
2950 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2951 lvItem.stateMask = LVIS_FOCUSED;
2952 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2954 return oldFocus != infoPtr->nFocusedItem;
2957 /* Helper function for LISTVIEW_ShiftIndices *only* */
2958 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2960 if (nShiftItem < nItem) return nShiftItem;
2962 if (nShiftItem > nItem) return nShiftItem + direction;
2964 if (direction > 0) return nShiftItem + direction;
2966 return min(nShiftItem, infoPtr->nItemCount - 1);
2971 * Updates the various indices after an item has been inserted or deleted.
2974 * [I] infoPtr : valid pointer to the listview structure
2975 * [I] nItem : item index
2976 * [I] direction : Direction of shift, +1 or -1.
2981 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2986 /* temporarily disable change notification while shifting items */
2987 bOldChange = infoPtr->bDoChangeNotify;
2988 infoPtr->bDoChangeNotify = FALSE;
2990 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2992 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2994 assert(abs(direction) == 1);
2996 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2998 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2999 if (nNewFocus != infoPtr->nFocusedItem)
3000 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3002 /* But we are not supposed to modify nHotItem! */
3004 infoPtr->bDoChangeNotify = bOldChange;
3010 * Adds a block of selections.
3013 * [I] infoPtr : valid pointer to the listview structure
3014 * [I] nItem : item index
3017 * Whether the window is still valid.
3019 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3021 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3022 INT nLast = max(infoPtr->nSelectionMark, nItem);
3023 HWND hwndSelf = infoPtr->hwndSelf;
3024 NMLVODSTATECHANGE nmlv;
3029 /* Temporarily disable change notification
3030 * If the control is LVS_OWNERDATA, we need to send
3031 * only one LVN_ODSTATECHANGED notification.
3032 * See MSDN documentation for LVN_ITEMCHANGED.
3034 bOldChange = infoPtr->bDoChangeNotify;
3035 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3037 if (nFirst == -1) nFirst = nItem;
3039 item.state = LVIS_SELECTED;
3040 item.stateMask = LVIS_SELECTED;
3042 for (i = nFirst; i <= nLast; i++)
3043 LISTVIEW_SetItemState(infoPtr,i,&item);
3045 ZeroMemory(&nmlv, sizeof(nmlv));
3046 nmlv.iFrom = nFirst;
3049 nmlv.uOldState = item.state;
3051 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3052 if (!IsWindow(hwndSelf))
3054 infoPtr->bDoChangeNotify = bOldChange;
3061 * Sets a single group selection.
3064 * [I] infoPtr : valid pointer to the listview structure
3065 * [I] nItem : item index
3070 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3072 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3078 if (!(selection = ranges_create(100))) return;
3080 item.state = LVIS_SELECTED;
3081 item.stateMask = LVIS_SELECTED;
3083 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3085 if (infoPtr->nSelectionMark == -1)
3087 infoPtr->nSelectionMark = nItem;
3088 ranges_additem(selection, nItem);
3094 sel.lower = min(infoPtr->nSelectionMark, nItem);
3095 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3096 ranges_add(selection, sel);
3101 RECT rcItem, rcSel, rcSelMark;
3104 rcItem.left = LVIR_BOUNDS;
3105 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3106 rcSelMark.left = LVIR_BOUNDS;
3107 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3108 UnionRect(&rcSel, &rcItem, &rcSelMark);
3109 iterator_frameditems(&i, infoPtr, &rcSel);
3110 while(iterator_next(&i))
3112 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3113 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3115 iterator_destroy(&i);
3118 bOldChange = infoPtr->bDoChangeNotify;
3119 infoPtr->bDoChangeNotify = FALSE;
3121 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3124 iterator_rangesitems(&i, selection);
3125 while(iterator_next(&i))
3126 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3127 /* this will also destroy the selection */
3128 iterator_destroy(&i);
3130 infoPtr->bDoChangeNotify = bOldChange;
3132 LISTVIEW_SetItemFocus(infoPtr, nItem);
3137 * Sets a single selection.
3140 * [I] infoPtr : valid pointer to the listview structure
3141 * [I] nItem : item index
3146 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3150 TRACE("nItem=%d\n", nItem);
3152 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3154 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3155 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3156 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3158 infoPtr->nSelectionMark = nItem;
3163 * Set selection(s) with keyboard.
3166 * [I] infoPtr : valid pointer to the listview structure
3167 * [I] nItem : item index
3170 * SUCCESS : TRUE (needs to be repainted)
3171 * FAILURE : FALSE (nothing has changed)
3173 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3175 /* FIXME: pass in the state */
3176 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3177 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3178 BOOL bResult = FALSE;
3180 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3181 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3183 if (infoPtr->dwStyle & LVS_SINGLESEL)
3186 LISTVIEW_SetSelection(infoPtr, nItem);
3193 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3198 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3199 lvItem.stateMask = LVIS_SELECTED;
3200 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3202 if (lvItem.state & LVIS_SELECTED)
3203 infoPtr->nSelectionMark = nItem;
3205 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3210 LISTVIEW_SetSelection(infoPtr, nItem);
3213 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3216 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3220 static BOOL LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3222 LVHITTESTINFO lvHitTestInfo;
3224 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3225 lvHitTestInfo.pt.x = pt.x;
3226 lvHitTestInfo.pt.y = pt.y;
3228 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3230 lpLVItem->mask = LVIF_PARAM;
3231 lpLVItem->iItem = lvHitTestInfo.iItem;
3232 lpLVItem->iSubItem = 0;
3234 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3239 * Called when the mouse is being actively tracked and has hovered for a specified
3243 * [I] infoPtr : valid pointer to the listview structure
3244 * [I] fwKeys : key indicator
3245 * [I] x,y : mouse position
3248 * 0 if the message was processed, non-zero if there was an error
3251 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3252 * over the item for a certain period of time.
3255 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, INT x, INT y)
3257 if (infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3265 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3266 LISTVIEW_SetSelection(infoPtr, item.iItem);
3274 * Called whenever WM_MOUSEMOVE is received.
3277 * [I] infoPtr : valid pointer to the listview structure
3278 * [I] fwKeys : key indicator
3279 * [I] x,y : mouse position
3282 * 0 if the message is processed, non-zero if there was an error
3284 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3286 TRACKMOUSEEVENT trackinfo;
3288 if (!(fwKeys & MK_LBUTTON))
3289 infoPtr->bLButtonDown = FALSE;
3291 if (infoPtr->bLButtonDown && DragDetect(infoPtr->hwndSelf, infoPtr->ptClickPos))
3293 LVHITTESTINFO lvHitTestInfo;
3296 lvHitTestInfo.pt = infoPtr->ptClickPos;
3297 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3299 ZeroMemory(&nmlv, sizeof(nmlv));
3300 nmlv.iItem = lvHitTestInfo.iItem;
3301 nmlv.ptAction = infoPtr->ptClickPos;
3303 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3308 infoPtr->bLButtonDown = FALSE;
3310 /* see if we are supposed to be tracking mouse hovering */
3311 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3312 /* fill in the trackinfo struct */
3313 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3314 trackinfo.dwFlags = TME_QUERY;
3315 trackinfo.hwndTrack = infoPtr->hwndSelf;
3316 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3318 /* see if we are already tracking this hwnd */
3319 _TrackMouseEvent(&trackinfo);
3321 if(!(trackinfo.dwFlags & TME_HOVER)) {
3322 trackinfo.dwFlags = TME_HOVER;
3324 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3325 _TrackMouseEvent(&trackinfo);
3334 * Tests wheather the item is assignable to a list with style lStyle
3336 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3338 if ( (lpLVItem->mask & LVIF_TEXT) &&
3339 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3340 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3348 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3351 * [I] infoPtr : valid pointer to the listview structure
3352 * [I] lpLVItem : valid pointer to new item atttributes
3353 * [I] isNew : the item being set is being inserted
3354 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3355 * [O] bChanged : will be set to TRUE if the item really changed
3361 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3363 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3371 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3373 if (lpLVItem->mask == 0) return TRUE;
3375 if (infoPtr->dwStyle & LVS_OWNERDATA)
3377 /* a virtual listview we stores only selection and focus */
3378 if (lpLVItem->mask & ~LVIF_STATE)
3384 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3385 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3389 /* we need to get the lParam and state of the item */
3390 item.iItem = lpLVItem->iItem;
3391 item.iSubItem = lpLVItem->iSubItem;
3392 item.mask = LVIF_STATE | LVIF_PARAM;
3393 item.stateMask = ~0;
3396 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3398 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3399 /* determine what fields will change */
3400 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3401 uChanged |= LVIF_STATE;
3403 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3404 uChanged |= LVIF_IMAGE;
3406 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3407 uChanged |= LVIF_PARAM;
3409 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3410 uChanged |= LVIF_INDENT;
3412 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3413 uChanged |= LVIF_TEXT;
3415 TRACE("uChanged=0x%x\n", uChanged);
3416 if (!uChanged) return TRUE;
3419 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3420 nmlv.iItem = lpLVItem->iItem;
3421 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3422 nmlv.uOldState = item.state;
3423 nmlv.uChanged = uChanged;
3424 nmlv.lParam = item.lParam;
3426 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3427 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3429 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3431 HWND hwndSelf = infoPtr->hwndSelf;
3433 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3435 if (!IsWindow(hwndSelf))
3439 /* copy information */
3440 if (lpLVItem->mask & LVIF_TEXT)
3441 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3443 if (lpLVItem->mask & LVIF_IMAGE)
3444 lpItem->hdr.iImage = lpLVItem->iImage;
3446 if (lpLVItem->mask & LVIF_PARAM)
3447 lpItem->lParam = lpLVItem->lParam;
3449 if (lpLVItem->mask & LVIF_INDENT)
3450 lpItem->iIndent = lpLVItem->iIndent;
3452 if (uChanged & LVIF_STATE)
3454 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3456 lpItem->state &= ~lpLVItem->stateMask;
3457 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3459 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3461 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3462 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3464 else if (lpLVItem->stateMask & LVIS_SELECTED)
3465 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3467 /* if we are asked to change focus, and we manage it, do it */
3468 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3470 if (lpLVItem->state & LVIS_FOCUSED)
3472 LISTVIEW_SetItemFocus(infoPtr, -1);
3473 infoPtr->nFocusedItem = lpLVItem->iItem;
3474 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3476 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3477 infoPtr->nFocusedItem = -1;
3481 /* if we're inserting the item, we're done */
3482 if (isNew) return TRUE;
3484 /* send LVN_ITEMCHANGED notification */
3485 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3486 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3493 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3496 * [I] infoPtr : valid pointer to the listview structure
3497 * [I] lpLVItem : valid pointer to new subitem atttributes
3498 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3499 * [O] bChanged : will be set to TRUE if the item really changed
3505 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3508 SUBITEM_INFO *lpSubItem;
3510 /* we do not support subitems for virtual listviews */
3511 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3513 /* set subitem only if column is present */
3514 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3516 /* First do some sanity checks */
3517 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3518 particularly useful. We currently do not actually do anything with
3519 the flag on subitems.
3521 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3522 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3524 /* get the subitem structure, and create it if not there */
3525 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3526 assert (hdpaSubItems);
3528 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3531 SUBITEM_INFO *tmpSubItem;
3534 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3535 if (!lpSubItem) return FALSE;
3536 /* we could binary search here, if need be...*/
3537 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3539 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3540 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3542 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3547 lpSubItem->iSubItem = lpLVItem->iSubItem;
3548 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3552 if (lpLVItem->mask & LVIF_IMAGE)
3553 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3555 lpSubItem->hdr.iImage = lpLVItem->iImage;
3559 if (lpLVItem->mask & LVIF_TEXT)
3560 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3562 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3571 * Sets item attributes.
3574 * [I] infoPtr : valid pointer to the listview structure
3575 * [I] lpLVItem : new item atttributes
3576 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3582 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3584 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3585 HWND hwndSelf = infoPtr->hwndSelf;
3586 LPWSTR pszText = NULL;
3587 BOOL bResult, bChanged = FALSE;
3589 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3591 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3594 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3595 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3597 pszText = lpLVItem->pszText;
3598 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3601 /* actually set the fields */
3602 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3604 if (lpLVItem->iSubItem)
3605 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3607 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3608 if (!IsWindow(hwndSelf))
3611 /* redraw item, if necessary */
3612 if (bChanged && !infoPtr->bIsDrawing)
3614 /* this little optimization eliminates some nasty flicker */
3615 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3616 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3617 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3618 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3620 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3625 textfreeT(lpLVItem->pszText, isW);
3626 ((LVITEMW *)lpLVItem)->pszText = pszText;
3634 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3637 * [I] infoPtr : valid pointer to the listview structure
3642 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3644 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3646 SCROLLINFO scrollInfo;
3648 scrollInfo.cbSize = sizeof(SCROLLINFO);
3649 scrollInfo.fMask = SIF_POS;
3651 if (uView == LVS_LIST)
3653 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3654 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3656 else if (uView == LVS_REPORT)
3658 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3659 nItem = scrollInfo.nPos;
3663 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3664 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3667 TRACE("nItem=%d\n", nItem);
3675 * Erases the background of the given rectangle
3678 * [I] infoPtr : valid pointer to the listview structure
3679 * [I] hdc : device context handle
3680 * [I] lprcBox : clipping rectangle
3686 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3688 if (!infoPtr->hBkBrush) return FALSE;
3690 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3692 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3700 * [I] infoPtr : valid pointer to the listview structure
3701 * [I] hdc : device context handle
3702 * [I] nItem : item index
3703 * [I] nSubItem : subitem index
3704 * [I] pos : item position in client coordinates
3705 * [I] cdmode : custom draw mode
3711 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3713 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3714 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3715 static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3716 DWORD cdsubitemmode = CDRF_DODEFAULT;
3717 RECT* lprcFocus, rcSelect, rcBox, rcIcon, rcLabel;
3718 NMLVCUSTOMDRAW nmlvcd;
3723 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3725 /* get information needed for drawing the item */
3726 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3727 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3728 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3729 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3730 lvItem.iItem = nItem;
3731 lvItem.iSubItem = nSubItem;
3734 lvItem.cchTextMax = DISP_TEXT_SIZE;
3735 lvItem.pszText = szDispText;
3736 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3737 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3738 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3739 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3740 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3742 /* now check if we need to update the focus rectangle */
3743 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3745 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3746 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcLabel);
3747 OffsetRect(&rcBox, pos.x, pos.y);
3748 OffsetRect(&rcSelect, pos.x, pos.y);
3749 OffsetRect(&rcIcon, pos.x, pos.y);
3750 OffsetRect(&rcLabel, pos.x, pos.y);
3751 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3752 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3753 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3755 /* fill in the custom draw structure */
3756 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3758 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3759 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3760 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3761 if (cdmode & CDRF_NOTIFYITEMDRAW)
3762 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3763 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3764 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3765 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3766 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3768 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3769 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3771 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3772 prepaint_setup(infoPtr, hdc, &nmlvcd);
3774 /* in full row select, subitems, will just use main item's colors */
3775 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3776 nmlvcd.clrTextBk = CLR_NONE;
3779 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3781 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3784 TRACE("uStateImage=%d\n", uStateImage);
3785 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3786 rcIcon.left - infoPtr->iconStateSize.cx,
3787 rcIcon.top, ILD_NORMAL);
3792 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3793 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3795 TRACE("iImage=%d\n", lvItem.iImage);
3796 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3797 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3798 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3801 /* Don't bother painting item being edited */
3802 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3804 /* FIXME: temporary hack */
3805 rcSelect.left = rcLabel.left;
3807 /* draw the selection background, if we're drawing the main item */
3810 /* in icon mode, the label rect is really what we want to draw the
3812 if (uView == LVS_ICON)
3815 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3816 rcSelect.right = rcBox.right;
3818 if (nmlvcd.clrTextBk != CLR_NONE)
3819 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3820 if(lprcFocus) *lprcFocus = rcSelect;
3823 /* figure out the text drawing flags */
3824 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3825 if (uView == LVS_ICON)
3826 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3829 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3831 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3832 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3833 default: uFormat |= DT_LEFT;
3836 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3838 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3839 else rcLabel.left += LABEL_HOR_PADDING;
3841 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3842 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3845 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3846 notify_postpaint(infoPtr, &nmlvcd);
3847 if (cdsubitemmode & CDRF_NEWFONT)
3848 SelectObject(hdc, hOldFont);
3854 * Draws listview items when in owner draw mode.
3857 * [I] infoPtr : valid pointer to the listview structure
3858 * [I] hdc : device context handle
3863 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3865 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3866 DWORD cditemmode = CDRF_DODEFAULT;
3867 NMLVCUSTOMDRAW nmlvcd;
3868 POINT Origin, Position;
3874 ZeroMemory(&dis, sizeof(dis));
3876 /* Get scroll info once before loop */
3877 LISTVIEW_GetOrigin(infoPtr, &Origin);
3879 /* iterate through the invalidated rows */
3880 while(iterator_next(i))
3882 item.iItem = i->nItem;
3884 item.mask = LVIF_PARAM | LVIF_STATE;
3885 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3886 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3888 dis.CtlType = ODT_LISTVIEW;
3890 dis.itemID = item.iItem;
3891 dis.itemAction = ODA_DRAWENTIRE;
3893 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3894 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3895 dis.hwndItem = infoPtr->hwndSelf;
3897 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3898 dis.rcItem.left = Position.x + Origin.x;
3899 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3900 dis.rcItem.top = Position.y + Origin.y;
3901 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3902 dis.itemData = item.lParam;
3904 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3907 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3908 * structure for the rest. of the paint cycle
3910 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3911 if (cdmode & CDRF_NOTIFYITEMDRAW)
3912 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3914 if (!(cditemmode & CDRF_SKIPDEFAULT))
3916 prepaint_setup (infoPtr, hdc, &nmlvcd);
3917 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3920 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3921 notify_postpaint(infoPtr, &nmlvcd);
3927 * Draws listview items when in report display mode.
3930 * [I] infoPtr : valid pointer to the listview structure
3931 * [I] hdc : device context handle
3932 * [I] cdmode : custom draw mode
3937 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3940 RECT rcClip, rcItem;
3941 POINT Origin, Position;
3947 /* figure out what to draw */
3948 rgntype = GetClipBox(hdc, &rcClip);
3949 if (rgntype == NULLREGION) return;
3951 /* Get scroll info once before loop */
3952 LISTVIEW_GetOrigin(infoPtr, &Origin);
3954 /* narrow down the columns we need to paint */
3955 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3957 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3958 if (rcItem.right + Origin.x >= rcClip.left) break;
3960 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3962 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3963 if (rcItem.left + Origin.x < rcClip.right) break;
3965 iterator_rangeitems(&j, colRange);
3967 /* in full row select, we _have_ to draw the main item */
3968 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3971 /* iterate through the invalidated rows */
3972 while(iterator_next(i))
3974 /* iterate through the invalidated columns */
3975 while(iterator_next(&j))
3977 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3978 Position.x += Origin.x;
3979 Position.y += Origin.y;
3981 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3983 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3985 rcItem.bottom = infoPtr->nItemHeight;
3986 OffsetRect(&rcItem, Position.x, Position.y);
3987 if (!RectVisible(hdc, &rcItem)) continue;
3990 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3993 iterator_destroy(&j);
3998 * Draws listview items when in list display mode.
4001 * [I] infoPtr : valid pointer to the listview structure
4002 * [I] hdc : device context handle
4003 * [I] cdmode : custom draw mode
4008 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4010 POINT Origin, Position;
4012 /* Get scroll info once before loop */
4013 LISTVIEW_GetOrigin(infoPtr, &Origin);
4015 while(iterator_prev(i))
4017 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4018 Position.x += Origin.x;
4019 Position.y += Origin.y;
4021 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4028 * Draws listview items.
4031 * [I] infoPtr : valid pointer to the listview structure
4032 * [I] hdc : device context handle
4033 * [I] prcErase : rect to be erased before refresh (may be NULL)
4038 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, RECT *prcErase)
4040 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4041 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4042 NMLVCUSTOMDRAW nmlvcd;
4049 HBITMAP hbmp = NULL;
4051 LISTVIEW_DUMP(infoPtr);
4053 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4054 TRACE("double buffering\n");
4056 hdc = CreateCompatibleDC(hdcOrig);
4058 ERR("Failed to create DC for backbuffer\n");
4061 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4062 infoPtr->rcList.bottom);
4064 ERR("Failed to create bitmap for backbuffer\n");
4069 SelectObject(hdc, hbmp);
4070 SelectObject(hdc, infoPtr->hFont);
4072 /* Save dc values we're gonna trash while drawing
4073 * FIXME: Should be done in LISTVIEW_DrawItem() */
4074 hOldFont = SelectObject(hdc, infoPtr->hFont);
4075 oldBkMode = GetBkMode(hdc);
4076 oldBkColor = GetBkColor(hdc);
4077 oldTextColor = GetTextColor(hdc);
4080 infoPtr->bIsDrawing = TRUE;
4083 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4084 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4085 /* If no erasing was done (usually because RedrawWindow was called
4086 * with RDW_INVALIDATE only) we need to copy the old contents into
4087 * the backbuffer before continuing. */
4088 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4089 infoPtr->rcList.right - infoPtr->rcList.left,
4090 infoPtr->rcList.bottom - infoPtr->rcList.top,
4091 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4094 /* FIXME: Shouldn't need to do this */
4095 oldClrTextBk = infoPtr->clrTextBk;
4096 oldClrText = infoPtr->clrText;
4098 infoPtr->cditemmode = CDRF_DODEFAULT;
4100 GetClientRect(infoPtr->hwndSelf, &rcClient);
4101 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4102 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4103 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4104 prepaint_setup(infoPtr, hdc, &nmlvcd);
4106 /* Use these colors to draw the items */
4107 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4108 infoPtr->clrText = nmlvcd.clrText;
4110 /* nothing to draw */
4111 if(infoPtr->nItemCount == 0) goto enddraw;
4113 /* figure out what we need to draw */
4114 iterator_visibleitems(&i, infoPtr, hdc);
4116 /* send cache hint notification */
4117 if (infoPtr->dwStyle & LVS_OWNERDATA)
4119 RANGE range = iterator_range(&i);
4122 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4123 nmlv.iFrom = range.lower;
4124 nmlv.iTo = range.upper - 1;
4125 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4128 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4129 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4132 if (uView == LVS_REPORT)
4133 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4134 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4135 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4137 /* if we have a focus rect, draw it */
4138 if (infoPtr->bFocus)
4139 DrawFocusRect(hdc, &infoPtr->rcFocus);
4141 iterator_destroy(&i);
4144 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4145 notify_postpaint(infoPtr, &nmlvcd);
4147 infoPtr->clrTextBk = oldClrTextBk;
4148 infoPtr->clrText = oldClrText;
4151 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4152 infoPtr->rcList.right - infoPtr->rcList.left,
4153 infoPtr->rcList.bottom - infoPtr->rcList.top,
4154 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4159 SelectObject(hdc, hOldFont);
4160 SetBkMode(hdc, oldBkMode);
4161 SetBkColor(hdc, oldBkColor);
4162 SetTextColor(hdc, oldTextColor);
4165 infoPtr->bIsDrawing = FALSE;
4171 * Calculates the approximate width and height of a given number of items.
4174 * [I] infoPtr : valid pointer to the listview structure
4175 * [I] nItemCount : number of items
4176 * [I] wWidth : width
4177 * [I] wHeight : height
4180 * Returns a DWORD. The width in the low word and the height in high word.
4182 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
4183 WORD wWidth, WORD wHeight)
4185 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4186 INT nItemCountPerColumn = 1;
4187 INT nColumnCount = 0;
4188 DWORD dwViewRect = 0;
4190 if (nItemCount == -1)
4191 nItemCount = infoPtr->nItemCount;
4193 if (uView == LVS_LIST)
4195 if (wHeight == 0xFFFF)
4197 /* use current height */
4198 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4201 if (wHeight < infoPtr->nItemHeight)
4202 wHeight = infoPtr->nItemHeight;
4206 if (infoPtr->nItemHeight > 0)
4208 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4209 if (nItemCountPerColumn == 0)
4210 nItemCountPerColumn = 1;
4212 if (nItemCount % nItemCountPerColumn != 0)
4213 nColumnCount = nItemCount / nItemCountPerColumn;
4215 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4219 /* Microsoft padding magic */
4220 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4221 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4223 dwViewRect = MAKELONG(wWidth, wHeight);
4225 else if (uView == LVS_REPORT)
4229 if (infoPtr->nItemCount > 0)
4231 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4232 wWidth = rcBox.right - rcBox.left;
4233 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4237 /* use current height and width */
4238 if (wHeight == 0xffff)
4239 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4240 if (wWidth == 0xffff)
4241 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4244 dwViewRect = MAKELONG(wWidth, wHeight);
4246 else if (uView == LVS_SMALLICON)
4247 FIXME("uView == LVS_SMALLICON: not implemented\n");
4248 else if (uView == LVS_ICON)
4249 FIXME("uView == LVS_ICON: not implemented\n");
4257 * Create a drag image list for the specified item.
4260 * [I] infoPtr : valid pointer to the listview structure
4261 * [I] iItem : index of item
4262 * [O] lppt : Upperr-left corner of the image
4265 * Returns a handle to the image list if successful, NULL otherwise.
4267 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4273 HBITMAP hbmp, hOldbmp;
4274 HIMAGELIST dragList = 0;
4275 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4277 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4280 rcItem.left = LVIR_BOUNDS;
4281 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4284 lppt->x = rcItem.left;
4285 lppt->y = rcItem.top;
4287 size.cx = rcItem.right - rcItem.left;
4288 size.cy = rcItem.bottom - rcItem.top;
4290 hdcOrig = GetDC(infoPtr->hwndSelf);
4291 hdc = CreateCompatibleDC(hdcOrig);
4292 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4293 hOldbmp = SelectObject(hdc, hbmp);
4295 rcItem.left = rcItem.top = 0;
4296 rcItem.right = size.cx;
4297 rcItem.bottom = size.cy;
4298 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4301 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4303 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4304 SelectObject(hdc, hOldbmp);
4305 ImageList_Add(dragList, hbmp, 0);
4308 SelectObject(hdc, hOldbmp);
4312 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4314 TRACE("ret=%p\n", dragList);
4322 * Removes all listview items and subitems.
4325 * [I] infoPtr : valid pointer to the listview structure
4331 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4334 HDPA hdpaSubItems = NULL;
4341 /* we do it directly, to avoid notifications */
4342 ranges_clear(infoPtr->selectionRanges);
4343 infoPtr->nSelectionMark = -1;
4344 infoPtr->nFocusedItem = -1;
4345 SetRectEmpty(&infoPtr->rcFocus);
4346 /* But we are supposed to leave nHotItem as is! */
4349 /* send LVN_DELETEALLITEMS notification */
4350 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4352 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4354 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4356 /* send LVN_DELETEITEM notification, if not suppressed */
4357 if (!bSuppress) notify_deleteitem(infoPtr, i);
4358 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4360 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4361 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4363 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4364 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4367 DPA_Destroy(hdpaSubItems);
4368 DPA_DeletePtr(infoPtr->hdpaItems, i);
4370 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4371 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4372 infoPtr->nItemCount --;
4375 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4376 LISTVIEW_UpdateScroll(infoPtr);
4377 LISTVIEW_InvalidateList(infoPtr);
4384 * Scrolls, and updates the columns, when a column is changing width.
4387 * [I] infoPtr : valid pointer to the listview structure
4388 * [I] nColumn : column to scroll
4389 * [I] dx : amount of scroll, in pixels
4394 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4396 COLUMN_INFO *lpColumnInfo;
4401 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4402 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4403 rcCol = lpColumnInfo->rcHeader;
4404 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4405 rcCol.left = rcCol.right;
4407 /* ajust the other columns */
4408 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4410 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4411 lpColumnInfo->rcHeader.left += dx;
4412 lpColumnInfo->rcHeader.right += dx;
4415 /* do not update screen if not in report mode */
4416 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4418 /* if we have a focus, must first erase the focus rect */
4419 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4421 /* Need to reset the item width when inserting a new column */
4422 infoPtr->nItemWidth += dx;
4424 LISTVIEW_UpdateScroll(infoPtr);
4425 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4427 /* scroll to cover the deleted column, and invalidate for redraw */
4428 rcOld = infoPtr->rcList;
4429 rcOld.left = ptOrigin.x + rcCol.left + dx;
4430 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4432 /* we can restore focus now */
4433 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4438 * Removes a column from the listview control.
4441 * [I] infoPtr : valid pointer to the listview structure
4442 * [I] nColumn : column index
4448 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4452 TRACE("nColumn=%d\n", nColumn);
4454 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4455 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4457 /* While the MSDN specifically says that column zero should not be deleted,
4458 what actually happens is that the column itself is deleted but no items or subitems
4462 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4464 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4467 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4468 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4470 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4472 SUBITEM_INFO *lpSubItem, *lpDelItem;
4474 INT nItem, nSubItem, i;
4476 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4478 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4481 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4483 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4484 if (lpSubItem->iSubItem == nColumn)
4487 lpDelItem = lpSubItem;
4489 else if (lpSubItem->iSubItem > nColumn)
4491 lpSubItem->iSubItem--;
4495 /* if we found our subitem, zapp it */
4499 if (is_textW(lpDelItem->hdr.pszText))
4500 Free(lpDelItem->hdr.pszText);
4505 /* free dpa memory */
4506 DPA_DeletePtr(hdpaSubItems, nSubItem);
4511 /* update the other column info */
4512 LISTVIEW_UpdateItemSize(infoPtr);
4513 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4514 LISTVIEW_InvalidateList(infoPtr);
4516 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4523 * Invalidates the listview after an item's insertion or deletion.
4526 * [I] infoPtr : valid pointer to the listview structure
4527 * [I] nItem : item index
4528 * [I] dir : -1 if deleting, 1 if inserting
4533 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4535 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4536 INT nPerCol, nItemCol, nItemRow;
4540 /* if we don't refresh, what's the point of scrolling? */
4541 if (!is_redrawing(infoPtr)) return;
4543 assert (abs(dir) == 1);
4545 /* arrange icons if autoarrange is on */
4546 if (is_autoarrange(infoPtr))
4548 BOOL arrange = TRUE;
4549 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4550 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4551 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4554 /* scrollbars need updating */
4555 LISTVIEW_UpdateScroll(infoPtr);
4557 /* figure out the item's position */
4558 if (uView == LVS_REPORT)
4559 nPerCol = infoPtr->nItemCount + 1;
4560 else if (uView == LVS_LIST)
4561 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4562 else /* LVS_ICON, or LVS_SMALLICON */
4565 nItemCol = nItem / nPerCol;
4566 nItemRow = nItem % nPerCol;
4567 LISTVIEW_GetOrigin(infoPtr, &Origin);
4569 /* move the items below up a slot */
4570 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4571 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4572 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4573 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4574 OffsetRect(&rcScroll, Origin.x, Origin.y);
4575 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4576 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4578 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4579 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4580 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4583 /* report has only that column, so we're done */
4584 if (uView == LVS_REPORT) return;
4586 /* now for LISTs, we have to deal with the columns to the right */
4587 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4589 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4590 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4591 OffsetRect(&rcScroll, Origin.x, Origin.y);
4592 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4593 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4594 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4599 * Removes an item from the listview control.
4602 * [I] infoPtr : valid pointer to the listview structure
4603 * [I] nItem : item index
4609 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4611 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4614 TRACE("(nItem=%d)\n", nItem);
4616 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4618 /* remove selection, and focus */
4620 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4621 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4623 /* send LVN_DELETEITEM notification. */
4624 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4626 /* we need to do this here, because we'll be deleting stuff */
4627 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4628 LISTVIEW_InvalidateItem(infoPtr, nItem);
4630 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4636 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4637 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4639 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4640 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4643 DPA_Destroy(hdpaSubItems);
4646 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4648 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4649 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4652 infoPtr->nItemCount--;
4653 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4655 /* now is the invalidation fun */
4656 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4663 * Callback implementation for editlabel control
4666 * [I] infoPtr : valid pointer to the listview structure
4667 * [I] pszText : modified text
4668 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4674 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4676 HWND hwndSelf = infoPtr->hwndSelf;
4677 NMLVDISPINFOW dispInfo;
4679 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4681 ZeroMemory(&dispInfo, sizeof(dispInfo));
4682 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4683 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4684 dispInfo.item.iSubItem = 0;
4685 dispInfo.item.stateMask = ~0;
4686 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4687 /* add the text from the edit in */
4688 dispInfo.item.mask |= LVIF_TEXT;
4689 dispInfo.item.pszText = pszText;
4690 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4692 /* Do we need to update the Item Text */
4693 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4694 if (!IsWindow(hwndSelf))
4696 if (!pszText) return TRUE;
4698 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4700 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4701 ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4702 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4704 LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4709 ZeroMemory(&dispInfo, sizeof(dispInfo));
4710 dispInfo.item.mask = LVIF_TEXT;
4711 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4712 dispInfo.item.iSubItem = 0;
4713 dispInfo.item.pszText = pszText;
4714 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4715 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4720 * Begin in place editing of specified list view item
4723 * [I] infoPtr : valid pointer to the listview structure
4724 * [I] nItem : item index
4725 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4731 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4733 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4734 NMLVDISPINFOW dispInfo;
4736 HWND hwndSelf = infoPtr->hwndSelf;
4738 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4740 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4741 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4743 infoPtr->nEditLabelItem = nItem;
4745 /* Is the EditBox still there, if so remove it */
4746 if(infoPtr->hwndEdit != 0)
4748 SetFocus(infoPtr->hwndSelf);
4749 infoPtr->hwndEdit = 0;
4752 LISTVIEW_SetSelection(infoPtr, nItem);
4753 LISTVIEW_SetItemFocus(infoPtr, nItem);
4754 LISTVIEW_InvalidateItem(infoPtr, nItem);
4756 rect.left = LVIR_LABEL;
4757 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4759 ZeroMemory(&dispInfo, sizeof(dispInfo));
4760 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4761 dispInfo.item.iItem = nItem;
4762 dispInfo.item.iSubItem = 0;
4763 dispInfo.item.stateMask = ~0;
4764 dispInfo.item.pszText = szDispText;
4765 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4766 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4768 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4769 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4770 if (!infoPtr->hwndEdit) return 0;
4772 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4774 if (!IsWindow(hwndSelf))
4776 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4777 infoPtr->hwndEdit = 0;
4781 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4782 SetFocus(infoPtr->hwndEdit);
4783 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4784 return infoPtr->hwndEdit;
4790 * Ensures the specified item is visible, scrolling into view if necessary.
4793 * [I] infoPtr : valid pointer to the listview structure
4794 * [I] nItem : item index
4795 * [I] bPartial : partially or entirely visible
4801 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4803 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4804 INT nScrollPosHeight = 0;
4805 INT nScrollPosWidth = 0;
4806 INT nHorzAdjust = 0;
4807 INT nVertAdjust = 0;
4810 RECT rcItem, rcTemp;
4812 rcItem.left = LVIR_BOUNDS;
4813 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4815 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4817 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4819 /* scroll left/right, but in LVS_REPORT mode */
4820 if (uView == LVS_LIST)
4821 nScrollPosWidth = infoPtr->nItemWidth;
4822 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4823 nScrollPosWidth = 1;
4825 if (rcItem.left < infoPtr->rcList.left)
4828 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4833 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4837 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4839 /* scroll up/down, but not in LVS_LIST mode */
4840 if (uView == LVS_REPORT)
4841 nScrollPosHeight = infoPtr->nItemHeight;
4842 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4843 nScrollPosHeight = 1;
4845 if (rcItem.top < infoPtr->rcList.top)
4848 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4853 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4857 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4859 if (nScrollPosWidth)
4861 INT diff = nHorzDiff / nScrollPosWidth;
4862 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4863 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4866 if (nScrollPosHeight)
4868 INT diff = nVertDiff / nScrollPosHeight;
4869 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4870 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4878 * Searches for an item with specific characteristics.
4881 * [I] hwnd : window handle
4882 * [I] nStart : base item index
4883 * [I] lpFindInfo : item information to look for
4886 * SUCCESS : index of item
4889 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4890 const LVFINDINFOW *lpFindInfo)
4892 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4893 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4894 BOOL bWrap = FALSE, bNearest = FALSE;
4895 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4896 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4897 POINT Position, Destination;
4900 if (!lpFindInfo || nItem < 0) return -1;
4903 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4905 lvItem.mask |= LVIF_TEXT;
4906 lvItem.pszText = szDispText;
4907 lvItem.cchTextMax = DISP_TEXT_SIZE;
4910 if (lpFindInfo->flags & LVFI_WRAP)
4913 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4914 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4919 LISTVIEW_GetOrigin(infoPtr, &Origin);
4920 Destination.x = lpFindInfo->pt.x - Origin.x;
4921 Destination.y = lpFindInfo->pt.y - Origin.y;
4922 switch(lpFindInfo->vkDirection)
4924 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4925 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4926 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4927 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4928 case VK_HOME: Destination.x = Destination.y = 0; break;
4929 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4930 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4932 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4933 Destination.x = rcArea.right;
4934 Destination.y = rcArea.bottom;
4936 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4940 else Destination.x = Destination.y = 0;
4942 /* if LVFI_PARAM is specified, all other flags are ignored */
4943 if (lpFindInfo->flags & LVFI_PARAM)
4945 lvItem.mask |= LVIF_PARAM;
4947 lvItem.mask &= ~LVIF_TEXT;
4951 for (; nItem < nLast; nItem++)
4953 lvItem.iItem = nItem;
4954 lvItem.iSubItem = 0;
4955 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4957 if (lvItem.mask & LVIF_PARAM)
4959 if (lpFindInfo->lParam == lvItem.lParam)
4965 if (lvItem.mask & LVIF_TEXT)
4967 if (lpFindInfo->flags & LVFI_PARTIAL)
4969 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4973 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4977 if (!bNearest) return nItem;
4979 /* This is very inefficient. To do a good job here,
4980 * we need a sorted array of (x,y) item positions */
4981 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4983 /* compute the distance^2 to the destination */
4984 xdist = Destination.x - Position.x;
4985 ydist = Destination.y - Position.y;
4986 dist = xdist * xdist + ydist * ydist;
4988 /* remember the distance, and item if it's closer */
4992 nNearestItem = nItem;
4999 nLast = min(nStart + 1, infoPtr->nItemCount);
5004 return nNearestItem;
5009 * Searches for an item with specific characteristics.
5012 * [I] hwnd : window handle
5013 * [I] nStart : base item index
5014 * [I] lpFindInfo : item information to look for
5017 * SUCCESS : index of item
5020 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
5021 const LVFINDINFOA *lpFindInfo)
5023 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5027 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5028 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5029 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5030 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
5036 * Retrieves the background image of the listview control.
5039 * [I] infoPtr : valid pointer to the listview structure
5040 * [O] lpBkImage : background image attributes
5046 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5048 /* FIXME (listview, "empty stub!\n"); */
5054 * Retrieves column attributes.
5057 * [I] infoPtr : valid pointer to the listview structure
5058 * [I] nColumn : column index
5059 * [IO] lpColumn : column information
5060 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5061 * otherwise it is in fact a LPLVCOLUMNA
5067 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5069 COLUMN_INFO *lpColumnInfo;
5072 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5073 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5075 /* initialize memory */
5076 ZeroMemory(&hdi, sizeof(hdi));
5078 if (lpColumn->mask & LVCF_TEXT)
5080 hdi.mask |= HDI_TEXT;
5081 hdi.pszText = lpColumn->pszText;
5082 hdi.cchTextMax = lpColumn->cchTextMax;
5085 if (lpColumn->mask & LVCF_IMAGE)
5086 hdi.mask |= HDI_IMAGE;
5088 if (lpColumn->mask & LVCF_ORDER)
5089 hdi.mask |= HDI_ORDER;
5091 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5093 if (lpColumn->mask & LVCF_FMT)
5094 lpColumn->fmt = lpColumnInfo->fmt;
5096 if (lpColumn->mask & LVCF_WIDTH)
5097 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5099 if (lpColumn->mask & LVCF_IMAGE)
5100 lpColumn->iImage = hdi.iImage;
5102 if (lpColumn->mask & LVCF_ORDER)
5103 lpColumn->iOrder = hdi.iOrder;
5109 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5116 /* FIXME: little hack */
5117 for (i = 0; i < iCount; i++)
5125 * Retrieves the column width.
5128 * [I] infoPtr : valid pointer to the listview structure
5129 * [I] int : column index
5132 * SUCCESS : column width
5135 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
5137 INT nColumnWidth = 0;
5140 TRACE("nColumn=%d\n", nColumn);
5142 /* we have a 'column' in LIST and REPORT mode only */
5143 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5146 nColumnWidth = infoPtr->nItemWidth;
5149 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
5150 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
5151 nColumnWidth = rcHeader.right - rcHeader.left;
5155 TRACE("nColumnWidth=%d\n", nColumnWidth);
5156 return nColumnWidth;
5161 * In list or report display mode, retrieves the number of items that can fit
5162 * vertically in the visible area. In icon or small icon display mode,
5163 * retrieves the total number of visible items.
5166 * [I] infoPtr : valid pointer to the listview structure
5169 * Number of fully visible items.
5171 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5173 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5177 return infoPtr->nItemCount;
5179 return LISTVIEW_GetCountPerColumn(infoPtr);
5181 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5189 * Retrieves an image list handle.
5192 * [I] infoPtr : valid pointer to the listview structure
5193 * [I] nImageList : image list identifier
5196 * SUCCESS : image list handle
5199 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5203 case LVSIL_NORMAL: return infoPtr->himlNormal;
5204 case LVSIL_SMALL: return infoPtr->himlSmall;
5205 case LVSIL_STATE: return infoPtr->himlState;
5210 /* LISTVIEW_GetISearchString */
5214 * Retrieves item attributes.
5217 * [I] hwnd : window handle
5218 * [IO] lpLVItem : item info
5219 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5220 * if FALSE, then lpLVItem is a LPLVITEMA.
5223 * This is the internal 'GetItem' interface -- it tries to
5224 * be smart and avoid text copies, if possible, by modifying
5225 * lpLVItem->pszText to point to the text string. Please note
5226 * that this is not always possible (e.g. OWNERDATA), so on
5227 * entry you *must* supply valid values for pszText, and cchTextMax.
5228 * The only difference to the documented interface is that upon
5229 * return, you should use *only* the lpLVItem->pszText, rather than
5230 * the buffer pointer you provided on input. Most code already does
5231 * that, so it's not a problem.
5232 * For the two cases when the text must be copied (that is,
5233 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5239 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5241 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5242 NMLVDISPINFOW dispInfo;
5248 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5250 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5253 if (lpLVItem->mask == 0) return TRUE;
5255 /* make a local copy */
5256 isubitem = lpLVItem->iSubItem;
5258 /* a quick optimization if all we're asked is the focus state
5259 * these queries are worth optimising since they are common,
5260 * and can be answered in constant time, without the heavy accesses */
5261 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5262 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5264 lpLVItem->state = 0;
5265 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5266 lpLVItem->state |= LVIS_FOCUSED;
5270 ZeroMemory(&dispInfo, sizeof(dispInfo));
5272 /* if the app stores all the data, handle it separately */
5273 if (infoPtr->dwStyle & LVS_OWNERDATA)
5275 dispInfo.item.state = 0;
5277 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5278 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5280 /* NOTE: copy only fields which we _know_ are initialized, some apps
5281 * depend on the uninitialized fields being 0 */
5282 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5283 dispInfo.item.iItem = lpLVItem->iItem;
5284 dispInfo.item.iSubItem = isubitem;
5285 if (lpLVItem->mask & LVIF_TEXT)
5287 dispInfo.item.pszText = lpLVItem->pszText;
5288 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5290 if (lpLVItem->mask & LVIF_STATE)
5291 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5292 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5293 dispInfo.item.stateMask = lpLVItem->stateMask;
5294 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5296 /* full size structure expected - _WIN32IE >= 0x560 */
5297 *lpLVItem = dispInfo.item;
5299 else if (lpLVItem->mask & LVIF_INDENT)
5301 /* indent member expected - _WIN32IE >= 0x300 */
5302 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5306 /* minimal structure expected */
5307 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5309 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5312 /* make sure lParam is zeroed out */
5313 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5315 /* we store only a little state, so if we're not asked, we're done */
5316 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5318 /* if focus is handled by us, report it */
5319 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5321 lpLVItem->state &= ~LVIS_FOCUSED;
5322 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5323 lpLVItem->state |= LVIS_FOCUSED;
5326 /* and do the same for selection, if we handle it */
5327 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5329 lpLVItem->state &= ~LVIS_SELECTED;
5330 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5331 lpLVItem->state |= LVIS_SELECTED;
5337 /* find the item and subitem structures before we proceed */
5338 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5339 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5344 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5345 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5348 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5353 pItemHdr = &lpItem->hdr;
5355 /* Do we need to query the state from the app? */
5356 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5358 dispInfo.item.mask |= LVIF_STATE;
5359 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5362 /* Do we need to enquire about the image? */
5363 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5364 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5366 dispInfo.item.mask |= LVIF_IMAGE;
5367 dispInfo.item.iImage = I_IMAGECALLBACK;
5370 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5371 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5373 dispInfo.item.mask |= LVIF_TEXT;
5374 dispInfo.item.pszText = lpLVItem->pszText;
5375 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5376 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5377 *dispInfo.item.pszText = '\0';
5380 /* If we don't have all the requested info, query the application */
5381 if (dispInfo.item.mask != 0)
5383 dispInfo.item.iItem = lpLVItem->iItem;
5384 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5385 dispInfo.item.lParam = lpItem->lParam;
5386 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5387 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5390 /* we should not store values for subitems */
5391 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5393 /* Now, handle the iImage field */
5394 if (dispInfo.item.mask & LVIF_IMAGE)
5396 lpLVItem->iImage = dispInfo.item.iImage;
5397 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5398 pItemHdr->iImage = dispInfo.item.iImage;
5400 else if (lpLVItem->mask & LVIF_IMAGE)
5402 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5403 lpLVItem->iImage = pItemHdr->iImage;
5405 lpLVItem->iImage = 0;
5408 /* The pszText field */
5409 if (dispInfo.item.mask & LVIF_TEXT)
5411 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5412 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5414 lpLVItem->pszText = dispInfo.item.pszText;
5416 else if (lpLVItem->mask & LVIF_TEXT)
5418 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5419 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5422 /* Next is the lParam field */
5423 if (dispInfo.item.mask & LVIF_PARAM)
5425 lpLVItem->lParam = dispInfo.item.lParam;
5426 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5427 lpItem->lParam = dispInfo.item.lParam;
5429 else if (lpLVItem->mask & LVIF_PARAM)
5430 lpLVItem->lParam = lpItem->lParam;
5432 /* if this is a subitem, we're done */
5433 if (isubitem) return TRUE;
5435 /* ... the state field (this one is different due to uCallbackmask) */
5436 if (lpLVItem->mask & LVIF_STATE)
5438 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5439 if (dispInfo.item.mask & LVIF_STATE)
5441 lpLVItem->state &= ~dispInfo.item.stateMask;
5442 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5444 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5446 lpLVItem->state &= ~LVIS_FOCUSED;
5447 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5448 lpLVItem->state |= LVIS_FOCUSED;
5450 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5452 lpLVItem->state &= ~LVIS_SELECTED;
5453 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5454 lpLVItem->state |= LVIS_SELECTED;
5458 /* and last, but not least, the indent field */
5459 if (lpLVItem->mask & LVIF_INDENT)
5460 lpLVItem->iIndent = lpItem->iIndent;
5467 * Retrieves item attributes.
5470 * [I] hwnd : window handle
5471 * [IO] lpLVItem : item info
5472 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5473 * if FALSE, then lpLVItem is a LPLVITEMA.
5476 * This is the external 'GetItem' interface -- it properly copies
5477 * the text in the provided buffer.
5483 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5488 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5491 pszText = lpLVItem->pszText;
5492 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5493 if (bResult && lpLVItem->pszText != pszText)
5494 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5495 lpLVItem->pszText = pszText;
5503 * Retrieves the position (upper-left) of the listview control item.
5504 * Note that for LVS_ICON style, the upper-left is that of the icon
5505 * and not the bounding box.
5508 * [I] infoPtr : valid pointer to the listview structure
5509 * [I] nItem : item index
5510 * [O] lpptPosition : coordinate information
5516 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5518 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5521 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5523 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5525 LISTVIEW_GetOrigin(infoPtr, &Origin);
5526 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5528 if (uView == LVS_ICON)
5530 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5531 lpptPosition->y += ICON_TOP_PADDING;
5533 lpptPosition->x += Origin.x;
5534 lpptPosition->y += Origin.y;
5536 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5543 * Retrieves the bounding rectangle for a listview control item.
5546 * [I] infoPtr : valid pointer to the listview structure
5547 * [I] nItem : item index
5548 * [IO] lprc : bounding rectangle coordinates
5549 * lprc->left specifies the portion of the item for which the bounding
5550 * rectangle will be retrieved.
5552 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5553 * including the icon and label.
5556 * * Experiment shows that native control returns:
5557 * * width = min (48, length of text line)
5558 * * .left = position.x - (width - iconsize.cx)/2
5559 * * .right = .left + width
5560 * * height = #lines of text * ntmHeight + icon height + 8
5561 * * .top = position.y - 2
5562 * * .bottom = .top + height
5563 * * separation between items .y = itemSpacing.cy - height
5564 * * .x = itemSpacing.cx - width
5565 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5568 * * Experiment shows that native control returns:
5569 * * width = iconSize.cx + 16
5570 * * .left = position.x - (width - iconsize.cx)/2
5571 * * .right = .left + width
5572 * * height = iconSize.cy + 4
5573 * * .top = position.y - 2
5574 * * .bottom = .top + height
5575 * * separation between items .y = itemSpacing.cy - height
5576 * * .x = itemSpacing.cx - width
5577 * LVIR_LABEL Returns the bounding rectangle of the item text.
5580 * * Experiment shows that native control returns:
5581 * * width = text length
5582 * * .left = position.x - width/2
5583 * * .right = .left + width
5584 * * height = ntmH * linecount + 2
5585 * * .top = position.y + iconSize.cy + 6
5586 * * .bottom = .top + height
5587 * * separation between items .y = itemSpacing.cy - height
5588 * * .x = itemSpacing.cx - width
5589 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5590 * rectangles, but excludes columns in report view.
5597 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5598 * upon whether the window has the focus currently and on whether the item
5599 * is the one with the focus. Ensure that the control's record of which
5600 * item has the focus agrees with the items' records.
5602 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5604 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5605 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5606 BOOL doLabel = TRUE, oversizedBox = FALSE;
5607 POINT Position, Origin;
5610 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5612 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5614 LISTVIEW_GetOrigin(infoPtr, &Origin);
5615 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5617 /* Be smart and try to figure out the minimum we have to do */
5618 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5619 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5620 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5621 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5622 oversizedBox = TRUE;
5624 /* get what we need from the item before hand, so we make
5625 * only one request. This can speed up things, if data
5626 * is stored on the app side */
5628 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5629 if (doLabel) lvItem.mask |= LVIF_TEXT;
5630 lvItem.iItem = nItem;
5631 lvItem.iSubItem = 0;
5632 lvItem.pszText = szDispText;
5633 lvItem.cchTextMax = DISP_TEXT_SIZE;
5634 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5635 /* we got the state already up, simulate it here, to avoid a reget */
5636 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5638 lvItem.mask |= LVIF_STATE;
5639 lvItem.stateMask = LVIS_FOCUSED;
5640 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5643 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5644 lprc->left = LVIR_BOUNDS;
5648 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5652 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5656 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5659 case LVIR_SELECTBOUNDS:
5660 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL);
5664 WARN("Unknown value: %d\n", lprc->left);
5668 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5670 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5677 * Retrieves the spacing between listview control items.
5680 * [I] infoPtr : valid pointer to the listview structure
5681 * [IO] lprc : rectangle to receive the output
5682 * on input, lprc->top = nSubItem
5683 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5685 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5686 * not only those of the first column.
5687 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5693 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5699 if (!lprc) return FALSE;
5701 nColumn = lprc->top;
5703 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5704 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5706 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5708 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5710 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5712 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5715 lvItem.iItem = nItem;
5716 lvItem.iSubItem = nColumn;
5718 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5722 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5727 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5731 ERR("Unknown bounds=%d\n", lprc->left);
5735 OffsetRect(lprc, Position.x, Position.y);
5742 * Retrieves the width of a label.
5745 * [I] infoPtr : valid pointer to the listview structure
5748 * SUCCESS : string width (in pixels)
5751 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5753 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5756 TRACE("(nItem=%d)\n", nItem);
5758 lvItem.mask = LVIF_TEXT;
5759 lvItem.iItem = nItem;
5760 lvItem.iSubItem = 0;
5761 lvItem.pszText = szDispText;
5762 lvItem.cchTextMax = DISP_TEXT_SIZE;
5763 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5765 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5770 * Retrieves the spacing between listview control items.
5773 * [I] infoPtr : valid pointer to the listview structure
5774 * [I] bSmall : flag for small or large icon
5777 * Horizontal + vertical spacing
5779 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5785 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5789 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5790 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5792 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5799 * Retrieves the state of a listview control item.
5802 * [I] infoPtr : valid pointer to the listview structure
5803 * [I] nItem : item index
5804 * [I] uMask : state mask
5807 * State specified by the mask.
5809 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5813 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5815 lvItem.iItem = nItem;
5816 lvItem.iSubItem = 0;
5817 lvItem.mask = LVIF_STATE;
5818 lvItem.stateMask = uMask;
5819 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5821 return lvItem.state & uMask;
5826 * Retrieves the text of a listview control item or subitem.
5829 * [I] hwnd : window handle
5830 * [I] nItem : item index
5831 * [IO] lpLVItem : item information
5832 * [I] isW : TRUE if lpLVItem is Unicode
5835 * SUCCESS : string length
5838 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5840 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5842 lpLVItem->mask = LVIF_TEXT;
5843 lpLVItem->iItem = nItem;
5844 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5846 return textlenT(lpLVItem->pszText, isW);
5851 * Searches for an item based on properties + relationships.
5854 * [I] infoPtr : valid pointer to the listview structure
5855 * [I] nItem : item index
5856 * [I] uFlags : relationship flag
5859 * SUCCESS : item index
5862 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5864 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5866 LVFINDINFOW lvFindInfo;
5867 INT nCountPerColumn;
5871 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5872 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5874 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5876 if (uFlags & LVNI_CUT)
5879 if (uFlags & LVNI_DROPHILITED)
5880 uMask |= LVIS_DROPHILITED;
5882 if (uFlags & LVNI_FOCUSED)
5883 uMask |= LVIS_FOCUSED;
5885 if (uFlags & LVNI_SELECTED)
5886 uMask |= LVIS_SELECTED;
5888 /* if we're asked for the focused item, that's only one,
5889 * so it's worth optimizing */
5890 if (uFlags & LVNI_FOCUSED)
5892 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5893 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5896 if (uFlags & LVNI_ABOVE)
5898 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5903 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5909 /* Special case for autoarrange - move 'til the top of a list */
5910 if (is_autoarrange(infoPtr))
5912 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5913 while (nItem - nCountPerRow >= 0)
5915 nItem -= nCountPerRow;
5916 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5921 lvFindInfo.flags = LVFI_NEARESTXY;
5922 lvFindInfo.vkDirection = VK_UP;
5923 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5924 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5926 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5931 else if (uFlags & LVNI_BELOW)
5933 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5935 while (nItem < infoPtr->nItemCount)
5938 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5944 /* Special case for autoarrange - move 'til the bottom of a list */
5945 if (is_autoarrange(infoPtr))
5947 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5948 while (nItem + nCountPerRow < infoPtr->nItemCount )
5950 nItem += nCountPerRow;
5951 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5956 lvFindInfo.flags = LVFI_NEARESTXY;
5957 lvFindInfo.vkDirection = VK_DOWN;
5958 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5959 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5961 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5966 else if (uFlags & LVNI_TOLEFT)
5968 if (uView == LVS_LIST)
5970 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5971 while (nItem - nCountPerColumn >= 0)
5973 nItem -= nCountPerColumn;
5974 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5978 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5980 /* Special case for autoarrange - move 'ti the beginning of a row */
5981 if (is_autoarrange(infoPtr))
5983 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5984 while (nItem % nCountPerRow > 0)
5987 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5992 lvFindInfo.flags = LVFI_NEARESTXY;
5993 lvFindInfo.vkDirection = VK_LEFT;
5994 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5995 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5997 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6002 else if (uFlags & LVNI_TORIGHT)
6004 if (uView == LVS_LIST)
6006 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6007 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6009 nItem += nCountPerColumn;
6010 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6014 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6016 /* Special case for autoarrange - move 'til the end of a row */
6017 if (is_autoarrange(infoPtr))
6019 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6020 while (nItem % nCountPerRow < nCountPerRow - 1 )
6023 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6028 lvFindInfo.flags = LVFI_NEARESTXY;
6029 lvFindInfo.vkDirection = VK_RIGHT;
6030 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6031 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6033 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6042 /* search by index */
6043 for (i = nItem; i < infoPtr->nItemCount; i++)
6045 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6053 /* LISTVIEW_GetNumberOfWorkAreas */
6057 * Retrieves the origin coordinates when in icon or small icon display mode.
6060 * [I] infoPtr : valid pointer to the listview structure
6061 * [O] lpptOrigin : coordinate information
6066 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6068 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6069 INT nHorzPos = 0, nVertPos = 0;
6070 SCROLLINFO scrollInfo;
6072 scrollInfo.cbSize = sizeof(SCROLLINFO);
6073 scrollInfo.fMask = SIF_POS;
6075 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6076 nHorzPos = scrollInfo.nPos;
6077 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6078 nVertPos = scrollInfo.nPos;
6080 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6082 lpptOrigin->x = infoPtr->rcList.left;
6083 lpptOrigin->y = infoPtr->rcList.top;
6084 if (uView == LVS_LIST)
6085 nHorzPos *= infoPtr->nItemWidth;
6086 else if (uView == LVS_REPORT)
6087 nVertPos *= infoPtr->nItemHeight;
6089 lpptOrigin->x -= nHorzPos;
6090 lpptOrigin->y -= nVertPos;
6092 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6097 * Retrieves the width of a string.
6100 * [I] hwnd : window handle
6101 * [I] lpszText : text string to process
6102 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6105 * SUCCESS : string width (in pixels)
6108 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6113 if (is_textT(lpszText, isW))
6115 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6116 HDC hdc = GetDC(infoPtr->hwndSelf);
6117 HFONT hOldFont = SelectObject(hdc, hFont);
6120 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6122 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6123 SelectObject(hdc, hOldFont);
6124 ReleaseDC(infoPtr->hwndSelf, hdc);
6126 return stringSize.cx;
6131 * Determines which listview item is located at the specified position.
6134 * [I] infoPtr : valid pointer to the listview structure
6135 * [IO] lpht : hit test information
6136 * [I] subitem : fill out iSubItem.
6137 * [I] select : return the index only if the hit selects the item
6140 * (mm 20001022): We must not allow iSubItem to be touched, for
6141 * an app might pass only a structure with space up to iItem!
6142 * (MS Office 97 does that for instance in the file open dialog)
6145 * SUCCESS : item index
6148 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6150 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6151 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6152 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6153 POINT Origin, Position, opt;
6158 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6162 if (subitem) lpht->iSubItem = 0;
6164 if (infoPtr->rcList.left > lpht->pt.x)
6165 lpht->flags |= LVHT_TOLEFT;
6166 else if (infoPtr->rcList.right < lpht->pt.x)
6167 lpht->flags |= LVHT_TORIGHT;
6169 if (infoPtr->rcList.top > lpht->pt.y)
6170 lpht->flags |= LVHT_ABOVE;
6171 else if (infoPtr->rcList.bottom < lpht->pt.y)
6172 lpht->flags |= LVHT_BELOW;
6174 TRACE("lpht->flags=0x%x\n", lpht->flags);
6175 if (lpht->flags) return -1;
6177 lpht->flags |= LVHT_NOWHERE;
6179 LISTVIEW_GetOrigin(infoPtr, &Origin);
6181 /* first deal with the large items */
6182 rcSearch.left = lpht->pt.x;
6183 rcSearch.top = lpht->pt.y;
6184 rcSearch.right = rcSearch.left + 1;
6185 rcSearch.bottom = rcSearch.top + 1;
6187 iterator_frameditems(&i, infoPtr, &rcSearch);
6188 iterator_next(&i); /* go to first item in the sequence */
6190 iterator_destroy(&i);
6192 TRACE("lpht->iItem=%d\n", iItem);
6193 if (iItem == -1) return -1;
6195 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6196 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6197 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6198 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6199 lvItem.iItem = iItem;
6200 lvItem.iSubItem = 0;
6201 lvItem.pszText = szDispText;
6202 lvItem.cchTextMax = DISP_TEXT_SIZE;
6203 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6204 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6206 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcLabel);
6207 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6208 opt.x = lpht->pt.x - Position.x - Origin.x;
6209 opt.y = lpht->pt.y - Position.y - Origin.y;
6211 if (uView == LVS_REPORT)
6214 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6215 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6216 if (!PtInRect(&rcBounds, opt)) return -1;
6219 OffsetRect(&rcState, -infoPtr->iconStateSize.cx, 0);
6221 if (PtInRect(&rcIcon, opt))
6222 lpht->flags |= LVHT_ONITEMICON;
6223 else if (PtInRect(&rcLabel, opt))
6224 lpht->flags |= LVHT_ONITEMLABEL;
6225 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6226 lpht->flags |= LVHT_ONITEMSTATEICON;
6227 if (lpht->flags & LVHT_ONITEM)
6228 lpht->flags &= ~LVHT_NOWHERE;
6230 TRACE("lpht->flags=0x%x\n", lpht->flags);
6231 if (uView == LVS_REPORT && subitem)
6235 rcBounds.right = rcBounds.left;
6236 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6238 rcBounds.left = rcBounds.right;
6239 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6240 if (PtInRect(&rcBounds, opt))
6248 if (select && !(uView == LVS_REPORT &&
6249 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6250 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6252 if (uView == LVS_REPORT)
6254 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6255 UnionRect(&rcBounds, &rcBounds, &rcState);
6257 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6259 return lpht->iItem = iItem;
6263 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6264 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6265 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6266 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6267 their own sort proc. when sending LVM_SORTITEMS.
6270 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6272 LVS_SORTXXX must be specified,
6273 LVS_OWNERDRAW is not set,
6274 <item>.pszText is not LPSTR_TEXTCALLBACK.
6276 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6277 are sorted based on item text..."
6279 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6281 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6282 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6283 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6285 /* if we're sorting descending, negate the return value */
6286 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6291 * Inserts a new item in the listview control.
6294 * [I] infoPtr : valid pointer to the listview structure
6295 * [I] lpLVItem : item information
6296 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6299 * SUCCESS : new item index
6302 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6304 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6309 BOOL is_sorted, has_changed;
6311 HWND hwndSelf = infoPtr->hwndSelf;
6313 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6315 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6317 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6318 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6320 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6322 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6324 /* insert item in listview control data structure */
6325 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6326 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6328 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6329 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6331 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6333 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6334 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6335 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6336 if (nItem == -1) goto fail;
6337 infoPtr->nItemCount++;
6339 /* shift indices first so they don't get tangled */
6340 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6342 /* set the item attributes */
6343 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6345 /* full size structure expected - _WIN32IE >= 0x560 */
6348 else if (lpLVItem->mask & LVIF_INDENT)
6350 /* indent member expected - _WIN32IE >= 0x300 */
6351 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6355 /* minimal structure expected */
6356 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6359 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6361 item.mask |= LVIF_STATE;
6362 item.stateMask |= LVIS_STATEIMAGEMASK;
6363 item.state &= ~LVIS_STATEIMAGEMASK;
6364 item.state |= INDEXTOSTATEIMAGEMASK(1);
6366 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6368 /* if we're sorted, sort the list, and update the index */
6371 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6372 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6373 assert(nItem != -1);
6376 /* make room for the position, if we are in the right mode */
6377 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6379 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6381 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6383 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6388 /* send LVN_INSERTITEM notification */
6389 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6391 nmlv.lParam = lpItem->lParam;
6392 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6393 if (!IsWindow(hwndSelf))
6396 /* align items (set position of each item) */
6397 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6401 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6402 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6404 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6406 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6409 /* now is the invalidation fun */
6410 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6414 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6415 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6416 infoPtr->nItemCount--;
6418 DPA_DeletePtr(hdpaSubItems, 0);
6419 DPA_Destroy (hdpaSubItems);
6426 * Redraws a range of items.
6429 * [I] infoPtr : valid pointer to the listview structure
6430 * [I] nFirst : first item
6431 * [I] nLast : last item
6437 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6441 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6442 max(nFirst, nLast) >= infoPtr->nItemCount)
6445 for (i = nFirst; i <= nLast; i++)
6446 LISTVIEW_InvalidateItem(infoPtr, i);
6453 * Scroll the content of a listview.
6456 * [I] infoPtr : valid pointer to the listview structure
6457 * [I] dx : horizontal scroll amount in pixels
6458 * [I] dy : vertical scroll amount in pixels
6465 * If the control is in report mode (LVS_REPORT) the control can
6466 * be scrolled only in line increments. "dy" will be rounded to the
6467 * nearest number of pixels that are a whole line. Ex: if line height
6468 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6469 * is passed the the scroll will be 0. (per MSDN 7/2002)
6471 * For: (per experimentaion with native control and CSpy ListView)
6472 * LVS_ICON dy=1 = 1 pixel (vertical only)
6474 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6476 * LVS_LIST dx=1 = 1 column (horizontal only)
6477 * but will only scroll 1 column per message
6478 * no matter what the value.
6479 * dy must be 0 or FALSE returned.
6480 * LVS_REPORT dx=1 = 1 pixel
6484 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6486 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6488 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6489 dy /= infoPtr->nItemHeight;
6492 if (dy != 0) return FALSE;
6499 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6500 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6507 * Sets the background color.
6510 * [I] infoPtr : valid pointer to the listview structure
6511 * [I] clrBk : background color
6517 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6519 TRACE("(clrBk=%x)\n", clrBk);
6521 if(infoPtr->clrBk != clrBk) {
6522 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6523 infoPtr->clrBk = clrBk;
6524 if (clrBk == CLR_NONE)
6525 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6527 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6528 LISTVIEW_InvalidateList(infoPtr);
6534 /* LISTVIEW_SetBkImage */
6536 /*** Helper for {Insert,Set}ColumnT *only* */
6537 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6539 if (lpColumn->mask & LVCF_FMT)
6541 /* format member is valid */
6542 lphdi->mask |= HDI_FORMAT;
6544 /* set text alignment (leftmost column must be left-aligned) */
6545 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6546 lphdi->fmt |= HDF_LEFT;
6547 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6548 lphdi->fmt |= HDF_RIGHT;
6549 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6550 lphdi->fmt |= HDF_CENTER;
6552 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6553 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6555 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6557 lphdi->fmt |= HDF_IMAGE;
6558 lphdi->iImage = I_IMAGECALLBACK;
6562 if (lpColumn->mask & LVCF_WIDTH)
6564 lphdi->mask |= HDI_WIDTH;
6565 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6567 /* make it fill the remainder of the controls width */
6571 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6573 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6574 lphdi->cxy += rcHeader.right - rcHeader.left;
6577 /* retrieve the layout of the header */
6578 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6579 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6581 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6584 lphdi->cxy = lpColumn->cx;
6587 if (lpColumn->mask & LVCF_TEXT)
6589 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6590 lphdi->fmt |= HDF_STRING;
6591 lphdi->pszText = lpColumn->pszText;
6592 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6595 if (lpColumn->mask & LVCF_IMAGE)
6597 lphdi->mask |= HDI_IMAGE;
6598 lphdi->iImage = lpColumn->iImage;
6601 if (lpColumn->mask & LVCF_ORDER)
6603 lphdi->mask |= HDI_ORDER;
6604 lphdi->iOrder = lpColumn->iOrder;
6611 * Inserts a new column.
6614 * [I] infoPtr : valid pointer to the listview structure
6615 * [I] nColumn : column index
6616 * [I] lpColumn : column information
6617 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6620 * SUCCESS : new column index
6623 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6624 const LVCOLUMNW *lpColumn, BOOL isW)
6626 COLUMN_INFO *lpColumnInfo;
6630 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6632 if (!lpColumn || nColumn < 0) return -1;
6633 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6635 ZeroMemory(&hdi, sizeof(HDITEMW));
6636 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6639 * when the iSubItem is available Windows copies it to the header lParam. It seems
6640 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6642 if (lpColumn->mask & LVCF_SUBITEM)
6644 hdi.mask |= HDI_LPARAM;
6645 hdi.lParam = lpColumn->iSubItem;
6648 /* insert item in header control */
6649 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6650 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6651 (WPARAM)nColumn, (LPARAM)&hdi);
6652 if (nNewColumn == -1) return -1;
6653 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6655 /* create our own column info */
6656 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6657 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6659 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6660 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6662 /* now we have to actually adjust the data */
6663 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6665 SUBITEM_INFO *lpSubItem;
6669 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6671 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6672 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6674 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6675 if (lpSubItem->iSubItem >= nNewColumn)
6676 lpSubItem->iSubItem++;
6681 /* make space for the new column */
6682 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6683 LISTVIEW_UpdateItemSize(infoPtr);
6688 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6691 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6699 * Sets the attributes of a header item.
6702 * [I] infoPtr : valid pointer to the listview structure
6703 * [I] nColumn : column index
6704 * [I] lpColumn : column attributes
6705 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6711 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6712 const LVCOLUMNW *lpColumn, BOOL isW)
6714 HDITEMW hdi, hdiget;
6717 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6719 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6721 ZeroMemory(&hdi, sizeof(HDITEMW));
6722 if (lpColumn->mask & LVCF_FMT)
6724 hdi.mask |= HDI_FORMAT;
6725 hdiget.mask = HDI_FORMAT;
6726 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6727 hdi.fmt = hdiget.fmt & HDF_STRING;
6729 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6731 /* set header item attributes */
6732 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6733 if (!bResult) return FALSE;
6735 if (lpColumn->mask & LVCF_FMT)
6737 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6738 int oldFmt = lpColumnInfo->fmt;
6740 lpColumnInfo->fmt = lpColumn->fmt;
6741 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6743 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6744 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6753 * Sets the column order array
6756 * [I] infoPtr : valid pointer to the listview structure
6757 * [I] iCount : number of elements in column order array
6758 * [I] lpiArray : pointer to column order array
6764 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6766 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6777 * Sets the width of a column
6780 * [I] infoPtr : valid pointer to the listview structure
6781 * [I] nColumn : column index
6782 * [I] cx : column width
6788 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6790 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6791 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6795 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6797 /* set column width only if in report or list mode */
6798 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6800 /* take care of invalid cx values */
6801 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6802 else if (uView == LVS_LIST && cx < 1) return FALSE;
6804 /* resize all columns if in LVS_LIST mode */
6805 if(uView == LVS_LIST)
6807 infoPtr->nItemWidth = cx;
6808 LISTVIEW_InvalidateList(infoPtr);
6812 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6814 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6819 lvItem.mask = LVIF_TEXT;
6821 lvItem.iSubItem = nColumn;
6822 lvItem.pszText = szDispText;
6823 lvItem.cchTextMax = DISP_TEXT_SIZE;
6824 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6826 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6827 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6828 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6830 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6831 max_cx += infoPtr->iconSize.cx;
6832 max_cx += TRAILING_LABEL_PADDING;
6835 /* autosize based on listview items width */
6836 if(cx == LVSCW_AUTOSIZE)
6838 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6840 /* if iCol is the last column make it fill the remainder of the controls width */
6841 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6846 LISTVIEW_GetOrigin(infoPtr, &Origin);
6847 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6849 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6853 /* Despite what the MS docs say, if this is not the last
6854 column, then MS resizes the column to the width of the
6855 largest text string in the column, including headers
6856 and items. This is different from LVSCW_AUTOSIZE in that
6857 LVSCW_AUTOSIZE ignores the header string length. */
6860 /* retrieve header text */
6861 hdi.mask = HDI_TEXT;
6862 hdi.cchTextMax = DISP_TEXT_SIZE;
6863 hdi.pszText = szDispText;
6864 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
6866 HDC hdc = GetDC(infoPtr->hwndSelf);
6867 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6870 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6871 cx = size.cx + TRAILING_HEADER_PADDING;
6872 /* FIXME: Take into account the header image, if one is present */
6873 SelectObject(hdc, old_font);
6874 ReleaseDC(infoPtr->hwndSelf, hdc);
6876 cx = max (cx, max_cx);
6880 if (cx < 0) return FALSE;
6882 /* call header to update the column change */
6883 hdi.mask = HDI_WIDTH;
6885 TRACE("hdi.cxy=%d\n", hdi.cxy);
6886 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6890 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6893 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6896 HBITMAP hbm_im, hbm_mask, hbm_orig;
6898 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6899 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6902 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6903 ILC_COLOR | ILC_MASK, 2, 2);
6904 hdc_wnd = GetDC(infoPtr->hwndSelf);
6905 hdc = CreateCompatibleDC(hdc_wnd);
6906 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6907 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6908 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6910 rc.left = rc.top = 0;
6911 rc.right = GetSystemMetrics(SM_CXSMICON);
6912 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6914 hbm_orig = SelectObject(hdc, hbm_mask);
6915 FillRect(hdc, &rc, hbr_white);
6916 InflateRect(&rc, -3, -3);
6917 FillRect(hdc, &rc, hbr_black);
6919 SelectObject(hdc, hbm_im);
6920 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6921 SelectObject(hdc, hbm_orig);
6922 ImageList_Add(himl, hbm_im, hbm_mask);
6924 SelectObject(hdc, hbm_im);
6925 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6926 SelectObject(hdc, hbm_orig);
6927 ImageList_Add(himl, hbm_im, hbm_mask);
6929 DeleteObject(hbm_mask);
6930 DeleteObject(hbm_im);
6938 * Sets the extended listview style.
6941 * [I] infoPtr : valid pointer to the listview structure
6943 * [I] dwStyle : style
6946 * SUCCESS : previous style
6949 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6951 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6955 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6957 infoPtr->dwLvExStyle = dwStyle;
6959 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6961 HIMAGELIST himl = 0;
6962 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6965 item.mask = LVIF_STATE;
6966 item.stateMask = LVIS_STATEIMAGEMASK;
6967 item.state = INDEXTOSTATEIMAGEMASK(1);
6968 LISTVIEW_SetItemState(infoPtr, -1, &item);
6970 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6972 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6975 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_HEADERDRAGDROP)
6977 DWORD dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
6978 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
6979 dwStyle |= HDS_DRAGDROP;
6981 dwStyle &= ~HDS_DRAGDROP;
6982 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
6990 * Sets the new hot cursor used during hot tracking and hover selection.
6993 * [I] infoPtr : valid pointer to the listview structure
6994 * [I] hCursor : the new hot cursor handle
6997 * Returns the previous hot cursor
6999 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7001 HCURSOR oldCursor = infoPtr->hHotCursor;
7003 infoPtr->hHotCursor = hCursor;
7011 * Sets the hot item index.
7014 * [I] infoPtr : valid pointer to the listview structure
7015 * [I] iIndex : index
7018 * SUCCESS : previous hot item index
7019 * FAILURE : -1 (no hot item)
7021 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7023 INT iOldIndex = infoPtr->nHotItem;
7025 infoPtr->nHotItem = iIndex;
7033 * Sets the amount of time the cursor must hover over an item before it is selected.
7036 * [I] infoPtr : valid pointer to the listview structure
7037 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7040 * Returns the previous hover time
7042 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7044 DWORD oldHoverTime = infoPtr->dwHoverTime;
7046 infoPtr->dwHoverTime = dwHoverTime;
7048 return oldHoverTime;
7053 * Sets spacing for icons of LVS_ICON style.
7056 * [I] infoPtr : valid pointer to the listview structure
7057 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7058 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7061 * MAKELONG(oldcx, oldcy)
7063 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7065 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7066 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7068 TRACE("requested=(%d,%d)\n", cx, cy);
7070 /* this is supported only for LVS_ICON style */
7071 if (uView != LVS_ICON) return oldspacing;
7073 /* set to defaults, if instructed to */
7074 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7075 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7077 /* if 0 then compute width
7078 * FIXME: Should scan each item and determine max width of
7079 * icon or label, then make that the width */
7081 cx = infoPtr->iconSpacing.cx;
7083 /* if 0 then compute height */
7085 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7086 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7089 infoPtr->iconSpacing.cx = cx;
7090 infoPtr->iconSpacing.cy = cy;
7092 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7093 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7094 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7095 infoPtr->ntmHeight);
7097 /* these depend on the iconSpacing */
7098 LISTVIEW_UpdateItemSize(infoPtr);
7103 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7107 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7114 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7115 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7124 * [I] infoPtr : valid pointer to the listview structure
7125 * [I] nType : image list type
7126 * [I] himl : image list handle
7129 * SUCCESS : old image list
7132 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7134 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7135 INT oldHeight = infoPtr->nItemHeight;
7136 HIMAGELIST himlOld = 0;
7138 TRACE("(nType=%d, himl=%p\n", nType, himl);
7143 himlOld = infoPtr->himlNormal;
7144 infoPtr->himlNormal = himl;
7145 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7146 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7150 himlOld = infoPtr->himlSmall;
7151 infoPtr->himlSmall = himl;
7152 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7156 himlOld = infoPtr->himlState;
7157 infoPtr->himlState = himl;
7158 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7159 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7163 ERR("Unknown icon type=%d\n", nType);
7167 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7168 if (infoPtr->nItemHeight != oldHeight)
7169 LISTVIEW_UpdateScroll(infoPtr);
7176 * Preallocates memory (does *not* set the actual count of items !)
7179 * [I] infoPtr : valid pointer to the listview structure
7180 * [I] nItems : item count (projected number of items to allocate)
7181 * [I] dwFlags : update flags
7187 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7189 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7191 if (infoPtr->dwStyle & LVS_OWNERDATA)
7193 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7194 INT nOldCount = infoPtr->nItemCount;
7196 if (nItems < nOldCount)
7198 RANGE range = { nItems, nOldCount };
7199 ranges_del(infoPtr->selectionRanges, range);
7200 if (infoPtr->nFocusedItem >= nItems)
7202 infoPtr->nFocusedItem = -1;
7203 SetRectEmpty(&infoPtr->rcFocus);
7207 infoPtr->nItemCount = nItems;
7208 LISTVIEW_UpdateScroll(infoPtr);
7210 /* the flags are valid only in ownerdata report and list modes */
7211 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7213 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7214 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7216 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7217 LISTVIEW_InvalidateList(infoPtr);
7224 LISTVIEW_GetOrigin(infoPtr, &Origin);
7225 nFrom = min(nOldCount, nItems);
7226 nTo = max(nOldCount, nItems);
7228 if (uView == LVS_REPORT)
7231 rcErase.top = nFrom * infoPtr->nItemHeight;
7232 rcErase.right = infoPtr->nItemWidth;
7233 rcErase.bottom = nTo * infoPtr->nItemHeight;
7234 OffsetRect(&rcErase, Origin.x, Origin.y);
7235 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7236 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7240 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7242 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7243 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7244 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7245 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7246 OffsetRect(&rcErase, Origin.x, Origin.y);
7247 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7248 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7250 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7252 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7253 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7254 OffsetRect(&rcErase, Origin.x, Origin.y);
7255 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7256 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7262 /* According to MSDN for non-LVS_OWNERDATA this is just
7263 * a performance issue. The control allocates its internal
7264 * data structures for the number of items specified. It
7265 * cuts down on the number of memory allocations. Therefore
7266 * we will just issue a WARN here
7268 WARN("for non-ownerdata performance option not implemented.\n");
7276 * Sets the position of an item.
7279 * [I] infoPtr : valid pointer to the listview structure
7280 * [I] nItem : item index
7281 * [I] pt : coordinate
7287 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7289 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7292 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7294 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7295 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7297 LISTVIEW_GetOrigin(infoPtr, &Origin);
7299 /* This point value seems to be an undocumented feature.
7300 * The best guess is that it means either at the origin,
7301 * or at true beginning of the list. I will assume the origin. */
7302 if ((pt.x == -1) && (pt.y == -1))
7305 if (uView == LVS_ICON)
7307 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7308 pt.y -= ICON_TOP_PADDING;
7313 infoPtr->bAutoarrange = FALSE;
7315 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7320 * Sets the state of one or many items.
7323 * [I] infoPtr : valid pointer to the listview structure
7324 * [I] nItem : item index
7325 * [I] lpLVItem : item or subitem info
7331 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7333 BOOL bResult = TRUE;
7336 lvItem.iItem = nItem;
7337 lvItem.iSubItem = 0;
7338 lvItem.mask = LVIF_STATE;
7339 lvItem.state = lpLVItem->state;
7340 lvItem.stateMask = lpLVItem->stateMask;
7341 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7345 /* apply to all items */
7346 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7347 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7350 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7353 * Update selection mark
7355 * Investigation on windows 2k showed that selection mark was updated
7356 * whenever a new selection was made, but if the selected item was
7357 * unselected it was not updated.
7359 * we are probably still not 100% accurate, but this at least sets the
7360 * proper selection mark when it is needed
7363 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7364 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7367 infoPtr->nSelectionMark = -1;
7368 for (i = 0; i < infoPtr->nItemCount; i++)
7370 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7372 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7374 infoPtr->nSelectionMark = i;
7378 else if (ranges_contain(infoPtr->selectionRanges, i))
7380 infoPtr->nSelectionMark = i;
7391 * Sets the text of an item or subitem.
7394 * [I] hwnd : window handle
7395 * [I] nItem : item index
7396 * [I] lpLVItem : item or subitem info
7397 * [I] isW : TRUE if input is Unicode
7403 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7407 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7409 lvItem.iItem = nItem;
7410 lvItem.iSubItem = lpLVItem->iSubItem;
7411 lvItem.mask = LVIF_TEXT;
7412 lvItem.pszText = lpLVItem->pszText;
7413 lvItem.cchTextMax = lpLVItem->cchTextMax;
7415 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7417 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7422 * Set item index that marks the start of a multiple selection.
7425 * [I] infoPtr : valid pointer to the listview structure
7426 * [I] nIndex : index
7429 * Index number or -1 if there is no selection mark.
7431 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7433 INT nOldIndex = infoPtr->nSelectionMark;
7435 TRACE("(nIndex=%d)\n", nIndex);
7437 infoPtr->nSelectionMark = nIndex;
7444 * Sets the text background color.
7447 * [I] infoPtr : valid pointer to the listview structure
7448 * [I] clrTextBk : text background color
7454 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7456 TRACE("(clrTextBk=%x)\n", clrTextBk);
7458 if (infoPtr->clrTextBk != clrTextBk)
7460 infoPtr->clrTextBk = clrTextBk;
7461 LISTVIEW_InvalidateList(infoPtr);
7469 * Sets the text foreground color.
7472 * [I] infoPtr : valid pointer to the listview structure
7473 * [I] clrText : text color
7479 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7481 TRACE("(clrText=%x)\n", clrText);
7483 if (infoPtr->clrText != clrText)
7485 infoPtr->clrText = clrText;
7486 LISTVIEW_InvalidateList(infoPtr);
7494 * Determines which listview item is located at the specified position.
7497 * [I] infoPtr : valid pointer to the listview structure
7498 * [I] hwndNewToolTip : handle to new ToolTip
7503 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7505 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7506 infoPtr->hwndToolTip = hwndNewToolTip;
7507 return hwndOldToolTip;
7512 * sets the Unicode character format flag for the control
7514 * [I] infoPtr :valid pointer to the listview structure
7515 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7518 * Old Unicode Format
7520 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7522 BOOL rc = infoPtr->notifyFormat;
7523 infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7527 /* LISTVIEW_SetWorkAreas */
7531 * Callback internally used by LISTVIEW_SortItems()
7534 * [I] first : pointer to first ITEM_INFO to compare
7535 * [I] second : pointer to second ITEM_INFO to compare
7536 * [I] lParam : HWND of control
7539 * if first comes before second : negative
7540 * if first comes after second : positive
7541 * if first and second are equivalent : zero
7543 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7545 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7546 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7547 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7549 /* Forward the call to the client defined callback */
7550 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7555 * Sorts the listview items.
7558 * [I] infoPtr : valid pointer to the listview structure
7559 * [I] pfnCompare : application-defined value
7560 * [I] lParamSort : pointer to comparision callback
7566 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7568 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7571 LPVOID selectionMarkItem;
7575 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7577 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7579 if (!pfnCompare) return FALSE;
7580 if (!infoPtr->hdpaItems) return FALSE;
7582 /* if there are 0 or 1 items, there is no need to sort */
7583 if (infoPtr->nItemCount < 2) return TRUE;
7585 if (infoPtr->nFocusedItem >= 0)
7587 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7588 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7589 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7591 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7592 /* clear the lpItem->state for non-selected ones */
7593 /* remove the selection ranges */
7595 infoPtr->pfnCompare = pfnCompare;
7596 infoPtr->lParamSort = lParamSort;
7597 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7599 /* Adjust selections and indices so that they are the way they should
7600 * be after the sort (otherwise, the list items move around, but
7601 * whatever is at the item's previous original position will be
7604 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7605 for (i=0; i < infoPtr->nItemCount; i++)
7607 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7608 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7610 if (lpItem->state & LVIS_SELECTED)
7612 item.state = LVIS_SELECTED;
7613 item.stateMask = LVIS_SELECTED;
7614 LISTVIEW_SetItemState(infoPtr, i, &item);
7616 if (lpItem->state & LVIS_FOCUSED)
7618 infoPtr->nFocusedItem = i;
7619 lpItem->state &= ~LVIS_FOCUSED;
7622 if (selectionMarkItem != NULL)
7623 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7624 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7626 /* refresh the display */
7627 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7628 LISTVIEW_InvalidateList(infoPtr);
7635 * Update theme handle after a theme change.
7638 * [I] infoPtr : valid pointer to the listview structure
7642 * FAILURE : something else
7644 static LRESULT LISTVIEW_ThemeChanged(LISTVIEW_INFO *infoPtr)
7646 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7647 CloseThemeData(theme);
7648 OpenThemeData(infoPtr->hwndSelf, themeClass);
7654 * Updates an items or rearranges the listview control.
7657 * [I] infoPtr : valid pointer to the listview structure
7658 * [I] nItem : item index
7664 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7666 TRACE("(nItem=%d)\n", nItem);
7668 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7670 /* rearrange with default alignment style */
7671 if (is_autoarrange(infoPtr))
7672 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7674 LISTVIEW_InvalidateItem(infoPtr, nItem);
7681 * Draw the track line at the place defined in the infoPtr structure.
7682 * The line is drawn with a XOR pen so drawing the line for the second time
7683 * in the same place erases the line.
7686 * [I] infoPtr : valid pointer to the listview structure
7692 static BOOL LISTVIEW_DrawTrackLine(LISTVIEW_INFO *infoPtr)
7698 if (infoPtr->xTrackLine == -1)
7701 if (!(hdc = GetDC(infoPtr->hwndSelf)))
7703 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7704 oldROP = SetROP2(hdc, R2_XORPEN);
7705 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7706 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7707 SetROP2(hdc, oldROP);
7708 SelectObject(hdc, hOldPen);
7709 ReleaseDC(infoPtr->hwndSelf, hdc);
7715 * Called when an edit control should be displayed. This function is called after
7716 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7719 * [I] hwnd : Handle to the listview
7720 * [I] uMsg : WM_TIMER (ignored)
7721 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7722 * [I] dwTimer : The elapsed time (ignored)
7727 static CALLBACK VOID LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
7729 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
7730 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7732 KillTimer(hwnd, idEvent);
7733 editItem->fEnabled = FALSE;
7734 /* check if the item is still selected */
7735 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
7736 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
7741 * Creates the listview control - the WM_NCCREATE phase.
7744 * [I] hwnd : window handle
7745 * [I] lpcs : the create parameters
7751 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
7753 LISTVIEW_INFO *infoPtr;
7756 TRACE("(lpcs=%p)\n", lpcs);
7758 /* initialize info pointer */
7759 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
7760 if (!infoPtr) return FALSE;
7762 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7764 infoPtr->hwndSelf = hwnd;
7765 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
7766 /* determine the type of structures to use */
7767 infoPtr->hwndNotify = lpcs->hwndParent;
7768 /* infoPtr->notifyFormat will be filled in WM_CREATE */
7770 /* initialize color information */
7771 infoPtr->clrBk = CLR_NONE;
7772 infoPtr->clrText = CLR_DEFAULT;
7773 infoPtr->clrTextBk = CLR_DEFAULT;
7774 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7776 /* set default values */
7777 infoPtr->nFocusedItem = -1;
7778 infoPtr->nSelectionMark = -1;
7779 infoPtr->nHotItem = -1;
7780 infoPtr->bRedraw = TRUE;
7781 infoPtr->bNoItemMetrics = TRUE;
7782 infoPtr->bDoChangeNotify = TRUE;
7783 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7784 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7785 infoPtr->nEditLabelItem = -1;
7786 infoPtr->dwHoverTime = -1; /* default system hover time */
7787 infoPtr->nMeasureItemHeight = 0;
7788 infoPtr->xTrackLine = -1; /* no track line */
7789 infoPtr->itemEdit.fEnabled = FALSE;
7791 /* get default font (icon title) */
7792 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7793 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7794 infoPtr->hFont = infoPtr->hDefaultFont;
7795 LISTVIEW_SaveTextMetrics(infoPtr);
7797 /* allocate memory for the data structure */
7798 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7799 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7800 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7801 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7802 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7806 DestroyWindow(infoPtr->hwndHeader);
7807 ranges_destroy(infoPtr->selectionRanges);
7808 DPA_Destroy(infoPtr->hdpaItems);
7809 DPA_Destroy(infoPtr->hdpaPosX);
7810 DPA_Destroy(infoPtr->hdpaPosY);
7811 DPA_Destroy(infoPtr->hdpaColumns);
7818 * Creates the listview control - the WM_CREATE phase. Most of the data is
7819 * already set up in LISTVIEW_NCCreate
7822 * [I] hwnd : window handle
7823 * [I] lpcs : the create parameters
7829 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7831 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7832 UINT uView = lpcs->style & LVS_TYPEMASK;
7834 TRACE("(lpcs=%p)\n", lpcs);
7836 infoPtr->dwStyle = lpcs->style;
7837 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7838 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7841 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7842 WS_CHILD | HDS_HORZ | HDS_FULLDRAG | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7843 0, 0, 0, 0, hwnd, NULL,
7844 lpcs->hInstance, NULL);
7845 if (!infoPtr->hwndHeader) return -1;
7847 /* set header unicode format */
7848 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7850 /* set header font */
7851 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7853 /* init item size to avoid division by 0 */
7854 LISTVIEW_UpdateItemSize (infoPtr);
7856 if (uView == LVS_REPORT)
7858 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7860 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7864 /* set HDS_HIDDEN flag to hide the header bar */
7865 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7866 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7870 OpenThemeData(hwnd, themeClass);
7872 /* initialize the icon sizes */
7873 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7874 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7880 * Destroys the listview control.
7883 * [I] infoPtr : valid pointer to the listview structure
7889 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
7891 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7892 CloseThemeData(theme);
7898 * Enables the listview control.
7901 * [I] infoPtr : valid pointer to the listview structure
7902 * [I] bEnable : specifies whether to enable or disable the window
7908 static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable)
7910 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7911 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7917 * Erases the background of the listview control.
7920 * [I] infoPtr : valid pointer to the listview structure
7921 * [I] hdc : device context handle
7927 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7931 TRACE("(hdc=%p)\n", hdc);
7933 if (!GetClipBox(hdc, &rc)) return FALSE;
7935 /* for double buffered controls we need to do this during refresh */
7936 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
7938 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7944 * Helper function for LISTVIEW_[HV]Scroll *only*.
7945 * Performs vertical/horizontal scrolling by a give amount.
7948 * [I] infoPtr : valid pointer to the listview structure
7949 * [I] dx : amount of horizontal scroll
7950 * [I] dy : amount of vertical scroll
7952 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7954 /* now we can scroll the list */
7955 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7956 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7957 /* if we have focus, adjust rect */
7958 OffsetRect(&infoPtr->rcFocus, dx, dy);
7959 UpdateWindow(infoPtr->hwndSelf);
7964 * Performs vertical scrolling.
7967 * [I] infoPtr : valid pointer to the listview structure
7968 * [I] nScrollCode : scroll code
7969 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7970 * [I] hScrollWnd : scrollbar control window handle
7976 * SB_LINEUP/SB_LINEDOWN:
7977 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7978 * for LVS_REPORT is 1 line
7979 * for LVS_LIST cannot occur
7982 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7983 INT nScrollDiff, HWND hScrollWnd)
7985 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7986 INT nOldScrollPos, nNewScrollPos;
7987 SCROLLINFO scrollInfo;
7990 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7991 debugscrollcode(nScrollCode), nScrollDiff);
7993 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7995 scrollInfo.cbSize = sizeof(SCROLLINFO);
7996 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7998 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8000 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8002 nOldScrollPos = scrollInfo.nPos;
8003 switch (nScrollCode)
8009 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8013 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8017 nScrollDiff = -scrollInfo.nPage;
8021 nScrollDiff = scrollInfo.nPage;
8024 case SB_THUMBPOSITION:
8026 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8033 /* quit right away if pos isn't changing */
8034 if (nScrollDiff == 0) return 0;
8036 /* calculate new position, and handle overflows */
8037 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8038 if (nScrollDiff > 0) {
8039 if (nNewScrollPos < nOldScrollPos ||
8040 nNewScrollPos > scrollInfo.nMax)
8041 nNewScrollPos = scrollInfo.nMax;
8043 if (nNewScrollPos > nOldScrollPos ||
8044 nNewScrollPos < scrollInfo.nMin)
8045 nNewScrollPos = scrollInfo.nMin;
8048 /* set the new position, and reread in case it changed */
8049 scrollInfo.fMask = SIF_POS;
8050 scrollInfo.nPos = nNewScrollPos;
8051 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8053 /* carry on only if it really changed */
8054 if (nNewScrollPos == nOldScrollPos) return 0;
8056 /* now adjust to client coordinates */
8057 nScrollDiff = nOldScrollPos - nNewScrollPos;
8058 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8060 /* and scroll the window */
8061 scroll_list(infoPtr, 0, nScrollDiff);
8068 * Performs horizontal scrolling.
8071 * [I] infoPtr : valid pointer to the listview structure
8072 * [I] nScrollCode : scroll code
8073 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8074 * [I] hScrollWnd : scrollbar control window handle
8080 * SB_LINELEFT/SB_LINERIGHT:
8081 * for LVS_ICON, LVS_SMALLICON 1 pixel
8082 * for LVS_REPORT is 1 pixel
8083 * for LVS_LIST is 1 column --> which is a 1 because the
8084 * scroll is based on columns not pixels
8087 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8088 INT nScrollDiff, HWND hScrollWnd)
8090 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8091 INT nOldScrollPos, nNewScrollPos;
8092 SCROLLINFO scrollInfo;
8094 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8095 debugscrollcode(nScrollCode), nScrollDiff);
8097 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8099 scrollInfo.cbSize = sizeof(SCROLLINFO);
8100 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8102 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8104 nOldScrollPos = scrollInfo.nPos;
8106 switch (nScrollCode)
8120 nScrollDiff = -scrollInfo.nPage;
8124 nScrollDiff = scrollInfo.nPage;
8127 case SB_THUMBPOSITION:
8129 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8136 /* quit right away if pos isn't changing */
8137 if (nScrollDiff == 0) return 0;
8139 /* calculate new position, and handle overflows */
8140 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8141 if (nScrollDiff > 0) {
8142 if (nNewScrollPos < nOldScrollPos ||
8143 nNewScrollPos > scrollInfo.nMax)
8144 nNewScrollPos = scrollInfo.nMax;
8146 if (nNewScrollPos > nOldScrollPos ||
8147 nNewScrollPos < scrollInfo.nMin)
8148 nNewScrollPos = scrollInfo.nMin;
8151 /* set the new position, and reread in case it changed */
8152 scrollInfo.fMask = SIF_POS;
8153 scrollInfo.nPos = nNewScrollPos;
8154 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8156 /* carry on only if it really changed */
8157 if (nNewScrollPos == nOldScrollPos) return 0;
8159 if(uView == LVS_REPORT)
8160 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8162 /* now adjust to client coordinates */
8163 nScrollDiff = nOldScrollPos - nNewScrollPos;
8164 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8166 /* and scroll the window */
8167 scroll_list(infoPtr, nScrollDiff, 0);
8172 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8174 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8175 INT gcWheelDelta = 0;
8176 INT pulScrollLines = 3;
8177 SCROLLINFO scrollInfo;
8179 TRACE("(wheelDelta=%d)\n", wheelDelta);
8181 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8182 gcWheelDelta -= wheelDelta;
8184 scrollInfo.cbSize = sizeof(SCROLLINFO);
8185 scrollInfo.fMask = SIF_POS;
8192 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8193 * should be fixed in the future.
8195 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8196 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8200 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8202 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8203 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8204 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8209 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8220 * [I] infoPtr : valid pointer to the listview structure
8221 * [I] nVirtualKey : virtual key
8222 * [I] lKeyData : key data
8227 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8229 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8230 HWND hwndSelf = infoPtr->hwndSelf;
8232 NMLVKEYDOWN nmKeyDown;
8234 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8236 /* send LVN_KEYDOWN notification */
8237 nmKeyDown.wVKey = nVirtualKey;
8238 nmKeyDown.flags = 0;
8239 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8240 if (!IsWindow(hwndSelf))
8243 switch (nVirtualKey)
8246 nItem = infoPtr->nFocusedItem;
8250 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8252 if (!notify(infoPtr, NM_RETURN)) return 0;
8253 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8258 if (infoPtr->nItemCount > 0)
8263 if (infoPtr->nItemCount > 0)
8264 nItem = infoPtr->nItemCount - 1;
8268 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8272 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8276 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8280 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8284 if (uView == LVS_REPORT)
8286 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8287 if (infoPtr->nFocusedItem == topidx)
8288 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8293 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8294 * LISTVIEW_GetCountPerRow(infoPtr);
8295 if(nItem < 0) nItem = 0;
8299 if (uView == LVS_REPORT)
8301 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8302 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8303 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8304 nItem = infoPtr->nFocusedItem + cnt - 1;
8306 nItem = topidx + cnt - 1;
8309 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8310 * LISTVIEW_GetCountPerRow(infoPtr);
8311 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8315 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8316 LISTVIEW_KeySelection(infoPtr, nItem);
8326 * [I] infoPtr : valid pointer to the listview structure
8331 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8335 /* if we did not have the focus, there's nothing to do */
8336 if (!infoPtr->bFocus) return 0;
8338 /* send NM_KILLFOCUS notification */
8339 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8341 /* if we have a focus rectagle, get rid of it */
8342 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8344 /* set window focus flag */
8345 infoPtr->bFocus = FALSE;
8347 /* invalidate the selected items before reseting focus flag */
8348 LISTVIEW_InvalidateSelectedItems(infoPtr);
8355 * Processes double click messages (left mouse button).
8358 * [I] infoPtr : valid pointer to the listview structure
8359 * [I] wKey : key flag
8360 * [I] x,y : mouse coordinate
8365 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8367 LVHITTESTINFO htInfo;
8369 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8371 /* Cancel the item edition if any */
8372 if (infoPtr->itemEdit.fEnabled)
8374 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8375 infoPtr->itemEdit.fEnabled = FALSE;
8378 /* send NM_RELEASEDCAPTURE notification */
8379 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8384 /* send NM_DBLCLK notification */
8385 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8386 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8388 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8389 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8396 * Processes mouse down messages (left mouse button).
8399 * infoPtr [I ] valid pointer to the listview structure
8400 * wKey [I ] key flag
8401 * x,y [I ] mouse coordinate
8406 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8408 LVHITTESTINFO lvHitTestInfo;
8409 static BOOL bGroupSelect = TRUE;
8410 BOOL bReceivedFocus = FALSE;
8411 POINT pt = { x, y };
8414 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8416 /* send NM_RELEASEDCAPTURE notification */
8417 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8419 if (!infoPtr->bFocus)
8421 bReceivedFocus = TRUE;
8422 SetFocus(infoPtr->hwndSelf);
8425 /* set left button down flag and record the click position */
8426 infoPtr->bLButtonDown = TRUE;
8427 infoPtr->ptClickPos = pt;
8429 lvHitTestInfo.pt.x = x;
8430 lvHitTestInfo.pt.y = y;
8432 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8433 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8434 infoPtr->nEditLabelItem = -1;
8435 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8437 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8439 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8440 if(state == 1 || state == 2)
8444 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8445 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8446 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8451 if (infoPtr->dwStyle & LVS_SINGLESEL)
8453 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8454 infoPtr->nEditLabelItem = nItem;
8456 LISTVIEW_SetSelection(infoPtr, nItem);
8460 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8464 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8465 LISTVIEW_SetItemFocus(infoPtr, nItem);
8466 infoPtr->nSelectionMark = nItem;
8472 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8473 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8475 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8476 infoPtr->nSelectionMark = nItem;
8479 else if (wKey & MK_CONTROL)
8483 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8485 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8486 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8487 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8488 infoPtr->nSelectionMark = nItem;
8490 else if (wKey & MK_SHIFT)
8492 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8496 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8497 infoPtr->nEditLabelItem = nItem;
8499 /* set selection (clears other pre-existing selections) */
8500 LISTVIEW_SetSelection(infoPtr, nItem);
8506 /* remove all selections */
8507 LISTVIEW_DeselectAll(infoPtr);
8512 infoPtr->nEditLabelItem = -1;
8519 * Processes mouse up messages (left mouse button).
8522 * infoPtr [I ] valid pointer to the listview structure
8523 * wKey [I ] key flag
8524 * x,y [I ] mouse coordinate
8529 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8531 LVHITTESTINFO lvHitTestInfo;
8533 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8535 if (!infoPtr->bLButtonDown) return 0;
8537 lvHitTestInfo.pt.x = x;
8538 lvHitTestInfo.pt.y = y;
8540 /* send NM_CLICK notification */
8541 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8542 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8544 /* set left button flag */
8545 infoPtr->bLButtonDown = FALSE;
8547 /* if we clicked on a selected item, edit the label */
8548 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8550 /* we want to make sure the user doesn't want to do a double click. So we will
8551 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8553 infoPtr->itemEdit.fEnabled = TRUE;
8554 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8555 SetTimer(infoPtr->hwndSelf,
8556 (UINT_PTR)&infoPtr->itemEdit,
8557 GetDoubleClickTime(),
8558 LISTVIEW_DelayedEditItem);
8566 * Destroys the listview control (called after WM_DESTROY).
8569 * [I] infoPtr : valid pointer to the listview structure
8574 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8578 /* delete all items */
8579 LISTVIEW_DeleteAllItems(infoPtr);
8581 /* destroy data structure */
8582 DPA_Destroy(infoPtr->hdpaItems);
8583 DPA_Destroy(infoPtr->hdpaPosX);
8584 DPA_Destroy(infoPtr->hdpaPosY);
8585 DPA_Destroy(infoPtr->hdpaColumns);
8586 ranges_destroy(infoPtr->selectionRanges);
8588 /* destroy image lists */
8589 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8591 if (infoPtr->himlNormal)
8592 ImageList_Destroy(infoPtr->himlNormal);
8593 if (infoPtr->himlSmall)
8594 ImageList_Destroy(infoPtr->himlSmall);
8595 if (infoPtr->himlState)
8596 ImageList_Destroy(infoPtr->himlState);
8599 /* destroy font, bkgnd brush */
8601 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8602 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8604 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8606 /* free listview info pointer*/
8614 * Handles notifications from header.
8617 * [I] infoPtr : valid pointer to the listview structure
8618 * [I] nCtrlId : control identifier
8619 * [I] lpnmh : notification information
8624 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8626 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8627 HWND hwndSelf = infoPtr->hwndSelf;
8629 TRACE("(lpnmh=%p)\n", lpnmh);
8631 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8633 switch (lpnmh->hdr.code)
8638 COLUMN_INFO *lpColumnInfo;
8642 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8645 /* remove the old line (if any) */
8646 LISTVIEW_DrawTrackLine(infoPtr);
8648 /* compute & draw the new line */
8649 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8650 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8651 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8652 infoPtr->xTrackLine = x + ptOrigin.x;
8653 LISTVIEW_DrawTrackLine(infoPtr);
8659 /* remove the track line (if any) */
8660 LISTVIEW_DrawTrackLine(infoPtr);
8661 infoPtr->xTrackLine = -1;
8665 FIXME("Changing column order not implemented\n");
8668 case HDN_ITEMCHANGINGW:
8669 case HDN_ITEMCHANGINGA:
8670 return notify_forward_header(infoPtr, lpnmh);
8672 case HDN_ITEMCHANGEDW:
8673 case HDN_ITEMCHANGEDA:
8675 COLUMN_INFO *lpColumnInfo;
8678 notify_forward_header(infoPtr, lpnmh);
8679 if (!IsWindow(hwndSelf))
8682 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8686 hdi.mask = HDI_WIDTH;
8687 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8691 cxy = lpnmh->pitem->cxy;
8693 /* determine how much we change since the last know position */
8694 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8695 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8698 lpColumnInfo->rcHeader.right += dx;
8699 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
8700 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8703 /* only needs to update the scrolls */
8704 infoPtr->nItemWidth += dx;
8705 LISTVIEW_UpdateScroll(infoPtr);
8707 LISTVIEW_UpdateItemSize(infoPtr);
8708 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8711 RECT rcCol = lpColumnInfo->rcHeader;
8713 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8714 OffsetRect(&rcCol, ptOrigin.x, 0);
8716 rcCol.top = infoPtr->rcList.top;
8717 rcCol.bottom = infoPtr->rcList.bottom;
8719 /* resizing left-aligned columns leaves most of the left side untouched */
8720 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8722 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
8725 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8728 /* when shrinking the last column clear the now unused field */
8729 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1 && dx < 0)
8732 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8738 case HDN_ITEMCLICKW:
8739 case HDN_ITEMCLICKA:
8741 /* Handle sorting by Header Column */
8744 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8746 nmlv.iSubItem = lpnmh->iItem;
8747 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8751 case HDN_DIVIDERDBLCLICKW:
8752 case HDN_DIVIDERDBLCLICKA:
8753 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8762 * Paint non-client area of control.
8765 * [I] infoPtr : valid pointer to the listview structureof the sender
8766 * [I] region : update region
8769 * TRUE - frame was painted
8770 * FALSE - call default window proc
8772 static BOOL LISTVIEW_NCPaint(LISTVIEW_INFO *infoPtr, HRGN region)
8774 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8778 int cxEdge = GetSystemMetrics (SM_CXEDGE),
8779 cyEdge = GetSystemMetrics (SM_CYEDGE);
8781 if (!theme) return FALSE;
8783 GetWindowRect(infoPtr->hwndSelf, &r);
8785 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8786 r.right - cxEdge, r.bottom - cyEdge);
8787 if (region != (HRGN)1)
8788 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8789 OffsetRect(&r, -r.left, -r.top);
8791 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8792 OffsetRect(&r, -r.left, -r.top);
8794 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8795 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8796 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8797 ReleaseDC(infoPtr->hwndSelf, dc);
8799 /* Call default proc to get the scrollbars etc. painted */
8800 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8807 * Determines the type of structure to use.
8810 * [I] infoPtr : valid pointer to the listview structureof the sender
8811 * [I] hwndFrom : listview window handle
8812 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8817 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8819 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8821 if (nCommand != NF_REQUERY) return 0;
8823 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8830 * Paints/Repaints the listview control.
8833 * [I] infoPtr : valid pointer to the listview structure
8834 * [I] hdc : device context handle
8839 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8841 TRACE("(hdc=%p)\n", hdc);
8843 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8845 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8847 infoPtr->bNoItemMetrics = FALSE;
8848 LISTVIEW_UpdateItemSize(infoPtr);
8849 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8850 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8851 LISTVIEW_UpdateScroll(infoPtr);
8854 UpdateWindow(infoPtr->hwndHeader);
8857 LISTVIEW_Refresh(infoPtr, hdc, NULL);
8862 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8864 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
8865 EndPaint(infoPtr->hwndSelf, &ps);
8874 * Paints/Repaints the listview control.
8877 * [I] infoPtr : valid pointer to the listview structure
8878 * [I] hdc : device context handle
8879 * [I] options : drawing options
8884 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8886 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
8888 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8891 if (options & PRF_ERASEBKGND)
8892 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8894 if (options & PRF_CLIENT)
8895 LISTVIEW_Paint(infoPtr, hdc);
8903 * Processes double click messages (right mouse button).
8906 * [I] infoPtr : valid pointer to the listview structure
8907 * [I] wKey : key flag
8908 * [I] x,y : mouse coordinate
8913 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8915 LVHITTESTINFO lvHitTestInfo;
8917 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8919 /* send NM_RELEASEDCAPTURE notification */
8920 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8922 /* send NM_RDBLCLK notification */
8923 lvHitTestInfo.pt.x = x;
8924 lvHitTestInfo.pt.y = y;
8925 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8926 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8933 * Processes mouse down messages (right mouse button).
8936 * [I] infoPtr : valid pointer to the listview structure
8937 * [I] wKey : key flag
8938 * [I] x,y : mouse coordinate
8943 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8945 LVHITTESTINFO lvHitTestInfo;
8948 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8950 /* send NM_RELEASEDCAPTURE notification */
8951 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8953 /* make sure the listview control window has the focus */
8954 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8956 /* set right button down flag */
8957 infoPtr->bRButtonDown = TRUE;
8959 /* determine the index of the selected item */
8960 lvHitTestInfo.pt.x = x;
8961 lvHitTestInfo.pt.y = y;
8962 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8964 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8966 LISTVIEW_SetItemFocus(infoPtr, nItem);
8967 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8968 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8969 LISTVIEW_SetSelection(infoPtr, nItem);
8973 LISTVIEW_DeselectAll(infoPtr);
8981 * Processes mouse up messages (right mouse button).
8984 * [I] infoPtr : valid pointer to the listview structure
8985 * [I] wKey : key flag
8986 * [I] x,y : mouse coordinate
8991 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8993 LVHITTESTINFO lvHitTestInfo;
8996 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8998 if (!infoPtr->bRButtonDown) return 0;
9000 /* set button flag */
9001 infoPtr->bRButtonDown = FALSE;
9003 /* Send NM_RClICK notification */
9004 lvHitTestInfo.pt.x = x;
9005 lvHitTestInfo.pt.y = y;
9006 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9007 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9009 /* Change to screen coordinate for WM_CONTEXTMENU */
9010 pt = lvHitTestInfo.pt;
9011 ClientToScreen(infoPtr->hwndSelf, &pt);
9013 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9014 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9015 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9026 * [I] infoPtr : valid pointer to the listview structure
9027 * [I] hwnd : window handle of window containing the cursor
9028 * [I] nHittest : hit-test code
9029 * [I] wMouseMsg : ideintifier of the mouse message
9032 * TRUE if cursor is set
9035 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9037 LVHITTESTINFO lvHitTestInfo;
9039 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
9041 if(!infoPtr->hHotCursor) return FALSE;
9043 GetCursorPos(&lvHitTestInfo.pt);
9044 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9046 SetCursor(infoPtr->hHotCursor);
9056 * [I] infoPtr : valid pointer to the listview structure
9057 * [I] hwndLoseFocus : handle of previously focused window
9062 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9064 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9066 /* if we have the focus already, there's nothing to do */
9067 if (infoPtr->bFocus) return 0;
9069 /* send NM_SETFOCUS notification */
9070 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9072 /* set window focus flag */
9073 infoPtr->bFocus = TRUE;
9075 /* put the focus rect back on */
9076 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9078 /* redraw all visible selected items */
9079 LISTVIEW_InvalidateSelectedItems(infoPtr);
9089 * [I] infoPtr : valid pointer to the listview structure
9090 * [I] fRedraw : font handle
9091 * [I] fRedraw : redraw flag
9096 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9098 HFONT oldFont = infoPtr->hFont;
9100 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9102 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9103 if (infoPtr->hFont == oldFont) return 0;
9105 LISTVIEW_SaveTextMetrics(infoPtr);
9107 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9108 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9110 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9117 * Message handling for WM_SETREDRAW.
9118 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9121 * [I] infoPtr : valid pointer to the listview structure
9122 * [I] bRedraw: state of redraw flag
9125 * DefWinProc return value
9127 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9129 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9131 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9132 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9134 infoPtr->bRedraw = bRedraw;
9136 if(!bRedraw) return 0;
9138 if (is_autoarrange(infoPtr))
9139 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9140 LISTVIEW_UpdateScroll(infoPtr);
9142 /* despite what the WM_SETREDRAW docs says, apps expect us
9143 * to invalidate the listview here... stupid! */
9144 LISTVIEW_InvalidateList(infoPtr);
9151 * Resizes the listview control. This function processes WM_SIZE
9152 * messages. At this time, the width and height are not used.
9155 * [I] infoPtr : valid pointer to the listview structure
9156 * [I] Width : new width
9157 * [I] Height : new height
9162 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9164 RECT rcOld = infoPtr->rcList;
9166 TRACE("(width=%d, height=%d)\n", Width, Height);
9168 LISTVIEW_UpdateSize(infoPtr);
9169 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9171 /* do not bother with display related stuff if we're not redrawing */
9172 if (!is_redrawing(infoPtr)) return 0;
9174 if (is_autoarrange(infoPtr))
9175 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9177 LISTVIEW_UpdateScroll(infoPtr);
9179 /* refresh all only for lists whose height changed significantly */
9180 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
9181 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9182 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9183 LISTVIEW_InvalidateList(infoPtr);
9190 * Sets the size information.
9193 * [I] infoPtr : valid pointer to the listview structure
9198 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9200 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9202 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9204 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9206 if (uView == LVS_LIST)
9208 /* Apparently the "LIST" style is supposed to have the same
9209 * number of items in a column even if there is no scroll bar.
9210 * Since if a scroll bar already exists then the bottom is already
9211 * reduced, only reduce if the scroll bar does not currently exist.
9212 * The "2" is there to mimic the native control. I think it may be
9213 * related to either padding or edges. (GLA 7/2002)
9215 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9216 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9217 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9219 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
9224 hl.prc = &infoPtr->rcList;
9226 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9228 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9230 infoPtr->rcList.top = max(wp.cy, 0);
9233 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9238 * Processes WM_STYLECHANGED messages.
9241 * [I] infoPtr : valid pointer to the listview structure
9242 * [I] wStyleType : window style type (normal or extended)
9243 * [I] lpss : window style information
9248 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9249 const STYLESTRUCT *lpss)
9251 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9252 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9254 TRACE("(styletype=%x, styleOld=0x%08x, styleNew=0x%08x)\n",
9255 wStyleType, lpss->styleOld, lpss->styleNew);
9257 if (wStyleType != GWL_STYLE) return 0;
9259 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9260 /* what if LVS_OWNERDATA changed? */
9261 /* or LVS_SINGLESEL */
9262 /* or LVS_SORT{AS,DES}CENDING */
9264 infoPtr->dwStyle = lpss->styleNew;
9266 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9267 ((lpss->styleNew & WS_HSCROLL) == 0))
9268 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9270 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9271 ((lpss->styleNew & WS_VSCROLL) == 0))
9272 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9274 if (uNewView != uOldView)
9276 SIZE oldIconSize = infoPtr->iconSize;
9279 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9280 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9282 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9283 SetRectEmpty(&infoPtr->rcFocus);
9285 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9286 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9288 if (uNewView == LVS_ICON)
9290 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9292 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9293 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9294 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9297 else if (uNewView == LVS_REPORT)
9302 hl.prc = &infoPtr->rcList;
9304 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9305 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9308 LISTVIEW_UpdateItemSize(infoPtr);
9311 if (uNewView == LVS_REPORT)
9312 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
9314 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9315 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9316 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9318 /* update the size of the client area */
9319 LISTVIEW_UpdateSize(infoPtr);
9321 /* add scrollbars if needed */
9322 LISTVIEW_UpdateScroll(infoPtr);
9324 /* invalidate client area + erase background */
9325 LISTVIEW_InvalidateList(infoPtr);
9332 * Window procedure of the listview control.
9335 static LRESULT WINAPI
9336 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9338 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9340 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
9342 if (!infoPtr && (uMsg != WM_NCCREATE))
9343 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9347 case LVM_APPROXIMATEVIEWRECT:
9348 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9349 LOWORD(lParam), HIWORD(lParam));
9351 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9353 /* case LVM_CANCELEDITLABEL: */
9355 case LVM_CREATEDRAGIMAGE:
9356 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9358 case LVM_DELETEALLITEMS:
9359 return LISTVIEW_DeleteAllItems(infoPtr);
9361 case LVM_DELETECOLUMN:
9362 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9364 case LVM_DELETEITEM:
9365 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9367 case LVM_EDITLABELW:
9368 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9370 case LVM_EDITLABELA:
9371 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9373 /* case LVM_ENABLEGROUPVIEW: */
9375 case LVM_ENSUREVISIBLE:
9376 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9379 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9382 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9384 case LVM_GETBKCOLOR:
9385 return infoPtr->clrBk;
9387 /* case LVM_GETBKIMAGE: */
9389 case LVM_GETCALLBACKMASK:
9390 return infoPtr->uCallbackMask;
9392 case LVM_GETCOLUMNA:
9393 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9395 case LVM_GETCOLUMNW:
9396 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9398 case LVM_GETCOLUMNORDERARRAY:
9399 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9401 case LVM_GETCOLUMNWIDTH:
9402 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9404 case LVM_GETCOUNTPERPAGE:
9405 return LISTVIEW_GetCountPerPage(infoPtr);
9407 case LVM_GETEDITCONTROL:
9408 return (LRESULT)infoPtr->hwndEdit;
9410 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9411 return infoPtr->dwLvExStyle;
9413 /* case LVM_GETGROUPINFO: */
9415 /* case LVM_GETGROUPMETRICS: */
9418 return (LRESULT)infoPtr->hwndHeader;
9420 case LVM_GETHOTCURSOR:
9421 return (LRESULT)infoPtr->hHotCursor;
9423 case LVM_GETHOTITEM:
9424 return infoPtr->nHotItem;
9426 case LVM_GETHOVERTIME:
9427 return infoPtr->dwHoverTime;
9429 case LVM_GETIMAGELIST:
9430 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9432 /* case LVM_GETINSERTMARK: */
9434 /* case LVM_GETINSERTMARKCOLOR: */
9436 /* case LVM_GETINSERTMARKRECT: */
9438 case LVM_GETISEARCHSTRINGA:
9439 case LVM_GETISEARCHSTRINGW:
9440 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9444 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9447 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9449 case LVM_GETITEMCOUNT:
9450 return infoPtr->nItemCount;
9452 case LVM_GETITEMPOSITION:
9453 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9455 case LVM_GETITEMRECT:
9456 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9458 case LVM_GETITEMSPACING:
9459 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9461 case LVM_GETITEMSTATE:
9462 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9464 case LVM_GETITEMTEXTA:
9465 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9467 case LVM_GETITEMTEXTW:
9468 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9470 case LVM_GETNEXTITEM:
9471 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9473 case LVM_GETNUMBEROFWORKAREAS:
9474 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9478 if (!lParam) return FALSE;
9479 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9482 /* case LVM_GETOUTLINECOLOR: */
9484 /* case LVM_GETSELECTEDCOLUMN: */
9486 case LVM_GETSELECTEDCOUNT:
9487 return LISTVIEW_GetSelectedCount(infoPtr);
9489 case LVM_GETSELECTIONMARK:
9490 return infoPtr->nSelectionMark;
9492 case LVM_GETSTRINGWIDTHA:
9493 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9495 case LVM_GETSTRINGWIDTHW:
9496 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9498 case LVM_GETSUBITEMRECT:
9499 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9501 case LVM_GETTEXTBKCOLOR:
9502 return infoPtr->clrTextBk;
9504 case LVM_GETTEXTCOLOR:
9505 return infoPtr->clrText;
9507 /* case LVM_GETTILEINFO: */
9509 /* case LVM_GETTILEVIEWINFO: */
9511 case LVM_GETTOOLTIPS:
9512 if( !infoPtr->hwndToolTip )
9513 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9514 return (LRESULT)infoPtr->hwndToolTip;
9516 case LVM_GETTOPINDEX:
9517 return LISTVIEW_GetTopIndex(infoPtr);
9519 /*case LVM_GETUNICODEFORMAT:
9520 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9523 /* case LVM_GETVIEW: */
9525 case LVM_GETVIEWRECT:
9526 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9528 case LVM_GETWORKAREAS:
9529 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9532 /* case LVM_HASGROUP: */
9535 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9537 case LVM_INSERTCOLUMNA:
9538 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9540 case LVM_INSERTCOLUMNW:
9541 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9543 /* case LVM_INSERTGROUP: */
9545 /* case LVM_INSERTGROUPSORTED: */
9547 case LVM_INSERTITEMA:
9548 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9550 case LVM_INSERTITEMW:
9551 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9553 /* case LVM_INSERTMARKHITTEST: */
9555 /* case LVM_ISGROUPVIEWENABLED: */
9557 /* case LVM_MAPIDTOINDEX: */
9559 /* case LVM_MAPINDEXTOID: */
9561 /* case LVM_MOVEGROUP: */
9563 /* case LVM_MOVEITEMTOGROUP: */
9565 case LVM_REDRAWITEMS:
9566 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9568 /* case LVM_REMOVEALLGROUPS: */
9570 /* case LVM_REMOVEGROUP: */
9573 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9575 case LVM_SETBKCOLOR:
9576 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9578 /* case LVM_SETBKIMAGE: */
9580 case LVM_SETCALLBACKMASK:
9581 infoPtr->uCallbackMask = (UINT)wParam;
9584 case LVM_SETCOLUMNA:
9585 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9587 case LVM_SETCOLUMNW:
9588 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9590 case LVM_SETCOLUMNORDERARRAY:
9591 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9593 case LVM_SETCOLUMNWIDTH:
9594 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9596 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9597 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9599 /* case LVM_SETGROUPINFO: */
9601 /* case LVM_SETGROUPMETRICS: */
9603 case LVM_SETHOTCURSOR:
9604 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9606 case LVM_SETHOTITEM:
9607 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9609 case LVM_SETHOVERTIME:
9610 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9612 case LVM_SETICONSPACING:
9613 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9615 case LVM_SETIMAGELIST:
9616 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9618 /* case LVM_SETINFOTIP: */
9620 /* case LVM_SETINSERTMARK: */
9622 /* case LVM_SETINSERTMARKCOLOR: */
9625 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9628 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9630 case LVM_SETITEMCOUNT:
9631 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9633 case LVM_SETITEMPOSITION:
9636 pt.x = (short)LOWORD(lParam);
9637 pt.y = (short)HIWORD(lParam);
9638 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9641 case LVM_SETITEMPOSITION32:
9642 if (lParam == 0) return FALSE;
9643 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9645 case LVM_SETITEMSTATE:
9646 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9648 case LVM_SETITEMTEXTA:
9649 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9651 case LVM_SETITEMTEXTW:
9652 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9654 /* case LVM_SETOUTLINECOLOR: */
9656 /* case LVM_SETSELECTEDCOLUMN: */
9658 case LVM_SETSELECTIONMARK:
9659 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9661 case LVM_SETTEXTBKCOLOR:
9662 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9664 case LVM_SETTEXTCOLOR:
9665 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9667 /* case LVM_SETTILEINFO: */
9669 /* case LVM_SETTILEVIEWINFO: */
9671 /* case LVM_SETTILEWIDTH: */
9673 case LVM_SETTOOLTIPS:
9674 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9676 case LVM_SETUNICODEFORMAT:
9677 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
9679 /* case LVM_SETVIEW: */
9681 /* case LVM_SETWORKAREAS: */
9683 /* case LVM_SORTGROUPS: */
9686 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9688 /* LVM_SORTITEMSEX: */
9690 case LVM_SUBITEMHITTEST:
9691 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9694 return LISTVIEW_Update(infoPtr, (INT)wParam);
9697 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9700 return LISTVIEW_Command(infoPtr, wParam, lParam);
9703 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
9706 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9709 return LISTVIEW_Destroy(infoPtr);
9712 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9715 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9718 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9721 return (LRESULT)infoPtr->hFont;
9724 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9727 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9730 return LISTVIEW_KillFocus(infoPtr);
9732 case WM_LBUTTONDBLCLK:
9733 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9735 case WM_LBUTTONDOWN:
9736 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9739 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9742 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9745 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9748 return LISTVIEW_NCDestroy(infoPtr);
9751 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9756 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9757 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9760 case WM_NOTIFYFORMAT:
9761 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9763 case WM_PRINTCLIENT:
9764 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9767 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9769 case WM_RBUTTONDBLCLK:
9770 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9772 case WM_RBUTTONDOWN:
9773 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9776 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9779 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9784 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9787 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9790 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9793 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9795 case WM_STYLECHANGED:
9796 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9798 case WM_SYSCOLORCHANGE:
9799 COMCTL32_RefreshSysColors();
9802 /* case WM_TIMER: */
9803 case WM_THEMECHANGED:
9804 return LISTVIEW_ThemeChanged(infoPtr);
9807 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9810 if (wParam & (MK_SHIFT | MK_CONTROL))
9811 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9812 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9814 case WM_WINDOWPOSCHANGED:
9815 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9817 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9818 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9819 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9821 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9823 MEASUREITEMSTRUCT mis;
9824 mis.CtlType = ODT_LISTVIEW;
9825 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9829 mis.itemHeight= infoPtr->nItemHeight;
9830 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9831 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9832 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9835 LISTVIEW_UpdateSize(infoPtr);
9836 LISTVIEW_UpdateScroll(infoPtr);
9838 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9840 /* case WM_WININICHANGE: */
9843 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9844 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9847 /* call default window procedure */
9848 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9855 * Registers the window class.
9863 void LISTVIEW_Register(void)
9867 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9868 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9869 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9870 wndClass.cbClsExtra = 0;
9871 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9872 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9873 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9874 wndClass.lpszClassName = WC_LISTVIEWW;
9875 RegisterClassW(&wndClass);
9880 * Unregisters the window class.
9888 void LISTVIEW_Unregister(void)
9890 UnregisterClassW(WC_LISTVIEWW, NULL);
9895 * Handle any WM_COMMAND messages
9898 * [I] infoPtr : valid pointer to the listview structure
9899 * [I] wParam : the first message parameter
9900 * [I] lParam : the second message parameter
9905 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9907 switch (HIWORD(wParam))
9912 * Adjust the edit window size
9915 HDC hdc = GetDC(infoPtr->hwndEdit);
9916 HFONT hFont, hOldFont = 0;
9921 if (!infoPtr->hwndEdit || !hdc) return 0;
9922 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9923 GetWindowRect(infoPtr->hwndEdit, &rect);
9925 /* Select font to get the right dimension of the string */
9926 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9929 hOldFont = SelectObject(hdc, hFont);
9932 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9934 TEXTMETRICW textMetric;
9936 /* Add Extra spacing for the next character */
9937 GetTextMetricsW(hdc, &textMetric);
9938 sz.cx += (textMetric.tmMaxCharWidth * 2);
9946 rect.bottom - rect.top,
9947 SWP_DRAWFRAME|SWP_NOMOVE);
9950 SelectObject(hdc, hOldFont);
9952 ReleaseDC(infoPtr->hwndEdit, hdc);
9958 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9967 * Subclassed edit control windproc function
9970 * [I] hwnd : the edit window handle
9971 * [I] uMsg : the message that is to be processed
9972 * [I] wParam : first message parameter
9973 * [I] lParam : second message parameter
9974 * [I] isW : TRUE if input is Unicode
9979 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9981 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9982 BOOL cancel = FALSE;
9984 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9985 hwnd, uMsg, wParam, lParam, isW);
9990 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9997 WNDPROC editProc = infoPtr->EditWndProc;
9998 infoPtr->EditWndProc = 0;
9999 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10000 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10004 if (VK_ESCAPE == (INT)wParam)
10009 else if (VK_RETURN == (INT)wParam)
10013 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10016 /* kill the edit */
10017 if (infoPtr->hwndEdit)
10019 LPWSTR buffer = NULL;
10021 infoPtr->hwndEdit = 0;
10024 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10028 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10030 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10031 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10035 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10040 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10046 * Subclassed edit control Unicode windproc function
10049 * [I] hwnd : the edit window handle
10050 * [I] uMsg : the message that is to be processed
10051 * [I] wParam : first message parameter
10052 * [I] lParam : second message parameter
10056 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10058 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10063 * Subclassed edit control ANSI windproc function
10066 * [I] hwnd : the edit window handle
10067 * [I] uMsg : the message that is to be processed
10068 * [I] wParam : first message parameter
10069 * [I] lParam : second message parameter
10073 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10075 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10080 * Creates a subclassed edit cotrol
10083 * [I] infoPtr : valid pointer to the listview structure
10084 * [I] text : initial text for the edit
10085 * [I] style : the window style
10086 * [I] isW : TRUE if input is Unicode
10090 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10091 INT x, INT y, INT width, INT height, BOOL isW)
10093 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10098 TEXTMETRICW textMetric;
10099 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10101 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10103 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10104 hdc = GetDC(infoPtr->hwndSelf);
10106 /* Select the font to get appropriate metric dimensions */
10107 if(infoPtr->hFont != 0)
10108 hOldFont = SelectObject(hdc, infoPtr->hFont);
10110 /*Get String Length in pixels */
10111 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10113 /*Add Extra spacing for the next character */
10114 GetTextMetricsW(hdc, &textMetric);
10115 sz.cx += (textMetric.tmMaxCharWidth * 2);
10117 if(infoPtr->hFont != 0)
10118 SelectObject(hdc, hOldFont);
10120 ReleaseDC(infoPtr->hwndSelf, hdc);
10122 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10124 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10126 if (!hedit) return 0;
10128 infoPtr->EditWndProc = (WNDPROC)
10129 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10130 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10132 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);