4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
35 * Default Message Processing
36 * -- EN_KILLFOCUS should be handled in WM_COMMAND
37 * -- WM_CREATE: create the icon and small icon image lists at this point only if
38 * the LVS_SHAREIMAGELISTS style is not specified.
39 * -- WM_ERASEBKGND: forward this message to the parent window if the bkgnd
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_[GS]etColumnOrderArray stubs
58 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
59 * -- LISTVIEW_SetIconSpacing is incomplete
60 * -- LISTVIEW_SortItems is broken
61 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
64 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
65 * linear in the number of items in the list, and this is
66 * unacceptable for large lists.
67 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
68 * instead of inserting in the right spot
69 * -- we should keep an ordered array of coordinates in iconic mode
70 * this would allow to frame items (iterator_frameditems),
71 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
79 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
86 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
89 * -- LVS_TYPESTYLEMASK
92 * -- LVS_EX_BORDERSELECT
95 * -- LVS_EX_HEADERDRAGDROP
98 * -- LVS_EX_MULTIWORKAREAS
99 * -- LVS_EX_ONECLICKACTIVATE
101 * -- LVS_EX_SIMPLESELECT
102 * -- LVS_EX_TRACKSELECT
103 * -- LVS_EX_TWOCLICKACTIVATE
104 * -- LVS_EX_UNDERLINECOLD
105 * -- LVS_EX_UNDERLINEHOT
108 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
111 * -- LVN_MARQUEEBEGIN
118 * -- LVM_CANCELEDITLABEL
119 * -- LVM_ENABLEGROUPVIEW
120 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
121 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
122 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
123 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
124 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
125 * -- LVM_GETINSERTMARKRECT
126 * -- LVM_GETNUMBEROFWORKAREAS
127 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
128 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
129 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
130 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
131 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
132 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
133 * -- LVM_GETVIEW, LVM_SETVIEW
134 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
135 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
136 * -- LVM_INSERTGROUPSORTED
137 * -- LVM_INSERTMARKHITTEST
138 * -- LVM_ISGROUPVIEWENABLED
139 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
141 * -- LVM_MOVEITEMTOGROUP
143 * -- LVM_SETTILEWIDTH
148 * -- ListView_GetCheckSate, ListView_SetCheckState
149 * -- ListView_GetHoverTime, ListView_SetHoverTime
150 * -- ListView_GetISearchString
151 * -- ListView_GetNumberOfWorkAreas
152 * -- ListView_GetOrigin
153 * -- ListView_GetTextBkColor
154 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
155 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
156 * -- ListView_SortItemsEx
161 * Known differences in message stream from native control (not known if
162 * these differences cause problems):
163 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
164 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
165 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
166 * processing for "USEDOUBLECLICKTIME".
170 #include "wine/port.h"
185 #include "commctrl.h"
186 #include "comctl32.h"
189 #include "wine/debug.h"
190 #include "wine/unicode.h"
192 WINE_DEFAULT_DEBUG_CHANNEL(listview);
194 /* make sure you set this to 0 for production use! */
195 #define DEBUG_RANGES 1
197 typedef struct tagCOLUMN_INFO
199 RECT rcHeader; /* tracks the header's rectangle */
200 int fmt; /* same as LVCOLUMN.fmt */
203 typedef struct tagITEMHDR
207 } ITEMHDR, *LPITEMHDR;
209 typedef struct tagSUBITEM_INFO
215 typedef struct tagITEM_INFO
223 typedef struct tagRANGE
229 typedef struct tagRANGES
234 typedef struct tagITERATOR
243 typedef struct tagLISTVIEW_INFO
250 COLORREF clrTextBkDefault;
251 HIMAGELIST himlNormal;
252 HIMAGELIST himlSmall;
253 HIMAGELIST himlState;
256 POINT ptClickPos; /* point where the user clicked */
257 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
260 RANGES selectionRanges;
265 RECT rcList; /* This rectangle is really the window
266 * client rectangle possibly reduced by the
267 * horizontal scroll bar and/or header - see
268 * LISTVIEW_UpdateSize. This rectangle offset
269 * by the LISTVIEW_GetOrigin value is in
270 * client coordinates */
279 INT ntmHeight; /* Some cached metrics of the font used */
280 INT ntmMaxCharWidth; /* by the listview to draw items */
282 BOOL bRedraw; /* Turns on/off repaints & invalidations */
283 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
285 BOOL bDoChangeNotify; /* send change notification messages? */
288 DWORD dwStyle; /* the cached window GWL_STYLE */
289 DWORD dwLvExStyle; /* extended listview style */
290 INT nItemCount; /* the number of items in the list */
291 HDPA hdpaItems; /* array ITEM_INFO pointers */
292 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
293 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
294 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
295 POINT currIconPos; /* this is the position next icon will be placed */
296 PFNLVCOMPARE pfnCompare;
304 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
306 DWORD lastKeyPressTimestamp;
308 INT nSearchParamLength;
309 WCHAR szSearchParam[ MAX_PATH ];
311 INT nMeasureItemHeight;
317 /* How many we debug buffer to allocate */
318 #define DEBUG_BUFFERS 20
319 /* The size of a single debug bbuffer */
320 #define DEBUG_BUFFER_SIZE 256
322 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
323 #define SB_INTERNAL -1
325 /* maximum size of a label */
326 #define DISP_TEXT_SIZE 512
328 /* padding for items in list and small icon display modes */
329 #define WIDTH_PADDING 12
331 /* padding for items in list, report and small icon display modes */
332 #define HEIGHT_PADDING 1
334 /* offset of items in report display mode */
335 #define REPORT_MARGINX 2
337 /* padding for icon in large icon display mode
338 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
339 * that HITTEST will see.
340 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
341 * ICON_TOP_PADDING - sum of the two above.
342 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
343 * LABEL_HOR_PADDING - between text and sides of box
344 * LABEL_VERT_PADDING - between bottom of text and end of box
346 * ICON_LR_PADDING - additional width above icon size.
347 * ICON_LR_HALF - half of the above value
349 #define ICON_TOP_PADDING_NOTHITABLE 2
350 #define ICON_TOP_PADDING_HITABLE 2
351 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
352 #define ICON_BOTTOM_PADDING 4
353 #define LABEL_HOR_PADDING 5
354 #define LABEL_VERT_PADDING 7
355 #define ICON_LR_PADDING 16
356 #define ICON_LR_HALF (ICON_LR_PADDING/2)
358 /* default label width for items in list and small icon display modes */
359 #define DEFAULT_LABEL_WIDTH 40
361 /* default column width for items in list display mode */
362 #define DEFAULT_COLUMN_WIDTH 128
364 /* Size of "line" scroll for V & H scrolls */
365 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
367 /* Padding betwen image and label */
368 #define IMAGE_PADDING 2
370 /* Padding behind the label */
371 #define TRAILING_LABEL_PADDING 12
372 #define TRAILING_HEADER_PADDING 11
374 /* Border for the icon caption */
375 #define CAPTION_BORDER 2
377 /* Standard DrawText flags */
378 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
379 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
380 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
382 /* Image index from state */
383 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
385 /* The time in milliseconds to reset the search in the list */
386 #define KEY_DELAY 450
388 /* Dump the LISTVIEW_INFO structure to the debug channel */
389 #define LISTVIEW_DUMP(iP) do { \
390 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
391 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
392 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
393 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
394 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
395 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
396 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
397 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
398 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
399 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
402 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
405 * forward declarations
407 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
408 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
409 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
410 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
411 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
412 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
413 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
414 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
415 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
416 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
417 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
418 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
419 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
420 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
421 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
422 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
423 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
424 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
425 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
426 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
427 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
428 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
429 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
430 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
431 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
432 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
433 static INT LISTVIEW_HitTest(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
435 /******** Text handling functions *************************************/
437 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
438 * text string. The string may be ANSI or Unicode, in which case
439 * the boolean isW tells us the type of the string.
441 * The name of the function tell what type of strings it expects:
442 * W: Unicode, T: ANSI/Unicode - function of isW
445 static inline BOOL is_textW(LPCWSTR text)
447 return text != NULL && text != LPSTR_TEXTCALLBACKW;
450 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
452 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
453 return is_textW(text);
456 static inline int textlenT(LPCWSTR text, BOOL isW)
458 return !is_textT(text, isW) ? 0 :
459 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
462 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
465 if (isSrcW) lstrcpynW(dest, src, max);
466 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
468 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
469 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
472 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
474 LPWSTR wstr = (LPWSTR)text;
476 if (!isW && is_textT(text, isW))
478 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
479 wstr = Alloc(len * sizeof(WCHAR));
480 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
482 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
486 static inline void textfreeT(LPWSTR wstr, BOOL isW)
488 if (!isW && is_textT(wstr, isW)) Free (wstr);
492 * dest is a pointer to a Unicode string
493 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
495 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
499 if (src == LPSTR_TEXTCALLBACKW)
501 if (is_textW(*dest)) Free(*dest);
502 *dest = LPSTR_TEXTCALLBACKW;
506 LPWSTR pszText = textdupTtoW(src, isW);
507 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
508 bResult = Str_SetPtrW(dest, pszText);
509 textfreeT(pszText, isW);
515 * compares a Unicode to a Unicode/ANSI text string
517 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
519 if (!aw) return bt ? -1 : 0;
520 if (!bt) return aw ? 1 : 0;
521 if (aw == LPSTR_TEXTCALLBACKW)
522 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
523 if (bt != LPSTR_TEXTCALLBACKW)
525 LPWSTR bw = textdupTtoW(bt, isW);
526 int r = bw ? lstrcmpW(aw, bw) : 1;
534 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
538 n = min(min(n, strlenW(s1)), strlenW(s2));
539 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
540 return res ? res - sizeof(WCHAR) : res;
543 /******** Debugging functions *****************************************/
545 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
547 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
548 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
551 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
553 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
554 n = min(textlenT(text, isW), n);
555 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
558 static char* debug_getbuf(void)
560 static int index = 0;
561 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
562 return buffers[index++ % DEBUG_BUFFERS];
565 static inline const char* debugrange(const RANGE *lprng)
569 char* buf = debug_getbuf();
570 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
572 } else return "(null)";
575 static inline const char* debugpoint(const POINT *lppt)
579 char* buf = debug_getbuf();
580 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
582 } else return "(null)";
585 static inline const char* debugrect(const RECT *rect)
589 char* buf = debug_getbuf();
590 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
591 rect->left, rect->top, rect->right, rect->bottom);
593 } else return "(null)";
596 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
598 char* buf = debug_getbuf(), *text = buf;
599 int len, size = DEBUG_BUFFER_SIZE;
601 if (pScrollInfo == NULL) return "(null)";
602 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
603 if (len == -1) goto end; buf += len; size -= len;
604 if (pScrollInfo->fMask & SIF_RANGE)
605 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
607 if (len == -1) goto end; buf += len; size -= len;
608 if (pScrollInfo->fMask & SIF_PAGE)
609 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
611 if (len == -1) goto end; buf += len; size -= len;
612 if (pScrollInfo->fMask & SIF_POS)
613 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
615 if (len == -1) goto end; buf += len; size -= len;
616 if (pScrollInfo->fMask & SIF_TRACKPOS)
617 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
619 if (len == -1) goto end; buf += len; size -= len;
622 buf = text + strlen(text);
624 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
628 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
632 char* buf = debug_getbuf();
633 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
634 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
635 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
636 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
638 } else return "(null)";
641 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
643 char* buf = debug_getbuf(), *text = buf;
644 int len, size = DEBUG_BUFFER_SIZE;
646 if (lpLVItem == NULL) return "(null)";
647 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
648 if (len == -1) goto end; buf += len; size -= len;
649 if (lpLVItem->mask & LVIF_STATE)
650 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
652 if (len == -1) goto end; buf += len; size -= len;
653 if (lpLVItem->mask & LVIF_TEXT)
654 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
656 if (len == -1) goto end; buf += len; size -= len;
657 if (lpLVItem->mask & LVIF_IMAGE)
658 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
660 if (len == -1) goto end; buf += len; size -= len;
661 if (lpLVItem->mask & LVIF_PARAM)
662 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
664 if (len == -1) goto end; buf += len; size -= len;
665 if (lpLVItem->mask & LVIF_INDENT)
666 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
668 if (len == -1) goto end; buf += len; size -= len;
671 buf = text + strlen(text);
673 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
677 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
679 char* buf = debug_getbuf(), *text = buf;
680 int len, size = DEBUG_BUFFER_SIZE;
682 if (lpColumn == NULL) return "(null)";
683 len = snprintf(buf, size, "{");
684 if (len == -1) goto end; buf += len; size -= len;
685 if (lpColumn->mask & LVCF_SUBITEM)
686 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
688 if (len == -1) goto end; buf += len; size -= len;
689 if (lpColumn->mask & LVCF_FMT)
690 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
692 if (len == -1) goto end; buf += len; size -= len;
693 if (lpColumn->mask & LVCF_WIDTH)
694 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
696 if (len == -1) goto end; buf += len; size -= len;
697 if (lpColumn->mask & LVCF_TEXT)
698 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
700 if (len == -1) goto end; buf += len; size -= len;
701 if (lpColumn->mask & LVCF_IMAGE)
702 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
704 if (len == -1) goto end; buf += len; size -= len;
705 if (lpColumn->mask & LVCF_ORDER)
706 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
708 if (len == -1) goto end; buf += len; size -= len;
711 buf = text + strlen(text);
713 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
717 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
721 char* buf = debug_getbuf();
722 snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
723 debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
725 } else return "(null)";
728 /* Return the corresponding text for a given scroll value */
729 static inline LPCSTR debugscrollcode(int nScrollCode)
733 case SB_LINELEFT: return "SB_LINELEFT";
734 case SB_LINERIGHT: return "SB_LINERIGHT";
735 case SB_PAGELEFT: return "SB_PAGELEFT";
736 case SB_PAGERIGHT: return "SB_PAGERIGHT";
737 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
738 case SB_THUMBTRACK: return "SB_THUMBTRACK";
739 case SB_ENDSCROLL: return "SB_ENDSCROLL";
740 case SB_INTERNAL: return "SB_INTERNAL";
741 default: return "unknown";
746 /******** Notification functions i************************************/
748 static LRESULT notify_forward_header(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
750 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
751 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
754 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
758 TRACE("(code=%d)\n", code);
760 pnmh->hwndFrom = infoPtr->hwndSelf;
761 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
763 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
764 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
766 TRACE(" <= %ld\n", result);
771 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code)
774 HWND hwnd = infoPtr->hwndSelf;
775 notify_hdr(infoPtr, code, &nmh);
776 return IsWindow(hwnd);
779 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
790 item.mask = LVIF_PARAM|LVIF_STATE;
791 item.iItem = htInfo->iItem;
793 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
794 nmia.lParam = item.lParam;
795 nmia.uOldState = item.state;
796 nmia.uNewState = item.state | LVIS_ACTIVATING;
797 nmia.uChanged = LVIF_STATE;
800 nmia.iItem = htInfo->iItem;
801 nmia.iSubItem = htInfo->iSubItem;
802 nmia.ptAction = htInfo->pt;
804 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
805 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
806 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
808 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
811 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
813 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
814 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
817 static BOOL notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
821 HWND hwnd = infoPtr->hwndSelf;
823 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
824 ZeroMemory(&nmlv, sizeof(nmlv));
825 nmlv.iItem = lvht->iItem;
826 nmlv.iSubItem = lvht->iSubItem;
827 nmlv.ptAction = lvht->pt;
828 item.mask = LVIF_PARAM;
829 item.iItem = lvht->iItem;
831 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
832 notify_listview(infoPtr, code, &nmlv);
833 return IsWindow(hwnd);
836 static BOOL notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
840 HWND hwnd = infoPtr->hwndSelf;
842 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
844 item.mask = LVIF_PARAM;
847 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
848 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
849 return IsWindow(hwnd);
852 static int get_ansi_notification(INT unicodeNotificationCode)
854 switch (unicodeNotificationCode)
856 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
857 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
858 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
859 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
860 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
861 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
863 ERR("unknown notification %x\n", unicodeNotificationCode);
869 Send notification. depends on dispinfoW having same
870 structure as dispinfoA.
871 infoPtr : listview struct
872 notificationCode : *Unicode* notification code
873 pdi : dispinfo structure (can be unicode or ansi)
874 isW : TRUE if dispinfo is Unicode
876 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
878 BOOL bResult = FALSE;
879 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
880 INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
881 LPWSTR pszTempBuf = NULL, savPszText = NULL;
883 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
885 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
886 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
889 if (convertToAnsi || convertToUnicode)
891 if (notificationCode != LVN_GETDISPINFOW)
893 cchTempBufMax = convertToUnicode ?
894 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
895 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
899 cchTempBufMax = pdi->item.cchTextMax;
900 *pdi->item.pszText = 0; /* make sure we don't process garbage */
903 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
904 if (!pszTempBuf) return FALSE;
906 if (convertToUnicode)
907 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
908 pszTempBuf, cchTempBufMax);
910 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
911 cchTempBufMax, NULL, NULL);
913 savCchTextMax = pdi->item.cchTextMax;
914 savPszText = pdi->item.pszText;
915 pdi->item.pszText = pszTempBuf;
916 pdi->item.cchTextMax = cchTempBufMax;
919 if (infoPtr->notifyFormat == NFR_ANSI)
920 realNotifCode = get_ansi_notification(notificationCode);
922 realNotifCode = notificationCode;
923 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
924 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
926 if (convertToUnicode || convertToAnsi)
928 if (convertToUnicode) /* note : pointer can be changed by app ! */
929 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
930 savCchTextMax, NULL, NULL);
932 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
933 savPszText, savCchTextMax);
934 pdi->item.pszText = savPszText; /* restores our buffer */
935 pdi->item.cchTextMax = savCchTextMax;
941 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
942 const RECT *rcBounds, const LVITEMW *lplvItem)
944 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
945 lpnmlvcd->nmcd.hdc = hdc;
946 lpnmlvcd->nmcd.rc = *rcBounds;
947 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
948 lpnmlvcd->clrText = infoPtr->clrText;
949 if (!lplvItem) return;
950 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
951 lpnmlvcd->iSubItem = lplvItem->iSubItem;
952 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
953 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
954 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
955 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
958 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
960 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
963 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
964 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
965 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
966 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
967 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
968 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
972 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
974 /* apprently, for selected items, we have to override the returned values */
975 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
979 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
980 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
982 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
984 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
985 lpnmlvcd->clrText = comctl32_color.clrBtnText;
989 /* Set the text attributes */
990 if (lpnmlvcd->clrTextBk != CLR_NONE)
992 SetBkMode(hdc, OPAQUE);
993 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
994 SetBkColor(hdc, infoPtr->clrTextBkDefault);
996 SetBkColor(hdc,lpnmlvcd->clrTextBk);
999 SetBkMode(hdc, TRANSPARENT);
1000 SetTextColor(hdc, lpnmlvcd->clrText);
1003 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
1005 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
1008 /******** Item iterator functions **********************************/
1010 static RANGES ranges_create(int count);
1011 static void ranges_destroy(RANGES ranges);
1012 static BOOL ranges_add(RANGES ranges, RANGE range);
1013 static BOOL ranges_del(RANGES ranges, RANGE range);
1014 static void ranges_dump(RANGES ranges);
1016 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
1018 RANGE range = { nItem, nItem + 1 };
1020 return ranges_add(ranges, range);
1023 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1025 RANGE range = { nItem, nItem + 1 };
1027 return ranges_del(ranges, range);
1031 * ITERATOR DOCUMENTATION
1033 * The iterator functions allow for easy, and convenient iteration
1034 * over items of iterest in the list. Typically, you create a
1035 * iterator, use it, and destroy it, as such:
1038 * iterator_xxxitems(&i, ...);
1039 * while (iterator_{prev,next}(&i)
1041 * //code which uses i.nItem
1043 * iterator_destroy(&i);
1045 * where xxx is either: framed, or visible.
1046 * Note that it is important that the code destroys the iterator
1047 * after it's done with it, as the creation of the iterator may
1048 * allocate memory, which thus needs to be freed.
1050 * You can iterate both forwards, and backwards through the list,
1051 * by using iterator_next or iterator_prev respectively.
1053 * Lower numbered items are draw on top of higher number items in
1054 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1055 * items may overlap). So, to test items, you should use
1057 * which lists the items top to bottom (in Z-order).
1058 * For drawing items, you should use
1060 * which lists the items bottom to top (in Z-order).
1061 * If you keep iterating over the items after the end-of-items
1062 * marker (-1) is returned, the iterator will start from the
1063 * beginning. Typically, you don't need to test for -1,
1064 * because iterator_{next,prev} will return TRUE if more items
1065 * are to be iterated over, or FALSE otherwise.
1067 * Note: the iterator is defined to be bidirectional. That is,
1068 * any number of prev followed by any number of next, or
1069 * five versa, should leave the iterator at the same item:
1070 * prev * n, next * n = next * n, prev * n
1072 * The iterator has a notion of an out-of-order, special item,
1073 * which sits at the start of the list. This is used in
1074 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1075 * which needs to be first, as it may overlap other items.
1077 * The code is a bit messy because we have:
1078 * - a special item to deal with
1079 * - simple range, or composite range
1081 * If you find bugs, or want to add features, please make sure you
1082 * always check/modify *both* iterator_prev, and iterator_next.
1086 * This function iterates through the items in increasing order,
1087 * but prefixed by the special item, then -1. That is:
1088 * special, 1, 2, 3, ..., n, -1.
1089 * Each item is listed only once.
1091 static inline BOOL iterator_next(ITERATOR* i)
1095 i->nItem = i->nSpecial;
1096 if (i->nItem != -1) return TRUE;
1098 if (i->nItem == i->nSpecial)
1100 if (i->ranges) i->index = 0;
1106 if (i->nItem == i->nSpecial) i->nItem++;
1107 if (i->nItem < i->range.upper) return TRUE;
1112 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1113 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1116 else if (i->nItem >= i->range.upper) goto end;
1118 i->nItem = i->range.lower;
1119 if (i->nItem >= 0) goto testitem;
1126 * This function iterates through the items in decreasing order,
1127 * followed by the special item, then -1. That is:
1128 * n, n-1, ..., 3, 2, 1, special, -1.
1129 * Each item is listed only once.
1131 static inline BOOL iterator_prev(ITERATOR* i)
1138 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1141 if (i->nItem == i->nSpecial)
1149 if (i->nItem == i->nSpecial) i->nItem--;
1150 if (i->nItem >= i->range.lower) return TRUE;
1156 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1159 else if (!start && i->nItem < i->range.lower) goto end;
1161 i->nItem = i->range.upper;
1162 if (i->nItem > 0) goto testitem;
1164 return (i->nItem = i->nSpecial) != -1;
1167 static RANGE iterator_range(ITERATOR* i)
1171 if (!i->ranges) return i->range;
1173 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1175 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1176 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1178 else range.lower = range.upper = 0;
1184 * Releases resources associated with this ierator.
1186 static inline void iterator_destroy(ITERATOR* i)
1188 ranges_destroy(i->ranges);
1192 * Create an empty iterator.
1194 static inline BOOL iterator_empty(ITERATOR* i)
1196 ZeroMemory(i, sizeof(*i));
1197 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1202 * Create an iterator over a range.
1204 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1212 * Create an iterator over a bunch of ranges.
1213 * Please note that the iterator will take ownership of the ranges,
1214 * and will free them upon destruction.
1216 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1224 * Creates an iterator over the items which intersect lprc.
1226 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1228 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1229 RECT frame = *lprc, rcItem, rcTemp;
1232 /* in case we fail, we want to return an empty iterator */
1233 if (!iterator_empty(i)) return FALSE;
1235 LISTVIEW_GetOrigin(infoPtr, &Origin);
1237 TRACE("(lprc=%s)\n", debugrect(lprc));
1238 OffsetRect(&frame, -Origin.x, -Origin.y);
1240 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1244 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1246 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1247 if (IntersectRect(&rcTemp, &rcItem, lprc))
1248 i->nSpecial = infoPtr->nFocusedItem;
1250 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1251 /* to do better here, we need to have PosX, and PosY sorted */
1252 TRACE("building icon ranges:\n");
1253 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1255 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1256 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1257 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1258 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1259 if (IntersectRect(&rcTemp, &rcItem, &frame))
1260 ranges_additem(i->ranges, nItem);
1264 else if (uView == LVS_REPORT)
1268 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1269 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1271 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1272 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1273 if (range.upper <= range.lower) return TRUE;
1274 if (!iterator_rangeitems(i, range)) return FALSE;
1275 TRACE(" report=%s\n", debugrange(&i->range));
1279 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1280 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1281 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1282 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1283 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1284 INT lower = nFirstCol * nPerCol + nFirstRow;
1288 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1289 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1291 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1293 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1294 TRACE("building list ranges:\n");
1295 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1297 item_range.lower = nCol * nPerCol + nFirstRow;
1298 if(item_range.lower >= infoPtr->nItemCount) break;
1299 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1300 TRACE(" list=%s\n", debugrange(&item_range));
1301 ranges_add(i->ranges, item_range);
1309 * Creates an iterator over the items which intersect the visible region of hdc.
1311 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1313 POINT Origin, Position;
1314 RECT rcItem, rcClip;
1317 rgntype = GetClipBox(hdc, &rcClip);
1318 if (rgntype == NULLREGION) return iterator_empty(i);
1319 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1320 if (rgntype == SIMPLEREGION) return TRUE;
1322 /* first deal with the special item */
1323 if (i->nSpecial != -1)
1325 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1326 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1329 /* if we can't deal with the region, we'll just go with the simple range */
1330 LISTVIEW_GetOrigin(infoPtr, &Origin);
1331 TRACE("building visible range:\n");
1332 if (!i->ranges && i->range.lower < i->range.upper)
1334 if (!(i->ranges = ranges_create(50))) return TRUE;
1335 if (!ranges_add(i->ranges, i->range))
1337 ranges_destroy(i->ranges);
1343 /* now delete the invisible items from the list */
1344 while(iterator_next(i))
1346 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1347 rcItem.left = Position.x + Origin.x;
1348 rcItem.top = Position.y + Origin.y;
1349 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1350 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1351 if (!RectVisible(hdc, &rcItem))
1352 ranges_delitem(i->ranges, i->nItem);
1354 /* the iterator should restart on the next iterator_next */
1360 /******** Misc helper functions ************************************/
1362 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1363 WPARAM wParam, LPARAM lParam, BOOL isW)
1365 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1366 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1369 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1371 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1373 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1374 (uView == LVS_ICON || uView == LVS_SMALLICON);
1377 /******** Internal API functions ************************************/
1379 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1381 static COLUMN_INFO mainItem;
1383 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1384 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1385 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1388 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1390 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1393 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1395 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1398 /* Listview invalidation functions: use _only_ these functions to invalidate */
1400 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1402 return infoPtr->bRedraw;
1405 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1407 if(!is_redrawing(infoPtr)) return;
1408 TRACE(" invalidating rect=%s\n", debugrect(rect));
1409 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1412 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1416 if(!is_redrawing(infoPtr)) return;
1417 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1418 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1421 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1423 POINT Origin, Position;
1426 if(!is_redrawing(infoPtr)) return;
1427 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1428 LISTVIEW_GetOrigin(infoPtr, &Origin);
1429 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1430 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1432 rcBox.bottom = infoPtr->nItemHeight;
1433 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1434 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1437 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1439 LISTVIEW_InvalidateRect(infoPtr, NULL);
1442 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1446 if(!is_redrawing(infoPtr)) return;
1447 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1448 rcCol.top = infoPtr->rcList.top;
1449 rcCol.bottom = infoPtr->rcList.bottom;
1450 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1455 * Retrieves the number of items that can fit vertically in the client area.
1458 * [I] infoPtr : valid pointer to the listview structure
1461 * Number of items per row.
1463 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1465 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1467 return max(nListWidth/infoPtr->nItemWidth, 1);
1472 * Retrieves the number of items that can fit horizontally in the client
1476 * [I] infoPtr : valid pointer to the listview structure
1479 * Number of items per column.
1481 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1483 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1485 return max(nListHeight / infoPtr->nItemHeight, 1);
1489 /*************************************************************************
1490 * LISTVIEW_ProcessLetterKeys
1492 * Processes keyboard messages generated by pressing the letter keys
1494 * What this does is perform a case insensitive search from the
1495 * current position with the following quirks:
1496 * - If two chars or more are pressed in quick succession we search
1497 * for the corresponding string (e.g. 'abc').
1498 * - If there is a delay we wipe away the current search string and
1499 * restart with just that char.
1500 * - If the user keeps pressing the same character, whether slowly or
1501 * fast, so that the search string is entirely composed of this
1502 * character ('aaaaa' for instance), then we search for first item
1503 * that starting with that character.
1504 * - If the user types the above character in quick succession, then
1505 * we must also search for the corresponding string ('aaaaa'), and
1506 * go to that string if there is a match.
1509 * [I] hwnd : handle to the window
1510 * [I] charCode : the character code, the actual character
1511 * [I] keyData : key data
1519 * - The current implementation has a list of characters it will
1520 * accept and it ignores averything else. In particular it will
1521 * ignore accentuated characters which seems to match what
1522 * Windows does. But I'm not sure it makes sense to follow
1524 * - We don't sound a beep when the search fails.
1528 * TREEVIEW_ProcessLetterKeys
1530 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1535 WCHAR buffer[MAX_PATH];
1536 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1538 /* simple parameter checking */
1539 if (!charCode || !keyData) return 0;
1541 /* only allow the valid WM_CHARs through */
1542 if (!isalnum(charCode) &&
1543 charCode != '.' && charCode != '`' && charCode != '!' &&
1544 charCode != '@' && charCode != '#' && charCode != '$' &&
1545 charCode != '%' && charCode != '^' && charCode != '&' &&
1546 charCode != '*' && charCode != '(' && charCode != ')' &&
1547 charCode != '-' && charCode != '_' && charCode != '+' &&
1548 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1549 charCode != '}' && charCode != '[' && charCode != '{' &&
1550 charCode != '/' && charCode != '?' && charCode != '>' &&
1551 charCode != '<' && charCode != ',' && charCode != '~')
1554 /* if there's one item or less, there is no where to go */
1555 if (infoPtr->nItemCount <= 1) return 0;
1557 /* update the search parameters */
1558 infoPtr->lastKeyPressTimestamp = GetTickCount();
1559 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1560 if (infoPtr->nSearchParamLength < MAX_PATH)
1561 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1562 if (infoPtr->charCode != charCode)
1563 infoPtr->charCode = charCode = 0;
1565 infoPtr->charCode=charCode;
1566 infoPtr->szSearchParam[0]=charCode;
1567 infoPtr->nSearchParamLength=1;
1568 /* Redundant with the 1 char string */
1572 /* and search from the current position */
1574 if (infoPtr->nFocusedItem >= 0) {
1575 endidx=infoPtr->nFocusedItem;
1577 /* if looking for single character match,
1578 * then we must always move forward
1580 if (infoPtr->nSearchParamLength == 1)
1583 endidx=infoPtr->nItemCount;
1587 if (idx == infoPtr->nItemCount) {
1588 if (endidx == infoPtr->nItemCount || endidx == 0)
1594 item.mask = LVIF_TEXT;
1597 item.pszText = buffer;
1598 item.cchTextMax = MAX_PATH;
1599 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1601 /* check for a match */
1602 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1605 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1606 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1607 /* This would work but we must keep looking for a longer match */
1611 } while (idx != endidx);
1614 LISTVIEW_KeySelection(infoPtr, nItem);
1619 /*************************************************************************
1620 * LISTVIEW_UpdateHeaderSize [Internal]
1622 * Function to resize the header control
1625 * [I] hwnd : handle to a window
1626 * [I] nNewScrollPos : scroll pos to set
1631 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1636 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1638 GetWindowRect(infoPtr->hwndHeader, &winRect);
1639 point[0].x = winRect.left;
1640 point[0].y = winRect.top;
1641 point[1].x = winRect.right;
1642 point[1].y = winRect.bottom;
1644 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1645 point[0].x = -nNewScrollPos;
1646 point[1].x += nNewScrollPos;
1648 SetWindowPos(infoPtr->hwndHeader,0,
1649 point[0].x,point[0].y,point[1].x,point[1].y,
1650 SWP_NOZORDER | SWP_NOACTIVATE);
1655 * Update the scrollbars. This functions should be called whenever
1656 * the content, size or view changes.
1659 * [I] infoPtr : valid pointer to the listview structure
1664 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1666 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1667 SCROLLINFO horzInfo, vertInfo;
1669 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1671 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1672 horzInfo.cbSize = sizeof(SCROLLINFO);
1673 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1675 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1676 if (uView == LVS_LIST)
1678 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1679 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1681 /* scroll by at least one column per page */
1682 if(horzInfo.nPage < infoPtr->nItemWidth)
1683 horzInfo.nPage = infoPtr->nItemWidth;
1685 horzInfo.nPage /= infoPtr->nItemWidth;
1687 else if (uView == LVS_REPORT)
1689 horzInfo.nMax = infoPtr->nItemWidth;
1691 else /* LVS_ICON, or LVS_SMALLICON */
1695 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1698 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1699 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1700 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1701 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1703 /* Setting the horizontal scroll can change the listview size
1704 * (and potentially everything else) so we need to recompute
1705 * everything again for the vertical scroll
1708 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1709 vertInfo.cbSize = sizeof(SCROLLINFO);
1710 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1712 if (uView == LVS_REPORT)
1714 vertInfo.nMax = infoPtr->nItemCount;
1716 /* scroll by at least one page */
1717 if(vertInfo.nPage < infoPtr->nItemHeight)
1718 vertInfo.nPage = infoPtr->nItemHeight;
1720 vertInfo.nPage /= infoPtr->nItemHeight;
1722 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1726 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1729 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1730 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1731 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1732 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1734 /* Update the Header Control */
1735 if (uView == LVS_REPORT)
1737 horzInfo.fMask = SIF_POS;
1738 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1739 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1746 * Shows/hides the focus rectangle.
1749 * [I] infoPtr : valid pointer to the listview structure
1750 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1755 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1757 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1760 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1762 if (infoPtr->nFocusedItem < 0) return;
1764 /* we need some gymnastics in ICON mode to handle large items */
1765 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1769 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1770 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1772 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1777 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1779 /* for some reason, owner draw should work only in report mode */
1780 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1785 item.iItem = infoPtr->nFocusedItem;
1787 item.mask = LVIF_PARAM;
1788 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1790 ZeroMemory(&dis, sizeof(dis));
1791 dis.CtlType = ODT_LISTVIEW;
1792 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1793 dis.itemID = item.iItem;
1794 dis.itemAction = ODA_FOCUS;
1795 if (fShow) dis.itemState |= ODS_FOCUS;
1796 dis.hwndItem = infoPtr->hwndSelf;
1798 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1799 dis.itemData = item.lParam;
1801 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1805 DrawFocusRect(hdc, &infoPtr->rcFocus);
1808 ReleaseDC(infoPtr->hwndSelf, hdc);
1812 * Invalidates all visible selected items.
1814 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1818 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1819 while(iterator_next(&i))
1821 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1822 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1824 iterator_destroy(&i);
1829 * DESCRIPTION: [INTERNAL]
1830 * Computes an item's (left,top) corner, relative to rcView.
1831 * That is, the position has NOT been made relative to the Origin.
1832 * This is deliberate, to avoid computing the Origin over, and
1833 * over again, when this function is call in a loop. Instead,
1834 * one ca factor the computation of the Origin before the loop,
1835 * and offset the value retured by this function, on every iteration.
1838 * [I] infoPtr : valid pointer to the listview structure
1839 * [I] nItem : item number
1840 * [O] lpptOrig : item top, left corner
1845 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1847 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1849 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1851 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1853 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1854 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1856 else if (uView == LVS_LIST)
1858 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1859 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1860 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1862 else /* LVS_REPORT */
1864 lpptPosition->x = 0;
1865 lpptPosition->y = nItem * infoPtr->nItemHeight;
1870 * DESCRIPTION: [INTERNAL]
1871 * Compute the rectangles of an item. This is to localize all
1872 * the computations in one place. If you are not interested in some
1873 * of these values, simply pass in a NULL -- the fucntion is smart
1874 * enough to compute only what's necessary. The function computes
1875 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1876 * one, the BOX rectangle. This rectangle is very cheap to compute,
1877 * and is guaranteed to contain all the other rectangles. Computing
1878 * the ICON rect is also cheap, but all the others are potentaily
1879 * expensive. This gives an easy and effective optimization when
1880 * searching (like point inclusion, or rectangle intersection):
1881 * first test against the BOX, and if TRUE, test agains the desired
1883 * If the function does not have all the necessary information
1884 * to computed the requested rectangles, will crash with a
1885 * failed assertion. This is done so we catch all programming
1886 * errors, given that the function is called only from our code.
1888 * We have the following 'special' meanings for a few fields:
1889 * * If LVIS_FOCUSED is set, we assume the item has the focus
1890 * This is important in ICON mode, where it might get a larger
1891 * then usual rectange
1893 * Please note that subitem support works only in REPORT mode.
1896 * [I] infoPtr : valid pointer to the listview structure
1897 * [I] lpLVItem : item to compute the measures for
1898 * [O] lprcBox : ptr to Box rectangle
1899 * The internal LVIR_BOX rectangle
1900 * [0] lprcState : ptr to State icon rectangle
1901 * The internal LVIR_STATE rectangle
1902 * [O] lprcIcon : ptr to Icon rectangle
1903 * Same as LVM_GETITEMRECT with LVIR_ICON
1904 * [O] lprcLabel : ptr to Label rectangle
1905 * Same as LVM_GETITEMRECT with LVIR_LABEL
1910 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1911 LPRECT lprcBox, LPRECT lprcState,
1912 LPRECT lprcIcon, LPRECT lprcLabel)
1914 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1915 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1916 RECT Box, State, Icon, Label;
1917 COLUMN_INFO *lpColumnInfo = NULL;
1919 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1921 /* Be smart and try to figure out the minimum we have to do */
1922 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1923 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1925 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1926 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1928 if (lprcLabel) doLabel = TRUE;
1929 if (doLabel || lprcIcon) doIcon = TRUE;
1930 if (doIcon || lprcState) doState = TRUE;
1932 /************************************************************/
1933 /* compute the box rectangle (it should be cheap to do) */
1934 /************************************************************/
1935 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1936 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1938 if (lpLVItem->iSubItem)
1940 Box = lpColumnInfo->rcHeader;
1945 Box.right = infoPtr->nItemWidth;
1948 Box.bottom = infoPtr->nItemHeight;
1950 /************************************************************/
1951 /* compute STATEICON bounding box */
1952 /************************************************************/
1955 if (uView == LVS_ICON)
1957 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1958 if (infoPtr->himlNormal)
1959 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1960 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1964 /* we need the ident in report mode, if we don't have it, we fail */
1965 State.left = Box.left;
1966 if (uView == LVS_REPORT)
1968 if (lpLVItem->iSubItem == 0)
1970 State.left += REPORT_MARGINX;
1971 assert(lpLVItem->mask & LVIF_INDENT);
1972 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1975 State.top = Box.top;
1977 State.right = State.left;
1978 State.bottom = State.top;
1979 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1981 State.right += infoPtr->iconStateSize.cx;
1982 State.bottom += infoPtr->iconStateSize.cy;
1984 if (lprcState) *lprcState = State;
1985 TRACE(" - state=%s\n", debugrect(&State));
1987 else State.right = 0;
1989 /************************************************************/
1990 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1991 /************************************************************/
1994 if (uView == LVS_ICON)
1996 Icon.left = Box.left;
1997 if (infoPtr->himlNormal)
1998 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1999 Icon.top = Box.top + ICON_TOP_PADDING;
2000 Icon.right = Icon.left;
2001 Icon.bottom = Icon.top;
2002 if (infoPtr->himlNormal)
2004 Icon.right += infoPtr->iconSize.cx;
2005 Icon.bottom += infoPtr->iconSize.cy;
2008 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2010 Icon.left = State.right;
2012 Icon.right = Icon.left;
2013 if (infoPtr->himlSmall &&
2014 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2015 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2016 Icon.right += infoPtr->iconSize.cx;
2017 Icon.bottom = Icon.top + infoPtr->nItemHeight;
2019 if(lprcIcon) *lprcIcon = Icon;
2020 TRACE(" - icon=%s\n", debugrect(&Icon));
2022 else Icon.right = 0;
2024 /************************************************************/
2025 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2026 /************************************************************/
2029 SIZE labelSize = { 0, 0 };
2031 /* calculate how far to the right can the label strech */
2032 Label.right = Box.right;
2033 if (uView == LVS_REPORT)
2035 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2038 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2040 labelSize.cx = infoPtr->nItemWidth;
2041 labelSize.cy = infoPtr->nItemHeight;
2045 /* we need the text in non owner draw mode */
2046 assert(lpLVItem->mask & LVIF_TEXT);
2047 if (is_textT(lpLVItem->pszText, TRUE))
2049 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2050 HDC hdc = GetDC(infoPtr->hwndSelf);
2051 HFONT hOldFont = SelectObject(hdc, hFont);
2055 /* compute rough rectangle where the label will go */
2056 SetRectEmpty(&rcText);
2057 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2058 rcText.bottom = infoPtr->nItemHeight;
2059 if (uView == LVS_ICON)
2060 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2062 /* now figure out the flags */
2063 if (uView == LVS_ICON)
2064 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2066 uFormat = LV_SL_DT_FLAGS;
2068 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2070 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2071 labelSize.cy = rcText.bottom - rcText.top;
2073 SelectObject(hdc, hOldFont);
2074 ReleaseDC(infoPtr->hwndSelf, hdc);
2078 if (uView == LVS_ICON)
2080 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2081 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2082 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2083 Label.right = Label.left + labelSize.cx;
2084 Label.bottom = Label.top + infoPtr->nItemHeight;
2085 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2087 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2088 labelSize.cy /= infoPtr->ntmHeight;
2089 labelSize.cy = max(labelSize.cy, 1);
2090 labelSize.cy *= infoPtr->ntmHeight;
2092 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2094 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2096 Label.left = Icon.right;
2097 Label.top = Box.top;
2098 Label.right = min(Label.left + labelSize.cx, Label.right);
2099 Label.bottom = Label.top + infoPtr->nItemHeight;
2102 if (lprcLabel) *lprcLabel = Label;
2103 TRACE(" - label=%s\n", debugrect(&Label));
2106 /* Fix the Box if necessary */
2109 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2110 else *lprcBox = Box;
2112 TRACE(" - box=%s\n", debugrect(&Box));
2116 * DESCRIPTION: [INTERNAL]
2119 * [I] infoPtr : valid pointer to the listview structure
2120 * [I] nItem : item number
2121 * [O] lprcBox : ptr to Box rectangle
2126 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2128 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2129 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2130 POINT Position, Origin;
2133 LISTVIEW_GetOrigin(infoPtr, &Origin);
2134 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2136 /* Be smart and try to figure out the minimum we have to do */
2138 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2139 lvItem.mask |= LVIF_TEXT;
2140 lvItem.iItem = nItem;
2141 lvItem.iSubItem = 0;
2142 lvItem.pszText = szDispText;
2143 lvItem.cchTextMax = DISP_TEXT_SIZE;
2144 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2145 if (uView == LVS_ICON)
2147 lvItem.mask |= LVIF_STATE;
2148 lvItem.stateMask = LVIS_FOCUSED;
2149 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2151 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2153 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2159 * Returns the current icon position, and advances it along the top.
2160 * The returned position is not offset by Origin.
2163 * [I] infoPtr : valid pointer to the listview structure
2164 * [O] lpPos : will get the current icon position
2169 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2171 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2173 *lpPos = infoPtr->currIconPos;
2175 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2176 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2178 infoPtr->currIconPos.x = 0;
2179 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2185 * Returns the current icon position, and advances it down the left edge.
2186 * The returned position is not offset by Origin.
2189 * [I] infoPtr : valid pointer to the listview structure
2190 * [O] lpPos : will get the current icon position
2195 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2197 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2199 *lpPos = infoPtr->currIconPos;
2201 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2202 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2204 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2205 infoPtr->currIconPos.y = 0;
2211 * Moves an icon to the specified position.
2212 * It takes care of invalidating the item, etc.
2215 * [I] infoPtr : valid pointer to the listview structure
2216 * [I] nItem : the item to move
2217 * [I] lpPos : the new icon position
2218 * [I] isNew : flags the item as being new
2224 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2230 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2231 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2233 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2234 LISTVIEW_InvalidateItem(infoPtr, nItem);
2237 /* Allocating a POINTER for every item is too resource intensive,
2238 * so we'll keep the (x,y) in different arrays */
2239 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2240 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2242 LISTVIEW_InvalidateItem(infoPtr, nItem);
2249 * Arranges listview items in icon display mode.
2252 * [I] infoPtr : valid pointer to the listview structure
2253 * [I] nAlignCode : alignment code
2259 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2261 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2262 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2266 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2268 TRACE("nAlignCode=%d\n", nAlignCode);
2270 if (nAlignCode == LVA_DEFAULT)
2272 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2273 else nAlignCode = LVA_ALIGNTOP;
2278 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2279 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2280 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2281 default: return FALSE;
2284 infoPtr->bAutoarrange = TRUE;
2285 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2286 for (i = 0; i < infoPtr->nItemCount; i++)
2288 next_pos(infoPtr, &pos);
2289 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2297 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2300 * [I] infoPtr : valid pointer to the listview structure
2301 * [O] lprcView : bounding rectangle
2307 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2311 SetRectEmpty(lprcView);
2313 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2317 for (i = 0; i < infoPtr->nItemCount; i++)
2319 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2320 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2321 lprcView->right = max(lprcView->right, x);
2322 lprcView->bottom = max(lprcView->bottom, y);
2324 if (infoPtr->nItemCount > 0)
2326 lprcView->right += infoPtr->nItemWidth;
2327 lprcView->bottom += infoPtr->nItemHeight;
2332 y = LISTVIEW_GetCountPerColumn(infoPtr);
2333 x = infoPtr->nItemCount / y;
2334 if (infoPtr->nItemCount % y) x++;
2335 lprcView->right = x * infoPtr->nItemWidth;
2336 lprcView->bottom = y * infoPtr->nItemHeight;
2340 lprcView->right = infoPtr->nItemWidth;
2341 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2348 * Retrieves the bounding rectangle of all the items.
2351 * [I] infoPtr : valid pointer to the listview structure
2352 * [O] lprcView : bounding rectangle
2358 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2362 TRACE("(lprcView=%p)\n", lprcView);
2364 if (!lprcView) return FALSE;
2366 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2367 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2368 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2370 TRACE("lprcView=%s\n", debugrect(lprcView));
2377 * Retrieves the subitem pointer associated with the subitem index.
2380 * [I] hdpaSubItems : DPA handle for a specific item
2381 * [I] nSubItem : index of subitem
2384 * SUCCESS : subitem pointer
2387 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2389 SUBITEM_INFO *lpSubItem;
2392 /* we should binary search here if need be */
2393 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2395 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2396 if (lpSubItem->iSubItem == nSubItem)
2406 * Caclulates the desired item width.
2409 * [I] infoPtr : valid pointer to the listview structure
2412 * The desired item width.
2414 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2416 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2419 TRACE("uView=%d\n", uView);
2421 if (uView == LVS_ICON)
2422 nItemWidth = infoPtr->iconSpacing.cx;
2423 else if (uView == LVS_REPORT)
2427 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2429 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2430 nItemWidth = rcHeader.right;
2433 else /* LVS_SMALLICON, or LVS_LIST */
2437 for (i = 0; i < infoPtr->nItemCount; i++)
2438 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2440 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2441 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2443 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2446 return max(nItemWidth, 1);
2451 * Caclulates the desired item height.
2454 * [I] infoPtr : valid pointer to the listview structure
2457 * The desired item height.
2459 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2461 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2464 TRACE("uView=%d\n", uView);
2466 if (uView == LVS_ICON)
2467 nItemHeight = infoPtr->iconSpacing.cy;
2470 nItemHeight = infoPtr->ntmHeight;
2471 if (infoPtr->himlState)
2472 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2473 if (infoPtr->himlSmall)
2474 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2475 if (infoPtr->himlState || infoPtr->himlSmall)
2476 nItemHeight += HEIGHT_PADDING;
2477 if (infoPtr->nMeasureItemHeight > 0)
2478 nItemHeight = infoPtr->nMeasureItemHeight;
2481 return max(nItemHeight, 1);
2486 * Updates the width, and height of an item.
2489 * [I] infoPtr : valid pointer to the listview structure
2494 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2496 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2497 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2503 * Retrieves and saves important text metrics info for the current
2507 * [I] infoPtr : valid pointer to the listview structure
2510 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2512 HDC hdc = GetDC(infoPtr->hwndSelf);
2513 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2514 HFONT hOldFont = SelectObject(hdc, hFont);
2518 if (GetTextMetricsW(hdc, &tm))
2520 infoPtr->ntmHeight = tm.tmHeight;
2521 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2524 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2525 infoPtr->nEllipsisWidth = sz.cx;
2527 SelectObject(hdc, hOldFont);
2528 ReleaseDC(infoPtr->hwndSelf, hdc);
2530 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2535 * A compare function for ranges
2538 * [I] range1 : pointer to range 1;
2539 * [I] range2 : pointer to range 2;
2543 * > 0 : if range 1 > range 2
2544 * < 0 : if range 2 > range 1
2545 * = 0 : if range intersects range 2
2547 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2551 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2553 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2558 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2564 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2566 #define ranges_check(ranges, desc) do { } while(0)
2569 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2574 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2576 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2577 ranges_dump(ranges);
2578 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2579 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2580 assert (prev->lower >= 0 && prev->lower < prev->upper);
2581 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2583 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2584 assert (prev->upper <= curr->lower);
2585 assert (curr->lower < curr->upper);
2588 TRACE("--- Done checking---\n");
2591 static RANGES ranges_create(int count)
2593 RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2594 if (!ranges) return NULL;
2595 ranges->hdpa = DPA_Create(count);
2596 if (ranges->hdpa) return ranges;
2601 static void ranges_clear(RANGES ranges)
2605 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2606 Free(DPA_GetPtr(ranges->hdpa, i));
2607 DPA_DeleteAllPtrs(ranges->hdpa);
2611 static void ranges_destroy(RANGES ranges)
2613 if (!ranges) return;
2614 ranges_clear(ranges);
2615 DPA_Destroy(ranges->hdpa);
2619 static RANGES ranges_clone(RANGES ranges)
2624 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2626 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2628 RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2629 if (!newrng) goto fail;
2630 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2631 DPA_SetPtr(clone->hdpa, i, newrng);
2636 TRACE ("clone failed\n");
2637 ranges_destroy(clone);
2641 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2645 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2646 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2651 static void ranges_dump(RANGES ranges)
2655 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2656 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2659 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2661 RANGE srchrng = { nItem, nItem + 1 };
2663 TRACE("(nItem=%d)\n", nItem);
2664 ranges_check(ranges, "before contain");
2665 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2668 static INT ranges_itemcount(RANGES ranges)
2672 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2674 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2675 count += sel->upper - sel->lower;
2681 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2683 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2686 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2687 if (index == -1) return TRUE;
2689 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2691 chkrng = DPA_GetPtr(ranges->hdpa, index);
2692 if (chkrng->lower >= nItem)
2693 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2694 if (chkrng->upper > nItem)
2695 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2700 static BOOL ranges_add(RANGES ranges, RANGE range)
2705 TRACE("(%s)\n", debugrange(&range));
2706 ranges_check(ranges, "before add");
2708 /* try find overlapping regions first */
2709 srchrgn.lower = range.lower - 1;
2710 srchrgn.upper = range.upper + 1;
2711 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2717 TRACE("Adding new range\n");
2719 /* create the brand new range to insert */
2720 newrgn = (RANGE *)Alloc(sizeof(RANGE));
2721 if(!newrgn) goto fail;
2724 /* figure out where to insert it */
2725 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2726 TRACE("index=%d\n", index);
2727 if (index == -1) index = 0;
2729 /* and get it over with */
2730 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2738 RANGE *chkrgn, *mrgrgn;
2739 INT fromindex, mergeindex;
2741 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2742 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2744 chkrgn->lower = min(range.lower, chkrgn->lower);
2745 chkrgn->upper = max(range.upper, chkrgn->upper);
2747 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2749 /* merge now common anges */
2751 srchrgn.lower = chkrgn->lower - 1;
2752 srchrgn.upper = chkrgn->upper + 1;
2756 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2757 if (mergeindex == -1) break;
2758 if (mergeindex == index)
2760 fromindex = index + 1;
2764 TRACE("Merge with index %i\n", mergeindex);
2766 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2767 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2768 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2770 DPA_DeletePtr(ranges->hdpa, mergeindex);
2771 if (mergeindex < index) index --;
2775 ranges_check(ranges, "after add");
2779 ranges_check(ranges, "failed add");
2783 static BOOL ranges_del(RANGES ranges, RANGE range)
2788 TRACE("(%s)\n", debugrange(&range));
2789 ranges_check(ranges, "before del");
2791 /* we don't use DPAS_SORTED here, since we need *
2792 * to find the first overlapping range */
2793 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2796 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2798 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2800 /* case 1: Same range */
2801 if ( (chkrgn->upper == range.upper) &&
2802 (chkrgn->lower == range.lower) )
2804 DPA_DeletePtr(ranges->hdpa, index);
2807 /* case 2: engulf */
2808 else if ( (chkrgn->upper <= range.upper) &&
2809 (chkrgn->lower >= range.lower) )
2811 DPA_DeletePtr(ranges->hdpa, index);
2813 /* case 3: overlap upper */
2814 else if ( (chkrgn->upper <= range.upper) &&
2815 (chkrgn->lower < range.lower) )
2817 chkrgn->upper = range.lower;
2819 /* case 4: overlap lower */
2820 else if ( (chkrgn->upper > range.upper) &&
2821 (chkrgn->lower >= range.lower) )
2823 chkrgn->lower = range.upper;
2826 /* case 5: fully internal */
2829 RANGE tmprgn = *chkrgn, *newrgn;
2831 if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2832 newrgn->lower = chkrgn->lower;
2833 newrgn->upper = range.lower;
2834 chkrgn->lower = range.upper;
2835 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2844 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2847 ranges_check(ranges, "after del");
2851 ranges_check(ranges, "failed del");
2857 * Removes all selection ranges
2860 * [I] infoPtr : valid pointer to the listview structure
2861 * [I] toSkip : item range to skip removing the selection
2867 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2876 lvItem.stateMask = LVIS_SELECTED;
2878 /* need to clone the DPA because callbacks can change it */
2879 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2880 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2881 while(iterator_next(&i))
2882 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2883 /* note that the iterator destructor will free the cloned range */
2884 iterator_destroy(&i);
2889 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2893 if (!(toSkip = ranges_create(1))) return FALSE;
2894 if (nItem != -1) ranges_additem(toSkip, nItem);
2895 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2896 ranges_destroy(toSkip);
2900 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2902 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2907 * Retrieves the number of items that are marked as selected.
2910 * [I] infoPtr : valid pointer to the listview structure
2913 * Number of items selected.
2915 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2917 INT nSelectedCount = 0;
2919 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2922 for (i = 0; i < infoPtr->nItemCount; i++)
2924 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2929 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2931 TRACE("nSelectedCount=%d\n", nSelectedCount);
2932 return nSelectedCount;
2937 * Manages the item focus.
2940 * [I] infoPtr : valid pointer to the listview structure
2941 * [I] nItem : item index
2944 * TRUE : focused item changed
2945 * FALSE : focused item has NOT changed
2947 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2949 INT oldFocus = infoPtr->nFocusedItem;
2952 if (nItem == infoPtr->nFocusedItem) return FALSE;
2954 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2955 lvItem.stateMask = LVIS_FOCUSED;
2956 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2958 return oldFocus != infoPtr->nFocusedItem;
2961 /* Helper function for LISTVIEW_ShiftIndices *only* */
2962 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2964 if (nShiftItem < nItem) return nShiftItem;
2966 if (nShiftItem > nItem) return nShiftItem + direction;
2968 if (direction > 0) return nShiftItem + direction;
2970 return min(nShiftItem, infoPtr->nItemCount - 1);
2975 * Updates the various indices after an item has been inserted or deleted.
2978 * [I] infoPtr : valid pointer to the listview structure
2979 * [I] nItem : item index
2980 * [I] direction : Direction of shift, +1 or -1.
2985 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2990 /* temporarily disable change notification while shifting items */
2991 bOldChange = infoPtr->bDoChangeNotify;
2992 infoPtr->bDoChangeNotify = FALSE;
2994 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2996 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2998 assert(abs(direction) == 1);
3000 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3002 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3003 if (nNewFocus != infoPtr->nFocusedItem)
3004 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3006 /* But we are not supposed to modify nHotItem! */
3008 infoPtr->bDoChangeNotify = bOldChange;
3014 * Adds a block of selections.
3017 * [I] infoPtr : valid pointer to the listview structure
3018 * [I] nItem : item index
3021 * Whether the window is still valid.
3023 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3025 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3026 INT nLast = max(infoPtr->nSelectionMark, nItem);
3027 HWND hwndSelf = infoPtr->hwndSelf;
3028 NMLVODSTATECHANGE nmlv;
3033 /* Temporarily disable change notification
3034 * If the control is LVS_OWNERDATA, we need to send
3035 * only one LVN_ODSTATECHANGED notification.
3036 * See MSDN documentation for LVN_ITEMCHANGED.
3038 bOldChange = infoPtr->bDoChangeNotify;
3039 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3041 if (nFirst == -1) nFirst = nItem;
3043 item.state = LVIS_SELECTED;
3044 item.stateMask = LVIS_SELECTED;
3046 for (i = nFirst; i <= nLast; i++)
3047 LISTVIEW_SetItemState(infoPtr,i,&item);
3049 ZeroMemory(&nmlv, sizeof(nmlv));
3050 nmlv.iFrom = nFirst;
3053 nmlv.uOldState = item.state;
3055 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3056 if (!IsWindow(hwndSelf))
3058 infoPtr->bDoChangeNotify = bOldChange;
3065 * Sets a single group selection.
3068 * [I] infoPtr : valid pointer to the listview structure
3069 * [I] nItem : item index
3074 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3076 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3081 if (!(selection = ranges_create(100))) return;
3083 item.state = LVIS_SELECTED;
3084 item.stateMask = LVIS_SELECTED;
3086 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3088 if (infoPtr->nSelectionMark == -1)
3090 infoPtr->nSelectionMark = nItem;
3091 ranges_additem(selection, nItem);
3097 sel.lower = min(infoPtr->nSelectionMark, nItem);
3098 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3099 ranges_add(selection, sel);
3104 RECT rcItem, rcSel, rcSelMark;
3107 rcItem.left = LVIR_BOUNDS;
3108 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3109 rcSelMark.left = LVIR_BOUNDS;
3110 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3111 UnionRect(&rcSel, &rcItem, &rcSelMark);
3112 iterator_frameditems(&i, infoPtr, &rcSel);
3113 while(iterator_next(&i))
3115 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3116 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3118 iterator_destroy(&i);
3121 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3122 iterator_rangesitems(&i, selection);
3123 while(iterator_next(&i))
3124 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3125 /* this will also destroy the selection */
3126 iterator_destroy(&i);
3128 LISTVIEW_SetItemFocus(infoPtr, nItem);
3133 * Sets a single selection.
3136 * [I] infoPtr : valid pointer to the listview structure
3137 * [I] nItem : item index
3142 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3146 TRACE("nItem=%d\n", nItem);
3148 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3150 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3151 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3152 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3154 infoPtr->nSelectionMark = nItem;
3159 * Set selection(s) with keyboard.
3162 * [I] infoPtr : valid pointer to the listview structure
3163 * [I] nItem : item index
3166 * SUCCESS : TRUE (needs to be repainted)
3167 * FAILURE : FALSE (nothing has changed)
3169 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3171 /* FIXME: pass in the state */
3172 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3173 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3174 BOOL bResult = FALSE;
3176 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3178 if (infoPtr->dwStyle & LVS_SINGLESEL)
3181 LISTVIEW_SetSelection(infoPtr, nItem);
3188 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3192 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3197 LISTVIEW_SetSelection(infoPtr, nItem);
3200 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3203 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3207 static BOOL LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3209 LVHITTESTINFO lvHitTestInfo;
3211 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3212 lvHitTestInfo.pt.x = pt.x;
3213 lvHitTestInfo.pt.y = pt.y;
3215 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3217 lpLVItem->mask = LVIF_PARAM;
3218 lpLVItem->iItem = lvHitTestInfo.iItem;
3219 lpLVItem->iSubItem = 0;
3221 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3226 * Called when the mouse is being actively tracked and has hovered for a specified
3230 * [I] infoPtr : valid pointer to the listview structure
3231 * [I] fwKeys : key indicator
3232 * [I] x,y : mouse position
3235 * 0 if the message was processed, non-zero if there was an error
3238 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3239 * over the item for a certain period of time.
3242 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, INT x, INT y)
3244 if (infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3252 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3253 LISTVIEW_SetSelection(infoPtr, item.iItem);
3261 * Called whenever WM_MOUSEMOVE is received.
3264 * [I] infoPtr : valid pointer to the listview structure
3265 * [I] fwKeys : key indicator
3266 * [I] x,y : mouse position
3269 * 0 if the message is processed, non-zero if there was an error
3271 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3273 TRACKMOUSEEVENT trackinfo;
3275 if (infoPtr->bLButtonDown && DragDetect(infoPtr->hwndSelf, infoPtr->ptClickPos))
3277 LVHITTESTINFO lvHitTestInfo;
3280 lvHitTestInfo.pt = infoPtr->ptClickPos;
3281 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3283 ZeroMemory(&nmlv, sizeof(nmlv));
3284 nmlv.iItem = lvHitTestInfo.iItem;
3285 nmlv.ptAction = infoPtr->ptClickPos;
3287 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3292 infoPtr->bLButtonDown = FALSE;
3294 /* see if we are supposed to be tracking mouse hovering */
3295 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3296 /* fill in the trackinfo struct */
3297 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3298 trackinfo.dwFlags = TME_QUERY;
3299 trackinfo.hwndTrack = infoPtr->hwndSelf;
3300 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3302 /* see if we are already tracking this hwnd */
3303 _TrackMouseEvent(&trackinfo);
3305 if(!(trackinfo.dwFlags & TME_HOVER)) {
3306 trackinfo.dwFlags = TME_HOVER;
3308 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3309 _TrackMouseEvent(&trackinfo);
3318 * Tests wheather the item is assignable to a list with style lStyle
3320 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3322 if ( (lpLVItem->mask & LVIF_TEXT) &&
3323 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3324 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3332 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3335 * [I] infoPtr : valid pointer to the listview structure
3336 * [I] lpLVItem : valid pointer to new item atttributes
3337 * [I] isNew : the item being set is being inserted
3338 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3339 * [O] bChanged : will be set to TRUE if the item really changed
3345 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3347 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3355 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3357 if (lpLVItem->mask == 0) return TRUE;
3359 if (infoPtr->dwStyle & LVS_OWNERDATA)
3361 /* a virtual listview we stores only selection and focus */
3362 if (lpLVItem->mask & ~LVIF_STATE)
3368 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3369 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3373 /* we need to get the lParam and state of the item */
3374 item.iItem = lpLVItem->iItem;
3375 item.iSubItem = lpLVItem->iSubItem;
3376 item.mask = LVIF_STATE | LVIF_PARAM;
3377 item.stateMask = ~0;
3380 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3382 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3383 /* determine what fields will change */
3384 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3385 uChanged |= LVIF_STATE;
3387 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3388 uChanged |= LVIF_IMAGE;
3390 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3391 uChanged |= LVIF_PARAM;
3393 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3394 uChanged |= LVIF_INDENT;
3396 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3397 uChanged |= LVIF_TEXT;
3399 TRACE("uChanged=0x%x\n", uChanged);
3400 if (!uChanged) return TRUE;
3403 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3404 nmlv.iItem = lpLVItem->iItem;
3405 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3406 nmlv.uOldState = item.state;
3407 nmlv.uChanged = uChanged;
3408 nmlv.lParam = item.lParam;
3410 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3411 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3413 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3415 HWND hwndSelf = infoPtr->hwndSelf;
3417 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3419 if (!IsWindow(hwndSelf))
3423 /* copy information */
3424 if (lpLVItem->mask & LVIF_TEXT)
3425 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3427 if (lpLVItem->mask & LVIF_IMAGE)
3428 lpItem->hdr.iImage = lpLVItem->iImage;
3430 if (lpLVItem->mask & LVIF_PARAM)
3431 lpItem->lParam = lpLVItem->lParam;
3433 if (lpLVItem->mask & LVIF_INDENT)
3434 lpItem->iIndent = lpLVItem->iIndent;
3436 if (uChanged & LVIF_STATE)
3438 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3440 lpItem->state &= ~lpLVItem->stateMask;
3441 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3443 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3445 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3446 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3448 else if (lpLVItem->stateMask & LVIS_SELECTED)
3449 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3451 /* if we are asked to change focus, and we manage it, do it */
3452 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3454 if (lpLVItem->state & LVIS_FOCUSED)
3456 LISTVIEW_SetItemFocus(infoPtr, -1);
3457 infoPtr->nFocusedItem = lpLVItem->iItem;
3458 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3460 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3461 infoPtr->nFocusedItem = -1;
3465 /* if we're inserting the item, we're done */
3466 if (isNew) return TRUE;
3468 /* send LVN_ITEMCHANGED notification */
3469 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3470 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3477 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3480 * [I] infoPtr : valid pointer to the listview structure
3481 * [I] lpLVItem : valid pointer to new subitem atttributes
3482 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3483 * [O] bChanged : will be set to TRUE if the item really changed
3489 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3492 SUBITEM_INFO *lpSubItem;
3494 /* we do not support subitems for virtual listviews */
3495 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3497 /* set subitem only if column is present */
3498 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3500 /* First do some sanity checks */
3501 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3502 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3504 /* get the subitem structure, and create it if not there */
3505 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3506 assert (hdpaSubItems);
3508 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3511 SUBITEM_INFO *tmpSubItem;
3514 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3515 if (!lpSubItem) return FALSE;
3516 /* we could binary search here, if need be...*/
3517 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3519 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3520 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3522 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3527 lpSubItem->iSubItem = lpLVItem->iSubItem;
3528 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3532 if (lpLVItem->mask & LVIF_IMAGE)
3533 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3535 lpSubItem->hdr.iImage = lpLVItem->iImage;
3539 if (lpLVItem->mask & LVIF_TEXT)
3540 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3542 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3551 * Sets item attributes.
3554 * [I] infoPtr : valid pointer to the listview structure
3555 * [I] lpLVItem : new item atttributes
3556 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3562 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3564 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3565 HWND hwndSelf = infoPtr->hwndSelf;
3566 LPWSTR pszText = NULL;
3567 BOOL bResult, bChanged = FALSE;
3569 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3571 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3574 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3575 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3577 pszText = lpLVItem->pszText;
3578 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3581 /* actually set the fields */
3582 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3584 if (lpLVItem->iSubItem)
3585 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3587 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3588 if (!IsWindow(hwndSelf))
3591 /* redraw item, if necessary */
3592 if (bChanged && !infoPtr->bIsDrawing)
3594 /* this little optimization eliminates some nasty flicker */
3595 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3596 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3597 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3599 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3604 textfreeT(lpLVItem->pszText, isW);
3605 ((LVITEMW *)lpLVItem)->pszText = pszText;
3613 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3616 * [I] infoPtr : valid pointer to the listview structure
3621 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3623 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3625 SCROLLINFO scrollInfo;
3627 scrollInfo.cbSize = sizeof(SCROLLINFO);
3628 scrollInfo.fMask = SIF_POS;
3630 if (uView == LVS_LIST)
3632 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3633 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3635 else if (uView == LVS_REPORT)
3637 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3638 nItem = scrollInfo.nPos;
3642 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3643 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3646 TRACE("nItem=%d\n", nItem);
3654 * Erases the background of the given rectangle
3657 * [I] infoPtr : valid pointer to the listview structure
3658 * [I] hdc : device context handle
3659 * [I] lprcBox : clipping rectangle
3665 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3667 if (!infoPtr->hBkBrush) return FALSE;
3669 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3671 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3679 * [I] infoPtr : valid pointer to the listview structure
3680 * [I] hdc : device context handle
3681 * [I] nItem : item index
3682 * [I] nSubItem : subitem index
3683 * [I] pos : item position in client coordinates
3684 * [I] cdmode : custom draw mode
3690 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3692 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3693 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3694 static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3695 DWORD cdsubitemmode = CDRF_DODEFAULT;
3696 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3697 NMLVCUSTOMDRAW nmlvcd;
3701 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3703 /* get information needed for drawing the item */
3704 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3705 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3706 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3707 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3708 lvItem.iItem = nItem;
3709 lvItem.iSubItem = nSubItem;
3712 lvItem.cchTextMax = DISP_TEXT_SIZE;
3713 lvItem.pszText = szDispText;
3714 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3715 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3716 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3717 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3718 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3720 /* now check if we need to update the focus rectangle */
3721 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3723 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3724 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3725 OffsetRect(&rcBox, pos.x, pos.y);
3726 OffsetRect(&rcState, pos.x, pos.y);
3727 OffsetRect(&rcIcon, pos.x, pos.y);
3728 OffsetRect(&rcLabel, pos.x, pos.y);
3729 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3730 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3732 /* fill in the custom draw structure */
3733 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3735 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3736 if (cdmode & CDRF_NOTIFYITEMDRAW)
3737 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3738 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3739 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3740 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3741 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3743 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3744 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3746 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3747 prepaint_setup(infoPtr, hdc, &nmlvcd);
3749 /* in full row select, subitems, will just use main item's colors */
3750 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3751 nmlvcd.clrTextBk = CLR_NONE;
3754 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3756 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3759 TRACE("uStateImage=%d\n", uStateImage);
3760 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3765 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3766 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3768 TRACE("iImage=%d\n", lvItem.iImage);
3769 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3770 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3773 /* Don't bother painting item being edited */
3774 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3776 /* draw the selection background, if we're drawing the main item */
3780 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3781 rcSelect.right = rcBox.right;
3783 if (nmlvcd.clrTextBk != CLR_NONE)
3784 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3785 if(lprcFocus) *lprcFocus = rcSelect;
3788 /* figure out the text drawing flags */
3789 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3790 if (uView == LVS_ICON)
3791 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3794 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3796 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3797 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3798 default: uFormat |= DT_LEFT;
3801 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3803 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3804 else rcLabel.left += LABEL_HOR_PADDING;
3806 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3807 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3810 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3811 notify_postpaint(infoPtr, &nmlvcd);
3817 * Draws listview items when in owner draw mode.
3820 * [I] infoPtr : valid pointer to the listview structure
3821 * [I] hdc : device context handle
3826 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3828 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3829 DWORD cditemmode = CDRF_DODEFAULT;
3830 NMLVCUSTOMDRAW nmlvcd;
3831 POINT Origin, Position;
3837 ZeroMemory(&dis, sizeof(dis));
3839 /* Get scroll info once before loop */
3840 LISTVIEW_GetOrigin(infoPtr, &Origin);
3842 /* iterate through the invalidated rows */
3843 while(iterator_next(i))
3845 item.iItem = i->nItem;
3847 item.mask = LVIF_PARAM | LVIF_STATE;
3848 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3849 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3851 dis.CtlType = ODT_LISTVIEW;
3853 dis.itemID = item.iItem;
3854 dis.itemAction = ODA_DRAWENTIRE;
3856 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3857 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3858 dis.hwndItem = infoPtr->hwndSelf;
3860 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3861 dis.rcItem.left = Position.x + Origin.x;
3862 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3863 dis.rcItem.top = Position.y + Origin.y;
3864 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3865 dis.itemData = item.lParam;
3867 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3870 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3871 * structure for the rest. of the paint cycle
3873 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3874 if (cdmode & CDRF_NOTIFYITEMDRAW)
3875 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3877 if (!(cditemmode & CDRF_SKIPDEFAULT))
3879 prepaint_setup (infoPtr, hdc, &nmlvcd);
3880 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3883 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3884 notify_postpaint(infoPtr, &nmlvcd);
3890 * Draws listview items when in report display mode.
3893 * [I] infoPtr : valid pointer to the listview structure
3894 * [I] hdc : device context handle
3895 * [I] cdmode : custom draw mode
3900 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3903 RECT rcClip, rcItem;
3904 POINT Origin, Position;
3910 /* figure out what to draw */
3911 rgntype = GetClipBox(hdc, &rcClip);
3912 if (rgntype == NULLREGION) return;
3914 /* Get scroll info once before loop */
3915 LISTVIEW_GetOrigin(infoPtr, &Origin);
3917 /* narrow down the columns we need to paint */
3918 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3920 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3921 if (rcItem.right + Origin.x >= rcClip.left) break;
3923 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3925 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3926 if (rcItem.left + Origin.x < rcClip.right) break;
3928 iterator_rangeitems(&j, colRange);
3930 /* in full row select, we _have_ to draw the main item */
3931 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3934 /* iterate through the invalidated rows */
3935 while(iterator_next(i))
3937 /* iterate through the invalidated columns */
3938 while(iterator_next(&j))
3940 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3941 Position.x += Origin.x;
3942 Position.y += Origin.y;
3944 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3946 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3948 rcItem.bottom = infoPtr->nItemHeight;
3949 OffsetRect(&rcItem, Position.x, Position.y);
3950 if (!RectVisible(hdc, &rcItem)) continue;
3953 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3956 iterator_destroy(&j);
3961 * Draws listview items when in list display mode.
3964 * [I] infoPtr : valid pointer to the listview structure
3965 * [I] hdc : device context handle
3966 * [I] cdmode : custom draw mode
3971 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3973 POINT Origin, Position;
3975 /* Get scroll info once before loop */
3976 LISTVIEW_GetOrigin(infoPtr, &Origin);
3978 while(iterator_prev(i))
3980 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3981 Position.x += Origin.x;
3982 Position.y += Origin.y;
3984 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3991 * Draws listview items.
3994 * [I] infoPtr : valid pointer to the listview structure
3995 * [I] hdc : device context handle
4000 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
4002 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4003 COLORREF oldTextColor, oldClrTextBk, oldClrText;
4004 NMLVCUSTOMDRAW nmlvcd;
4011 LISTVIEW_DUMP(infoPtr);
4013 infoPtr->bIsDrawing = TRUE;
4015 /* save dc values we're gonna trash while drawing */
4016 hOldFont = SelectObject(hdc, infoPtr->hFont);
4017 oldBkMode = GetBkMode(hdc);
4018 infoPtr->clrTextBkDefault = GetBkColor(hdc);
4019 oldTextColor = GetTextColor(hdc);
4021 oldClrTextBk = infoPtr->clrTextBk;
4022 oldClrText = infoPtr->clrText;
4024 infoPtr->cditemmode = CDRF_DODEFAULT;
4026 GetClientRect(infoPtr->hwndSelf, &rcClient);
4027 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4028 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4029 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4030 prepaint_setup(infoPtr, hdc, &nmlvcd);
4032 /* Use these colors to draw the items */
4033 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4034 infoPtr->clrText = nmlvcd.clrText;
4036 /* nothing to draw */
4037 if(infoPtr->nItemCount == 0) goto enddraw;
4039 /* figure out what we need to draw */
4040 iterator_visibleitems(&i, infoPtr, hdc);
4042 /* send cache hint notification */
4043 if (infoPtr->dwStyle & LVS_OWNERDATA)
4045 RANGE range = iterator_range(&i);
4048 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4049 nmlv.iFrom = range.lower;
4050 nmlv.iTo = range.upper - 1;
4051 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4054 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4055 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4058 if (uView == LVS_REPORT)
4059 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4060 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4061 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4063 /* if we have a focus rect, draw it */
4064 if (infoPtr->bFocus)
4065 DrawFocusRect(hdc, &infoPtr->rcFocus);
4067 iterator_destroy(&i);
4070 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4071 notify_postpaint(infoPtr, &nmlvcd);
4073 infoPtr->clrTextBk = oldClrTextBk;
4074 infoPtr->clrText = oldClrText;
4076 SelectObject(hdc, hOldFont);
4077 SetBkMode(hdc, oldBkMode);
4078 SetBkColor(hdc, infoPtr->clrTextBkDefault);
4079 SetTextColor(hdc, oldTextColor);
4080 infoPtr->bIsDrawing = FALSE;
4086 * Calculates the approximate width and height of a given number of items.
4089 * [I] infoPtr : valid pointer to the listview structure
4090 * [I] nItemCount : number of items
4091 * [I] wWidth : width
4092 * [I] wHeight : height
4095 * Returns a DWORD. The width in the low word and the height in high word.
4097 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
4098 WORD wWidth, WORD wHeight)
4100 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4101 INT nItemCountPerColumn = 1;
4102 INT nColumnCount = 0;
4103 DWORD dwViewRect = 0;
4105 if (nItemCount == -1)
4106 nItemCount = infoPtr->nItemCount;
4108 if (uView == LVS_LIST)
4110 if (wHeight == 0xFFFF)
4112 /* use current height */
4113 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4116 if (wHeight < infoPtr->nItemHeight)
4117 wHeight = infoPtr->nItemHeight;
4121 if (infoPtr->nItemHeight > 0)
4123 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4124 if (nItemCountPerColumn == 0)
4125 nItemCountPerColumn = 1;
4127 if (nItemCount % nItemCountPerColumn != 0)
4128 nColumnCount = nItemCount / nItemCountPerColumn;
4130 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4134 /* Microsoft padding magic */
4135 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4136 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4138 dwViewRect = MAKELONG(wWidth, wHeight);
4140 else if (uView == LVS_REPORT)
4144 if (infoPtr->nItemCount > 0)
4146 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4147 wWidth = rcBox.right - rcBox.left;
4148 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4152 /* use current height and width */
4153 if (wHeight == 0xffff)
4154 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4155 if (wWidth == 0xffff)
4156 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4159 dwViewRect = MAKELONG(wWidth, wHeight);
4161 else if (uView == LVS_SMALLICON)
4162 FIXME("uView == LVS_SMALLICON: not implemented\n");
4163 else if (uView == LVS_ICON)
4164 FIXME("uView == LVS_ICON: not implemented\n");
4172 * Create a drag image list for the specified item.
4175 * [I] infoPtr : valid pointer to the listview structure
4176 * [I] iItem : index of item
4177 * [O] lppt : Upperr-left corner of the image
4180 * Returns a handle to the image list if successful, NULL otherwise.
4182 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4188 HBITMAP hbmp, hOldbmp;
4189 HIMAGELIST dragList = 0;
4190 TRACE("iItem=%d Count=%d \n", iItem, infoPtr->nItemCount);
4192 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4195 rcItem.left = LVIR_BOUNDS;
4196 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4199 lppt->x = rcItem.left;
4200 lppt->y = rcItem.top;
4202 size.cx = rcItem.right - rcItem.left;
4203 size.cy = rcItem.bottom - rcItem.top;
4205 hdcOrig = GetDC(infoPtr->hwndSelf);
4206 hdc = CreateCompatibleDC(hdcOrig);
4207 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4208 hOldbmp = SelectObject(hdc, hbmp);
4210 rcItem.left = rcItem.top = 0;
4211 rcItem.right = size.cx;
4212 rcItem.bottom = size.cy;
4213 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4216 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4218 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4219 SelectObject(hdc, hOldbmp);
4220 ImageList_Add(dragList, hbmp, 0);
4223 SelectObject(hdc, hOldbmp);
4227 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4229 TRACE("ret=%p\n", dragList);
4237 * Removes all listview items and subitems.
4240 * [I] infoPtr : valid pointer to the listview structure
4246 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4249 HDPA hdpaSubItems = NULL;
4256 /* we do it directly, to avoid notifications */
4257 ranges_clear(infoPtr->selectionRanges);
4258 infoPtr->nSelectionMark = -1;
4259 infoPtr->nFocusedItem = -1;
4260 SetRectEmpty(&infoPtr->rcFocus);
4261 /* But we are supposed to leave nHotItem as is! */
4264 /* send LVN_DELETEALLITEMS notification */
4265 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4267 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4269 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4271 /* send LVN_DELETEITEM notification, if not suppressed */
4272 if (!bSuppress) notify_deleteitem(infoPtr, i);
4273 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4275 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4276 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4278 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4279 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4282 DPA_Destroy(hdpaSubItems);
4283 DPA_DeletePtr(infoPtr->hdpaItems, i);
4285 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4286 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4287 infoPtr->nItemCount --;
4290 LISTVIEW_UpdateScroll(infoPtr);
4292 LISTVIEW_InvalidateList(infoPtr);
4299 * Scrolls, and updates the columns, when a column is changing width.
4302 * [I] infoPtr : valid pointer to the listview structure
4303 * [I] nColumn : column to scroll
4304 * [I] dx : amount of scroll, in pixels
4309 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4311 COLUMN_INFO *lpColumnInfo;
4316 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4317 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4318 rcCol = lpColumnInfo->rcHeader;
4319 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4320 rcCol.left = rcCol.right;
4322 /* ajust the other columns */
4323 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4325 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4326 lpColumnInfo->rcHeader.left += dx;
4327 lpColumnInfo->rcHeader.right += dx;
4330 /* do not update screen if not in report mode */
4331 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4333 /* if we have a focus, must first erase the focus rect */
4334 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4336 /* Need to reset the item width when inserting a new column */
4337 infoPtr->nItemWidth += dx;
4339 LISTVIEW_UpdateScroll(infoPtr);
4340 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4342 /* scroll to cover the deleted column, and invalidate for redraw */
4343 rcOld = infoPtr->rcList;
4344 rcOld.left = ptOrigin.x + rcCol.left + dx;
4345 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4347 /* we can restore focus now */
4348 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4353 * Removes a column from the listview control.
4356 * [I] infoPtr : valid pointer to the listview structure
4357 * [I] nColumn : column index
4363 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4367 TRACE("nColumn=%d\n", nColumn);
4369 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4370 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4372 /* While the MSDN specifically says that column zero should not be deleted,
4373 what actually happens is that the column itself is deleted but no items or subitems
4377 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4379 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4382 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4383 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4385 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4387 SUBITEM_INFO *lpSubItem, *lpDelItem;
4389 INT nItem, nSubItem, i;
4391 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4393 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4396 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4398 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4399 if (lpSubItem->iSubItem == nColumn)
4402 lpDelItem = lpSubItem;
4404 else if (lpSubItem->iSubItem > nColumn)
4406 lpSubItem->iSubItem--;
4410 /* if we found our subitem, zapp it */
4414 if (is_textW(lpDelItem->hdr.pszText))
4415 Free(lpDelItem->hdr.pszText);
4420 /* free dpa memory */
4421 DPA_DeletePtr(hdpaSubItems, nSubItem);
4426 /* update the other column info */
4427 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4428 LISTVIEW_InvalidateList(infoPtr);
4430 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4437 * Invalidates the listview after an item's insertion or deletion.
4440 * [I] infoPtr : valid pointer to the listview structure
4441 * [I] nItem : item index
4442 * [I] dir : -1 if deleting, 1 if inserting
4447 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4449 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4450 INT nPerCol, nItemCol, nItemRow;
4454 /* if we don't refresh, what's the point of scrolling? */
4455 if (!is_redrawing(infoPtr)) return;
4457 assert (abs(dir) == 1);
4459 /* arrange icons if autoarrange is on */
4460 if (is_autoarrange(infoPtr))
4462 BOOL arrange = TRUE;
4463 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4464 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4465 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4468 /* scrollbars need updating */
4469 LISTVIEW_UpdateScroll(infoPtr);
4471 /* figure out the item's position */
4472 if (uView == LVS_REPORT)
4473 nPerCol = infoPtr->nItemCount + 1;
4474 else if (uView == LVS_LIST)
4475 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4476 else /* LVS_ICON, or LVS_SMALLICON */
4479 nItemCol = nItem / nPerCol;
4480 nItemRow = nItem % nPerCol;
4481 LISTVIEW_GetOrigin(infoPtr, &Origin);
4483 /* move the items below up a slot */
4484 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4485 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4486 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4487 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4488 OffsetRect(&rcScroll, Origin.x, Origin.y);
4489 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4490 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4492 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4493 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4494 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4497 /* report has only that column, so we're done */
4498 if (uView == LVS_REPORT) return;
4500 /* now for LISTs, we have to deal with the columns to the right */
4501 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4503 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4504 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4505 OffsetRect(&rcScroll, Origin.x, Origin.y);
4506 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4507 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4508 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4513 * Removes an item from the listview control.
4516 * [I] infoPtr : valid pointer to the listview structure
4517 * [I] nItem : item index
4523 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4525 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4528 TRACE("(nItem=%d)\n", nItem);
4530 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4532 /* remove selection, and focus */
4534 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4535 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4537 /* send LVN_DELETEITEM notification. */
4538 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4540 /* we need to do this here, because we'll be deleting stuff */
4541 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4542 LISTVIEW_InvalidateItem(infoPtr, nItem);
4544 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4550 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4551 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4553 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4554 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4557 DPA_Destroy(hdpaSubItems);
4560 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4562 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4563 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4566 infoPtr->nItemCount--;
4567 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4569 /* now is the invalidation fun */
4570 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4577 * Callback implementation for editlabel control
4580 * [I] infoPtr : valid pointer to the listview structure
4581 * [I] pszText : modified text
4582 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4588 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4590 HWND hwndSelf = infoPtr->hwndSelf;
4591 NMLVDISPINFOW dispInfo;
4593 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4595 ZeroMemory(&dispInfo, sizeof(dispInfo));
4596 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4597 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4598 dispInfo.item.iSubItem = 0;
4599 dispInfo.item.stateMask = ~0;
4600 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4601 /* add the text from the edit in */
4602 dispInfo.item.mask |= LVIF_TEXT;
4603 dispInfo.item.pszText = pszText;
4604 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4606 /* Do we need to update the Item Text */
4607 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4608 if (!IsWindow(hwndSelf))
4610 if (!pszText) return TRUE;
4612 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4614 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4615 ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4616 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4618 LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4623 ZeroMemory(&dispInfo, sizeof(dispInfo));
4624 dispInfo.item.mask = LVIF_TEXT;
4625 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4626 dispInfo.item.iSubItem = 0;
4627 dispInfo.item.pszText = pszText;
4628 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4629 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4634 * Begin in place editing of specified list view item
4637 * [I] infoPtr : valid pointer to the listview structure
4638 * [I] nItem : item index
4639 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4645 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4647 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4648 NMLVDISPINFOW dispInfo;
4650 HWND hwndSelf = infoPtr->hwndSelf;
4652 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4654 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4655 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4657 infoPtr->nEditLabelItem = nItem;
4659 /* Is the EditBox still there, if so remove it */
4660 if(infoPtr->hwndEdit != 0)
4662 SetFocus(infoPtr->hwndSelf);
4663 infoPtr->hwndEdit = 0;
4666 LISTVIEW_SetSelection(infoPtr, nItem);
4667 LISTVIEW_SetItemFocus(infoPtr, nItem);
4668 LISTVIEW_InvalidateItem(infoPtr, nItem);
4670 rect.left = LVIR_LABEL;
4671 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4673 ZeroMemory(&dispInfo, sizeof(dispInfo));
4674 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4675 dispInfo.item.iItem = nItem;
4676 dispInfo.item.iSubItem = 0;
4677 dispInfo.item.stateMask = ~0;
4678 dispInfo.item.pszText = szDispText;
4679 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4680 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4682 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4683 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4684 if (!infoPtr->hwndEdit) return 0;
4686 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4688 if (!IsWindow(hwndSelf))
4690 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4691 infoPtr->hwndEdit = 0;
4695 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4696 SetFocus(infoPtr->hwndEdit);
4697 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4698 return infoPtr->hwndEdit;
4704 * Ensures the specified item is visible, scrolling into view if necessary.
4707 * [I] infoPtr : valid pointer to the listview structure
4708 * [I] nItem : item index
4709 * [I] bPartial : partially or entirely visible
4715 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4717 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4718 INT nScrollPosHeight = 0;
4719 INT nScrollPosWidth = 0;
4720 INT nHorzAdjust = 0;
4721 INT nVertAdjust = 0;
4724 RECT rcItem, rcTemp;
4726 rcItem.left = LVIR_BOUNDS;
4727 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4729 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4731 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4733 /* scroll left/right, but in LVS_REPORT mode */
4734 if (uView == LVS_LIST)
4735 nScrollPosWidth = infoPtr->nItemWidth;
4736 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4737 nScrollPosWidth = 1;
4739 if (rcItem.left < infoPtr->rcList.left)
4742 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4747 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4751 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4753 /* scroll up/down, but not in LVS_LIST mode */
4754 if (uView == LVS_REPORT)
4755 nScrollPosHeight = infoPtr->nItemHeight;
4756 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4757 nScrollPosHeight = 1;
4759 if (rcItem.top < infoPtr->rcList.top)
4762 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4767 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4771 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4773 if (nScrollPosWidth)
4775 INT diff = nHorzDiff / nScrollPosWidth;
4776 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4777 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4780 if (nScrollPosHeight)
4782 INT diff = nVertDiff / nScrollPosHeight;
4783 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4784 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4792 * Searches for an item with specific characteristics.
4795 * [I] hwnd : window handle
4796 * [I] nStart : base item index
4797 * [I] lpFindInfo : item information to look for
4800 * SUCCESS : index of item
4803 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4804 const LVFINDINFOW *lpFindInfo)
4806 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4807 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4808 BOOL bWrap = FALSE, bNearest = FALSE;
4809 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4810 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4811 POINT Position, Destination;
4814 if (!lpFindInfo || nItem < 0) return -1;
4817 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4819 lvItem.mask |= LVIF_TEXT;
4820 lvItem.pszText = szDispText;
4821 lvItem.cchTextMax = DISP_TEXT_SIZE;
4824 if (lpFindInfo->flags & LVFI_WRAP)
4827 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4828 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4833 LISTVIEW_GetOrigin(infoPtr, &Origin);
4834 Destination.x = lpFindInfo->pt.x - Origin.x;
4835 Destination.y = lpFindInfo->pt.y - Origin.y;
4836 switch(lpFindInfo->vkDirection)
4838 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4839 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4840 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4841 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4842 case VK_HOME: Destination.x = Destination.y = 0; break;
4843 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4844 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4846 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4847 Destination.x = rcArea.right;
4848 Destination.y = rcArea.bottom;
4850 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4854 else Destination.x = Destination.y = 0;
4856 /* if LVFI_PARAM is specified, all other flags are ignored */
4857 if (lpFindInfo->flags & LVFI_PARAM)
4859 lvItem.mask |= LVIF_PARAM;
4861 lvItem.mask &= ~LVIF_TEXT;
4865 for (; nItem < nLast; nItem++)
4867 lvItem.iItem = nItem;
4868 lvItem.iSubItem = 0;
4869 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4871 if (lvItem.mask & LVIF_PARAM)
4873 if (lpFindInfo->lParam == lvItem.lParam)
4879 if (lvItem.mask & LVIF_TEXT)
4881 if (lpFindInfo->flags & LVFI_PARTIAL)
4883 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4887 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4891 if (!bNearest) return nItem;
4893 /* This is very inefficient. To do a good job here,
4894 * we need a sorted array of (x,y) item positions */
4895 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4897 /* compute the distance^2 to the destination */
4898 xdist = Destination.x - Position.x;
4899 ydist = Destination.y - Position.y;
4900 dist = xdist * xdist + ydist * ydist;
4902 /* remember the distance, and item if it's closer */
4906 nNearestItem = nItem;
4913 nLast = min(nStart + 1, infoPtr->nItemCount);
4918 return nNearestItem;
4923 * Searches for an item with specific characteristics.
4926 * [I] hwnd : window handle
4927 * [I] nStart : base item index
4928 * [I] lpFindInfo : item information to look for
4931 * SUCCESS : index of item
4934 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4935 const LVFINDINFOA *lpFindInfo)
4937 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4941 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4942 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4943 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4944 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4950 * Retrieves the background image of the listview control.
4953 * [I] infoPtr : valid pointer to the listview structure
4954 * [O] lpBkImage : background image attributes
4960 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4962 /* FIXME (listview, "empty stub!\n"); */
4968 * Retrieves column attributes.
4971 * [I] infoPtr : valid pointer to the listview structure
4972 * [I] nColumn : column index
4973 * [IO] lpColumn : column information
4974 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4975 * otherwise it is in fact a LPLVCOLUMNA
4981 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4983 COLUMN_INFO *lpColumnInfo;
4986 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4987 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4989 /* initialize memory */
4990 ZeroMemory(&hdi, sizeof(hdi));
4992 if (lpColumn->mask & LVCF_TEXT)
4994 hdi.mask |= HDI_TEXT;
4995 hdi.pszText = lpColumn->pszText;
4996 hdi.cchTextMax = lpColumn->cchTextMax;
4999 if (lpColumn->mask & LVCF_IMAGE)
5000 hdi.mask |= HDI_IMAGE;
5002 if (lpColumn->mask & LVCF_ORDER)
5003 hdi.mask |= HDI_ORDER;
5005 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5007 if (lpColumn->mask & LVCF_FMT)
5008 lpColumn->fmt = lpColumnInfo->fmt;
5010 if (lpColumn->mask & LVCF_WIDTH)
5011 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5013 if (lpColumn->mask & LVCF_IMAGE)
5014 lpColumn->iImage = hdi.iImage;
5016 if (lpColumn->mask & LVCF_ORDER)
5017 lpColumn->iOrder = hdi.iOrder;
5023 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5030 /* FIXME: little hack */
5031 for (i = 0; i < iCount; i++)
5039 * Retrieves the column width.
5042 * [I] infoPtr : valid pointer to the listview structure
5043 * [I] int : column index
5046 * SUCCESS : column width
5049 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
5051 INT nColumnWidth = 0;
5054 TRACE("nColumn=%d\n", nColumn);
5056 /* we have a 'column' in LIST and REPORT mode only */
5057 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5060 nColumnWidth = infoPtr->nItemWidth;
5063 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
5064 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
5065 nColumnWidth = rcHeader.right - rcHeader.left;
5069 TRACE("nColumnWidth=%d\n", nColumnWidth);
5070 return nColumnWidth;
5075 * In list or report display mode, retrieves the number of items that can fit
5076 * vertically in the visible area. In icon or small icon display mode,
5077 * retrieves the total number of visible items.
5080 * [I] infoPtr : valid pointer to the listview structure
5083 * Number of fully visible items.
5085 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5087 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5091 return infoPtr->nItemCount;
5093 return LISTVIEW_GetCountPerColumn(infoPtr);
5095 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5103 * Retrieves an image list handle.
5106 * [I] infoPtr : valid pointer to the listview structure
5107 * [I] nImageList : image list identifier
5110 * SUCCESS : image list handle
5113 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5117 case LVSIL_NORMAL: return infoPtr->himlNormal;
5118 case LVSIL_SMALL: return infoPtr->himlSmall;
5119 case LVSIL_STATE: return infoPtr->himlState;
5124 /* LISTVIEW_GetISearchString */
5128 * Retrieves item attributes.
5131 * [I] hwnd : window handle
5132 * [IO] lpLVItem : item info
5133 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5134 * if FALSE, the lpLVItem is a LPLVITEMA.
5137 * This is the internal 'GetItem' interface -- it tries to
5138 * be smart, and avoids text copies, if possible, by modifing
5139 * lpLVItem->pszText to point to the text string. Please note
5140 * that this is not always possible (e.g. OWNERDATA), so on
5141 * entry you *must* supply valid values for pszText, and cchTextMax.
5142 * The only difference to the documented interface is that upon
5143 * return, you should use *only* the lpLVItem->pszText, rather than
5144 * the buffer pointer you provided on input. Most code already does
5145 * that, so it's not a problem.
5146 * For the two cases when the text must be copied (that is,
5147 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5153 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5155 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5156 NMLVDISPINFOW dispInfo;
5162 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5164 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5167 if (lpLVItem->mask == 0) return TRUE;
5169 /* make a local copy */
5170 isubitem = lpLVItem->iSubItem;
5172 /* a quick optimization if all we're asked is the focus state
5173 * these queries are worth optimising since they are common,
5174 * and can be answered in constant time, without the heavy accesses */
5175 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5176 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5178 lpLVItem->state = 0;
5179 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5180 lpLVItem->state |= LVIS_FOCUSED;
5184 ZeroMemory(&dispInfo, sizeof(dispInfo));
5186 /* if the app stores all the data, handle it separately */
5187 if (infoPtr->dwStyle & LVS_OWNERDATA)
5189 dispInfo.item.state = 0;
5191 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5192 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5194 /* NOTE: copy only fields which we _know_ are initialized, some apps
5195 * depend on the uninitialized fields being 0 */
5196 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5197 dispInfo.item.iItem = lpLVItem->iItem;
5198 dispInfo.item.iSubItem = isubitem;
5199 if (lpLVItem->mask & LVIF_TEXT)
5201 dispInfo.item.pszText = lpLVItem->pszText;
5202 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5204 if (lpLVItem->mask & LVIF_STATE)
5205 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5206 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5207 dispInfo.item.stateMask = lpLVItem->stateMask;
5208 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5210 /* full size structure expected - _WIN32IE >= 0x560 */
5211 *lpLVItem = dispInfo.item;
5213 else if (lpLVItem->mask & LVIF_INDENT)
5215 /* indent member expected - _WIN32IE >= 0x300 */
5216 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5220 /* minimal structure expected */
5221 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5223 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5226 /* make sure lParam is zeroed out */
5227 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5229 /* we store only a little state, so if we're not asked, we're done */
5230 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5232 /* if focus is handled by us, report it */
5233 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5235 lpLVItem->state &= ~LVIS_FOCUSED;
5236 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5237 lpLVItem->state |= LVIS_FOCUSED;
5240 /* and do the same for selection, if we handle it */
5241 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5243 lpLVItem->state &= ~LVIS_SELECTED;
5244 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5245 lpLVItem->state |= LVIS_SELECTED;
5251 /* find the item and subitem structures before we proceed */
5252 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5253 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5258 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5259 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5262 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5267 pItemHdr = &lpItem->hdr;
5269 /* Do we need to query the state from the app? */
5270 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5272 dispInfo.item.mask |= LVIF_STATE;
5273 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5276 /* Do we need to enquire about the image? */
5277 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5278 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5280 dispInfo.item.mask |= LVIF_IMAGE;
5281 dispInfo.item.iImage = I_IMAGECALLBACK;
5284 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5285 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5287 dispInfo.item.mask |= LVIF_TEXT;
5288 dispInfo.item.pszText = lpLVItem->pszText;
5289 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5290 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5291 *dispInfo.item.pszText = '\0';
5294 /* If we don't have all the requested info, query the application */
5295 if (dispInfo.item.mask != 0)
5297 dispInfo.item.iItem = lpLVItem->iItem;
5298 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5299 dispInfo.item.lParam = lpItem->lParam;
5300 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5301 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5304 /* we should not store values for subitems */
5305 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5307 /* Now, handle the iImage field */
5308 if (dispInfo.item.mask & LVIF_IMAGE)
5310 lpLVItem->iImage = dispInfo.item.iImage;
5311 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5312 pItemHdr->iImage = dispInfo.item.iImage;
5314 else if (lpLVItem->mask & LVIF_IMAGE)
5316 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5317 lpLVItem->iImage = pItemHdr->iImage;
5319 lpLVItem->iImage = 0;
5322 /* The pszText field */
5323 if (dispInfo.item.mask & LVIF_TEXT)
5325 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5326 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5328 lpLVItem->pszText = dispInfo.item.pszText;
5330 else if (lpLVItem->mask & LVIF_TEXT)
5332 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5333 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5336 /* if this is a subitem, we're done */
5337 if (isubitem) return TRUE;
5339 /* Next is the lParam field */
5340 if (dispInfo.item.mask & LVIF_PARAM)
5342 lpLVItem->lParam = dispInfo.item.lParam;
5343 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5344 lpItem->lParam = dispInfo.item.lParam;
5346 else if (lpLVItem->mask & LVIF_PARAM)
5347 lpLVItem->lParam = lpItem->lParam;
5349 /* ... the state field (this one is different due to uCallbackmask) */
5350 if (lpLVItem->mask & LVIF_STATE)
5352 lpLVItem->state = lpItem->state;
5353 if (dispInfo.item.mask & LVIF_STATE)
5355 lpLVItem->state &= ~dispInfo.item.stateMask;
5356 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5358 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5360 lpLVItem->state &= ~LVIS_FOCUSED;
5361 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5362 lpLVItem->state |= LVIS_FOCUSED;
5364 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5366 lpLVItem->state &= ~LVIS_SELECTED;
5367 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5368 lpLVItem->state |= LVIS_SELECTED;
5372 /* and last, but not least, the indent field */
5373 if (lpLVItem->mask & LVIF_INDENT)
5374 lpLVItem->iIndent = lpItem->iIndent;
5381 * Retrieves item attributes.
5384 * [I] hwnd : window handle
5385 * [IO] lpLVItem : item info
5386 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5387 * if FALSE, the lpLVItem is a LPLVITEMA.
5390 * This is the external 'GetItem' interface -- it properly copies
5391 * the text in the provided buffer.
5397 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5402 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5405 pszText = lpLVItem->pszText;
5406 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5407 if (bResult && lpLVItem->pszText != pszText)
5408 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5409 lpLVItem->pszText = pszText;
5417 * Retrieves the position (upper-left) of the listview control item.
5418 * Note that for LVS_ICON style, the upper-left is that of the icon
5419 * and not the bounding box.
5422 * [I] infoPtr : valid pointer to the listview structure
5423 * [I] nItem : item index
5424 * [O] lpptPosition : coordinate information
5430 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5432 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5435 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5437 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5439 LISTVIEW_GetOrigin(infoPtr, &Origin);
5440 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5442 if (uView == LVS_ICON)
5444 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5445 lpptPosition->y += ICON_TOP_PADDING;
5447 lpptPosition->x += Origin.x;
5448 lpptPosition->y += Origin.y;
5450 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5457 * Retrieves the bounding rectangle for a listview control item.
5460 * [I] infoPtr : valid pointer to the listview structure
5461 * [I] nItem : item index
5462 * [IO] lprc : bounding rectangle coordinates
5463 * lprc->left specifies the portion of the item for which the bounding
5464 * rectangle will be retrieved.
5466 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5467 * including the icon and label.
5470 * * Experiment shows that native control returns:
5471 * * width = min (48, length of text line)
5472 * * .left = position.x - (width - iconsize.cx)/2
5473 * * .right = .left + width
5474 * * height = #lines of text * ntmHeight + icon height + 8
5475 * * .top = position.y - 2
5476 * * .bottom = .top + height
5477 * * separation between items .y = itemSpacing.cy - height
5478 * * .x = itemSpacing.cx - width
5479 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5482 * * Experiment shows that native control returns:
5483 * * width = iconSize.cx + 16
5484 * * .left = position.x - (width - iconsize.cx)/2
5485 * * .right = .left + width
5486 * * height = iconSize.cy + 4
5487 * * .top = position.y - 2
5488 * * .bottom = .top + height
5489 * * separation between items .y = itemSpacing.cy - height
5490 * * .x = itemSpacing.cx - width
5491 * LVIR_LABEL Returns the bounding rectangle of the item text.
5494 * * Experiment shows that native control returns:
5495 * * width = text length
5496 * * .left = position.x - width/2
5497 * * .right = .left + width
5498 * * height = ntmH * linecount + 2
5499 * * .top = position.y + iconSize.cy + 6
5500 * * .bottom = .top + height
5501 * * separation between items .y = itemSpacing.cy - height
5502 * * .x = itemSpacing.cx - width
5503 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5504 * rectangles, but excludes columns in report view.
5511 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5512 * upon whether the window has the focus currently and on whether the item
5513 * is the one with the focus. Ensure that the control's record of which
5514 * item has the focus agrees with the items' records.
5516 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5518 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5519 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5520 BOOL doLabel = TRUE, oversizedBox = FALSE;
5521 POINT Position, Origin;
5525 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5527 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5529 LISTVIEW_GetOrigin(infoPtr, &Origin);
5530 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5532 /* Be smart and try to figure out the minimum we have to do */
5533 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5534 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5535 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5536 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5537 oversizedBox = TRUE;
5539 /* get what we need from the item before hand, so we make
5540 * only one request. This can speed up things, if data
5541 * is stored on the app side */
5543 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5544 if (doLabel) lvItem.mask |= LVIF_TEXT;
5545 lvItem.iItem = nItem;
5546 lvItem.iSubItem = 0;
5547 lvItem.pszText = szDispText;
5548 lvItem.cchTextMax = DISP_TEXT_SIZE;
5549 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5550 /* we got the state already up, simulate it here, to avoid a reget */
5551 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5553 lvItem.mask |= LVIF_STATE;
5554 lvItem.stateMask = LVIS_FOCUSED;
5555 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5558 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5559 lprc->left = LVIR_BOUNDS;
5563 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5567 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5571 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5574 case LVIR_SELECTBOUNDS:
5575 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5576 UnionRect(lprc, lprc, &label_rect);
5580 WARN("Unknown value: %ld\n", lprc->left);
5584 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5586 TRACE(" rect=%s\n", debugrect(lprc));
5593 * Retrieves the spacing between listview control items.
5596 * [I] infoPtr : valid pointer to the listview structure
5597 * [IO] lprc : rectangle to receive the output
5598 * on input, lprc->top = nSubItem
5599 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5601 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5602 * not only those of the first column.
5603 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5609 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5614 if (!lprc) return FALSE;
5616 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5617 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5619 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5621 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5623 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5626 lvItem.iItem = nItem;
5627 lvItem.iSubItem = lprc->top;
5629 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5633 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5638 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5642 ERR("Unknown bounds=%ld\n", lprc->left);
5646 OffsetRect(lprc, Position.x, Position.y);
5653 * Retrieves the width of a label.
5656 * [I] infoPtr : valid pointer to the listview structure
5659 * SUCCESS : string width (in pixels)
5662 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5664 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5667 TRACE("(nItem=%d)\n", nItem);
5669 lvItem.mask = LVIF_TEXT;
5670 lvItem.iItem = nItem;
5671 lvItem.iSubItem = 0;
5672 lvItem.pszText = szDispText;
5673 lvItem.cchTextMax = DISP_TEXT_SIZE;
5674 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5676 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5681 * Retrieves the spacing between listview control items.
5684 * [I] infoPtr : valid pointer to the listview structure
5685 * [I] bSmall : flag for small or large icon
5688 * Horizontal + vertical spacing
5690 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5696 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5700 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5701 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5703 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5710 * Retrieves the state of a listview control item.
5713 * [I] infoPtr : valid pointer to the listview structure
5714 * [I] nItem : item index
5715 * [I] uMask : state mask
5718 * State specified by the mask.
5720 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5724 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5726 lvItem.iItem = nItem;
5727 lvItem.iSubItem = 0;
5728 lvItem.mask = LVIF_STATE;
5729 lvItem.stateMask = uMask;
5730 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5732 return lvItem.state & uMask;
5737 * Retrieves the text of a listview control item or subitem.
5740 * [I] hwnd : window handle
5741 * [I] nItem : item index
5742 * [IO] lpLVItem : item information
5743 * [I] isW : TRUE if lpLVItem is Unicode
5746 * SUCCESS : string length
5749 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5751 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5753 lpLVItem->mask = LVIF_TEXT;
5754 lpLVItem->iItem = nItem;
5755 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5757 return textlenT(lpLVItem->pszText, isW);
5762 * Searches for an item based on properties + relationships.
5765 * [I] infoPtr : valid pointer to the listview structure
5766 * [I] nItem : item index
5767 * [I] uFlags : relationship flag
5770 * SUCCESS : item index
5773 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5775 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5777 LVFINDINFOW lvFindInfo;
5778 INT nCountPerColumn;
5782 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5783 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5785 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5787 if (uFlags & LVNI_CUT)
5790 if (uFlags & LVNI_DROPHILITED)
5791 uMask |= LVIS_DROPHILITED;
5793 if (uFlags & LVNI_FOCUSED)
5794 uMask |= LVIS_FOCUSED;
5796 if (uFlags & LVNI_SELECTED)
5797 uMask |= LVIS_SELECTED;
5799 /* if we're asked for the focused item, that's only one,
5800 * so it's worth optimizing */
5801 if (uFlags & LVNI_FOCUSED)
5803 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5804 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5807 if (uFlags & LVNI_ABOVE)
5809 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5814 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5820 /* Special case for autoarrange - move 'til the top of a list */
5821 if (is_autoarrange(infoPtr))
5823 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5824 while (nItem - nCountPerRow >= 0)
5826 nItem -= nCountPerRow;
5827 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5832 lvFindInfo.flags = LVFI_NEARESTXY;
5833 lvFindInfo.vkDirection = VK_UP;
5834 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5835 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5837 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5842 else if (uFlags & LVNI_BELOW)
5844 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5846 while (nItem < infoPtr->nItemCount)
5849 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5855 /* Special case for autoarrange - move 'til the bottom of a list */
5856 if (is_autoarrange(infoPtr))
5858 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5859 while (nItem + nCountPerRow < infoPtr->nItemCount )
5861 nItem += nCountPerRow;
5862 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5867 lvFindInfo.flags = LVFI_NEARESTXY;
5868 lvFindInfo.vkDirection = VK_DOWN;
5869 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5870 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5872 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5877 else if (uFlags & LVNI_TOLEFT)
5879 if (uView == LVS_LIST)
5881 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5882 while (nItem - nCountPerColumn >= 0)
5884 nItem -= nCountPerColumn;
5885 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5889 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5891 /* Special case for autoarrange - move 'ti the beginning of a row */
5892 if (is_autoarrange(infoPtr))
5894 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5895 while (nItem % nCountPerRow > 0)
5898 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5903 lvFindInfo.flags = LVFI_NEARESTXY;
5904 lvFindInfo.vkDirection = VK_LEFT;
5905 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5906 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5908 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5913 else if (uFlags & LVNI_TORIGHT)
5915 if (uView == LVS_LIST)
5917 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5918 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5920 nItem += nCountPerColumn;
5921 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5925 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5927 /* Special case for autoarrange - move 'til the end of a row */
5928 if (is_autoarrange(infoPtr))
5930 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5931 while (nItem % nCountPerRow < nCountPerRow - 1 )
5934 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5939 lvFindInfo.flags = LVFI_NEARESTXY;
5940 lvFindInfo.vkDirection = VK_RIGHT;
5941 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5942 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5944 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5953 /* search by index */
5954 for (i = nItem; i < infoPtr->nItemCount; i++)
5956 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5964 /* LISTVIEW_GetNumberOfWorkAreas */
5968 * Retrieves the origin coordinates when in icon or small icon display mode.
5971 * [I] infoPtr : valid pointer to the listview structure
5972 * [O] lpptOrigin : coordinate information
5977 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5979 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5980 INT nHorzPos = 0, nVertPos = 0;
5981 SCROLLINFO scrollInfo;
5983 scrollInfo.cbSize = sizeof(SCROLLINFO);
5984 scrollInfo.fMask = SIF_POS;
5986 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5987 nHorzPos = scrollInfo.nPos;
5988 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5989 nVertPos = scrollInfo.nPos;
5991 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5993 lpptOrigin->x = infoPtr->rcList.left;
5994 lpptOrigin->y = infoPtr->rcList.top;
5995 if (uView == LVS_LIST)
5996 nHorzPos *= infoPtr->nItemWidth;
5997 else if (uView == LVS_REPORT)
5998 nVertPos *= infoPtr->nItemHeight;
6000 lpptOrigin->x -= nHorzPos;
6001 lpptOrigin->y -= nVertPos;
6003 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
6008 * Retrieves the width of a string.
6011 * [I] hwnd : window handle
6012 * [I] lpszText : text string to process
6013 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6016 * SUCCESS : string width (in pixels)
6019 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6024 if (is_textT(lpszText, isW))
6026 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6027 HDC hdc = GetDC(infoPtr->hwndSelf);
6028 HFONT hOldFont = SelectObject(hdc, hFont);
6031 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6033 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6034 SelectObject(hdc, hOldFont);
6035 ReleaseDC(infoPtr->hwndSelf, hdc);
6037 return stringSize.cx;
6042 * Determines which listview item is located at the specified position.
6045 * [I] infoPtr : valid pointer to the listview structure
6046 * [IO] lpht : hit test information
6047 * [I] subitem : fill out iSubItem.
6048 * [I] select : return the index only if the hit selects the item
6051 * (mm 20001022): We must not allow iSubItem to be touched, for
6052 * an app might pass only a structure with space up to iItem!
6053 * (MS Office 97 does that for instance in the file open dialog)
6056 * SUCCESS : item index
6059 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6061 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6062 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6063 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6064 POINT Origin, Position, opt;
6069 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
6073 if (subitem) lpht->iSubItem = 0;
6075 if (infoPtr->rcList.left > lpht->pt.x)
6076 lpht->flags |= LVHT_TOLEFT;
6077 else if (infoPtr->rcList.right < lpht->pt.x)
6078 lpht->flags |= LVHT_TORIGHT;
6080 if (infoPtr->rcList.top > lpht->pt.y)
6081 lpht->flags |= LVHT_ABOVE;
6082 else if (infoPtr->rcList.bottom < lpht->pt.y)
6083 lpht->flags |= LVHT_BELOW;
6085 TRACE("lpht->flags=0x%x\n", lpht->flags);
6086 if (lpht->flags) return -1;
6088 lpht->flags |= LVHT_NOWHERE;
6090 LISTVIEW_GetOrigin(infoPtr, &Origin);
6092 /* first deal with the large items */
6093 rcSearch.left = lpht->pt.x;
6094 rcSearch.top = lpht->pt.y;
6095 rcSearch.right = rcSearch.left + 1;
6096 rcSearch.bottom = rcSearch.top + 1;
6098 iterator_frameditems(&i, infoPtr, &rcSearch);
6099 iterator_next(&i); /* go to first item in the sequence */
6101 iterator_destroy(&i);
6103 TRACE("lpht->iItem=%d\n", iItem);
6104 if (iItem == -1) return -1;
6106 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6107 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6108 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6109 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6110 lvItem.iItem = iItem;
6111 lvItem.iSubItem = 0;
6112 lvItem.pszText = szDispText;
6113 lvItem.cchTextMax = DISP_TEXT_SIZE;
6114 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6115 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6117 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
6118 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6119 opt.x = lpht->pt.x - Position.x - Origin.x;
6120 opt.y = lpht->pt.y - Position.y - Origin.y;
6122 if (uView == LVS_REPORT)
6125 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6126 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
6127 if (!PtInRect(&rcBounds, opt)) return -1;
6129 if (PtInRect(&rcIcon, opt))
6130 lpht->flags |= LVHT_ONITEMICON;
6131 else if (PtInRect(&rcLabel, opt))
6132 lpht->flags |= LVHT_ONITEMLABEL;
6133 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6134 lpht->flags |= LVHT_ONITEMSTATEICON;
6135 if (lpht->flags & LVHT_ONITEM)
6136 lpht->flags &= ~LVHT_NOWHERE;
6138 TRACE("lpht->flags=0x%x\n", lpht->flags);
6139 if (uView == LVS_REPORT && subitem)
6143 rcBounds.right = rcBounds.left;
6144 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6146 rcBounds.left = rcBounds.right;
6147 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6148 if (PtInRect(&rcBounds, opt))
6156 if (select && !(uView == LVS_REPORT &&
6157 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6158 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6160 if (uView == LVS_REPORT)
6162 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6163 UnionRect(&rcBounds, &rcBounds, &rcState);
6165 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6167 return lpht->iItem = iItem;
6171 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6172 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6173 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6174 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6175 their own sort proc. when sending LVM_SORTITEMS.
6178 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6180 LVS_SORTXXX must be specified,
6181 LVS_OWNERDRAW is not set,
6182 <item>.pszText is not LPSTR_TEXTCALLBACK.
6184 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6185 are sorted based on item text..."
6187 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6189 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6190 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6191 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6193 /* if we're sorting descending, negate the return value */
6194 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6199 * Inserts a new item in the listview control.
6202 * [I] infoPtr : valid pointer to the listview structure
6203 * [I] lpLVItem : item information
6204 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6207 * SUCCESS : new item index
6210 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6212 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6217 BOOL is_sorted, has_changed;
6219 HWND hwndSelf = infoPtr->hwndSelf;
6221 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6223 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6225 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6226 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6228 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6230 if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6232 /* insert item in listview control data structure */
6233 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6234 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6236 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6237 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6239 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6240 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6241 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6242 if (nItem == -1) goto fail;
6243 infoPtr->nItemCount++;
6245 /* shift indices first so they don't get tangled */
6246 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6248 /* set the item attributes */
6249 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6251 /* full size structure expected - _WIN32IE >= 0x560 */
6254 else if (lpLVItem->mask & LVIF_INDENT)
6256 /* indent member expected - _WIN32IE >= 0x300 */
6257 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6261 /* minimal structure expected */
6262 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6265 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6266 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6268 /* if we're sorted, sort the list, and update the index */
6271 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6272 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6273 assert(nItem != -1);
6276 /* make room for the position, if we are in the right mode */
6277 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6279 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6281 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6283 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6288 /* send LVN_INSERTITEM notification */
6289 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6291 nmlv.lParam = lpItem->lParam;
6292 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6293 if (!IsWindow(hwndSelf))
6296 /* align items (set position of each item) */
6297 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6301 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6302 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6304 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6306 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6309 /* now is the invalidation fun */
6310 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6314 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6315 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6316 infoPtr->nItemCount--;
6318 DPA_DeletePtr(hdpaSubItems, 0);
6319 DPA_Destroy (hdpaSubItems);
6326 * Redraws a range of items.
6329 * [I] infoPtr : valid pointer to the listview structure
6330 * [I] nFirst : first item
6331 * [I] nLast : last item
6337 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6341 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6342 max(nFirst, nLast) >= infoPtr->nItemCount)
6345 for (i = nFirst; i <= nLast; i++)
6346 LISTVIEW_InvalidateItem(infoPtr, i);
6353 * Scroll the content of a listview.
6356 * [I] infoPtr : valid pointer to the listview structure
6357 * [I] dx : horizontal scroll amount in pixels
6358 * [I] dy : vertical scroll amount in pixels
6365 * If the control is in report mode (LVS_REPORT) the control can
6366 * be scrolled only in line increments. "dy" will be rounded to the
6367 * nearest number of pixels that are a whole line. Ex: if line height
6368 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6369 * is passed the the scroll will be 0. (per MSDN 7/2002)
6371 * For: (per experimentaion with native control and CSpy ListView)
6372 * LVS_ICON dy=1 = 1 pixel (vertical only)
6374 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6376 * LVS_LIST dx=1 = 1 column (horizontal only)
6377 * but will only scroll 1 column per message
6378 * no matter what the value.
6379 * dy must be 0 or FALSE returned.
6380 * LVS_REPORT dx=1 = 1 pixel
6384 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6386 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6388 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6389 dy /= infoPtr->nItemHeight;
6392 if (dy != 0) return FALSE;
6399 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6400 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6407 * Sets the background color.
6410 * [I] infoPtr : valid pointer to the listview structure
6411 * [I] clrBk : background color
6417 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6419 TRACE("(clrBk=%lx)\n", clrBk);
6421 if(infoPtr->clrBk != clrBk) {
6422 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6423 infoPtr->clrBk = clrBk;
6424 if (clrBk == CLR_NONE)
6425 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6427 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6428 LISTVIEW_InvalidateList(infoPtr);
6434 /* LISTVIEW_SetBkImage */
6436 /*** Helper for {Insert,Set}ColumnT *only* */
6437 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6439 if (lpColumn->mask & LVCF_FMT)
6441 /* format member is valid */
6442 lphdi->mask |= HDI_FORMAT;
6444 /* set text alignment (leftmost column must be left-aligned) */
6445 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6446 lphdi->fmt |= HDF_LEFT;
6447 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6448 lphdi->fmt |= HDF_RIGHT;
6449 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6450 lphdi->fmt |= HDF_CENTER;
6452 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6453 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6455 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6457 lphdi->fmt |= HDF_IMAGE;
6458 lphdi->iImage = I_IMAGECALLBACK;
6462 if (lpColumn->mask & LVCF_WIDTH)
6464 lphdi->mask |= HDI_WIDTH;
6465 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6467 /* make it fill the remainder of the controls width */
6471 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6473 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6474 lphdi->cxy += rcHeader.right - rcHeader.left;
6477 /* retrieve the layout of the header */
6478 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6479 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6481 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6484 lphdi->cxy = lpColumn->cx;
6487 if (lpColumn->mask & LVCF_TEXT)
6489 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6490 lphdi->fmt |= HDF_STRING;
6491 lphdi->pszText = lpColumn->pszText;
6492 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6495 if (lpColumn->mask & LVCF_IMAGE)
6497 lphdi->mask |= HDI_IMAGE;
6498 lphdi->iImage = lpColumn->iImage;
6501 if (lpColumn->mask & LVCF_ORDER)
6503 lphdi->mask |= HDI_ORDER;
6504 lphdi->iOrder = lpColumn->iOrder;
6511 * Inserts a new column.
6514 * [I] infoPtr : valid pointer to the listview structure
6515 * [I] nColumn : column index
6516 * [I] lpColumn : column information
6517 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6520 * SUCCESS : new column index
6523 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6524 const LVCOLUMNW *lpColumn, BOOL isW)
6526 COLUMN_INFO *lpColumnInfo;
6530 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6532 if (!lpColumn || nColumn < 0) return -1;
6533 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6535 ZeroMemory(&hdi, sizeof(HDITEMW));
6536 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6538 /* insert item in header control */
6539 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6540 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6541 (WPARAM)nColumn, (LPARAM)&hdi);
6542 if (nNewColumn == -1) return -1;
6543 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6545 /* create our own column info */
6546 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6547 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6549 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6550 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6552 /* now we have to actually adjust the data */
6553 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6555 SUBITEM_INFO *lpSubItem;
6559 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6561 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6562 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6564 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6565 if (lpSubItem->iSubItem >= nNewColumn)
6566 lpSubItem->iSubItem++;
6571 /* make space for the new column */
6572 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6577 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6580 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6588 * Sets the attributes of a header item.
6591 * [I] infoPtr : valid pointer to the listview structure
6592 * [I] nColumn : column index
6593 * [I] lpColumn : column attributes
6594 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6600 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6601 const LVCOLUMNW *lpColumn, BOOL isW)
6603 HDITEMW hdi, hdiget;
6606 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6608 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6610 ZeroMemory(&hdi, sizeof(HDITEMW));
6611 if (lpColumn->mask & LVCF_FMT)
6613 hdi.mask |= HDI_FORMAT;
6614 hdiget.mask = HDI_FORMAT;
6615 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6616 hdi.fmt = hdiget.fmt & HDF_STRING;
6618 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6620 /* set header item attributes */
6621 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6622 if (!bResult) return FALSE;
6624 if (lpColumn->mask & LVCF_FMT)
6626 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6627 int oldFmt = lpColumnInfo->fmt;
6629 lpColumnInfo->fmt = lpColumn->fmt;
6630 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6632 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6633 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6642 * Sets the column order array
6645 * [I] infoPtr : valid pointer to the listview structure
6646 * [I] iCount : number of elements in column order array
6647 * [I] lpiArray : pointer to column order array
6653 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6655 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6666 * Sets the width of a column
6669 * [I] infoPtr : valid pointer to the listview structure
6670 * [I] nColumn : column index
6671 * [I] cx : column width
6677 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6679 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6680 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6684 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6686 /* set column width only if in report or list mode */
6687 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6689 /* take care of invalid cx values */
6690 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6691 else if (uView == LVS_LIST && cx < 1) return FALSE;
6693 /* resize all columns if in LVS_LIST mode */
6694 if(uView == LVS_LIST)
6696 infoPtr->nItemWidth = cx;
6697 LISTVIEW_InvalidateList(infoPtr);
6701 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6703 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6708 lvItem.mask = LVIF_TEXT;
6710 lvItem.iSubItem = nColumn;
6711 lvItem.pszText = szDispText;
6712 lvItem.cchTextMax = DISP_TEXT_SIZE;
6713 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6715 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6716 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6717 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6719 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6720 max_cx += infoPtr->iconSize.cx;
6721 max_cx += TRAILING_LABEL_PADDING;
6724 /* autosize based on listview items width */
6725 if(cx == LVSCW_AUTOSIZE)
6727 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6729 /* if iCol is the last column make it fill the remainder of the controls width */
6730 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6735 LISTVIEW_GetOrigin(infoPtr, &Origin);
6736 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6738 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6742 /* Despite what the MS docs say, if this is not the last
6743 column, then MS resizes the column to the width of the
6744 largest text string in the column, including headers
6745 and items. This is different from LVSCW_AUTOSIZE in that
6746 LVSCW_AUTOSIZE ignores the header string length. */
6749 /* retrieve header text */
6750 hdi.mask = HDI_TEXT;
6751 hdi.cchTextMax = DISP_TEXT_SIZE;
6752 hdi.pszText = szDispText;
6753 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6755 HDC hdc = GetDC(infoPtr->hwndSelf);
6756 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6759 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6760 cx = size.cx + TRAILING_HEADER_PADDING;
6761 /* FIXME: Take into account the header image, if one is present */
6762 SelectObject(hdc, old_font);
6763 ReleaseDC(infoPtr->hwndSelf, hdc);
6765 cx = max (cx, max_cx);
6769 if (cx < 0) return FALSE;
6771 /* call header to update the column change */
6772 hdi.mask = HDI_WIDTH;
6774 TRACE("hdi.cxy=%d\n", hdi.cxy);
6775 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6779 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6782 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6785 HBITMAP hbm_im, hbm_mask, hbm_orig;
6787 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6788 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6791 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6792 ILC_COLOR | ILC_MASK, 2, 2);
6793 hdc_wnd = GetDC(infoPtr->hwndSelf);
6794 hdc = CreateCompatibleDC(hdc_wnd);
6795 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6796 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6797 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6799 rc.left = rc.top = 0;
6800 rc.right = GetSystemMetrics(SM_CXSMICON);
6801 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6803 hbm_orig = SelectObject(hdc, hbm_mask);
6804 FillRect(hdc, &rc, hbr_white);
6805 InflateRect(&rc, -3, -3);
6806 FillRect(hdc, &rc, hbr_black);
6808 SelectObject(hdc, hbm_im);
6809 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6810 SelectObject(hdc, hbm_orig);
6811 ImageList_Add(himl, hbm_im, hbm_mask);
6813 SelectObject(hdc, hbm_im);
6814 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6815 SelectObject(hdc, hbm_orig);
6816 ImageList_Add(himl, hbm_im, hbm_mask);
6818 DeleteObject(hbm_mask);
6819 DeleteObject(hbm_im);
6827 * Sets the extended listview style.
6830 * [I] infoPtr : valid pointer to the listview structure
6832 * [I] dwStyle : style
6835 * SUCCESS : previous style
6838 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6840 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6844 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6846 infoPtr->dwLvExStyle = dwStyle;
6848 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6850 HIMAGELIST himl = 0;
6851 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6852 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6853 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6861 * Sets the new hot cursor used during hot tracking and hover selection.
6864 * [I] infoPtr : valid pointer to the listview structure
6865 * [I] hCursor : the new hot cursor handle
6868 * Returns the previous hot cursor
6870 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6872 HCURSOR oldCursor = infoPtr->hHotCursor;
6874 infoPtr->hHotCursor = hCursor;
6882 * Sets the hot item index.
6885 * [I] infoPtr : valid pointer to the listview structure
6886 * [I] iIndex : index
6889 * SUCCESS : previous hot item index
6890 * FAILURE : -1 (no hot item)
6892 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6894 INT iOldIndex = infoPtr->nHotItem;
6896 infoPtr->nHotItem = iIndex;
6904 * Sets the amount of time the cursor must hover over an item before it is selected.
6907 * [I] infoPtr : valid pointer to the listview structure
6908 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6911 * Returns the previous hover time
6913 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6915 DWORD oldHoverTime = infoPtr->dwHoverTime;
6917 infoPtr->dwHoverTime = dwHoverTime;
6919 return oldHoverTime;
6924 * Sets spacing for icons of LVS_ICON style.
6927 * [I] infoPtr : valid pointer to the listview structure
6928 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6929 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6932 * MAKELONG(oldcx, oldcy)
6934 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6936 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6937 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6939 TRACE("requested=(%d,%d)\n", cx, cy);
6941 /* this is supported only for LVS_ICON style */
6942 if (uView != LVS_ICON) return oldspacing;
6944 /* set to defaults, if instructed to */
6945 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6946 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6948 /* if 0 then compute width
6949 * FIXME: Should scan each item and determine max width of
6950 * icon or label, then make that the width */
6952 cx = infoPtr->iconSpacing.cx;
6954 /* if 0 then compute height */
6956 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6957 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6960 infoPtr->iconSpacing.cx = cx;
6961 infoPtr->iconSpacing.cy = cy;
6963 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6964 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6965 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6966 infoPtr->ntmHeight);
6968 /* these depend on the iconSpacing */
6969 LISTVIEW_UpdateItemSize(infoPtr);
6974 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6978 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6985 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6986 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6995 * [I] infoPtr : valid pointer to the listview structure
6996 * [I] nType : image list type
6997 * [I] himl : image list handle
7000 * SUCCESS : old image list
7003 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7005 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7006 INT oldHeight = infoPtr->nItemHeight;
7007 HIMAGELIST himlOld = 0;
7009 TRACE("(nType=%d, himl=%p\n", nType, himl);
7014 himlOld = infoPtr->himlNormal;
7015 infoPtr->himlNormal = himl;
7016 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7017 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7021 himlOld = infoPtr->himlSmall;
7022 infoPtr->himlSmall = himl;
7023 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7027 himlOld = infoPtr->himlState;
7028 infoPtr->himlState = himl;
7029 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7030 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7034 ERR("Unknown icon type=%d\n", nType);
7038 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7039 if (infoPtr->nItemHeight != oldHeight)
7040 LISTVIEW_UpdateScroll(infoPtr);
7047 * Preallocates memory (does *not* set the actual count of items !)
7050 * [I] infoPtr : valid pointer to the listview structure
7051 * [I] nItems : item count (projected number of items to allocate)
7052 * [I] dwFlags : update flags
7058 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7060 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
7062 if (infoPtr->dwStyle & LVS_OWNERDATA)
7064 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7065 INT nOldCount = infoPtr->nItemCount;
7067 if (nItems < nOldCount)
7069 RANGE range = { nItems, nOldCount };
7070 ranges_del(infoPtr->selectionRanges, range);
7071 if (infoPtr->nFocusedItem >= nItems)
7073 infoPtr->nFocusedItem = -1;
7074 SetRectEmpty(&infoPtr->rcFocus);
7078 infoPtr->nItemCount = nItems;
7079 LISTVIEW_UpdateScroll(infoPtr);
7081 /* the flags are valid only in ownerdata report and list modes */
7082 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7084 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7085 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7087 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7088 LISTVIEW_InvalidateList(infoPtr);
7095 LISTVIEW_GetOrigin(infoPtr, &Origin);
7096 nFrom = min(nOldCount, nItems);
7097 nTo = max(nOldCount, nItems);
7099 if (uView == LVS_REPORT)
7102 rcErase.top = nFrom * infoPtr->nItemHeight;
7103 rcErase.right = infoPtr->nItemWidth;
7104 rcErase.bottom = nTo * infoPtr->nItemHeight;
7105 OffsetRect(&rcErase, Origin.x, Origin.y);
7106 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7107 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7111 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7113 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7114 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7115 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7116 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7117 OffsetRect(&rcErase, Origin.x, Origin.y);
7118 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7119 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7121 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7123 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7124 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7125 OffsetRect(&rcErase, Origin.x, Origin.y);
7126 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7127 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7133 /* According to MSDN for non-LVS_OWNERDATA this is just
7134 * a performance issue. The control allocates its internal
7135 * data structures for the number of items specified. It
7136 * cuts down on the number of memory allocations. Therefore
7137 * we will just issue a WARN here
7139 WARN("for non-ownerdata performance option not implemented.\n");
7147 * Sets the position of an item.
7150 * [I] infoPtr : valid pointer to the listview structure
7151 * [I] nItem : item index
7152 * [I] pt : coordinate
7158 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7160 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7163 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
7165 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7166 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7168 LISTVIEW_GetOrigin(infoPtr, &Origin);
7170 /* This point value seems to be an undocumented feature.
7171 * The best guess is that it means either at the origin,
7172 * or at true beginning of the list. I will assume the origin. */
7173 if ((pt.x == -1) && (pt.y == -1))
7176 if (uView == LVS_ICON)
7178 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7179 pt.y -= ICON_TOP_PADDING;
7184 infoPtr->bAutoarrange = FALSE;
7186 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7191 * Sets the state of one or many items.
7194 * [I] infoPtr : valid pointer to the listview structure
7195 * [I] nItem : item index
7196 * [I] lpLVItem : item or subitem info
7202 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7204 BOOL bResult = TRUE;
7207 lvItem.iItem = nItem;
7208 lvItem.iSubItem = 0;
7209 lvItem.mask = LVIF_STATE;
7210 lvItem.state = lpLVItem->state;
7211 lvItem.stateMask = lpLVItem->stateMask;
7212 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7216 /* apply to all items */
7217 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7218 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7221 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7224 * Update selection mark
7226 * Investigation on windows 2k showed that selection mark was updated
7227 * whenever a new selection was made, but if the selected item was
7228 * unselected it was not updated.
7230 * we are probably still not 100% accurate, but this at least sets the
7231 * proper selection mark when it is needed
7234 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7235 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7238 infoPtr->nSelectionMark = -1;
7239 for (i = 0; i < infoPtr->nItemCount; i++)
7241 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7243 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7245 infoPtr->nSelectionMark = i;
7249 else if (ranges_contain(infoPtr->selectionRanges, i))
7251 infoPtr->nSelectionMark = i;
7262 * Sets the text of an item or subitem.
7265 * [I] hwnd : window handle
7266 * [I] nItem : item index
7267 * [I] lpLVItem : item or subitem info
7268 * [I] isW : TRUE if input is Unicode
7274 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7278 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7280 lvItem.iItem = nItem;
7281 lvItem.iSubItem = lpLVItem->iSubItem;
7282 lvItem.mask = LVIF_TEXT;
7283 lvItem.pszText = lpLVItem->pszText;
7284 lvItem.cchTextMax = lpLVItem->cchTextMax;
7286 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7288 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7293 * Set item index that marks the start of a multiple selection.
7296 * [I] infoPtr : valid pointer to the listview structure
7297 * [I] nIndex : index
7300 * Index number or -1 if there is no selection mark.
7302 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7304 INT nOldIndex = infoPtr->nSelectionMark;
7306 TRACE("(nIndex=%d)\n", nIndex);
7308 infoPtr->nSelectionMark = nIndex;
7315 * Sets the text background color.
7318 * [I] infoPtr : valid pointer to the listview structure
7319 * [I] clrTextBk : text background color
7325 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7327 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7329 if (infoPtr->clrTextBk != clrTextBk)
7331 infoPtr->clrTextBk = clrTextBk;
7332 LISTVIEW_InvalidateList(infoPtr);
7340 * Sets the text foreground color.
7343 * [I] infoPtr : valid pointer to the listview structure
7344 * [I] clrText : text color
7350 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7352 TRACE("(clrText=%lx)\n", clrText);
7354 if (infoPtr->clrText != clrText)
7356 infoPtr->clrText = clrText;
7357 LISTVIEW_InvalidateList(infoPtr);
7365 * Determines which listview item is located at the specified position.
7368 * [I] infoPtr : valid pointer to the listview structure
7369 * [I] hwndNewToolTip : handle to new ToolTip
7374 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7376 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7377 infoPtr->hwndToolTip = hwndNewToolTip;
7378 return hwndOldToolTip;
7381 /* LISTVIEW_SetUnicodeFormat */
7382 /* LISTVIEW_SetWorkAreas */
7386 * Callback internally used by LISTVIEW_SortItems()
7389 * [I] first : pointer to first ITEM_INFO to compare
7390 * [I] second : pointer to second ITEM_INFO to compare
7391 * [I] lParam : HWND of control
7394 * if first comes before second : negative
7395 * if first comes after second : positive
7396 * if first and second are equivalent : zero
7398 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7400 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7401 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7402 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7404 /* Forward the call to the client defined callback */
7405 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7410 * Sorts the listview items.
7413 * [I] infoPtr : valid pointer to the listview structure
7414 * [I] pfnCompare : application-defined value
7415 * [I] lParamSort : pointer to comparision callback
7421 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7423 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7426 LPVOID selectionMarkItem;
7430 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7432 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7434 if (!pfnCompare) return FALSE;
7435 if (!infoPtr->hdpaItems) return FALSE;
7437 /* if there are 0 or 1 items, there is no need to sort */
7438 if (infoPtr->nItemCount < 2) return TRUE;
7440 if (infoPtr->nFocusedItem >= 0)
7442 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7443 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7444 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7446 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7447 /* clear the lpItem->state for non-selected ones */
7448 /* remove the selection ranges */
7450 infoPtr->pfnCompare = pfnCompare;
7451 infoPtr->lParamSort = lParamSort;
7452 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7454 /* Adjust selections and indices so that they are the way they should
7455 * be after the sort (otherwise, the list items move around, but
7456 * whatever is at the item's previous original position will be
7459 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7460 for (i=0; i < infoPtr->nItemCount; i++)
7462 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7463 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7465 if (lpItem->state & LVIS_SELECTED)
7467 item.state = LVIS_SELECTED;
7468 item.stateMask = LVIS_SELECTED;
7469 LISTVIEW_SetItemState(infoPtr, i, &item);
7471 if (lpItem->state & LVIS_FOCUSED)
7473 infoPtr->nFocusedItem = i;
7474 lpItem->state &= ~LVIS_FOCUSED;
7477 if (selectionMarkItem != NULL)
7478 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7479 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7481 /* refresh the display */
7482 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7483 LISTVIEW_InvalidateList(infoPtr);
7490 * Update theme handle after a theme change.
7493 * [I] infoPtr : valid pointer to the listview structure
7497 * FAILURE : something else
7499 static LRESULT LISTVIEW_ThemeChanged(LISTVIEW_INFO *infoPtr)
7501 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7502 CloseThemeData(theme);
7503 OpenThemeData(infoPtr->hwndSelf, themeClass);
7509 * Updates an items or rearranges the listview control.
7512 * [I] infoPtr : valid pointer to the listview structure
7513 * [I] nItem : item index
7519 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7521 TRACE("(nItem=%d)\n", nItem);
7523 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7525 /* rearrange with default alignment style */
7526 if (is_autoarrange(infoPtr))
7527 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7529 LISTVIEW_InvalidateItem(infoPtr, nItem);
7537 * Creates the listview control.
7540 * [I] hwnd : window handle
7541 * [I] lpcs : the create parameters
7547 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7549 LISTVIEW_INFO *infoPtr;
7550 UINT uView = lpcs->style & LVS_TYPEMASK;
7553 TRACE("(lpcs=%p)\n", lpcs);
7555 /* initialize info pointer */
7556 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7557 if (!infoPtr) return -1;
7559 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7561 infoPtr->hwndSelf = hwnd;
7562 infoPtr->dwStyle = lpcs->style;
7563 /* determine the type of structures to use */
7564 infoPtr->hwndNotify = lpcs->hwndParent;
7565 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7566 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7568 /* initialize color information */
7569 infoPtr->clrBk = CLR_NONE;
7570 infoPtr->clrText = comctl32_color.clrWindowText;
7571 infoPtr->clrTextBk = CLR_DEFAULT;
7572 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7574 /* set default values */
7575 infoPtr->nFocusedItem = -1;
7576 infoPtr->nSelectionMark = -1;
7577 infoPtr->nHotItem = -1;
7578 infoPtr->bRedraw = TRUE;
7579 infoPtr->bNoItemMetrics = TRUE;
7580 infoPtr->bDoChangeNotify = TRUE;
7581 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7582 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7583 infoPtr->nEditLabelItem = -1;
7584 infoPtr->dwHoverTime = -1; /* default system hover time */
7585 infoPtr->nMeasureItemHeight = 0;
7587 /* get default font (icon title) */
7588 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7589 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7590 infoPtr->hFont = infoPtr->hDefaultFont;
7591 LISTVIEW_SaveTextMetrics(infoPtr);
7594 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7595 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7596 0, 0, 0, 0, hwnd, NULL,
7597 lpcs->hInstance, NULL);
7598 if (!infoPtr->hwndHeader) goto fail;
7600 /* set header unicode format */
7601 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7603 /* set header font */
7604 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7606 /* allocate memory for the data structure */
7607 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7608 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7609 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7610 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7611 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7613 /* initialize the icon sizes */
7614 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7615 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7617 /* init item size to avoid division by 0 */
7618 LISTVIEW_UpdateItemSize (infoPtr);
7620 if (uView == LVS_REPORT)
7622 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7624 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7628 /* set HDS_HIDDEN flag to hide the header bar */
7629 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7630 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7634 OpenThemeData(hwnd, themeClass);
7639 DestroyWindow(infoPtr->hwndHeader);
7640 ranges_destroy(infoPtr->selectionRanges);
7641 DPA_Destroy(infoPtr->hdpaItems);
7642 DPA_Destroy(infoPtr->hdpaPosX);
7643 DPA_Destroy(infoPtr->hdpaPosY);
7644 DPA_Destroy(infoPtr->hdpaColumns);
7651 * Destroys the listview control.
7654 * [I] infoPtr : valid pointer to the listview structure
7660 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
7662 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7663 CloseThemeData(theme);
7669 * Enables the listview control.
7672 * [I] infoPtr : valid pointer to the listview structure
7673 * [I] bEnable : specifies whether to enable or disable the window
7679 static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable)
7681 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7682 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7688 * Erases the background of the listview control.
7691 * [I] infoPtr : valid pointer to the listview structure
7692 * [I] hdc : device context handle
7698 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7702 TRACE("(hdc=%p)\n", hdc);
7704 if (!GetClipBox(hdc, &rc)) return FALSE;
7706 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7712 * Helper function for LISTVIEW_[HV]Scroll *only*.
7713 * Performs vertical/horizontal scrolling by a give amount.
7716 * [I] infoPtr : valid pointer to the listview structure
7717 * [I] dx : amount of horizontal scroll
7718 * [I] dy : amount of vertical scroll
7720 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7722 /* now we can scroll the list */
7723 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7724 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7725 /* if we have focus, adjust rect */
7726 OffsetRect(&infoPtr->rcFocus, dx, dy);
7727 UpdateWindow(infoPtr->hwndSelf);
7732 * Performs vertical scrolling.
7735 * [I] infoPtr : valid pointer to the listview structure
7736 * [I] nScrollCode : scroll code
7737 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7738 * [I] hScrollWnd : scrollbar control window handle
7744 * SB_LINEUP/SB_LINEDOWN:
7745 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7746 * for LVS_REPORT is 1 line
7747 * for LVS_LIST cannot occur
7750 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7751 INT nScrollDiff, HWND hScrollWnd)
7753 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7754 INT nOldScrollPos, nNewScrollPos;
7755 SCROLLINFO scrollInfo;
7758 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7759 debugscrollcode(nScrollCode), nScrollDiff);
7761 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7763 scrollInfo.cbSize = sizeof(SCROLLINFO);
7764 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7766 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7768 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7770 nOldScrollPos = scrollInfo.nPos;
7771 switch (nScrollCode)
7777 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7781 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7785 nScrollDiff = -scrollInfo.nPage;
7789 nScrollDiff = scrollInfo.nPage;
7792 case SB_THUMBPOSITION:
7794 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7801 /* quit right away if pos isn't changing */
7802 if (nScrollDiff == 0) return 0;
7804 /* calculate new position, and handle overflows */
7805 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7806 if (nScrollDiff > 0) {
7807 if (nNewScrollPos < nOldScrollPos ||
7808 nNewScrollPos > scrollInfo.nMax)
7809 nNewScrollPos = scrollInfo.nMax;
7811 if (nNewScrollPos > nOldScrollPos ||
7812 nNewScrollPos < scrollInfo.nMin)
7813 nNewScrollPos = scrollInfo.nMin;
7816 /* set the new position, and reread in case it changed */
7817 scrollInfo.fMask = SIF_POS;
7818 scrollInfo.nPos = nNewScrollPos;
7819 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7821 /* carry on only if it really changed */
7822 if (nNewScrollPos == nOldScrollPos) return 0;
7824 /* now adjust to client coordinates */
7825 nScrollDiff = nOldScrollPos - nNewScrollPos;
7826 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7828 /* and scroll the window */
7829 scroll_list(infoPtr, 0, nScrollDiff);
7836 * Performs horizontal scrolling.
7839 * [I] infoPtr : valid pointer to the listview structure
7840 * [I] nScrollCode : scroll code
7841 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7842 * [I] hScrollWnd : scrollbar control window handle
7848 * SB_LINELEFT/SB_LINERIGHT:
7849 * for LVS_ICON, LVS_SMALLICON 1 pixel
7850 * for LVS_REPORT is 1 pixel
7851 * for LVS_LIST is 1 column --> which is a 1 because the
7852 * scroll is based on columns not pixels
7855 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7856 INT nScrollDiff, HWND hScrollWnd)
7858 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7859 INT nOldScrollPos, nNewScrollPos;
7860 SCROLLINFO scrollInfo;
7862 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7863 debugscrollcode(nScrollCode), nScrollDiff);
7865 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7867 scrollInfo.cbSize = sizeof(SCROLLINFO);
7868 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7870 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7872 nOldScrollPos = scrollInfo.nPos;
7874 switch (nScrollCode)
7888 nScrollDiff = -scrollInfo.nPage;
7892 nScrollDiff = scrollInfo.nPage;
7895 case SB_THUMBPOSITION:
7897 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7904 /* quit right away if pos isn't changing */
7905 if (nScrollDiff == 0) return 0;
7907 /* calculate new position, and handle overflows */
7908 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7909 if (nScrollDiff > 0) {
7910 if (nNewScrollPos < nOldScrollPos ||
7911 nNewScrollPos > scrollInfo.nMax)
7912 nNewScrollPos = scrollInfo.nMax;
7914 if (nNewScrollPos > nOldScrollPos ||
7915 nNewScrollPos < scrollInfo.nMin)
7916 nNewScrollPos = scrollInfo.nMin;
7919 /* set the new position, and reread in case it changed */
7920 scrollInfo.fMask = SIF_POS;
7921 scrollInfo.nPos = nNewScrollPos;
7922 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7924 /* carry on only if it really changed */
7925 if (nNewScrollPos == nOldScrollPos) return 0;
7927 if(uView == LVS_REPORT)
7928 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7930 /* now adjust to client coordinates */
7931 nScrollDiff = nOldScrollPos - nNewScrollPos;
7932 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7934 /* and scroll the window */
7935 scroll_list(infoPtr, nScrollDiff, 0);
7940 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7942 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7943 INT gcWheelDelta = 0;
7944 INT pulScrollLines = 3;
7945 SCROLLINFO scrollInfo;
7947 TRACE("(wheelDelta=%d)\n", wheelDelta);
7949 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7950 gcWheelDelta -= wheelDelta;
7952 scrollInfo.cbSize = sizeof(SCROLLINFO);
7953 scrollInfo.fMask = SIF_POS;
7960 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7961 * should be fixed in the future.
7963 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7964 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7968 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7970 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7971 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7972 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7977 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7988 * [I] infoPtr : valid pointer to the listview structure
7989 * [I] nVirtualKey : virtual key
7990 * [I] lKeyData : key data
7995 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7997 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7998 HWND hwndSelf = infoPtr->hwndSelf;
8000 NMLVKEYDOWN nmKeyDown;
8002 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
8004 /* send LVN_KEYDOWN notification */
8005 nmKeyDown.wVKey = nVirtualKey;
8006 nmKeyDown.flags = 0;
8007 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8008 if (!IsWindow(hwndSelf))
8011 switch (nVirtualKey)
8014 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8016 if (!notify(infoPtr, NM_RETURN)) return 0;
8017 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8022 if (infoPtr->nItemCount > 0)
8027 if (infoPtr->nItemCount > 0)
8028 nItem = infoPtr->nItemCount - 1;
8032 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8036 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8040 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8044 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8048 if (uView == LVS_REPORT)
8050 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8051 if (infoPtr->nFocusedItem == topidx)
8052 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8057 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8058 * LISTVIEW_GetCountPerRow(infoPtr);
8059 if(nItem < 0) nItem = 0;
8063 if (uView == LVS_REPORT)
8065 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8066 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8067 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8068 nItem = infoPtr->nFocusedItem + cnt - 1;
8070 nItem = topidx + cnt - 1;
8073 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8074 * LISTVIEW_GetCountPerRow(infoPtr);
8075 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8079 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8080 LISTVIEW_KeySelection(infoPtr, nItem);
8090 * [I] infoPtr : valid pointer to the listview structure
8095 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8099 /* if we did not have the focus, there's nothing to do */
8100 if (!infoPtr->bFocus) return 0;
8102 /* send NM_KILLFOCUS notification */
8103 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8105 /* if we have a focus rectagle, get rid of it */
8106 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8108 /* set window focus flag */
8109 infoPtr->bFocus = FALSE;
8111 /* invalidate the selected items before reseting focus flag */
8112 LISTVIEW_InvalidateSelectedItems(infoPtr);
8119 * Processes double click messages (left mouse button).
8122 * [I] infoPtr : valid pointer to the listview structure
8123 * [I] wKey : key flag
8124 * [I] x,y : mouse coordinate
8129 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8131 LVHITTESTINFO htInfo;
8133 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8135 /* send NM_RELEASEDCAPTURE notification */
8136 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8141 /* send NM_DBLCLK notification */
8142 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8143 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8145 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8146 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8153 * Processes mouse down messages (left mouse button).
8156 * infoPtr [I ] valid pointer to the listview structure
8157 * wKey [I ] key flag
8158 * x,y [I ] mouse coordinate
8163 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8165 LVHITTESTINFO lvHitTestInfo;
8166 static BOOL bGroupSelect = TRUE;
8167 POINT pt = { x, y };
8170 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8172 /* send NM_RELEASEDCAPTURE notification */
8173 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8175 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8177 /* set left button down flag and record the click position */
8178 infoPtr->bLButtonDown = TRUE;
8179 infoPtr->ptClickPos = pt;
8181 lvHitTestInfo.pt.x = x;
8182 lvHitTestInfo.pt.y = y;
8184 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8185 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
8186 infoPtr->nEditLabelItem = -1;
8187 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8189 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8191 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8192 if(state == 1 || state == 2)
8196 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8197 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8198 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8203 if (infoPtr->dwStyle & LVS_SINGLESEL)
8205 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8206 infoPtr->nEditLabelItem = nItem;
8208 LISTVIEW_SetSelection(infoPtr, nItem);
8212 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8216 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8217 LISTVIEW_SetItemFocus(infoPtr, nItem);
8218 infoPtr->nSelectionMark = nItem;
8224 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8225 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8227 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8228 infoPtr->nSelectionMark = nItem;
8231 else if (wKey & MK_CONTROL)
8235 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8237 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8238 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8239 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8240 infoPtr->nSelectionMark = nItem;
8242 else if (wKey & MK_SHIFT)
8244 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8248 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8249 infoPtr->nEditLabelItem = nItem;
8251 /* set selection (clears other pre-existing selections) */
8252 LISTVIEW_SetSelection(infoPtr, nItem);
8258 /* remove all selections */
8259 LISTVIEW_DeselectAll(infoPtr);
8268 * Processes mouse up messages (left mouse button).
8271 * infoPtr [I ] valid pointer to the listview structure
8272 * wKey [I ] key flag
8273 * x,y [I ] mouse coordinate
8278 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8280 LVHITTESTINFO lvHitTestInfo;
8282 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8284 if (!infoPtr->bLButtonDown) return 0;
8286 lvHitTestInfo.pt.x = x;
8287 lvHitTestInfo.pt.y = y;
8289 /* send NM_CLICK notification */
8290 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8291 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8293 /* set left button flag */
8294 infoPtr->bLButtonDown = FALSE;
8296 /* if we clicked on a selected item, edit the label */
8297 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8298 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8305 * Destroys the listview control (called after WM_DESTROY).
8308 * [I] infoPtr : valid pointer to the listview structure
8313 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8317 /* delete all items */
8318 LISTVIEW_DeleteAllItems(infoPtr);
8320 /* destroy data structure */
8321 DPA_Destroy(infoPtr->hdpaItems);
8322 DPA_Destroy(infoPtr->hdpaPosX);
8323 DPA_Destroy(infoPtr->hdpaPosY);
8324 DPA_Destroy(infoPtr->hdpaColumns);
8325 ranges_destroy(infoPtr->selectionRanges);
8327 /* destroy image lists */
8328 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8330 if (infoPtr->himlNormal)
8331 ImageList_Destroy(infoPtr->himlNormal);
8332 if (infoPtr->himlSmall)
8333 ImageList_Destroy(infoPtr->himlSmall);
8334 if (infoPtr->himlState)
8335 ImageList_Destroy(infoPtr->himlState);
8338 /* destroy font, bkgnd brush */
8340 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8341 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8343 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8345 /* free listview info pointer*/
8353 * Handles notifications from header.
8356 * [I] infoPtr : valid pointer to the listview structure
8357 * [I] nCtrlId : control identifier
8358 * [I] lpnmh : notification information
8363 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8365 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8366 HWND hwndSelf = infoPtr->hwndSelf;
8368 TRACE("(lpnmh=%p)\n", lpnmh);
8370 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8372 switch (lpnmh->hdr.code)
8374 case HDN_ITEMCHANGINGW:
8375 case HDN_ITEMCHANGINGA:
8376 return notify_forward_header(infoPtr, lpnmh);
8377 case HDN_ITEMCHANGEDW:
8378 case HDN_ITEMCHANGEDA:
8379 notify_forward_header(infoPtr, lpnmh);
8380 if (!IsWindow(hwndSelf))
8386 COLUMN_INFO *lpColumnInfo;
8389 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8393 hdi.mask = HDI_WIDTH;
8394 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8398 cxy = lpnmh->pitem->cxy;
8400 /* determine how much we change since the last know position */
8401 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8402 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8405 lpColumnInfo->rcHeader.right += dx;
8406 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8407 LISTVIEW_UpdateItemSize(infoPtr);
8408 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8411 RECT rcCol = lpColumnInfo->rcHeader;
8413 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8414 OffsetRect(&rcCol, ptOrigin.x, 0);
8416 rcCol.top = infoPtr->rcList.top;
8417 rcCol.bottom = infoPtr->rcList.bottom;
8419 /* resizing left-aligned columns leaves most of the left side untouched */
8420 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8422 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth + dx;
8423 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8426 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8432 case HDN_ITEMCLICKW:
8433 case HDN_ITEMCLICKA:
8435 /* Handle sorting by Header Column */
8438 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8440 nmlv.iSubItem = lpnmh->iItem;
8441 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8445 case HDN_DIVIDERDBLCLICKW:
8446 case HDN_DIVIDERDBLCLICKA:
8447 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8456 * Paint non-client area of control.
8459 * [I] infoPtr : valid pointer to the listview structureof the sender
8460 * [I] region : update region
8463 * TRUE - frame was painted
8464 * FALSE - call default window proc
8466 static BOOL LISTVIEW_NCPaint(LISTVIEW_INFO *infoPtr, HRGN region)
8468 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8472 int cxEdge = GetSystemMetrics (SM_CXEDGE),
8473 cyEdge = GetSystemMetrics (SM_CYEDGE);
8475 if (!theme) return FALSE;
8477 GetWindowRect(infoPtr->hwndSelf, &r);
8479 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8480 r.right - cxEdge, r.bottom - cyEdge);
8481 if (region != (HRGN)1)
8482 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8483 OffsetRect(&r, -r.left, -r.top);
8485 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8486 OffsetRect(&r, -r.left, -r.top);
8488 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8489 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8490 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8491 ReleaseDC(infoPtr->hwndSelf, dc);
8493 /* Call default proc to get the scrollbars etc. painted */
8494 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8501 * Determines the type of structure to use.
8504 * [I] infoPtr : valid pointer to the listview structureof the sender
8505 * [I] hwndFrom : listview window handle
8506 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8511 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8513 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8515 if (nCommand != NF_REQUERY) return 0;
8517 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8524 * Paints/Repaints the listview control.
8527 * [I] infoPtr : valid pointer to the listview structure
8528 * [I] hdc : device context handle
8533 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8535 TRACE("(hdc=%p)\n", hdc);
8537 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8539 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8541 infoPtr->bNoItemMetrics = FALSE;
8542 LISTVIEW_UpdateItemSize(infoPtr);
8543 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8544 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8545 LISTVIEW_UpdateScroll(infoPtr);
8548 LISTVIEW_Refresh(infoPtr, hdc);
8553 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8555 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8556 LISTVIEW_Refresh(infoPtr, hdc);
8557 EndPaint(infoPtr->hwndSelf, &ps);
8566 * Paints/Repaints the listview control.
8569 * [I] infoPtr : valid pointer to the listview structure
8570 * [I] hdc : device context handle
8571 * [I] options : drawing options
8576 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8578 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8580 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8583 if (options & PRF_ERASEBKGND)
8584 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8586 if (options & PRF_CLIENT)
8587 LISTVIEW_Paint(infoPtr, hdc);
8595 * Processes double click messages (right mouse button).
8598 * [I] infoPtr : valid pointer to the listview structure
8599 * [I] wKey : key flag
8600 * [I] x,y : mouse coordinate
8605 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8607 LVHITTESTINFO lvHitTestInfo;
8609 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8611 /* send NM_RELEASEDCAPTURE notification */
8612 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8614 /* send NM_RDBLCLK notification */
8615 lvHitTestInfo.pt.x = x;
8616 lvHitTestInfo.pt.y = y;
8617 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8618 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8625 * Processes mouse down messages (right mouse button).
8628 * [I] infoPtr : valid pointer to the listview structure
8629 * [I] wKey : key flag
8630 * [I] x,y : mouse coordinate
8635 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8637 LVHITTESTINFO lvHitTestInfo;
8640 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8642 /* send NM_RELEASEDCAPTURE notification */
8643 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8645 /* make sure the listview control window has the focus */
8646 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8648 /* set right button down flag */
8649 infoPtr->bRButtonDown = TRUE;
8651 /* determine the index of the selected item */
8652 lvHitTestInfo.pt.x = x;
8653 lvHitTestInfo.pt.y = y;
8654 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8656 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8658 LISTVIEW_SetItemFocus(infoPtr, nItem);
8659 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8660 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8661 LISTVIEW_SetSelection(infoPtr, nItem);
8665 LISTVIEW_DeselectAll(infoPtr);
8673 * Processes mouse up messages (right mouse button).
8676 * [I] infoPtr : valid pointer to the listview structure
8677 * [I] wKey : key flag
8678 * [I] x,y : mouse coordinate
8683 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8685 LVHITTESTINFO lvHitTestInfo;
8688 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8690 if (!infoPtr->bRButtonDown) return 0;
8692 /* set button flag */
8693 infoPtr->bRButtonDown = FALSE;
8695 /* Send NM_RClICK notification */
8696 lvHitTestInfo.pt.x = x;
8697 lvHitTestInfo.pt.y = y;
8698 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8699 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
8701 /* Change to screen coordinate for WM_CONTEXTMENU */
8702 pt = lvHitTestInfo.pt;
8703 ClientToScreen(infoPtr->hwndSelf, &pt);
8705 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8706 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8707 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8718 * [I] infoPtr : valid pointer to the listview structure
8719 * [I] hwnd : window handle of window containing the cursor
8720 * [I] nHittest : hit-test code
8721 * [I] wMouseMsg : ideintifier of the mouse message
8724 * TRUE if cursor is set
8727 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8729 LVHITTESTINFO lvHitTestInfo;
8731 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8733 if(!infoPtr->hHotCursor) return FALSE;
8735 GetCursorPos(&lvHitTestInfo.pt);
8736 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8738 SetCursor(infoPtr->hHotCursor);
8748 * [I] infoPtr : valid pointer to the listview structure
8749 * [I] hwndLoseFocus : handle of previously focused window
8754 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8756 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8758 /* if we have the focus already, there's nothing to do */
8759 if (infoPtr->bFocus) return 0;
8761 /* send NM_SETFOCUS notification */
8762 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
8764 /* set window focus flag */
8765 infoPtr->bFocus = TRUE;
8767 /* put the focus rect back on */
8768 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8770 /* redraw all visible selected items */
8771 LISTVIEW_InvalidateSelectedItems(infoPtr);
8781 * [I] infoPtr : valid pointer to the listview structure
8782 * [I] fRedraw : font handle
8783 * [I] fRedraw : redraw flag
8788 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8790 HFONT oldFont = infoPtr->hFont;
8792 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8794 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8795 if (infoPtr->hFont == oldFont) return 0;
8797 LISTVIEW_SaveTextMetrics(infoPtr);
8799 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8800 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8802 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8809 * Message handling for WM_SETREDRAW.
8810 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8813 * [I] infoPtr : valid pointer to the listview structure
8814 * [I] bRedraw: state of redraw flag
8817 * DefWinProc return value
8819 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8821 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8823 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
8824 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8826 infoPtr->bRedraw = bRedraw;
8828 if(!bRedraw) return 0;
8830 if (is_autoarrange(infoPtr))
8831 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8832 LISTVIEW_UpdateScroll(infoPtr);
8834 /* despite what the WM_SETREDRAW docs says, apps expect us
8835 * to invalidate the listview here... stupid! */
8836 LISTVIEW_InvalidateList(infoPtr);
8843 * Resizes the listview control. This function processes WM_SIZE
8844 * messages. At this time, the width and height are not used.
8847 * [I] infoPtr : valid pointer to the listview structure
8848 * [I] Width : new width
8849 * [I] Height : new height
8854 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8856 RECT rcOld = infoPtr->rcList;
8858 TRACE("(width=%d, height=%d)\n", Width, Height);
8860 LISTVIEW_UpdateSize(infoPtr);
8861 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8863 /* do not bother with display related stuff if we're not redrawing */
8864 if (!is_redrawing(infoPtr)) return 0;
8866 if (is_autoarrange(infoPtr))
8867 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8869 LISTVIEW_UpdateScroll(infoPtr);
8871 /* refresh all only for lists whose height changed significantly */
8872 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8873 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8874 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8875 LISTVIEW_InvalidateList(infoPtr);
8882 * Sets the size information.
8885 * [I] infoPtr : valid pointer to the listview structure
8890 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8892 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8894 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8896 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8898 if (uView == LVS_LIST)
8900 /* Apparently the "LIST" style is supposed to have the same
8901 * number of items in a column even if there is no scroll bar.
8902 * Since if a scroll bar already exists then the bottom is already
8903 * reduced, only reduce if the scroll bar does not currently exist.
8904 * The "2" is there to mimic the native control. I think it may be
8905 * related to either padding or edges. (GLA 7/2002)
8907 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
8908 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8909 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8911 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8916 hl.prc = &infoPtr->rcList;
8918 Header_Layout(infoPtr->hwndHeader, &hl);
8920 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8922 infoPtr->rcList.top = max(wp.cy, 0);
8925 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8930 * Processes WM_STYLECHANGED messages.
8933 * [I] infoPtr : valid pointer to the listview structure
8934 * [I] wStyleType : window style type (normal or extended)
8935 * [I] lpss : window style information
8940 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8941 const STYLESTRUCT *lpss)
8943 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8944 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8946 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8947 wStyleType, lpss->styleOld, lpss->styleNew);
8949 if (wStyleType != GWL_STYLE) return 0;
8951 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8952 /* what if LVS_OWNERDATA changed? */
8953 /* or LVS_SINGLESEL */
8954 /* or LVS_SORT{AS,DES}CENDING */
8956 infoPtr->dwStyle = lpss->styleNew;
8958 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8959 ((lpss->styleNew & WS_HSCROLL) == 0))
8960 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8962 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8963 ((lpss->styleNew & WS_VSCROLL) == 0))
8964 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8966 if (uNewView != uOldView)
8968 SIZE oldIconSize = infoPtr->iconSize;
8971 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8972 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8974 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8975 SetRectEmpty(&infoPtr->rcFocus);
8977 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8978 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8980 if (uNewView == LVS_ICON)
8982 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8984 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8985 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8986 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8989 else if (uNewView == LVS_REPORT)
8994 hl.prc = &infoPtr->rcList;
8996 Header_Layout(infoPtr->hwndHeader, &hl);
8997 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9000 LISTVIEW_UpdateItemSize(infoPtr);
9003 if (uNewView == LVS_REPORT)
9004 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
9006 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9007 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9008 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9010 /* update the size of the client area */
9011 LISTVIEW_UpdateSize(infoPtr);
9013 /* add scrollbars if needed */
9014 LISTVIEW_UpdateScroll(infoPtr);
9016 /* invalidate client area + erase background */
9017 LISTVIEW_InvalidateList(infoPtr);
9024 * Window procedure of the listview control.
9027 static LRESULT WINAPI
9028 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9030 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9032 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
9034 if (!infoPtr && (uMsg != WM_CREATE))
9035 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9039 case LVM_APPROXIMATEVIEWRECT:
9040 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9041 LOWORD(lParam), HIWORD(lParam));
9043 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9045 /* case LVM_CANCELEDITLABEL: */
9047 case LVM_CREATEDRAGIMAGE:
9048 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9050 case LVM_DELETEALLITEMS:
9051 return LISTVIEW_DeleteAllItems(infoPtr);
9053 case LVM_DELETECOLUMN:
9054 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9056 case LVM_DELETEITEM:
9057 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9059 case LVM_EDITLABELW:
9060 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9062 case LVM_EDITLABELA:
9063 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9065 /* case LVM_ENABLEGROUPVIEW: */
9067 case LVM_ENSUREVISIBLE:
9068 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9071 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9074 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9076 case LVM_GETBKCOLOR:
9077 return infoPtr->clrBk;
9079 /* case LVM_GETBKIMAGE: */
9081 case LVM_GETCALLBACKMASK:
9082 return infoPtr->uCallbackMask;
9084 case LVM_GETCOLUMNA:
9085 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9087 case LVM_GETCOLUMNW:
9088 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9090 case LVM_GETCOLUMNORDERARRAY:
9091 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9093 case LVM_GETCOLUMNWIDTH:
9094 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9096 case LVM_GETCOUNTPERPAGE:
9097 return LISTVIEW_GetCountPerPage(infoPtr);
9099 case LVM_GETEDITCONTROL:
9100 return (LRESULT)infoPtr->hwndEdit;
9102 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9103 return infoPtr->dwLvExStyle;
9105 /* case LVM_GETGROUPINFO: */
9107 /* case LVM_GETGROUPMETRICS: */
9110 return (LRESULT)infoPtr->hwndHeader;
9112 case LVM_GETHOTCURSOR:
9113 return (LRESULT)infoPtr->hHotCursor;
9115 case LVM_GETHOTITEM:
9116 return infoPtr->nHotItem;
9118 case LVM_GETHOVERTIME:
9119 return infoPtr->dwHoverTime;
9121 case LVM_GETIMAGELIST:
9122 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9124 /* case LVM_GETINSERTMARK: */
9126 /* case LVM_GETINSERTMARKCOLOR: */
9128 /* case LVM_GETINSERTMARKRECT: */
9130 case LVM_GETISEARCHSTRINGA:
9131 case LVM_GETISEARCHSTRINGW:
9132 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9136 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9139 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9141 case LVM_GETITEMCOUNT:
9142 return infoPtr->nItemCount;
9144 case LVM_GETITEMPOSITION:
9145 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9147 case LVM_GETITEMRECT:
9148 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9150 case LVM_GETITEMSPACING:
9151 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9153 case LVM_GETITEMSTATE:
9154 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9156 case LVM_GETITEMTEXTA:
9157 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9159 case LVM_GETITEMTEXTW:
9160 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9162 case LVM_GETNEXTITEM:
9163 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9165 case LVM_GETNUMBEROFWORKAREAS:
9166 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9170 if (!lParam) return FALSE;
9171 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9174 /* case LVM_GETOUTLINECOLOR: */
9176 /* case LVM_GETSELECTEDCOLUMN: */
9178 case LVM_GETSELECTEDCOUNT:
9179 return LISTVIEW_GetSelectedCount(infoPtr);
9181 case LVM_GETSELECTIONMARK:
9182 return infoPtr->nSelectionMark;
9184 case LVM_GETSTRINGWIDTHA:
9185 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9187 case LVM_GETSTRINGWIDTHW:
9188 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9190 case LVM_GETSUBITEMRECT:
9191 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9193 case LVM_GETTEXTBKCOLOR:
9194 return infoPtr->clrTextBk;
9196 case LVM_GETTEXTCOLOR:
9197 return infoPtr->clrText;
9199 /* case LVM_GETTILEINFO: */
9201 /* case LVM_GETTILEVIEWINFO: */
9203 case LVM_GETTOOLTIPS:
9204 if( !infoPtr->hwndToolTip )
9205 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9206 return (LRESULT)infoPtr->hwndToolTip;
9208 case LVM_GETTOPINDEX:
9209 return LISTVIEW_GetTopIndex(infoPtr);
9211 /*case LVM_GETUNICODEFORMAT:
9212 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9215 /* case LVM_GETVIEW: */
9217 case LVM_GETVIEWRECT:
9218 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9220 case LVM_GETWORKAREAS:
9221 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9224 /* case LVM_HASGROUP: */
9227 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9229 case LVM_INSERTCOLUMNA:
9230 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9232 case LVM_INSERTCOLUMNW:
9233 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9235 /* case LVM_INSERTGROUP: */
9237 /* case LVM_INSERTGROUPSORTED: */
9239 case LVM_INSERTITEMA:
9240 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9242 case LVM_INSERTITEMW:
9243 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9245 /* case LVM_INSERTMARKHITTEST: */
9247 /* case LVM_ISGROUPVIEWENABLED: */
9249 /* case LVM_MAPIDTOINDEX: */
9251 /* case LVM_MAPINDEXTOID: */
9253 /* case LVM_MOVEGROUP: */
9255 /* case LVM_MOVEITEMTOGROUP: */
9257 case LVM_REDRAWITEMS:
9258 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9260 /* case LVM_REMOVEALLGROUPS: */
9262 /* case LVM_REMOVEGROUP: */
9265 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9267 case LVM_SETBKCOLOR:
9268 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9270 /* case LVM_SETBKIMAGE: */
9272 case LVM_SETCALLBACKMASK:
9273 infoPtr->uCallbackMask = (UINT)wParam;
9276 case LVM_SETCOLUMNA:
9277 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9279 case LVM_SETCOLUMNW:
9280 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9282 case LVM_SETCOLUMNORDERARRAY:
9283 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9285 case LVM_SETCOLUMNWIDTH:
9286 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9288 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9289 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9291 /* case LVM_SETGROUPINFO: */
9293 /* case LVM_SETGROUPMETRICS: */
9295 case LVM_SETHOTCURSOR:
9296 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9298 case LVM_SETHOTITEM:
9299 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9301 case LVM_SETHOVERTIME:
9302 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9304 case LVM_SETICONSPACING:
9305 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9307 case LVM_SETIMAGELIST:
9308 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9310 /* case LVM_SETINFOTIP: */
9312 /* case LVM_SETINSERTMARK: */
9314 /* case LVM_SETINSERTMARKCOLOR: */
9317 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9320 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9322 case LVM_SETITEMCOUNT:
9323 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9325 case LVM_SETITEMPOSITION:
9328 pt.x = (short)LOWORD(lParam);
9329 pt.y = (short)HIWORD(lParam);
9330 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9333 case LVM_SETITEMPOSITION32:
9334 if (lParam == 0) return FALSE;
9335 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9337 case LVM_SETITEMSTATE:
9338 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9340 case LVM_SETITEMTEXTA:
9341 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9343 case LVM_SETITEMTEXTW:
9344 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9346 /* case LVM_SETOUTLINECOLOR: */
9348 /* case LVM_SETSELECTEDCOLUMN: */
9350 case LVM_SETSELECTIONMARK:
9351 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9353 case LVM_SETTEXTBKCOLOR:
9354 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9356 case LVM_SETTEXTCOLOR:
9357 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9359 /* case LVM_SETTILEINFO: */
9361 /* case LVM_SETTILEVIEWINFO: */
9363 /* case LVM_SETTILEWIDTH: */
9365 case LVM_SETTOOLTIPS:
9366 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9368 /* case LVM_SETUNICODEFORMAT: */
9370 /* case LVM_SETVIEW: */
9372 /* case LVM_SETWORKAREAS: */
9374 /* case LVM_SORTGROUPS: */
9377 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9379 /* LVM_SORTITEMSEX: */
9381 case LVM_SUBITEMHITTEST:
9382 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9385 return LISTVIEW_Update(infoPtr, (INT)wParam);
9388 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9391 return LISTVIEW_Command(infoPtr, wParam, lParam);
9394 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9397 return LISTVIEW_Destroy(infoPtr);
9400 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9403 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9406 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9409 return (LRESULT)infoPtr->hFont;
9412 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9415 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9418 return LISTVIEW_KillFocus(infoPtr);
9420 case WM_LBUTTONDBLCLK:
9421 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9423 case WM_LBUTTONDOWN:
9424 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9427 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9430 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9433 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9436 return LISTVIEW_NCDestroy(infoPtr);
9439 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9444 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9445 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9448 case WM_NOTIFYFORMAT:
9449 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9451 case WM_PRINTCLIENT:
9452 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9455 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9457 case WM_RBUTTONDBLCLK:
9458 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9460 case WM_RBUTTONDOWN:
9461 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9464 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9467 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9472 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9475 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9478 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9481 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9483 case WM_STYLECHANGED:
9484 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9486 case WM_SYSCOLORCHANGE:
9487 COMCTL32_RefreshSysColors();
9490 /* case WM_TIMER: */
9491 case WM_THEMECHANGED:
9492 return LISTVIEW_ThemeChanged(infoPtr);
9495 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9498 if (wParam & (MK_SHIFT | MK_CONTROL))
9499 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9500 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9502 case WM_WINDOWPOSCHANGED:
9503 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9505 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9506 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9507 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9509 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9511 MEASUREITEMSTRUCT mis;
9512 mis.CtlType = ODT_LISTVIEW;
9513 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9517 mis.itemHeight= infoPtr->nItemHeight;
9518 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9519 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9520 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9523 LISTVIEW_UpdateSize(infoPtr);
9524 LISTVIEW_UpdateScroll(infoPtr);
9526 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9528 /* case WM_WININICHANGE: */
9531 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9532 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9535 /* call default window procedure */
9536 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9543 * Registers the window class.
9551 void LISTVIEW_Register(void)
9555 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9556 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9557 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9558 wndClass.cbClsExtra = 0;
9559 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9560 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9561 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9562 wndClass.lpszClassName = WC_LISTVIEWW;
9563 RegisterClassW(&wndClass);
9568 * Unregisters the window class.
9576 void LISTVIEW_Unregister(void)
9578 UnregisterClassW(WC_LISTVIEWW, NULL);
9583 * Handle any WM_COMMAND messages
9586 * [I] infoPtr : valid pointer to the listview structure
9587 * [I] wParam : the first message parameter
9588 * [I] lParam : the second message parameter
9593 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9595 switch (HIWORD(wParam))
9600 * Adjust the edit window size
9603 HDC hdc = GetDC(infoPtr->hwndEdit);
9604 HFONT hFont, hOldFont = 0;
9609 if (!infoPtr->hwndEdit || !hdc) return 0;
9610 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9611 GetWindowRect(infoPtr->hwndEdit, &rect);
9613 /* Select font to get the right dimension of the string */
9614 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9617 hOldFont = SelectObject(hdc, hFont);
9620 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9622 TEXTMETRICW textMetric;
9624 /* Add Extra spacing for the next character */
9625 GetTextMetricsW(hdc, &textMetric);
9626 sz.cx += (textMetric.tmMaxCharWidth * 2);
9634 rect.bottom - rect.top,
9635 SWP_DRAWFRAME|SWP_NOMOVE);
9638 SelectObject(hdc, hOldFont);
9640 ReleaseDC(infoPtr->hwndEdit, hdc);
9646 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9655 * Subclassed edit control windproc function
9658 * [I] hwnd : the edit window handle
9659 * [I] uMsg : the message that is to be processed
9660 * [I] wParam : first message parameter
9661 * [I] lParam : second message parameter
9662 * [I] isW : TRUE if input is Unicode
9667 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9669 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9670 BOOL cancel = FALSE;
9672 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9673 hwnd, uMsg, wParam, lParam, isW);
9678 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9685 WNDPROC editProc = infoPtr->EditWndProc;
9686 infoPtr->EditWndProc = 0;
9687 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9688 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9692 if (VK_ESCAPE == (INT)wParam)
9697 else if (VK_RETURN == (INT)wParam)
9701 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9705 if (infoPtr->hwndEdit)
9707 LPWSTR buffer = NULL;
9709 infoPtr->hwndEdit = 0;
9712 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9716 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9718 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9719 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9723 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9725 if (buffer) Free(buffer);
9729 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9735 * Subclassed edit control Unicode windproc function
9738 * [I] hwnd : the edit window handle
9739 * [I] uMsg : the message that is to be processed
9740 * [I] wParam : first message parameter
9741 * [I] lParam : second message parameter
9745 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9747 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9752 * Subclassed edit control ANSI windproc function
9755 * [I] hwnd : the edit window handle
9756 * [I] uMsg : the message that is to be processed
9757 * [I] wParam : first message parameter
9758 * [I] lParam : second message parameter
9762 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9764 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9769 * Creates a subclassed edit cotrol
9772 * [I] infoPtr : valid pointer to the listview structure
9773 * [I] text : initial text for the edit
9774 * [I] style : the window style
9775 * [I] isW : TRUE if input is Unicode
9779 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9780 INT x, INT y, INT width, INT height, BOOL isW)
9782 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9787 TEXTMETRICW textMetric;
9788 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9790 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9792 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9793 hdc = GetDC(infoPtr->hwndSelf);
9795 /* Select the font to get appropriate metric dimensions */
9796 if(infoPtr->hFont != 0)
9797 hOldFont = SelectObject(hdc, infoPtr->hFont);
9799 /*Get String Length in pixels */
9800 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9802 /*Add Extra spacing for the next character */
9803 GetTextMetricsW(hdc, &textMetric);
9804 sz.cx += (textMetric.tmMaxCharWidth * 2);
9806 if(infoPtr->hFont != 0)
9807 SelectObject(hdc, hOldFont);
9809 ReleaseDC(infoPtr->hwndSelf, hdc);
9811 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9813 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9815 if (!hedit) return 0;
9817 infoPtr->EditWndProc = (WNDPROC)
9818 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9819 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9821 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);