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 Oct. 21, 2002, by Dimitrie O. Paun.
29 * Unless otherwise noted, we belive this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
36 * -- Hot item handling, mouse hovering
37 * -- Workareas support
42 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
43 * -- Support CustonDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs.
44 * -- in LISTVIEW_AddGroupSelection, se whould send LVN_ODSTATECHANGED
45 * -- LVA_SNAPTOGRID not implemented
46 * -- LISTVIEW_ApproximateViewRect partially implemented
47 * -- LISTVIEW_[GS]etColumnOrderArray stubs
48 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
49 * -- LISTVIEW_SetIconSpacing is incomplete
50 * -- LISTVIEW_SortItems is broken
51 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
54 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
55 * linear in the number of items in the list, and this is
56 * unacceptable for large lists.
57 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
58 * instead of inserting in the right spot
59 * -- we should keep an ordered array of coordinates in iconic mode
60 * this would allow to frame items (iterator_frameditems),
61 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
69 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
76 * -- LVS_NOSCROLL (see Q137520)
77 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
80 * -- LVS_EX_BORDERSELECT
83 * -- LVS_EX_HEADERDRAGDROP
86 * -- LVS_EX_MULTIWORKAREAS
87 * -- LVS_EX_ONECLICKACTIVATE
89 * -- LVS_EX_SIMPLESELECT
90 * -- LVS_EX_TRACKSELECT
91 * -- LVS_EX_TWOCLICKACTIVATE
92 * -- LVS_EX_UNDERLINECOLD
93 * -- LVS_EX_UNDERLINEHOT
97 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
100 * -- LVN_MARQUEEBEGIN
102 * -- LVN_ODSTATECHANGED
107 * -- LVM_CANCELEDITLABEL
108 * -- LVM_ENABLEGROUPVIEW
109 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
110 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
111 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
112 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
113 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
114 * -- LVM_GETINSERTMARKRECT
115 * -- LVM_GETNUMBEROFWORKAREAS
116 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
117 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
118 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
119 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
120 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
121 * -- LVM_GETTOOLTIPS, LVM_SETTOOLTIPS
122 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
123 * -- LVM_GETVIEW, LVM_SETVIEW
124 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
125 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
126 * -- LVM_INSERTGROUPSORTED
127 * -- LVM_INSERTMARKHITTEST
128 * -- LVM_ISGROUPVIEWENABLED
129 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
131 * -- LVM_MOVEITEMTOGROUP
133 * -- LVM_SETTILEWIDTH
137 * Known differences in message stream from native control (not known if
138 * these differences cause problems):
139 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
140 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
141 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
142 * processing for "USEDOUBLECLICKTIME".
146 #include "wine/port.h"
161 #include "commctrl.h"
162 #include "comctl32.h"
164 #include "wine/debug.h"
165 #include "wine/unicode.h"
167 WINE_DEFAULT_DEBUG_CHANNEL(listview);
169 /* make sure you set this to 0 for production use! */
170 #define DEBUG_RANGES 1
172 typedef struct tagCOLUMN_INFO
174 RECT rcHeader; /* tracks the header's rectangle */
175 int fmt; /* same as LVCOLUMN.fmt */
178 typedef struct tagITEMHDR
182 } ITEMHDR, *LPITEMHDR;
184 typedef struct tagSUBITEM_INFO
190 typedef struct tagITEM_INFO
198 typedef struct tagRANGE
204 typedef struct tagRANGES
209 typedef struct tagITERATOR
218 typedef struct tagLISTVIEW_INFO
225 COLORREF clrTextBkDefault;
226 HIMAGELIST himlNormal;
227 HIMAGELIST himlSmall;
228 HIMAGELIST himlState;
231 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
234 RANGES selectionRanges;
239 RECT rcList; /* This rectangle is really the window
240 * client rectangle possibly reduced by the
241 * horizontal scroll bar and/or header - see
242 * LISTVIEW_UpdateSize. This rectangle offset
243 * by the LISTVIEW_GetOrigin value is in
244 * client coordinates */
253 INT ntmHeight; /* Some cached metrics of the font used */
254 INT ntmAveCharWidth; /* by the listview to draw items */
255 BOOL bRedraw; /* Turns on/off repaints & invalidations */
256 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
258 BOOL bDoChangeNotify; /* send change notification messages? */
261 DWORD dwStyle; /* the cached window GWL_STYLE */
262 DWORD dwLvExStyle; /* extended listview style */
263 INT nItemCount; /* the number of items in the list */
264 HDPA hdpaItems; /* array ITEM_INFO pointers */
265 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
266 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
267 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
268 POINT currIconPos; /* this is the position next icon will be placed */
269 PFNLVCOMPARE pfnCompare;
277 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
279 DWORD lastKeyPressTimestamp;
281 INT nSearchParamLength;
282 WCHAR szSearchParam[ MAX_PATH ];
289 /* How many we debug buffer to allocate */
290 #define DEBUG_BUFFERS 20
291 /* The size of a single debug bbuffer */
292 #define DEBUG_BUFFER_SIZE 256
294 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
295 #define SB_INTERNAL -1
297 /* maximum size of a label */
298 #define DISP_TEXT_SIZE 512
300 /* padding for items in list and small icon display modes */
301 #define WIDTH_PADDING 12
303 /* padding for items in list, report and small icon display modes */
304 #define HEIGHT_PADDING 1
306 /* offset of items in report display mode */
307 #define REPORT_MARGINX 2
309 /* padding for icon in large icon display mode
310 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
311 * that HITTEST will see.
312 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
313 * ICON_TOP_PADDING - sum of the two above.
314 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
315 * LABEL_HOR_PADDING - between text and sides of box
316 * LABEL_VERT_PADDING - between bottom of text and end of box
318 * ICON_LR_PADDING - additional width above icon size.
319 * ICON_LR_HALF - half of the above value
321 #define ICON_TOP_PADDING_NOTHITABLE 2
322 #define ICON_TOP_PADDING_HITABLE 2
323 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
324 #define ICON_BOTTOM_PADDING 4
325 #define LABEL_HOR_PADDING 5
326 #define LABEL_VERT_PADDING 7
327 #define ICON_LR_PADDING 16
328 #define ICON_LR_HALF (ICON_LR_PADDING/2)
330 /* default label width for items in list and small icon display modes */
331 #define DEFAULT_LABEL_WIDTH 40
333 /* default column width for items in list display mode */
334 #define DEFAULT_COLUMN_WIDTH 128
336 /* Size of "line" scroll for V & H scrolls */
337 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
339 /* Padding betwen image and label */
340 #define IMAGE_PADDING 2
342 /* Padding behind the label */
343 #define TRAILING_LABEL_PADDING 12
344 #define TRAILING_HEADER_PADDING 11
346 /* Border for the icon caption */
347 #define CAPTION_BORDER 2
349 /* Standard DrawText flags */
350 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
351 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
352 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
354 /* The time in milliseconds to reset the search in the list */
355 #define KEY_DELAY 450
357 /* Dump the LISTVIEW_INFO structure to the debug channel */
358 #define LISTVIEW_DUMP(iP) do { \
359 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
360 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
361 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
362 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
363 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
364 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
365 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
366 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
367 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
368 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, debugrect(&iP->rcList)); \
372 * forward declarations
374 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
375 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
376 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
377 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
378 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
379 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
380 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
381 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
382 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
383 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
384 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
385 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
386 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
387 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
388 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
389 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
390 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
391 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
392 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
393 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
394 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
395 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
396 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
397 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
398 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
399 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
401 /******** Text handling functions *************************************/
403 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
404 * text string. The string may be ANSI or Unicode, in which case
405 * the boolean isW tells us the type of the string.
407 * The name of the function tell what type of strings it expects:
408 * W: Unicode, T: ANSI/Unicode - function of isW
411 static inline BOOL is_textW(LPCWSTR text)
413 return text != NULL && text != LPSTR_TEXTCALLBACKW;
416 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
418 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
419 return is_textW(text);
422 static inline int textlenT(LPCWSTR text, BOOL isW)
424 return !is_textT(text, isW) ? 0 :
425 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
428 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
431 if (isSrcW) lstrcpynW(dest, src, max);
432 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
434 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
435 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
438 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
440 LPWSTR wstr = (LPWSTR)text;
442 if (!isW && is_textT(text, isW))
444 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
445 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
446 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
448 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
452 static inline void textfreeT(LPWSTR wstr, BOOL isW)
454 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
458 * dest is a pointer to a Unicode string
459 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
461 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
465 if (src == LPSTR_TEXTCALLBACKW)
467 if (is_textW(*dest)) Free(*dest);
468 *dest = LPSTR_TEXTCALLBACKW;
472 LPWSTR pszText = textdupTtoW(src, isW);
473 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
474 bResult = Str_SetPtrW(dest, pszText);
475 textfreeT(pszText, isW);
481 * compares a Unicode to a Unicode/ANSI text string
483 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
485 if (!aw) return bt ? -1 : 0;
486 if (!bt) return aw ? 1 : 0;
487 if (aw == LPSTR_TEXTCALLBACKW)
488 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
489 if (bt != LPSTR_TEXTCALLBACKW)
491 LPWSTR bw = textdupTtoW(bt, isW);
492 int r = bw ? lstrcmpW(aw, bw) : 1;
500 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
504 n = min(min(n, strlenW(s1)), strlenW(s2));
505 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
506 return res ? res - sizeof(WCHAR) : res;
509 /******** Debugging functions *****************************************/
511 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
513 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
514 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
517 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
519 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
520 n = min(textlenT(text, isW), n);
521 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
524 static char* debug_getbuf()
526 static int index = 0;
527 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
528 return buffers[index++ % DEBUG_BUFFERS];
531 static inline const char* debugrange(const RANGE *lprng)
535 char* buf = debug_getbuf();
536 snprintf(buf, DEBUG_BUFFER_SIZE, "[%d, %d)", lprng->lower, lprng->upper);
538 } else return "(null)";
541 static inline const char* debugpoint(const POINT *lppt)
545 char* buf = debug_getbuf();
546 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
548 } else return "(null)";
551 static inline const char* debugrect(const RECT *rect)
555 char* buf = debug_getbuf();
556 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%ld, %ld);(%ld, %ld)]",
557 rect->left, rect->top, rect->right, rect->bottom);
559 } else return "(null)";
562 static const char * debugscrollinfo(const SCROLLINFO *pScrollInfo)
564 char* buf = debug_getbuf(), *text = buf;
565 int len, size = DEBUG_BUFFER_SIZE;
567 if (pScrollInfo == NULL) return "(null)";
568 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
569 if (len == -1) goto end; buf += len; size -= len;
570 if (pScrollInfo->fMask & SIF_RANGE)
571 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
573 if (len == -1) goto end; buf += len; size -= len;
574 if (pScrollInfo->fMask & SIF_PAGE)
575 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
577 if (len == -1) goto end; buf += len; size -= len;
578 if (pScrollInfo->fMask & SIF_POS)
579 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
581 if (len == -1) goto end; buf += len; size -= len;
582 if (pScrollInfo->fMask & SIF_TRACKPOS)
583 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
585 if (len == -1) goto end; buf += len; size -= len;
588 buf = text + strlen(text);
590 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
594 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
598 char* buf = debug_getbuf();
599 snprintf(buf, DEBUG_BUFFER_SIZE, "iItem=%d, iSubItem=%d, uNewState=0x%x,"
600 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
601 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
602 plvnm->uChanged, debugpoint(&plvnm->ptAction), plvnm->lParam);
604 } else return "(null)";
607 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
609 char* buf = debug_getbuf(), *text = buf;
610 int len, size = DEBUG_BUFFER_SIZE;
612 if (lpLVItem == NULL) return "(null)";
613 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
614 if (len == -1) goto end; buf += len; size -= len;
615 if (lpLVItem->mask & LVIF_STATE)
616 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
618 if (len == -1) goto end; buf += len; size -= len;
619 if (lpLVItem->mask & LVIF_TEXT)
620 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
622 if (len == -1) goto end; buf += len; size -= len;
623 if (lpLVItem->mask & LVIF_IMAGE)
624 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
626 if (len == -1) goto end; buf += len; size -= len;
627 if (lpLVItem->mask & LVIF_PARAM)
628 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
630 if (len == -1) goto end; buf += len; size -= len;
631 if (lpLVItem->mask & LVIF_INDENT)
632 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
634 if (len == -1) goto end; buf += len; size -= len;
637 buf = text + strlen(text);
639 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
643 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
645 char* buf = debug_getbuf(), *text = buf;
646 int len, size = DEBUG_BUFFER_SIZE;
648 if (lpColumn == NULL) return "(null)";
649 len = snprintf(buf, size, "{");
650 if (len == -1) goto end; buf += len; size -= len;
651 if (lpColumn->mask & LVCF_SUBITEM)
652 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
654 if (len == -1) goto end; buf += len; size -= len;
655 if (lpColumn->mask & LVCF_FMT)
656 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
658 if (len == -1) goto end; buf += len; size -= len;
659 if (lpColumn->mask & LVCF_WIDTH)
660 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
662 if (len == -1) goto end; buf += len; size -= len;
663 if (lpColumn->mask & LVCF_TEXT)
664 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
666 if (len == -1) goto end; buf += len; size -= len;
667 if (lpColumn->mask & LVCF_IMAGE)
668 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
670 if (len == -1) goto end; buf += len; size -= len;
671 if (lpColumn->mask & LVCF_ORDER)
672 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
674 if (len == -1) goto end; buf += len; size -= len;
677 buf = text + strlen(text);
679 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
683 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
687 char* buf = debug_getbuf();
688 snprintf(buf, DEBUG_BUFFER_SIZE, "{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
689 debugpoint(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
691 } else return "(null)";
694 /* Return the corresponding text for a given scroll value */
695 static inline LPCSTR debugscrollcode(int nScrollCode)
699 case SB_LINELEFT: return "SB_LINELEFT";
700 case SB_LINERIGHT: return "SB_LINERIGHT";
701 case SB_PAGELEFT: return "SB_PAGELEFT";
702 case SB_PAGERIGHT: return "SB_PAGERIGHT";
703 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
704 case SB_THUMBTRACK: return "SB_THUMBTRACK";
705 case SB_ENDSCROLL: return "SB_ENDSCROLL";
706 case SB_INTERNAL: return "SB_INTERNAL";
707 default: return "unknown";
712 /******** Notification functions i************************************/
714 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
718 TRACE("(code=%d)\n", code);
720 pnmh->hwndFrom = infoPtr->hwndSelf;
721 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
723 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
724 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
726 TRACE(" <= %ld\n", result);
731 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
734 return notify_hdr(infoPtr, code, &nmh);
737 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
748 item.mask = LVIF_PARAM|LVIF_STATE;
749 item.iItem = htInfo->iItem;
751 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
752 nmia.lParam = item.lParam;
753 nmia.uOldState = item.state;
754 nmia.uNewState = item.state | LVIS_ACTIVATING;
755 nmia.uChanged = LVIF_STATE;
758 nmia.iItem = htInfo->iItem;
759 nmia.iSubItem = htInfo->iSubItem;
760 nmia.ptAction = htInfo->pt;
762 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
763 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
764 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
766 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
769 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
771 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
772 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
775 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
780 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
781 ZeroMemory(&nmlv, sizeof(nmlv));
782 nmlv.iItem = lvht->iItem;
783 nmlv.iSubItem = lvht->iSubItem;
784 nmlv.ptAction = lvht->pt;
785 item.mask = LVIF_PARAM;
786 item.iItem = lvht->iItem;
788 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
789 return notify_listview(infoPtr, code, &nmlv);
792 static void notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
797 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
799 item.mask = LVIF_PARAM;
802 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
803 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
806 static int get_ansi_notification(INT unicodeNotificationCode)
808 switch (unicodeNotificationCode)
810 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
811 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
812 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
813 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
814 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
815 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
817 ERR("unknown notification %x\n", unicodeNotificationCode);
823 With testing on Windows 2000 it looks like the notify format
824 has nothing to do with this message. It ALWAYS seems to be
827 infoPtr : listview struct
828 notificationCode : *Unicode* notification code
829 pdi : dispinfo structure (can be unicode or ansi)
830 isW : TRUE if dispinfo is Unicode
832 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
834 BOOL bResult = FALSE;
835 BOOL convertToAnsi = FALSE;
836 INT cchTempBufMax = 0, savCchTextMax = 0;
837 LPWSTR pszTempBuf = NULL, savPszText = NULL;
839 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
844 if (notificationCode != LVN_GETDISPINFOW)
846 cchTempBufMax = WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText,
847 -1, NULL, 0, NULL, NULL);
851 cchTempBufMax = pdi->item.cchTextMax;
852 *pdi->item.pszText = 0; /* make sure we don't process garbage */
855 pszTempBuf = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR) *
857 if (!pszTempBuf) return FALSE;
859 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR)
860 pszTempBuf, cchTempBufMax, NULL, NULL);
862 savCchTextMax = pdi->item.cchTextMax;
863 savPszText = pdi->item.pszText;
864 pdi->item.pszText = pszTempBuf;
865 pdi->item.cchTextMax = cchTempBufMax;
868 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat !=
871 bResult = notify_hdr(infoPtr, get_ansi_notification(notificationCode),
876 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
877 savPszText, savCchTextMax);
878 pdi->item.pszText = savPszText; /* restores our buffer */
879 pdi->item.cchTextMax = savCchTextMax;
880 HeapFree(GetProcessHeap(), 0, pszTempBuf);
885 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
886 const RECT *rcBounds, const LVITEMW *lplvItem)
888 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
889 lpnmlvcd->nmcd.hdc = hdc;
890 lpnmlvcd->nmcd.rc = *rcBounds;
891 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
892 lpnmlvcd->clrText = infoPtr->clrText;
893 if (!lplvItem) return;
894 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
895 lpnmlvcd->iSubItem = lplvItem->iSubItem;
896 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
897 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
898 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
899 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
902 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
904 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
907 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
908 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
909 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
910 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
911 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
912 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
916 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
918 /* apprently, for selected items, we have to override the returned values */
919 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
923 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
924 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
926 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
928 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
929 lpnmlvcd->clrText = comctl32_color.clrBtnText;
933 /* Set the text attributes */
934 if (lpnmlvcd->clrTextBk != CLR_NONE)
936 SetBkMode(hdc, OPAQUE);
937 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
938 SetBkColor(hdc, infoPtr->clrTextBkDefault);
940 SetBkColor(hdc,lpnmlvcd->clrTextBk);
943 SetBkMode(hdc, TRANSPARENT);
944 SetTextColor(hdc, lpnmlvcd->clrText);
947 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
949 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
952 /******** Item iterator functions **********************************/
954 static RANGES ranges_create(int count);
955 static void ranges_destroy(RANGES ranges);
956 static BOOL ranges_add(RANGES ranges, RANGE range);
957 static BOOL ranges_del(RANGES ranges, RANGE range);
958 static void ranges_dump(RANGES ranges);
960 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
962 RANGE range = { nItem, nItem + 1 };
964 return ranges_add(ranges, range);
967 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
969 RANGE range = { nItem, nItem + 1 };
971 return ranges_del(ranges, range);
975 * ITERATOR DOCUMENTATION
977 * The iterator functions allow for easy, and convenient iteration
978 * over items of iterest in the list. Typically, you create a
979 * iterator, use it, and destroy it, as such:
982 * iterator_xxxitems(&i, ...);
983 * while (iterator_{prev,next}(&i)
985 * //code which uses i.nItem
987 * iterator_destroy(&i);
989 * where xxx is either: framed, or visible.
990 * Note that it is important that the code destroys the iterator
991 * after it's done with it, as the creation of the iterator may
992 * allocate memory, which thus needs to be freed.
994 * You can iterate both forwards, and backwards through the list,
995 * by using iterator_next or iterator_prev respectively.
997 * Lower numbered items are draw on top of higher number items in
998 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
999 * items may overlap). So, to test items, you should use
1001 * which lists the items top to bottom (in Z-order).
1002 * For drawing items, you should use
1004 * which lists the items bottom to top (in Z-order).
1005 * If you keep iterating over the items after the end-of-items
1006 * marker (-1) is returned, the iterator will start from the
1007 * beginning. Typically, you don't need to test for -1,
1008 * because iterator_{next,prev} will return TRUE if more items
1009 * are to be iterated over, or FALSE otherwise.
1011 * Note: the iterator is defined to be bidirectional. That is,
1012 * any number of prev followed by any number of next, or
1013 * five versa, should leave the iterator at the same item:
1014 * prev * n, next * n = next * n, prev * n
1016 * The iterator has a notion of a out-of-order, special item,
1017 * which sits at the start of the list. This is used in
1018 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1019 * which needs to be first, as it may overlap other items.
1021 * The code is a bit messy because we have:
1022 * - a special item to deal with
1023 * - simple range, or composite range
1025 * If you find bugs, or want to add features, please make sure you
1026 * always check/modify *both* iterator_prev, and iterator_next.
1030 * This function iterates through the items in increasing order,
1031 * but prefixed by the special item, then -1. That is:
1032 * special, 1, 2, 3, ..., n, -1.
1033 * Each item is listed only once.
1035 static inline BOOL iterator_next(ITERATOR* i)
1039 i->nItem = i->nSpecial;
1040 if (i->nItem != -1) return TRUE;
1042 if (i->nItem == i->nSpecial)
1044 if (i->ranges) i->index = 0;
1050 if (i->nItem == i->nSpecial) i->nItem++;
1051 if (i->nItem < i->range.upper) return TRUE;
1056 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1057 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1060 else if (i->nItem >= i->range.upper) goto end;
1062 i->nItem = i->range.lower;
1063 if (i->nItem >= 0) goto testitem;
1070 * This function iterates through the items in decreasing order,
1071 * followed by the special item, then -1. That is:
1072 * n, n-1, ..., 3, 2, 1, special, -1.
1073 * Each item is listed only once.
1075 static inline BOOL iterator_prev(ITERATOR* i)
1082 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1085 if (i->nItem == i->nSpecial)
1093 if (i->nItem == i->nSpecial) i->nItem--;
1094 if (i->nItem >= i->range.lower) return TRUE;
1100 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1103 else if (!start && i->nItem < i->range.lower) goto end;
1105 i->nItem = i->range.upper;
1106 if (i->nItem > 0) goto testitem;
1108 return (i->nItem = i->nSpecial) != -1;
1111 static RANGE iterator_range(ITERATOR* i)
1115 if (!i->ranges) return i->range;
1117 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1118 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1123 * Releases resources associated with this ierator.
1125 static inline void iterator_destroy(ITERATOR* i)
1127 ranges_destroy(i->ranges);
1131 * Create an empty iterator.
1133 static inline BOOL iterator_empty(ITERATOR* i)
1135 ZeroMemory(i, sizeof(*i));
1136 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1141 * Create an iterator over a range.
1143 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1151 * Create an iterator over a bunch of ranges.
1152 * Please note that the iterator will take ownership of the ranges,
1153 * and will free them upon destruction.
1155 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1163 * Creates an iterator over the items which intersect lprc.
1165 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1167 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1168 RECT frame = *lprc, rcItem, rcTemp;
1171 /* in case we fail, we want to return an empty iterator */
1172 if (!iterator_empty(i)) return FALSE;
1174 LISTVIEW_GetOrigin(infoPtr, &Origin);
1176 TRACE("(lprc=%s)\n", debugrect(lprc));
1177 OffsetRect(&frame, -Origin.x, -Origin.y);
1179 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1183 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1185 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1186 if (IntersectRect(&rcTemp, &rcItem, lprc))
1187 i->nSpecial = infoPtr->nFocusedItem;
1189 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1190 /* to do better here, we need to have PosX, and PosY sorted */
1191 TRACE("building icon ranges:\n");
1192 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1194 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1195 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1196 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1197 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1198 if (IntersectRect(&rcTemp, &rcItem, &frame))
1199 ranges_additem(i->ranges, nItem);
1203 else if (uView == LVS_REPORT)
1207 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1208 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1210 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1211 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1212 if (range.upper <= range.lower) return TRUE;
1213 if (!iterator_rangeitems(i, range)) return FALSE;
1214 TRACE(" report=%s\n", debugrange(&i->range));
1218 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1219 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1220 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1221 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1222 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1223 INT lower = nFirstCol * nPerCol + nFirstRow;
1227 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1228 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1230 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1232 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1233 TRACE("building list ranges:\n");
1234 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1236 item_range.lower = nCol * nPerCol + nFirstRow;
1237 if(item_range.lower >= infoPtr->nItemCount) break;
1238 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1239 TRACE(" list=%s\n", debugrange(&item_range));
1240 ranges_add(i->ranges, item_range);
1248 * Creates an iterator over the items which intersect the visible region of hdc.
1250 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1252 POINT Origin, Position;
1253 RECT rcItem, rcClip;
1256 rgntype = GetClipBox(hdc, &rcClip);
1257 if (rgntype == NULLREGION) return iterator_empty(i);
1258 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1259 if (rgntype == SIMPLEREGION) return TRUE;
1261 /* first deal with the special item */
1262 if (i->nSpecial != -1)
1264 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1265 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1268 /* if we can't deal with the region, we'll just go with the simple range */
1269 LISTVIEW_GetOrigin(infoPtr, &Origin);
1270 TRACE("building visible range:\n");
1271 if (!i->ranges && i->range.lower < i->range.upper)
1273 if (!(i->ranges = ranges_create(50))) return TRUE;
1274 if (!ranges_add(i->ranges, i->range))
1276 ranges_destroy(i->ranges);
1282 /* now delete the invisible items from the list */
1283 while(iterator_next(i))
1285 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1286 rcItem.left = Position.x + Origin.x;
1287 rcItem.top = Position.y + Origin.y;
1288 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1289 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1290 if (!RectVisible(hdc, &rcItem))
1291 ranges_delitem(i->ranges, i->nItem);
1293 /* the iterator should restart on the next iterator_next */
1299 /******** Misc helper functions ************************************/
1301 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1302 WPARAM wParam, LPARAM lParam, BOOL isW)
1304 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1305 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1308 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1310 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1312 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1313 (uView == LVS_ICON || uView == LVS_SMALLICON);
1316 /******** Internal API functions ************************************/
1318 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1320 static COLUMN_INFO mainItem;
1322 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1323 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1324 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1327 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1329 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1332 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1334 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1337 /* Listview invalidation functions: use _only_ these functions to invalidate */
1339 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1341 return infoPtr->bRedraw;
1344 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1346 if(!is_redrawing(infoPtr)) return;
1347 TRACE(" invalidating rect=%s\n", debugrect(rect));
1348 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1351 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1355 if(!is_redrawing(infoPtr)) return;
1356 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1357 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1360 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1362 POINT Origin, Position;
1365 if(!is_redrawing(infoPtr)) return;
1366 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1367 LISTVIEW_GetOrigin(infoPtr, &Origin);
1368 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1369 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1371 rcBox.bottom = infoPtr->nItemHeight;
1372 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1373 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1376 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1378 LISTVIEW_InvalidateRect(infoPtr, NULL);
1381 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1385 if(!is_redrawing(infoPtr)) return;
1386 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1387 rcCol.top = infoPtr->rcList.top;
1388 rcCol.bottom = infoPtr->rcList.bottom;
1389 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1394 * Retrieves the number of items that can fit vertically in the client area.
1397 * [I] infoPtr : valid pointer to the listview structure
1400 * Number of items per row.
1402 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1404 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1406 return max(nListWidth/infoPtr->nItemWidth, 1);
1411 * Retrieves the number of items that can fit horizontally in the client
1415 * [I] infoPtr : valid pointer to the listview structure
1418 * Number of items per column.
1420 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1422 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1424 return max(nListHeight / infoPtr->nItemHeight, 1);
1428 /*************************************************************************
1429 * LISTVIEW_ProcessLetterKeys
1431 * Processes keyboard messages generated by pressing the letter keys
1433 * What this does is perform a case insensitive search from the
1434 * current position with the following quirks:
1435 * - If two chars or more are pressed in quick succession we search
1436 * for the corresponding string (e.g. 'abc').
1437 * - If there is a delay we wipe away the current search string and
1438 * restart with just that char.
1439 * - If the user keeps pressing the same character, whether slowly or
1440 * fast, so that the search string is entirely composed of this
1441 * character ('aaaaa' for instance), then we search for first item
1442 * that starting with that character.
1443 * - If the user types the above character in quick succession, then
1444 * we must also search for the corresponding string ('aaaaa'), and
1445 * go to that string if there is a match.
1448 * [I] hwnd : handle to the window
1449 * [I] charCode : the character code, the actual character
1450 * [I] keyData : key data
1458 * - The current implementation has a list of characters it will
1459 * accept and it ignores averything else. In particular it will
1460 * ignore accentuated characters which seems to match what
1461 * Windows does. But I'm not sure it makes sense to follow
1463 * - We don't sound a beep when the search fails.
1467 * TREEVIEW_ProcessLetterKeys
1469 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1474 WCHAR buffer[MAX_PATH];
1475 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1477 /* simple parameter checking */
1478 if (!charCode || !keyData) return 0;
1480 /* only allow the valid WM_CHARs through */
1481 if (!isalnum(charCode) &&
1482 charCode != '.' && charCode != '`' && charCode != '!' &&
1483 charCode != '@' && charCode != '#' && charCode != '$' &&
1484 charCode != '%' && charCode != '^' && charCode != '&' &&
1485 charCode != '*' && charCode != '(' && charCode != ')' &&
1486 charCode != '-' && charCode != '_' && charCode != '+' &&
1487 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1488 charCode != '}' && charCode != '[' && charCode != '{' &&
1489 charCode != '/' && charCode != '?' && charCode != '>' &&
1490 charCode != '<' && charCode != ',' && charCode != '~')
1493 /* if there's one item or less, there is no where to go */
1494 if (infoPtr->nItemCount <= 1) return 0;
1496 /* update the search parameters */
1497 infoPtr->lastKeyPressTimestamp = GetTickCount();
1498 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1499 if (infoPtr->nSearchParamLength < MAX_PATH)
1500 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1501 if (infoPtr->charCode != charCode)
1502 infoPtr->charCode = charCode = 0;
1504 infoPtr->charCode=charCode;
1505 infoPtr->szSearchParam[0]=charCode;
1506 infoPtr->nSearchParamLength=1;
1507 /* Redundant with the 1 char string */
1511 /* and search from the current position */
1513 if (infoPtr->nFocusedItem >= 0) {
1514 endidx=infoPtr->nFocusedItem;
1516 /* if looking for single character match,
1517 * then we must always move forward
1519 if (infoPtr->nSearchParamLength == 1)
1522 endidx=infoPtr->nItemCount;
1526 if (idx == infoPtr->nItemCount) {
1527 if (endidx == infoPtr->nItemCount || endidx == 0)
1533 item.mask = LVIF_TEXT;
1536 item.pszText = buffer;
1537 item.cchTextMax = MAX_PATH;
1538 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1540 /* check for a match */
1541 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1544 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1545 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1546 /* This would work but we must keep looking for a longer match */
1550 } while (idx != endidx);
1553 LISTVIEW_KeySelection(infoPtr, nItem);
1558 /*************************************************************************
1559 * LISTVIEW_UpdateHeaderSize [Internal]
1561 * Function to resize the header control
1564 * [I] hwnd : handle to a window
1565 * [I] nNewScrollPos : scroll pos to set
1570 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1575 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1577 GetWindowRect(infoPtr->hwndHeader, &winRect);
1578 point[0].x = winRect.left;
1579 point[0].y = winRect.top;
1580 point[1].x = winRect.right;
1581 point[1].y = winRect.bottom;
1583 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1584 point[0].x = -nNewScrollPos;
1585 point[1].x += nNewScrollPos;
1587 SetWindowPos(infoPtr->hwndHeader,0,
1588 point[0].x,point[0].y,point[1].x,point[1].y,
1589 SWP_NOZORDER | SWP_NOACTIVATE);
1594 * Update the scrollbars. This functions should be called whenever
1595 * the content, size or view changes.
1598 * [I] infoPtr : valid pointer to the listview structure
1603 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1605 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1606 SCROLLINFO horzInfo, vertInfo;
1608 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1610 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1611 horzInfo.cbSize = sizeof(SCROLLINFO);
1612 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1614 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1615 if (uView == LVS_LIST)
1617 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1618 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1620 /* scroll by at least one column per page */
1621 if(horzInfo.nPage < infoPtr->nItemWidth)
1622 horzInfo.nPage = infoPtr->nItemWidth;
1624 horzInfo.nPage /= infoPtr->nItemWidth;
1626 else if (uView == LVS_REPORT)
1628 horzInfo.nMax = infoPtr->nItemWidth;
1630 else /* LVS_ICON, or LVS_SMALLICON */
1634 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1637 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1638 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1639 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1640 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1642 /* Setting the horizontal scroll can change the listview size
1643 * (and potentially everything else) so we need to recompute
1644 * everything again for the vertical scroll
1647 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1648 vertInfo.cbSize = sizeof(SCROLLINFO);
1649 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1651 if (uView == LVS_REPORT)
1653 vertInfo.nMax = infoPtr->nItemCount;
1655 /* scroll by at least one page */
1656 if(vertInfo.nPage < infoPtr->nItemHeight)
1657 vertInfo.nPage = infoPtr->nItemHeight;
1659 vertInfo.nPage /= infoPtr->nItemHeight;
1661 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1665 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1668 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1669 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1670 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1671 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1673 /* Update the Header Control */
1674 if (uView == LVS_REPORT)
1676 horzInfo.fMask = SIF_POS;
1677 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1678 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1685 * Shows/hides the focus rectangle.
1688 * [I] infoPtr : valid pointer to the listview structure
1689 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1694 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1696 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1699 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1701 if (infoPtr->nFocusedItem < 0) return;
1703 /* we need some gymnastics in ICON mode to handle large items */
1704 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1708 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1709 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1711 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1716 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1718 /* for some reason, owner draw should work only in report mode */
1719 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1724 item.iItem = infoPtr->nFocusedItem;
1726 item.mask = LVIF_PARAM;
1727 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1729 ZeroMemory(&dis, sizeof(dis));
1730 dis.CtlType = ODT_LISTVIEW;
1731 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1732 dis.itemID = item.iItem;
1733 dis.itemAction = ODA_FOCUS;
1734 if (fShow) dis.itemState |= ODS_FOCUS;
1735 dis.hwndItem = infoPtr->hwndSelf;
1737 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1738 dis.itemData = item.lParam;
1740 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1744 DrawFocusRect(hdc, &infoPtr->rcFocus);
1747 ReleaseDC(infoPtr->hwndSelf, hdc);
1751 * Invalidates all visible selected items.
1753 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1757 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1758 while(iterator_next(&i))
1760 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1761 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1763 iterator_destroy(&i);
1768 * DESCRIPTION: [INTERNAL]
1769 * Computes an item's (left,top) corner, relative to rcView.
1770 * That is, the position has NOT been made relative to the Origin.
1771 * This is deliberate, to avoid computing the Origin over, and
1772 * over again, when this function is call in a loop. Instead,
1773 * one ca factor the computation of the Origin before the loop,
1774 * and offset the value retured by this function, on every iteration.
1777 * [I] infoPtr : valid pointer to the listview structure
1778 * [I] nItem : item number
1779 * [O] lpptOrig : item top, left corner
1784 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1786 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1788 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1790 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1792 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1793 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1795 else if (uView == LVS_LIST)
1797 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1798 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1799 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1801 else /* LVS_REPORT */
1803 lpptPosition->x = 0;
1804 lpptPosition->y = nItem * infoPtr->nItemHeight;
1809 * DESCRIPTION: [INTERNAL]
1810 * Compute the rectangles of an item. This is to localize all
1811 * the computations in one place. If you are not interested in some
1812 * of these values, simply pass in a NULL -- the fucntion is smart
1813 * enough to compute only what's necessary. The function computes
1814 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1815 * one, the BOX rectangle. This rectangle is very cheap to compute,
1816 * and is guaranteed to contain all the other rectangles. Computing
1817 * the ICON rect is also cheap, but all the others are potentaily
1818 * expensive. This gives an easy and effective optimization when
1819 * searching (like point inclusion, or rectangle intersection):
1820 * first test against the BOX, and if TRUE, test agains the desired
1822 * If the function does not have all the necessary information
1823 * to computed the requested rectangles, will crash with a
1824 * failed assertion. This is done so we catch all programming
1825 * errors, given that the function is called only from our code.
1827 * We have the following 'special' meanings for a few fields:
1828 * * If LVIS_FOCUSED is set, we assume the item has the focus
1829 * This is important in ICON mode, where it might get a larger
1830 * then usual rectange
1832 * Please note that subitem support works only in REPORT mode.
1835 * [I] infoPtr : valid pointer to the listview structure
1836 * [I] lpLVItem : item to compute the measures for
1837 * [O] lprcBox : ptr to Box rectangle
1838 * The internal LVIR_BOX rectangle
1839 * [0] lprcState : ptr to State icon rectangle
1840 * The internal LVIR_STATE rectangle
1841 * [O] lprcIcon : ptr to Icon rectangle
1842 * Same as LVM_GETITEMRECT with LVIR_ICON
1843 * [O] lprcLabel : ptr to Label rectangle
1844 * Same as LVM_GETITEMRECT with LVIR_LABEL
1849 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1850 LPRECT lprcBox, LPRECT lprcState,
1851 LPRECT lprcIcon, LPRECT lprcLabel)
1853 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1854 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1855 RECT Box, State, Icon, Label;
1856 COLUMN_INFO *lpColumnInfo = NULL;
1858 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1860 /* Be smart and try to figure out the minimum we have to do */
1861 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1862 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1864 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1865 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1867 if (lprcLabel) doLabel = TRUE;
1868 if (doLabel || lprcIcon) doIcon = TRUE;
1869 if (doIcon || lprcState) doState = TRUE;
1871 /************************************************************/
1872 /* compute the box rectangle (it should be cheap to do) */
1873 /************************************************************/
1874 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1875 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1877 if (lpLVItem->iSubItem)
1879 Box = lpColumnInfo->rcHeader;
1884 Box.right = infoPtr->nItemWidth;
1887 Box.bottom = infoPtr->nItemHeight;
1889 /************************************************************/
1890 /* compute STATEICON bounding box */
1891 /************************************************************/
1894 if (uView == LVS_ICON)
1896 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1897 if (infoPtr->himlNormal)
1898 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1899 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1903 /* we need the ident in report mode, if we don't have it, we fail */
1904 State.left = Box.left;
1905 if (uView == LVS_REPORT)
1907 if (lpLVItem->iSubItem == 0)
1909 State.left += REPORT_MARGINX;
1910 assert(lpLVItem->mask & LVIF_INDENT);
1911 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1914 State.top = Box.top;
1916 State.right = State.left;
1917 State.bottom = State.top;
1918 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1920 State.right += infoPtr->iconStateSize.cx;
1921 State.bottom += infoPtr->iconStateSize.cy;
1923 if (lprcState) *lprcState = State;
1924 TRACE(" - state=%s\n", debugrect(&State));
1927 /************************************************************/
1928 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1929 /************************************************************/
1932 if (uView == LVS_ICON)
1934 Icon.left = Box.left;
1935 if (infoPtr->himlNormal)
1936 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1937 Icon.top = Box.top + ICON_TOP_PADDING;
1938 Icon.right = Icon.left;
1939 Icon.bottom = Icon.top;
1940 if (infoPtr->himlNormal)
1942 Icon.right += infoPtr->iconSize.cx;
1943 Icon.bottom += infoPtr->iconSize.cy;
1946 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1948 Icon.left = State.right;
1950 Icon.right = Icon.left;
1951 if (infoPtr->himlSmall &&
1952 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1953 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1954 Icon.right += infoPtr->iconSize.cx;
1955 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1957 if(lprcIcon) *lprcIcon = Icon;
1958 TRACE(" - icon=%s\n", debugrect(&Icon));
1961 /************************************************************/
1962 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1963 /************************************************************/
1966 SIZE labelSize = { 0, 0 };
1968 /* calculate how far to the right can the label strech */
1969 Label.right = Box.right;
1970 if (uView == LVS_REPORT)
1972 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
1975 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1977 labelSize.cx = infoPtr->nItemWidth;
1978 labelSize.cy = infoPtr->nItemHeight;
1982 /* we need the text in non owner draw mode */
1983 assert(lpLVItem->mask & LVIF_TEXT);
1984 if (is_textT(lpLVItem->pszText, TRUE))
1986 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1987 HDC hdc = GetDC(infoPtr->hwndSelf);
1988 HFONT hOldFont = SelectObject(hdc, hFont);
1992 /* compute rough rectangle where the label will go */
1993 SetRectEmpty(&rcText);
1994 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
1995 rcText.bottom = infoPtr->nItemHeight;
1996 if (uView == LVS_ICON)
1997 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1999 /* now figure out the flags */
2000 if (uView == LVS_ICON)
2001 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2003 uFormat = LV_SL_DT_FLAGS;
2005 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2007 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2008 labelSize.cy = rcText.bottom - rcText.top;
2010 SelectObject(hdc, hOldFont);
2011 ReleaseDC(infoPtr->hwndSelf, hdc);
2015 if (uView == LVS_ICON)
2017 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2018 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2019 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2020 Label.right = Label.left + labelSize.cx;
2021 Label.bottom = Label.top + infoPtr->nItemHeight;
2022 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2024 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2025 labelSize.cy /= infoPtr->ntmHeight;
2026 labelSize.cy = max(labelSize.cy, 1);
2027 labelSize.cy *= infoPtr->ntmHeight;
2029 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2031 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2033 Label.left = Icon.right;
2034 Label.top = Box.top;
2035 Label.right = min(Label.left + labelSize.cx, Label.right);
2036 Label.bottom = Label.top + infoPtr->nItemHeight;
2039 if (lprcLabel) *lprcLabel = Label;
2040 TRACE(" - label=%s\n", debugrect(&Label));
2043 /* Fix the Box if necessary */
2046 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2047 else *lprcBox = Box;
2049 TRACE(" - box=%s\n", debugrect(&Box));
2053 * DESCRIPTION: [INTERNAL]
2056 * [I] infoPtr : valid pointer to the listview structure
2057 * [I] nItem : item number
2058 * [O] lprcBox : ptr to Box rectangle
2063 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2065 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2066 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2067 POINT Position, Origin;
2070 LISTVIEW_GetOrigin(infoPtr, &Origin);
2071 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2073 /* Be smart and try to figure out the minimum we have to do */
2075 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2076 lvItem.mask |= LVIF_TEXT;
2077 lvItem.iItem = nItem;
2078 lvItem.iSubItem = 0;
2079 lvItem.pszText = szDispText;
2080 lvItem.cchTextMax = DISP_TEXT_SIZE;
2081 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2082 if (uView == LVS_ICON)
2084 lvItem.mask |= LVIF_STATE;
2085 lvItem.stateMask = LVIS_FOCUSED;
2086 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2088 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2090 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2096 * Returns the current icon position, and advances it along the top.
2097 * The returned position is not offset by Origin.
2100 * [I] infoPtr : valid pointer to the listview structure
2101 * [O] lpPos : will get the current icon position
2106 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2108 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2110 *lpPos = infoPtr->currIconPos;
2112 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2113 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2115 infoPtr->currIconPos.x = 0;
2116 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2122 * Returns the current icon position, and advances it down the left edge.
2123 * The returned position is not offset by Origin.
2126 * [I] infoPtr : valid pointer to the listview structure
2127 * [O] lpPos : will get the current icon position
2132 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2134 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2136 *lpPos = infoPtr->currIconPos;
2138 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2139 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2141 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2142 infoPtr->currIconPos.y = 0;
2148 * Moves an icon to the specified position.
2149 * It takes care of invalidating the item, etc.
2152 * [I] infoPtr : valid pointer to the listview structure
2153 * [I] nItem : the item to move
2154 * [I] lpPos : the new icon position
2155 * [I] isNew : flags the item as being new
2161 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2167 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2168 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2170 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2171 LISTVIEW_InvalidateItem(infoPtr, nItem);
2174 /* Allocating a POINTER for every item is too resource intensive,
2175 * so we'll keep the (x,y) in different arrays */
2176 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)lppt->x)) return FALSE;
2177 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)lppt->y)) return FALSE;
2179 LISTVIEW_InvalidateItem(infoPtr, nItem);
2186 * Arranges listview items in icon display mode.
2189 * [I] infoPtr : valid pointer to the listview structure
2190 * [I] nAlignCode : alignment code
2196 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2198 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2199 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2203 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2205 TRACE("nAlignCode=%d\n", nAlignCode);
2207 if (nAlignCode == LVA_DEFAULT)
2209 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2210 else nAlignCode = LVA_ALIGNTOP;
2215 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2216 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2217 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2218 default: return FALSE;
2221 infoPtr->bAutoarrange = TRUE;
2222 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2223 for (i = 0; i < infoPtr->nItemCount; i++)
2225 next_pos(infoPtr, &pos);
2226 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2234 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2237 * [I] infoPtr : valid pointer to the listview structure
2238 * [O] lprcView : bounding rectangle
2244 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2248 SetRectEmpty(lprcView);
2250 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2254 for (i = 0; i < infoPtr->nItemCount; i++)
2256 x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, i);
2257 y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, i);
2258 lprcView->right = max(lprcView->right, x);
2259 lprcView->bottom = max(lprcView->bottom, y);
2261 if (infoPtr->nItemCount > 0)
2263 lprcView->right += infoPtr->nItemWidth;
2264 lprcView->bottom += infoPtr->nItemHeight;
2269 y = LISTVIEW_GetCountPerColumn(infoPtr);
2270 x = infoPtr->nItemCount / y;
2271 if (infoPtr->nItemCount % y) x++;
2272 lprcView->right = x * infoPtr->nItemWidth;
2273 lprcView->bottom = y * infoPtr->nItemHeight;
2277 lprcView->right = infoPtr->nItemWidth;
2278 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2285 * Retrieves the bounding rectangle of all the items.
2288 * [I] infoPtr : valid pointer to the listview structure
2289 * [O] lprcView : bounding rectangle
2295 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2299 TRACE("(lprcView=%p)\n", lprcView);
2301 if (!lprcView) return FALSE;
2303 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2304 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2305 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2307 TRACE("lprcView=%s\n", debugrect(lprcView));
2314 * Retrieves the subitem pointer associated with the subitem index.
2317 * [I] hdpaSubItems : DPA handle for a specific item
2318 * [I] nSubItem : index of subitem
2321 * SUCCESS : subitem pointer
2324 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2326 SUBITEM_INFO *lpSubItem;
2329 /* we should binary search here if need be */
2330 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2332 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2333 if (lpSubItem->iSubItem == nSubItem)
2343 * Caclulates the desired item width.
2346 * [I] infoPtr : valid pointer to the listview structure
2349 * The desired item width.
2351 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2353 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2356 TRACE("uView=%d\n", uView);
2358 if (uView == LVS_ICON)
2359 nItemWidth = infoPtr->iconSpacing.cx;
2360 else if (uView == LVS_REPORT)
2364 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2366 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2367 nItemWidth = rcHeader.right;
2370 else /* LVS_SMALLICON, or LVS_LIST */
2374 for (i = 0; i < infoPtr->nItemCount; i++)
2375 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2377 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2378 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2380 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2383 return max(nItemWidth, 1);
2388 * Caclulates the desired item height.
2391 * [I] infoPtr : valid pointer to the listview structure
2394 * The desired item height.
2396 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2398 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2401 TRACE("uView=%d\n", uView);
2403 if (uView == LVS_ICON)
2404 nItemHeight = infoPtr->iconSpacing.cy;
2407 nItemHeight = infoPtr->ntmHeight;
2408 if (infoPtr->himlState)
2409 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2410 if (infoPtr->himlSmall)
2411 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2412 if (infoPtr->himlState || infoPtr->himlSmall)
2413 nItemHeight += HEIGHT_PADDING;
2416 return max(nItemHeight, 1);
2421 * Updates the width, and height of an item.
2424 * [I] infoPtr : valid pointer to the listview structure
2429 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2431 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2432 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2438 * Retrieves and saves important text metrics info for the current
2442 * [I] infoPtr : valid pointer to the listview structure
2445 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2447 HDC hdc = GetDC(infoPtr->hwndSelf);
2448 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2449 HFONT hOldFont = SelectObject(hdc, hFont);
2452 if (GetTextMetricsW(hdc, &tm))
2454 infoPtr->ntmHeight = tm.tmHeight;
2455 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
2457 SelectObject(hdc, hOldFont);
2458 ReleaseDC(infoPtr->hwndSelf, hdc);
2460 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2465 * A compare function for ranges
2468 * [I] range1 : pointer to range 1;
2469 * [I] range2 : pointer to range 2;
2473 * > 0 : if range 1 > range 2
2474 * < 0 : if range 2 > range 1
2475 * = 0 : if range intersects range 2
2477 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2481 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2483 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2488 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2494 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2496 #define ranges_check(ranges, desc) do { } while(0)
2499 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2504 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2506 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2507 ranges_dump(ranges);
2508 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2509 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2510 assert (prev->lower >= 0 && prev->lower < prev->upper);
2511 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2513 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2514 assert (prev->upper <= curr->lower);
2515 assert (curr->lower < curr->upper);
2518 TRACE("--- Done checking---\n");
2521 static RANGES ranges_create(int count)
2523 RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2524 if (!ranges) return NULL;
2525 ranges->hdpa = DPA_Create(count);
2526 if (ranges->hdpa) return ranges;
2531 static void ranges_clear(RANGES ranges)
2535 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2536 Free(DPA_GetPtr(ranges->hdpa, i));
2537 DPA_DeleteAllPtrs(ranges->hdpa);
2541 static void ranges_destroy(RANGES ranges)
2543 if (!ranges) return;
2544 ranges_clear(ranges);
2545 DPA_Destroy(ranges->hdpa);
2549 static RANGES ranges_clone(RANGES ranges)
2554 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2556 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2558 RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2559 if (!newrng) goto fail;
2560 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2561 DPA_SetPtr(clone->hdpa, i, newrng);
2566 TRACE ("clone failed\n");
2567 ranges_destroy(clone);
2571 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2575 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2576 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2581 static void ranges_dump(RANGES ranges)
2585 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2586 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2589 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2591 RANGE srchrng = { nItem, nItem + 1 };
2593 TRACE("(nItem=%d)\n", nItem);
2594 ranges_check(ranges, "before contain");
2595 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2598 static INT ranges_itemcount(RANGES ranges)
2602 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2604 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2605 count += sel->upper - sel->lower;
2611 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2613 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2616 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2617 if (index == -1) return TRUE;
2619 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2621 chkrng = DPA_GetPtr(ranges->hdpa, index);
2622 if (chkrng->lower >= nItem)
2623 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2624 if (chkrng->upper > nItem)
2625 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2630 static BOOL ranges_add(RANGES ranges, RANGE range)
2635 TRACE("(%s)\n", debugrange(&range));
2636 ranges_check(ranges, "before add");
2638 /* try find overlapping regions first */
2639 srchrgn.lower = range.lower - 1;
2640 srchrgn.upper = range.upper + 1;
2641 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2647 TRACE("Adding new range\n");
2649 /* create the brand new range to insert */
2650 newrgn = (RANGE *)Alloc(sizeof(RANGE));
2651 if(!newrgn) goto fail;
2654 /* figure out where to insert it */
2655 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2656 TRACE("index=%d\n", index);
2657 if (index == -1) index = 0;
2659 /* and get it over with */
2660 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2668 RANGE *chkrgn, *mrgrgn;
2669 INT fromindex, mergeindex;
2671 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2672 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2674 chkrgn->lower = min(range.lower, chkrgn->lower);
2675 chkrgn->upper = max(range.upper, chkrgn->upper);
2677 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2679 /* merge now common anges */
2681 srchrgn.lower = chkrgn->lower - 1;
2682 srchrgn.upper = chkrgn->upper + 1;
2686 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2687 if (mergeindex == -1) break;
2688 if (mergeindex == index)
2690 fromindex = index + 1;
2694 TRACE("Merge with index %i\n", mergeindex);
2696 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2697 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2698 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2700 DPA_DeletePtr(ranges->hdpa, mergeindex);
2701 if (mergeindex < index) index --;
2705 ranges_check(ranges, "after add");
2709 ranges_check(ranges, "failed add");
2713 static BOOL ranges_del(RANGES ranges, RANGE range)
2718 TRACE("(%s)\n", debugrange(&range));
2719 ranges_check(ranges, "before del");
2721 /* we don't use DPAS_SORTED here, since we need *
2722 * to find the first overlapping range */
2723 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2726 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2728 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2730 /* case 1: Same range */
2731 if ( (chkrgn->upper == range.upper) &&
2732 (chkrgn->lower == range.lower) )
2734 DPA_DeletePtr(ranges->hdpa, index);
2737 /* case 2: engulf */
2738 else if ( (chkrgn->upper <= range.upper) &&
2739 (chkrgn->lower >= range.lower) )
2741 DPA_DeletePtr(ranges->hdpa, index);
2743 /* case 3: overlap upper */
2744 else if ( (chkrgn->upper <= range.upper) &&
2745 (chkrgn->lower < range.lower) )
2747 chkrgn->upper = range.lower;
2749 /* case 4: overlap lower */
2750 else if ( (chkrgn->upper > range.upper) &&
2751 (chkrgn->lower >= range.lower) )
2753 chkrgn->lower = range.upper;
2756 /* case 5: fully internal */
2759 RANGE tmprgn = *chkrgn, *newrgn;
2761 if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2762 newrgn->lower = chkrgn->lower;
2763 newrgn->upper = range.lower;
2764 chkrgn->lower = range.upper;
2765 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2774 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2777 ranges_check(ranges, "after del");
2781 ranges_check(ranges, "failed del");
2787 * Removes all selection ranges
2790 * [I] infoPtr : valid pointer to the listview structure
2791 * [I] toSkip : item range to skip removing the selection
2797 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2806 lvItem.stateMask = LVIS_SELECTED;
2808 /* need to clone the DPA because callbacks can change it */
2809 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2810 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2811 while(iterator_next(&i))
2812 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2813 /* note that the iterator destructor will free the cloned range */
2814 iterator_destroy(&i);
2819 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2823 if (!(toSkip = ranges_create(1))) return FALSE;
2824 if (nItem != -1) ranges_additem(toSkip, nItem);
2825 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2826 ranges_destroy(toSkip);
2830 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2832 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2837 * Retrieves the number of items that are marked as selected.
2840 * [I] infoPtr : valid pointer to the listview structure
2843 * Number of items selected.
2845 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2847 INT nSelectedCount = 0;
2849 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2852 for (i = 0; i < infoPtr->nItemCount; i++)
2854 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2859 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2861 TRACE("nSelectedCount=%d\n", nSelectedCount);
2862 return nSelectedCount;
2867 * Manages the item focus.
2870 * [I] infoPtr : valid pointer to the listview structure
2871 * [I] nItem : item index
2874 * TRUE : focused item changed
2875 * FALSE : focused item has NOT changed
2877 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2879 INT oldFocus = infoPtr->nFocusedItem;
2882 if (nItem == infoPtr->nFocusedItem) return FALSE;
2884 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2885 lvItem.stateMask = LVIS_FOCUSED;
2886 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2888 return oldFocus != infoPtr->nFocusedItem;
2891 /* Helper function for LISTVIEW_ShiftIndices *only* */
2892 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2894 if (nShiftItem < nItem) return nShiftItem;
2896 if (nShiftItem > nItem) return nShiftItem + direction;
2898 if (direction > 0) return nShiftItem + direction;
2900 return min(nShiftItem, infoPtr->nItemCount - 1);
2905 * Updates the various indices after an item has been inserted or deleted.
2908 * [I] infoPtr : valid pointer to the listview structure
2909 * [I] nItem : item index
2910 * [I] direction : Direction of shift, +1 or -1.
2915 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2920 /* temporarily disable change notification while shifting items */
2921 bOldChange = infoPtr->bDoChangeNotify;
2922 infoPtr->bDoChangeNotify = FALSE;
2924 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2926 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2928 assert(abs(direction) == 1);
2930 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2932 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2933 if (nNewFocus != infoPtr->nFocusedItem)
2934 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2936 /* But we are not supposed to modify nHotItem! */
2938 infoPtr->bDoChangeNotify = bOldChange;
2944 * Adds a block of selections.
2947 * [I] infoPtr : valid pointer to the listview structure
2948 * [I] nItem : item index
2953 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2955 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2956 INT nLast = max(infoPtr->nSelectionMark, nItem);
2960 if (nFirst == -1) nFirst = nItem;
2962 item.state = LVIS_SELECTED;
2963 item.stateMask = LVIS_SELECTED;
2965 /* FIXME: this is not correct LVS_OWNERDATA
2966 * setting the item states individually will generate
2967 * a LVN_ITEMCHANGED notification for each one. Instead,
2968 * we have to send a LVN_ODSTATECHANGED notification.
2969 * See MSDN documentation for LVN_ITEMCHANGED.
2971 for (i = nFirst; i <= nLast; i++)
2972 LISTVIEW_SetItemState(infoPtr,i,&item);
2978 * Sets a single group selection.
2981 * [I] infoPtr : valid pointer to the listview structure
2982 * [I] nItem : item index
2987 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2989 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2994 if (!(selection = ranges_create(100))) return;
2996 item.state = LVIS_SELECTED;
2997 item.stateMask = LVIS_SELECTED;
2999 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3001 if (infoPtr->nSelectionMark == -1)
3003 infoPtr->nSelectionMark = nItem;
3004 ranges_additem(selection, nItem);
3010 sel.lower = min(infoPtr->nSelectionMark, nItem);
3011 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3012 ranges_add(selection, sel);
3017 RECT rcItem, rcSel, rcSelMark;
3020 rcItem.left = LVIR_BOUNDS;
3021 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3022 rcSelMark.left = LVIR_BOUNDS;
3023 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3024 UnionRect(&rcSel, &rcItem, &rcSelMark);
3025 iterator_frameditems(&i, infoPtr, &rcSel);
3026 while(iterator_next(&i))
3028 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3029 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3031 iterator_destroy(&i);
3034 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3035 iterator_rangesitems(&i, selection);
3036 while(iterator_next(&i))
3037 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3038 /* this will also destroy the selection */
3039 iterator_destroy(&i);
3041 LISTVIEW_SetItemFocus(infoPtr, nItem);
3046 * Sets a single selection.
3049 * [I] infoPtr : valid pointer to the listview structure
3050 * [I] nItem : item index
3055 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3059 TRACE("nItem=%d\n", nItem);
3061 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3063 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3064 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3065 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3067 infoPtr->nSelectionMark = nItem;
3072 * Set selection(s) with keyboard.
3075 * [I] infoPtr : valid pointer to the listview structure
3076 * [I] nItem : item index
3079 * SUCCESS : TRUE (needs to be repainted)
3080 * FAILURE : FALSE (nothing has changed)
3082 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3084 /* FIXME: pass in the state */
3085 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3086 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3087 BOOL bResult = FALSE;
3089 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3091 if (infoPtr->dwStyle & LVS_SINGLESEL)
3094 LISTVIEW_SetSelection(infoPtr, nItem);
3101 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3105 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3110 LISTVIEW_SetSelection(infoPtr, nItem);
3113 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3116 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3123 * Called when the mouse is being actively tracked and has hovered for a specified
3127 * [I] infoPtr : valid pointer to the listview structure
3128 * [I] fwKeys : key indicator
3129 * [I] pts : mouse position
3132 * 0 if the message was processed, non-zero if there was an error
3135 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3136 * over the item for a certain period of time.
3139 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
3141 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3142 /* FIXME: select the item!!! */
3143 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
3150 * Called whenever WM_MOUSEMOVE is received.
3153 * [I] infoPtr : valid pointer to the listview structure
3154 * [I] fwKeys : key indicator
3155 * [I] pts : mouse position
3158 * 0 if the message is processed, non-zero if there was an error
3160 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
3162 TRACKMOUSEEVENT trackinfo;
3164 /* see if we are supposed to be tracking mouse hovering */
3165 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3166 /* fill in the trackinfo struct */
3167 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3168 trackinfo.dwFlags = TME_QUERY;
3169 trackinfo.hwndTrack = infoPtr->hwndSelf;
3170 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3172 /* see if we are already tracking this hwnd */
3173 _TrackMouseEvent(&trackinfo);
3175 if(!(trackinfo.dwFlags & TME_HOVER)) {
3176 trackinfo.dwFlags = TME_HOVER;
3178 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3179 _TrackMouseEvent(&trackinfo);
3188 * Tests wheather the item is assignable to a list with style lStyle
3190 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3192 if ( (lpLVItem->mask & LVIF_TEXT) &&
3193 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3194 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3202 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3205 * [I] infoPtr : valid pointer to the listview structure
3206 * [I] lpLVItem : valid pointer to new item atttributes
3207 * [I] isNew : the item being set is being inserted
3208 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3209 * [O] bChanged : will be set to TRUE if the item really changed
3215 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3217 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3225 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3227 if (lpLVItem->mask == 0) return TRUE;
3229 if (infoPtr->dwStyle & LVS_OWNERDATA)
3231 /* a virtual listview we stores only selection and focus */
3232 if (lpLVItem->mask & ~LVIF_STATE)
3238 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3239 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3243 /* we need to get the lParam and state of the item */
3244 item.iItem = lpLVItem->iItem;
3245 item.iSubItem = lpLVItem->iSubItem;
3246 item.mask = LVIF_STATE | LVIF_PARAM;
3247 item.stateMask = ~0;
3250 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3252 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3253 /* determine what fields will change */
3254 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3255 uChanged |= LVIF_STATE;
3257 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3258 uChanged |= LVIF_IMAGE;
3260 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3261 uChanged |= LVIF_PARAM;
3263 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3264 uChanged |= LVIF_INDENT;
3266 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3267 uChanged |= LVIF_TEXT;
3269 TRACE("uChanged=0x%x\n", uChanged);
3270 if (!uChanged) return TRUE;
3273 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3274 nmlv.iItem = lpLVItem->iItem;
3275 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3276 nmlv.uOldState = item.state;
3277 nmlv.uChanged = uChanged;
3278 nmlv.lParam = item.lParam;
3280 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3281 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3283 if(lpItem && !isNew && infoPtr->bDoChangeNotify &&
3284 notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3287 /* copy information */
3288 if (lpLVItem->mask & LVIF_TEXT)
3289 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3291 if (lpLVItem->mask & LVIF_IMAGE)
3292 lpItem->hdr.iImage = lpLVItem->iImage;
3294 if (lpLVItem->mask & LVIF_PARAM)
3295 lpItem->lParam = lpLVItem->lParam;
3297 if (lpLVItem->mask & LVIF_INDENT)
3298 lpItem->iIndent = lpLVItem->iIndent;
3300 if (uChanged & LVIF_STATE)
3302 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3304 lpItem->state &= ~lpLVItem->stateMask;
3305 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3307 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3309 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3310 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3312 else if (lpLVItem->stateMask & LVIS_SELECTED)
3313 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3315 /* if we are asked to change focus, and we manage it, do it */
3316 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3318 if (lpLVItem->state & LVIS_FOCUSED)
3320 LISTVIEW_SetItemFocus(infoPtr, -1);
3321 infoPtr->nFocusedItem = lpLVItem->iItem;
3322 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3324 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3325 infoPtr->nFocusedItem = -1;
3329 /* if we're inserting the item, we're done */
3330 if (isNew) return TRUE;
3332 /* send LVN_ITEMCHANGED notification */
3333 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3334 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3341 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3344 * [I] infoPtr : valid pointer to the listview structure
3345 * [I] lpLVItem : valid pointer to new subitem atttributes
3346 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3347 * [O] bChanged : will be set to TRUE if the item really changed
3353 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3356 SUBITEM_INFO *lpSubItem;
3358 /* we do not support subitems for virtual listviews */
3359 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3361 /* set subitem only if column is present */
3362 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3364 /* First do some sanity checks */
3365 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3366 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3368 /* get the subitem structure, and create it if not there */
3369 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3370 assert (hdpaSubItems);
3372 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3375 SUBITEM_INFO *tmpSubItem;
3378 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3379 if (!lpSubItem) return FALSE;
3380 /* we could binary search here, if need be...*/
3381 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3383 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3384 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3386 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3391 lpSubItem->iSubItem = lpLVItem->iSubItem;
3392 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3396 if (lpLVItem->mask & LVIF_IMAGE)
3397 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3399 lpSubItem->hdr.iImage = lpLVItem->iImage;
3403 if (lpLVItem->mask & LVIF_TEXT)
3404 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3406 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3415 * Sets item attributes.
3418 * [I] infoPtr : valid pointer to the listview structure
3419 * [I] lpLVItem : new item atttributes
3420 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3426 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3428 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3429 LPWSTR pszText = NULL;
3430 BOOL bResult, bChanged = FALSE;
3432 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3434 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3437 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3438 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3440 pszText = lpLVItem->pszText;
3441 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3444 /* actually set the fields */
3445 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3447 if (lpLVItem->iSubItem)
3448 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3450 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3452 /* redraw item, if necessary */
3453 if (bChanged && !infoPtr->bIsDrawing)
3455 /* this little optimization eliminates some nasty flicker */
3456 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3457 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3458 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3460 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3465 textfreeT(lpLVItem->pszText, isW);
3466 ((LVITEMW *)lpLVItem)->pszText = pszText;
3474 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3477 * [I] infoPtr : valid pointer to the listview structure
3482 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3484 LONG lStyle = infoPtr->dwStyle;
3485 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3487 SCROLLINFO scrollInfo;
3489 scrollInfo.cbSize = sizeof(SCROLLINFO);
3490 scrollInfo.fMask = SIF_POS;
3492 if (uView == LVS_LIST)
3494 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3495 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3497 else if (uView == LVS_REPORT)
3499 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3500 nItem = scrollInfo.nPos;
3504 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3505 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3508 TRACE("nItem=%d\n", nItem);
3516 * Erases the background of the given rectangle
3519 * [I] infoPtr : valid pointer to the listview structure
3520 * [I] hdc : device context handle
3521 * [I] lprcBox : clipping rectangle
3527 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3529 if (!infoPtr->hBkBrush) return FALSE;
3531 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3533 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3541 * [I] infoPtr : valid pointer to the listview structure
3542 * [I] hdc : device context handle
3543 * [I] nItem : item index
3544 * [I] nSubItem : subitem index
3545 * [I] pos : item position in client coordinates
3546 * [I] cdmode : custom draw mode
3552 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3554 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3555 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3556 WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3557 DWORD cdsubitemmode = CDRF_DODEFAULT;
3558 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3559 NMLVCUSTOMDRAW nmlvcd;
3563 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3565 /* get information needed for drawing the item */
3566 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3567 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3568 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3569 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3570 lvItem.iItem = nItem;
3571 lvItem.iSubItem = nSubItem;
3574 lvItem.cchTextMax = DISP_TEXT_SIZE;
3575 lvItem.pszText = szDispText;
3576 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3577 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3578 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3579 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3580 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3582 /* now check if we need to update the focus rectangle */
3583 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3585 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3586 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3587 OffsetRect(&rcBox, pos.x, pos.y);
3588 OffsetRect(&rcState, pos.x, pos.y);
3589 OffsetRect(&rcIcon, pos.x, pos.y);
3590 OffsetRect(&rcLabel, pos.x, pos.y);
3591 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3592 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3594 /* fill in the custom draw structure */
3595 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3597 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3598 if (cdmode & CDRF_NOTIFYITEMDRAW)
3599 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3600 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3601 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3602 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3603 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3605 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3606 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3608 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3609 prepaint_setup(infoPtr, hdc, &nmlvcd);
3611 /* in full row select, subitems, will just use main item's colors */
3612 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3613 nmlvcd.clrTextBk = CLR_NONE;
3616 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3618 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3621 TRACE("uStateImage=%d\n", uStateImage);
3622 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3627 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3628 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3630 TRACE("iImage=%d\n", lvItem.iImage);
3631 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3632 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3635 /* Don't bother painting item being edited */
3636 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3638 /* draw the selection background, if we're drawing the main item */
3642 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3643 rcSelect.right = rcBox.right;
3645 if (nmlvcd.clrTextBk != CLR_NONE)
3646 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3647 if(lprcFocus) *lprcFocus = rcSelect;
3650 /* figure out the text drawing flags */
3651 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3652 if (uView == LVS_ICON)
3653 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3656 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3658 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3659 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3660 default: uFormat |= DT_LEFT;
3663 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3665 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3666 else rcLabel.left += LABEL_HOR_PADDING;
3668 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3669 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3672 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3673 notify_postpaint(infoPtr, &nmlvcd);
3679 * Draws listview items when in owner draw mode.
3682 * [I] infoPtr : valid pointer to the listview structure
3683 * [I] hdc : device context handle
3688 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3690 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3691 DWORD cditemmode = CDRF_DODEFAULT;
3692 NMLVCUSTOMDRAW nmlvcd;
3693 POINT Origin, Position;
3699 ZeroMemory(&dis, sizeof(dis));
3701 /* Get scroll info once before loop */
3702 LISTVIEW_GetOrigin(infoPtr, &Origin);
3704 /* iterate through the invalidated rows */
3705 while(iterator_next(i))
3707 item.iItem = i->nItem;
3709 item.mask = LVIF_PARAM | LVIF_STATE;
3710 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3711 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3713 dis.CtlType = ODT_LISTVIEW;
3715 dis.itemID = item.iItem;
3716 dis.itemAction = ODA_DRAWENTIRE;
3718 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3719 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3720 dis.hwndItem = infoPtr->hwndSelf;
3722 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3723 dis.rcItem.left = Position.x + Origin.x;
3724 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3725 dis.rcItem.top = Position.y + Origin.y;
3726 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3727 dis.itemData = item.lParam;
3729 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3732 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3733 * structure for the rest. of the paint cycle
3735 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3736 if (cdmode & CDRF_NOTIFYITEMDRAW)
3737 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3739 if (!(cditemmode & CDRF_SKIPDEFAULT))
3741 prepaint_setup (infoPtr, hdc, &nmlvcd);
3742 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3745 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3746 notify_postpaint(infoPtr, &nmlvcd);
3752 * Draws listview items when in report display mode.
3755 * [I] infoPtr : valid pointer to the listview structure
3756 * [I] hdc : device context handle
3757 * [I] cdmode : custom draw mode
3762 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3765 RECT rcClip, rcItem;
3766 POINT Origin, Position;
3772 /* figure out what to draw */
3773 rgntype = GetClipBox(hdc, &rcClip);
3774 if (rgntype == NULLREGION) return;
3776 /* Get scroll info once before loop */
3777 LISTVIEW_GetOrigin(infoPtr, &Origin);
3779 /* narrow down the columns we need to paint */
3780 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3782 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3783 if (rcItem.right + Origin.x >= rcClip.left) break;
3785 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3787 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3788 if (rcItem.left + Origin.x < rcClip.right) break;
3790 iterator_rangeitems(&j, colRange);
3792 /* in full row select, we _have_ to draw the main item */
3793 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3796 /* iterate through the invalidated rows */
3797 while(iterator_next(i))
3799 /* iterate through the invalidated columns */
3800 while(iterator_next(&j))
3802 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3803 Position.x += Origin.x;
3804 Position.y += Origin.y;
3806 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3808 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3810 rcItem.bottom = infoPtr->nItemHeight;
3811 OffsetRect(&rcItem, Position.x, Position.y);
3812 if (!RectVisible(hdc, &rcItem)) continue;
3815 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3818 iterator_destroy(&j);
3823 * Draws listview items when in list display mode.
3826 * [I] infoPtr : valid pointer to the listview structure
3827 * [I] hdc : device context handle
3828 * [I] cdmode : custom draw mode
3833 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3835 POINT Origin, Position;
3837 /* Get scroll info once before loop */
3838 LISTVIEW_GetOrigin(infoPtr, &Origin);
3840 while(iterator_prev(i))
3842 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3843 Position.x += Origin.x;
3844 Position.y += Origin.y;
3846 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3853 * Draws listview items.
3856 * [I] infoPtr : valid pointer to the listview structure
3857 * [I] hdc : device context handle
3862 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3864 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3865 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3866 NMLVCUSTOMDRAW nmlvcd;
3873 LISTVIEW_DUMP(infoPtr);
3875 infoPtr->bIsDrawing = TRUE;
3877 /* save dc values we're gonna trash while drawing */
3878 hOldFont = SelectObject(hdc, infoPtr->hFont);
3879 oldBkMode = GetBkMode(hdc);
3880 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3881 oldTextColor = GetTextColor(hdc);
3883 oldClrTextBk = infoPtr->clrTextBk;
3884 oldClrText = infoPtr->clrText;
3886 infoPtr->cditemmode = CDRF_DODEFAULT;
3888 GetClientRect(infoPtr->hwndSelf, &rcClient);
3889 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3890 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3891 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3892 prepaint_setup(infoPtr, hdc, &nmlvcd);
3894 /* Use these colors to draw the items */
3895 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3896 infoPtr->clrText = nmlvcd.clrText;
3898 /* nothing to draw */
3899 if(infoPtr->nItemCount == 0) goto enddraw;
3901 /* figure out what we need to draw */
3902 iterator_visibleitems(&i, infoPtr, hdc);
3904 /* send cache hint notification */
3905 if (infoPtr->dwStyle & LVS_OWNERDATA)
3907 RANGE range = iterator_range(&i);
3910 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3911 nmlv.iFrom = range.lower;
3912 nmlv.iTo = range.upper - 1;
3913 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3916 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3917 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3920 if (uView == LVS_REPORT)
3921 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3922 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3923 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3925 /* if we have a focus rect, draw it */
3926 if (infoPtr->bFocus)
3927 DrawFocusRect(hdc, &infoPtr->rcFocus);
3929 iterator_destroy(&i);
3932 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3933 notify_postpaint(infoPtr, &nmlvcd);
3935 infoPtr->clrTextBk = oldClrTextBk;
3936 infoPtr->clrText = oldClrText;
3938 SelectObject(hdc, hOldFont);
3939 SetBkMode(hdc, oldBkMode);
3940 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3941 SetTextColor(hdc, oldTextColor);
3942 infoPtr->bIsDrawing = FALSE;
3948 * Calculates the approximate width and height of a given number of items.
3951 * [I] infoPtr : valid pointer to the listview structure
3952 * [I] nItemCount : number of items
3953 * [I] wWidth : width
3954 * [I] wHeight : height
3957 * Returns a DWORD. The width in the low word and the height in high word.
3959 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3960 WORD wWidth, WORD wHeight)
3962 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3963 INT nItemCountPerColumn = 1;
3964 INT nColumnCount = 0;
3965 DWORD dwViewRect = 0;
3967 if (nItemCount == -1)
3968 nItemCount = infoPtr->nItemCount;
3970 if (uView == LVS_LIST)
3972 if (wHeight == 0xFFFF)
3974 /* use current height */
3975 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3978 if (wHeight < infoPtr->nItemHeight)
3979 wHeight = infoPtr->nItemHeight;
3983 if (infoPtr->nItemHeight > 0)
3985 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3986 if (nItemCountPerColumn == 0)
3987 nItemCountPerColumn = 1;
3989 if (nItemCount % nItemCountPerColumn != 0)
3990 nColumnCount = nItemCount / nItemCountPerColumn;
3992 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3996 /* Microsoft padding magic */
3997 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3998 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4000 dwViewRect = MAKELONG(wWidth, wHeight);
4002 else if (uView == LVS_REPORT)
4006 if (infoPtr->nItemCount > 0)
4008 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4009 wWidth = rcBox.right - rcBox.left;
4010 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4014 /* use current height and width */
4015 if (wHeight == 0xffff)
4016 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4017 if (wWidth == 0xffff)
4018 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4021 dwViewRect = MAKELONG(wWidth, wHeight);
4023 else if (uView == LVS_SMALLICON)
4024 FIXME("uView == LVS_SMALLICON: not implemented\n");
4025 else if (uView == LVS_ICON)
4026 FIXME("uView == LVS_ICON: not implemented\n");
4034 * Create a drag image list for the specified item.
4037 * [I] infoPtr : valid pointer to the listview structure
4038 * [I] iItem : index of item
4039 * [O] lppt : Upperr-left corner of the image
4042 * Returns a handle to the image list if successful, NULL otherwise.
4044 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4050 HBITMAP hbmp, hOldbmp;
4051 HIMAGELIST dragList = 0;
4052 TRACE("iItem=%d Count=%d \n", iItem, infoPtr->nItemCount);
4054 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4057 rcItem.left = LVIR_BOUNDS;
4058 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4061 lppt->x = rcItem.left;
4062 lppt->y = rcItem.top;
4064 size.cx = rcItem.right - rcItem.left;
4065 size.cy = rcItem.bottom - rcItem.top;
4067 hdcOrig = GetDC(infoPtr->hwndSelf);
4068 hdc = CreateCompatibleDC(hdcOrig);
4069 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4070 hOldbmp = SelectObject(hdc, hbmp);
4072 rcItem.left = rcItem.top = 0;
4073 rcItem.right = size.cx;
4074 rcItem.bottom = size.cy;
4075 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4078 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4080 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4081 SelectObject(hdc, hOldbmp);
4082 ImageList_Add(dragList, hbmp, 0);
4085 SelectObject(hdc, hOldbmp);
4089 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4091 TRACE("ret=%p\n", dragList);
4099 * Removes all listview items and subitems.
4102 * [I] infoPtr : valid pointer to the listview structure
4108 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4111 HDPA hdpaSubItems = NULL;
4118 /* we do it directly, to avoid notifications */
4119 ranges_clear(infoPtr->selectionRanges);
4120 infoPtr->nSelectionMark = -1;
4121 infoPtr->nFocusedItem = -1;
4122 SetRectEmpty(&infoPtr->rcFocus);
4123 /* But we are supposed to leave nHotItem as is! */
4126 /* send LVN_DELETEALLITEMS notification */
4127 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4129 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4131 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4133 /* send LVN_DELETEITEM notification, if not supressed */
4134 if (!bSuppress) notify_deleteitem(infoPtr, i);
4135 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4137 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4138 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4140 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4141 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4144 DPA_Destroy(hdpaSubItems);
4145 DPA_DeletePtr(infoPtr->hdpaItems, i);
4147 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4148 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4149 infoPtr->nItemCount --;
4152 LISTVIEW_UpdateScroll(infoPtr);
4154 LISTVIEW_InvalidateList(infoPtr);
4161 * Scrolls, and updates the columns, when a column is changing width.
4164 * [I] infoPtr : valid pointer to the listview structure
4165 * [I] nColumn : column to scroll
4166 * [I] dx : amount of scroll, in pixels
4171 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4173 COLUMN_INFO *lpColumnInfo;
4177 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4178 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4179 rcCol = lpColumnInfo->rcHeader;
4180 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4181 rcCol.left = rcCol.right;
4183 /* ajust the other columns */
4184 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4186 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4187 lpColumnInfo->rcHeader.left += dx;
4188 lpColumnInfo->rcHeader.right += dx;
4191 /* do not update screen if not in report mode */
4192 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4194 /* if we have a focus, must first erase the focus rect */
4195 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4197 /* Need to reset the item width when inserting a new column */
4198 infoPtr->nItemWidth += dx;
4200 LISTVIEW_UpdateScroll(infoPtr);
4202 /* scroll to cover the deleted column, and invalidate for redraw */
4203 rcOld = infoPtr->rcList;
4204 rcOld.left = rcCol.left;
4205 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4207 /* we can restore focus now */
4208 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4213 * Removes a column from the listview control.
4216 * [I] infoPtr : valid pointer to the listview structure
4217 * [I] nColumn : column index
4223 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4227 TRACE("nColumn=%d\n", nColumn);
4229 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4230 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4232 /* While the MSDN specifically says that column zero should not be deleted,
4233 it does in fact work on WinNT, and at least one app depends on it. On
4234 WinNT, deleting column zero deletes the last column of items but the
4235 first header. Since no app will ever depend on that bizarre behavior,
4236 we just delete the last column including the header.
4239 nColumn = DPA_GetPtrCount(infoPtr->hdpaColumns) - 1;
4241 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4243 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4246 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4247 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4249 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4251 SUBITEM_INFO *lpSubItem, *lpDelItem;
4253 INT nItem, nSubItem, i;
4256 return LISTVIEW_DeleteAllItems(infoPtr);
4258 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4260 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4263 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4265 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4266 if (lpSubItem->iSubItem == nColumn)
4269 lpDelItem = lpSubItem;
4271 else if (lpSubItem->iSubItem > nColumn)
4273 lpSubItem->iSubItem--;
4277 /* if we found our subitem, zapp it */
4281 if (is_textW(lpDelItem->hdr.pszText))
4282 Free(lpDelItem->hdr.pszText);
4287 /* free dpa memory */
4288 DPA_DeletePtr(hdpaSubItems, nSubItem);
4293 /* update the other column info */
4294 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4301 * Invalidates the listview after an item's insertion or deletion.
4304 * [I] infoPtr : valid pointer to the listview structure
4305 * [I] nItem : item index
4306 * [I] dir : -1 if deleting, 1 if inserting
4311 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4313 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4314 INT nPerCol, nItemCol, nItemRow;
4318 /* if we don't refresh, what's the point of scrolling? */
4319 if (!is_redrawing(infoPtr)) return;
4321 assert (abs(dir) == 1);
4323 /* arrange icons if autoarrange is on */
4324 if (is_autoarrange(infoPtr))
4326 BOOL arrange = TRUE;
4327 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4328 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4329 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4332 /* scrollbars need updating */
4333 LISTVIEW_UpdateScroll(infoPtr);
4335 /* figure out the item's position */
4336 if (uView == LVS_REPORT)
4337 nPerCol = infoPtr->nItemCount + 1;
4338 else if (uView == LVS_LIST)
4339 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4340 else /* LVS_ICON, or LVS_SMALLICON */
4343 nItemCol = nItem / nPerCol;
4344 nItemRow = nItem % nPerCol;
4345 LISTVIEW_GetOrigin(infoPtr, &Origin);
4347 /* move the items below up a slot */
4348 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4349 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4350 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4351 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4352 OffsetRect(&rcScroll, Origin.x, Origin.y);
4353 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4354 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4356 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4357 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4358 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4361 /* report has only that column, so we're done */
4362 if (uView == LVS_REPORT) return;
4364 /* now for LISTs, we have to deal with the columns to the right */
4365 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4367 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4368 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4369 OffsetRect(&rcScroll, Origin.x, Origin.y);
4370 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4371 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4372 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4377 * Removes an item from the listview control.
4380 * [I] infoPtr : valid pointer to the listview structure
4381 * [I] nItem : item index
4387 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4389 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4392 TRACE("(nItem=%d)\n", nItem);
4394 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4396 /* remove selection, and focus */
4398 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4399 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4401 /* send LVN_DELETEITEM notification. */
4402 notify_deleteitem(infoPtr, nItem);
4404 /* we need to do this here, because we'll be deleting stuff */
4405 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4406 LISTVIEW_InvalidateItem(infoPtr, nItem);
4408 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4414 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4415 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4417 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4418 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4421 DPA_Destroy(hdpaSubItems);
4424 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4426 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4427 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4430 infoPtr->nItemCount--;
4431 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4433 /* now is the invalidation fun */
4434 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4441 * Callback implementation for editlabel control
4444 * [I] infoPtr : valid pointer to the listview structure
4445 * [I] pszText : modified text
4446 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4452 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4454 NMLVDISPINFOW dispInfo;
4456 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4458 ZeroMemory(&dispInfo, sizeof(dispInfo));
4459 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4460 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4461 dispInfo.item.iSubItem = 0;
4462 dispInfo.item.stateMask = ~0;
4463 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4464 /* add the text from the edit in */
4465 dispInfo.item.mask |= LVIF_TEXT;
4466 dispInfo.item.pszText = pszText;
4467 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4469 /* Do we need to update the Item Text */
4470 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4471 if (!pszText) return TRUE;
4473 ZeroMemory(&dispInfo, sizeof(dispInfo));
4474 dispInfo.item.mask = LVIF_TEXT;
4475 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4476 dispInfo.item.iSubItem = 0;
4477 dispInfo.item.pszText = pszText;
4478 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4479 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4484 * Begin in place editing of specified list view item
4487 * [I] infoPtr : valid pointer to the listview structure
4488 * [I] nItem : item index
4489 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4495 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4497 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4498 NMLVDISPINFOW dispInfo;
4501 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4503 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4504 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4506 infoPtr->nEditLabelItem = nItem;
4508 /* Is the EditBox still there, if so remove it */
4509 if(infoPtr->hwndEdit != 0)
4511 SetFocus(infoPtr->hwndSelf);
4512 infoPtr->hwndEdit = 0;
4515 LISTVIEW_SetSelection(infoPtr, nItem);
4516 LISTVIEW_SetItemFocus(infoPtr, nItem);
4517 LISTVIEW_InvalidateItem(infoPtr, nItem);
4519 rect.left = LVIR_LABEL;
4520 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4522 ZeroMemory(&dispInfo, sizeof(dispInfo));
4523 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4524 dispInfo.item.iItem = nItem;
4525 dispInfo.item.iSubItem = 0;
4526 dispInfo.item.stateMask = ~0;
4527 dispInfo.item.pszText = szDispText;
4528 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4529 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4531 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4532 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4533 if (!infoPtr->hwndEdit) return 0;
4535 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4537 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4538 infoPtr->hwndEdit = 0;
4542 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4543 SetFocus(infoPtr->hwndEdit);
4544 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4545 return infoPtr->hwndEdit;
4551 * Ensures the specified item is visible, scrolling into view if necessary.
4554 * [I] infoPtr : valid pointer to the listview structure
4555 * [I] nItem : item index
4556 * [I] bPartial : partially or entirely visible
4562 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4564 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4565 INT nScrollPosHeight = 0;
4566 INT nScrollPosWidth = 0;
4567 INT nHorzAdjust = 0;
4568 INT nVertAdjust = 0;
4571 RECT rcItem, rcTemp;
4573 rcItem.left = LVIR_BOUNDS;
4574 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4576 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4578 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4580 /* scroll left/right, but in LVS_REPORT mode */
4581 if (uView == LVS_LIST)
4582 nScrollPosWidth = infoPtr->nItemWidth;
4583 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4584 nScrollPosWidth = 1;
4586 if (rcItem.left < infoPtr->rcList.left)
4589 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4594 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4598 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4600 /* scroll up/down, but not in LVS_LIST mode */
4601 if (uView == LVS_REPORT)
4602 nScrollPosHeight = infoPtr->nItemHeight;
4603 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4604 nScrollPosHeight = 1;
4606 if (rcItem.top < infoPtr->rcList.top)
4609 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4614 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4618 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4620 if (nScrollPosWidth)
4622 INT diff = nHorzDiff / nScrollPosWidth;
4623 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4624 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4627 if (nScrollPosHeight)
4629 INT diff = nVertDiff / nScrollPosHeight;
4630 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4631 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4639 * Searches for an item with specific characteristics.
4642 * [I] hwnd : window handle
4643 * [I] nStart : base item index
4644 * [I] lpFindInfo : item information to look for
4647 * SUCCESS : index of item
4650 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4651 const LVFINDINFOW *lpFindInfo)
4653 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4654 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4655 BOOL bWrap = FALSE, bNearest = FALSE;
4656 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4657 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4658 POINT Position, Destination;
4661 if (!lpFindInfo || nItem < 0) return -1;
4664 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4666 lvItem.mask |= LVIF_TEXT;
4667 lvItem.pszText = szDispText;
4668 lvItem.cchTextMax = DISP_TEXT_SIZE;
4671 if (lpFindInfo->flags & LVFI_WRAP)
4674 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4675 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4680 LISTVIEW_GetOrigin(infoPtr, &Origin);
4681 Destination.x = lpFindInfo->pt.x - Origin.x;
4682 Destination.y = lpFindInfo->pt.y - Origin.y;
4683 switch(lpFindInfo->vkDirection)
4685 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4686 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4687 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4688 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4689 case VK_HOME: Destination.x = Destination.y = 0; break;
4690 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4691 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4693 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4694 Destination.x = rcArea.right;
4695 Destination.y = rcArea.bottom;
4697 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4702 /* if LVFI_PARAM is specified, all other flags are ignored */
4703 if (lpFindInfo->flags & LVFI_PARAM)
4705 lvItem.mask |= LVIF_PARAM;
4707 lvItem.mask &= ~LVIF_TEXT;
4711 for (; nItem < nLast; nItem++)
4713 lvItem.iItem = nItem;
4714 lvItem.iSubItem = 0;
4715 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4717 if (lvItem.mask & LVIF_PARAM)
4719 if (lpFindInfo->lParam == lvItem.lParam)
4725 if (lvItem.mask & LVIF_TEXT)
4727 if (lpFindInfo->flags & LVFI_PARTIAL)
4729 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4733 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4737 if (!bNearest) return nItem;
4739 /* This is very inefficient. To do a good job here,
4740 * we need a sorted array of (x,y) item positions */
4741 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4743 /* compute the distance^2 to the destination */
4744 xdist = Destination.x - Position.x;
4745 ydist = Destination.y - Position.y;
4746 dist = xdist * xdist + ydist * ydist;
4748 /* remember the distance, and item if it's closer */
4752 nNearestItem = nItem;
4759 nLast = min(nStart + 1, infoPtr->nItemCount);
4764 return nNearestItem;
4769 * Searches for an item with specific characteristics.
4772 * [I] hwnd : window handle
4773 * [I] nStart : base item index
4774 * [I] lpFindInfo : item information to look for
4777 * SUCCESS : index of item
4780 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4781 const LVFINDINFOA *lpFindInfo)
4783 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4787 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4788 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4789 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4790 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4796 * Retrieves the background image of the listview control.
4799 * [I] infoPtr : valid pointer to the listview structure
4800 * [O] lpBkImage : background image attributes
4806 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4808 /* FIXME (listview, "empty stub!\n"); */
4814 * Retrieves column attributes.
4817 * [I] infoPtr : valid pointer to the listview structure
4818 * [I] nColumn : column index
4819 * [IO] lpColumn : column information
4820 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4821 * otherwise it is in fact a LPLVCOLUMNA
4827 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4829 COLUMN_INFO *lpColumnInfo;
4832 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4833 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4835 /* initialize memory */
4836 ZeroMemory(&hdi, sizeof(hdi));
4838 if (lpColumn->mask & LVCF_TEXT)
4840 hdi.mask |= HDI_TEXT;
4841 hdi.pszText = lpColumn->pszText;
4842 hdi.cchTextMax = lpColumn->cchTextMax;
4845 if (lpColumn->mask & LVCF_IMAGE)
4846 hdi.mask |= HDI_IMAGE;
4848 if (lpColumn->mask & LVCF_ORDER)
4849 hdi.mask |= HDI_ORDER;
4851 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4853 if (lpColumn->mask & LVCF_FMT)
4854 lpColumn->fmt = lpColumnInfo->fmt;
4856 if (lpColumn->mask & LVCF_WIDTH)
4857 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4859 if (lpColumn->mask & LVCF_IMAGE)
4860 lpColumn->iImage = hdi.iImage;
4862 if (lpColumn->mask & LVCF_ORDER)
4863 lpColumn->iOrder = hdi.iOrder;
4869 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4876 /* FIXME: little hack */
4877 for (i = 0; i < iCount; i++)
4885 * Retrieves the column width.
4888 * [I] infoPtr : valid pointer to the listview structure
4889 * [I] int : column index
4892 * SUCCESS : column width
4895 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4897 INT nColumnWidth = 0;
4900 TRACE("nColumn=%d\n", nColumn);
4902 /* we have a 'column' in LIST and REPORT mode only */
4903 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4906 nColumnWidth = infoPtr->nItemWidth;
4909 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
4910 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4911 nColumnWidth = rcHeader.right - rcHeader.left;
4915 TRACE("nColumnWidth=%d\n", nColumnWidth);
4916 return nColumnWidth;
4921 * In list or report display mode, retrieves the number of items that can fit
4922 * vertically in the visible area. In icon or small icon display mode,
4923 * retrieves the total number of visible items.
4926 * [I] infoPtr : valid pointer to the listview structure
4929 * Number of fully visible items.
4931 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4933 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4937 return infoPtr->nItemCount;
4939 return LISTVIEW_GetCountPerColumn(infoPtr);
4941 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4949 * Retrieves an image list handle.
4952 * [I] infoPtr : valid pointer to the listview structure
4953 * [I] nImageList : image list identifier
4956 * SUCCESS : image list handle
4959 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4963 case LVSIL_NORMAL: return infoPtr->himlNormal;
4964 case LVSIL_SMALL: return infoPtr->himlSmall;
4965 case LVSIL_STATE: return infoPtr->himlState;
4970 /* LISTVIEW_GetISearchString */
4974 * Retrieves item attributes.
4977 * [I] hwnd : window handle
4978 * [IO] lpLVItem : item info
4979 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4980 * if FALSE, the lpLVItem is a LPLVITEMA.
4983 * This is the internal 'GetItem' interface -- it tries to
4984 * be smart, and avoids text copies, if possible, by modifing
4985 * lpLVItem->pszText to point to the text string. Please note
4986 * that this is not always possible (e.g. OWNERDATA), so on
4987 * entry you *must* supply valid values for pszText, and cchTextMax.
4988 * The only difference to the documented interface is that upon
4989 * return, you should use *only* the lpLVItem->pszText, rather than
4990 * the buffer pointer you provided on input. Most code already does
4991 * that, so it's not a problem.
4992 * For the two cases when the text must be copied (that is,
4993 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4999 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5001 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5002 NMLVDISPINFOW dispInfo;
5008 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5010 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5013 if (lpLVItem->mask == 0) return TRUE;
5015 /* make a local copy */
5016 isubitem = lpLVItem->iSubItem;
5018 /* a quick optimization if all we're asked is the focus state
5019 * these queries are worth optimising since they are common,
5020 * and can be answered in constant time, without the heavy accesses */
5021 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5022 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5024 lpLVItem->state = 0;
5025 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5026 lpLVItem->state |= LVIS_FOCUSED;
5030 ZeroMemory(&dispInfo, sizeof(dispInfo));
5032 /* if the app stores all the data, handle it separately */
5033 if (infoPtr->dwStyle & LVS_OWNERDATA)
5035 dispInfo.item.state = 0;
5037 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5038 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5040 /* NOTE: copy only fields which we _know_ are initialized, some apps
5041 * depend on the uninitialized fields being 0 */
5042 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5043 dispInfo.item.iItem = lpLVItem->iItem;
5044 dispInfo.item.iSubItem = isubitem;
5045 if (lpLVItem->mask & LVIF_TEXT)
5047 dispInfo.item.pszText = lpLVItem->pszText;
5048 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5050 if (lpLVItem->mask & LVIF_STATE)
5051 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5052 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5053 dispInfo.item.stateMask = lpLVItem->stateMask;
5054 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5056 /* full size structure expected - _WIN32IE >= 0x560 */
5057 *lpLVItem = dispInfo.item;
5059 else if (lpLVItem->mask & LVIF_INDENT)
5061 /* indent member expected - _WIN32IE >= 0x300 */
5062 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5066 /* minimal structure expected */
5067 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5069 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5072 /* make sure lParam is zeroed out */
5073 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5075 /* we store only a little state, so if we're not asked, we're done */
5076 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5078 /* if focus is handled by us, report it */
5079 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5081 lpLVItem->state &= ~LVIS_FOCUSED;
5082 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5083 lpLVItem->state |= LVIS_FOCUSED;
5086 /* and do the same for selection, if we handle it */
5087 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5089 lpLVItem->state &= ~LVIS_SELECTED;
5090 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5091 lpLVItem->state |= LVIS_SELECTED;
5097 /* find the item and subitem structures before we proceed */
5098 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5099 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5104 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5105 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5108 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5113 pItemHdr = &lpItem->hdr;
5115 /* Do we need to query the state from the app? */
5116 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5118 dispInfo.item.mask |= LVIF_STATE;
5119 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5122 /* Do we need to enquire about the image? */
5123 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5124 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5126 dispInfo.item.mask |= LVIF_IMAGE;
5127 dispInfo.item.iImage = I_IMAGECALLBACK;
5130 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5131 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5133 dispInfo.item.mask |= LVIF_TEXT;
5134 dispInfo.item.pszText = lpLVItem->pszText;
5135 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5136 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5137 *dispInfo.item.pszText = '\0';
5140 /* If we don't have all the requested info, query the application */
5141 if (dispInfo.item.mask != 0)
5143 dispInfo.item.iItem = lpLVItem->iItem;
5144 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5145 dispInfo.item.lParam = lpItem->lParam;
5146 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5147 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5150 /* we should not store values for subitems */
5151 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5153 /* Now, handle the iImage field */
5154 if (dispInfo.item.mask & LVIF_IMAGE)
5156 lpLVItem->iImage = dispInfo.item.iImage;
5157 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5158 pItemHdr->iImage = dispInfo.item.iImage;
5160 else if (lpLVItem->mask & LVIF_IMAGE)
5162 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5163 lpLVItem->iImage = pItemHdr->iImage;
5165 lpLVItem->iImage = 0;
5168 /* The pszText field */
5169 if (dispInfo.item.mask & LVIF_TEXT)
5171 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5172 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5174 lpLVItem->pszText = dispInfo.item.pszText;
5176 else if (lpLVItem->mask & LVIF_TEXT)
5178 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5179 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5182 /* if this is a subitem, we're done */
5183 if (isubitem) return TRUE;
5185 /* Next is the lParam field */
5186 if (dispInfo.item.mask & LVIF_PARAM)
5188 lpLVItem->lParam = dispInfo.item.lParam;
5189 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5190 lpItem->lParam = dispInfo.item.lParam;
5192 else if (lpLVItem->mask & LVIF_PARAM)
5193 lpLVItem->lParam = lpItem->lParam;
5195 /* ... the state field (this one is different due to uCallbackmask) */
5196 if (lpLVItem->mask & LVIF_STATE)
5198 lpLVItem->state = lpItem->state;
5199 if (dispInfo.item.mask & LVIF_STATE)
5201 lpLVItem->state &= ~dispInfo.item.stateMask;
5202 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5204 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5206 lpLVItem->state &= ~LVIS_FOCUSED;
5207 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5208 lpLVItem->state |= LVIS_FOCUSED;
5210 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5212 lpLVItem->state &= ~LVIS_SELECTED;
5213 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5214 lpLVItem->state |= LVIS_SELECTED;
5218 /* and last, but not least, the indent field */
5219 if (lpLVItem->mask & LVIF_INDENT)
5220 lpLVItem->iIndent = lpItem->iIndent;
5227 * Retrieves item attributes.
5230 * [I] hwnd : window handle
5231 * [IO] lpLVItem : item info
5232 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5233 * if FALSE, the lpLVItem is a LPLVITEMA.
5236 * This is the external 'GetItem' interface -- it properly copies
5237 * the text in the provided buffer.
5243 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5248 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5251 pszText = lpLVItem->pszText;
5252 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5253 if (bResult && lpLVItem->pszText != pszText)
5254 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5255 lpLVItem->pszText = pszText;
5263 * Retrieves the position (upper-left) of the listview control item.
5264 * Note that for LVS_ICON style, the upper-left is that of the icon
5265 * and not the bounding box.
5268 * [I] infoPtr : valid pointer to the listview structure
5269 * [I] nItem : item index
5270 * [O] lpptPosition : coordinate information
5276 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5278 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5281 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5283 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5285 LISTVIEW_GetOrigin(infoPtr, &Origin);
5286 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5288 if (uView == LVS_ICON)
5290 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5291 lpptPosition->y += ICON_TOP_PADDING;
5293 lpptPosition->x += Origin.x;
5294 lpptPosition->y += Origin.y;
5296 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5303 * Retrieves the bounding rectangle for a listview control item.
5306 * [I] infoPtr : valid pointer to the listview structure
5307 * [I] nItem : item index
5308 * [IO] lprc : bounding rectangle coordinates
5309 * lprc->left specifies the portion of the item for which the bounding
5310 * rectangle will be retrieved.
5312 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5313 * including the icon and label.
5316 * * Experiment shows that native control returns:
5317 * * width = min (48, length of text line)
5318 * * .left = position.x - (width - iconsize.cx)/2
5319 * * .right = .left + width
5320 * * height = #lines of text * ntmHeight + icon height + 8
5321 * * .top = position.y - 2
5322 * * .bottom = .top + height
5323 * * separation between items .y = itemSpacing.cy - height
5324 * * .x = itemSpacing.cx - width
5325 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5328 * * Experiment shows that native control returns:
5329 * * width = iconSize.cx + 16
5330 * * .left = position.x - (width - iconsize.cx)/2
5331 * * .right = .left + width
5332 * * height = iconSize.cy + 4
5333 * * .top = position.y - 2
5334 * * .bottom = .top + height
5335 * * separation between items .y = itemSpacing.cy - height
5336 * * .x = itemSpacing.cx - width
5337 * LVIR_LABEL Returns the bounding rectangle of the item text.
5340 * * Experiment shows that native control returns:
5341 * * width = text length
5342 * * .left = position.x - width/2
5343 * * .right = .left + width
5344 * * height = ntmH * linecount + 2
5345 * * .top = position.y + iconSize.cy + 6
5346 * * .bottom = .top + height
5347 * * separation between items .y = itemSpacing.cy - height
5348 * * .x = itemSpacing.cx - width
5349 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5350 * rectangles, but excludes columns in report view.
5357 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5358 * upon whether the window has the focus currently and on whether the item
5359 * is the one with the focus. Ensure that the control's record of which
5360 * item has the focus agrees with the items' records.
5362 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5364 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5365 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5366 BOOL doLabel = TRUE, oversizedBox = FALSE;
5367 POINT Position, Origin;
5371 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5373 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5375 LISTVIEW_GetOrigin(infoPtr, &Origin);
5376 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5378 /* Be smart and try to figure out the minimum we have to do */
5379 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5380 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5381 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5382 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5383 oversizedBox = TRUE;
5385 /* get what we need from the item before hand, so we make
5386 * only one request. This can speed up things, if data
5387 * is stored on the app side */
5389 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5390 if (doLabel) lvItem.mask |= LVIF_TEXT;
5391 lvItem.iItem = nItem;
5392 lvItem.iSubItem = 0;
5393 lvItem.pszText = szDispText;
5394 lvItem.cchTextMax = DISP_TEXT_SIZE;
5395 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5396 /* we got the state already up, simulate it here, to avoid a reget */
5397 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5399 lvItem.mask |= LVIF_STATE;
5400 lvItem.stateMask = LVIS_FOCUSED;
5401 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5404 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5405 lprc->left = LVIR_BOUNDS;
5409 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5413 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5417 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5420 case LVIR_SELECTBOUNDS:
5421 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5422 UnionRect(lprc, lprc, &label_rect);
5426 WARN("Unknown value: %ld\n", lprc->left);
5430 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5432 TRACE(" rect=%s\n", debugrect(lprc));
5439 * Retrieves the spacing between listview control items.
5442 * [I] infoPtr : valid pointer to the listview structure
5443 * [IO] lprc : rectangle to receive the output
5444 * on input, lprc->top = nSubItem
5445 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5447 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5448 * not only those of the first column.
5449 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5455 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5460 if (!lprc) return FALSE;
5462 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5463 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5465 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5467 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5469 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5472 lvItem.iItem = nItem;
5473 lvItem.iSubItem = lprc->top;
5475 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5479 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5484 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5488 ERR("Unknown bounds=%ld\n", lprc->left);
5492 OffsetRect(lprc, Position.x, Position.y);
5499 * Retrieves the width of a label.
5502 * [I] infoPtr : valid pointer to the listview structure
5505 * SUCCESS : string width (in pixels)
5508 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5510 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5513 TRACE("(nItem=%d)\n", nItem);
5515 lvItem.mask = LVIF_TEXT;
5516 lvItem.iItem = nItem;
5517 lvItem.iSubItem = 0;
5518 lvItem.pszText = szDispText;
5519 lvItem.cchTextMax = DISP_TEXT_SIZE;
5520 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5522 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5527 * Retrieves the spacing between listview control items.
5530 * [I] infoPtr : valid pointer to the listview structure
5531 * [I] bSmall : flag for small or large icon
5534 * Horizontal + vertical spacing
5536 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5542 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5546 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5547 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5549 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5556 * Retrieves the state of a listview control item.
5559 * [I] infoPtr : valid pointer to the listview structure
5560 * [I] nItem : item index
5561 * [I] uMask : state mask
5564 * State specified by the mask.
5566 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5570 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5572 lvItem.iItem = nItem;
5573 lvItem.iSubItem = 0;
5574 lvItem.mask = LVIF_STATE;
5575 lvItem.stateMask = uMask;
5576 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5578 return lvItem.state & uMask;
5583 * Retrieves the text of a listview control item or subitem.
5586 * [I] hwnd : window handle
5587 * [I] nItem : item index
5588 * [IO] lpLVItem : item information
5589 * [I] isW : TRUE if lpLVItem is Unicode
5592 * SUCCESS : string length
5595 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5597 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5599 lpLVItem->mask = LVIF_TEXT;
5600 lpLVItem->iItem = nItem;
5601 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5603 return textlenT(lpLVItem->pszText, isW);
5608 * Searches for an item based on properties + relationships.
5611 * [I] infoPtr : valid pointer to the listview structure
5612 * [I] nItem : item index
5613 * [I] uFlags : relationship flag
5616 * SUCCESS : item index
5619 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5621 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5623 LVFINDINFOW lvFindInfo;
5624 INT nCountPerColumn;
5628 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5629 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5631 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5633 if (uFlags & LVNI_CUT)
5636 if (uFlags & LVNI_DROPHILITED)
5637 uMask |= LVIS_DROPHILITED;
5639 if (uFlags & LVNI_FOCUSED)
5640 uMask |= LVIS_FOCUSED;
5642 if (uFlags & LVNI_SELECTED)
5643 uMask |= LVIS_SELECTED;
5645 /* if we're asked for the focused item, that's only one,
5646 * so it's worth optimizing */
5647 if (uFlags & LVNI_FOCUSED)
5649 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5650 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5653 if (uFlags & LVNI_ABOVE)
5655 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5660 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5666 /* Special case for autoarrange - move 'til the top of a list */
5667 if (is_autoarrange(infoPtr))
5669 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5670 while (nItem - nCountPerRow >= 0)
5672 nItem -= nCountPerRow;
5673 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5678 lvFindInfo.flags = LVFI_NEARESTXY;
5679 lvFindInfo.vkDirection = VK_UP;
5680 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5681 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5683 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5688 else if (uFlags & LVNI_BELOW)
5690 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5692 while (nItem < infoPtr->nItemCount)
5695 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5701 /* Special case for autoarrange - move 'til the bottom of a list */
5702 if (is_autoarrange(infoPtr))
5704 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5705 while (nItem + nCountPerRow < infoPtr->nItemCount )
5707 nItem += nCountPerRow;
5708 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5713 lvFindInfo.flags = LVFI_NEARESTXY;
5714 lvFindInfo.vkDirection = VK_DOWN;
5715 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5716 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5718 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5723 else if (uFlags & LVNI_TOLEFT)
5725 if (uView == LVS_LIST)
5727 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5728 while (nItem - nCountPerColumn >= 0)
5730 nItem -= nCountPerColumn;
5731 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5735 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5737 /* Special case for autoarrange - move 'ti the beginning of a row */
5738 if (is_autoarrange(infoPtr))
5740 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5741 while (nItem % nCountPerRow > 0)
5744 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5749 lvFindInfo.flags = LVFI_NEARESTXY;
5750 lvFindInfo.vkDirection = VK_LEFT;
5751 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5752 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5754 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5759 else if (uFlags & LVNI_TORIGHT)
5761 if (uView == LVS_LIST)
5763 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5764 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5766 nItem += nCountPerColumn;
5767 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5771 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5773 /* Special case for autoarrange - move 'til the end of a row */
5774 if (is_autoarrange(infoPtr))
5776 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5777 while (nItem % nCountPerRow < nCountPerRow - 1 )
5780 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5785 lvFindInfo.flags = LVFI_NEARESTXY;
5786 lvFindInfo.vkDirection = VK_RIGHT;
5787 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5788 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5790 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5799 /* search by index */
5800 for (i = nItem; i < infoPtr->nItemCount; i++)
5802 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5810 /* LISTVIEW_GetNumberOfWorkAreas */
5814 * Retrieves the origin coordinates when in icon or small icon display mode.
5817 * [I] infoPtr : valid pointer to the listview structure
5818 * [O] lpptOrigin : coordinate information
5823 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5825 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5826 INT nHorzPos = 0, nVertPos = 0;
5827 SCROLLINFO scrollInfo;
5829 scrollInfo.cbSize = sizeof(SCROLLINFO);
5830 scrollInfo.fMask = SIF_POS;
5832 if ((infoPtr->dwStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5833 nHorzPos = scrollInfo.nPos;
5834 if ((infoPtr->dwStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5835 nVertPos = scrollInfo.nPos;
5837 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5839 lpptOrigin->x = infoPtr->rcList.left;
5840 lpptOrigin->y = infoPtr->rcList.top;
5841 if (uView == LVS_LIST)
5842 nHorzPos *= infoPtr->nItemWidth;
5843 else if (uView == LVS_REPORT)
5844 nVertPos *= infoPtr->nItemHeight;
5846 lpptOrigin->x -= nHorzPos;
5847 lpptOrigin->y -= nVertPos;
5849 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5854 * Retrieves the width of a string.
5857 * [I] hwnd : window handle
5858 * [I] lpszText : text string to process
5859 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5862 * SUCCESS : string width (in pixels)
5865 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5870 if (is_textT(lpszText, isW))
5872 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5873 HDC hdc = GetDC(infoPtr->hwndSelf);
5874 HFONT hOldFont = SelectObject(hdc, hFont);
5877 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5879 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5880 SelectObject(hdc, hOldFont);
5881 ReleaseDC(infoPtr->hwndSelf, hdc);
5883 return stringSize.cx;
5888 * Determines which listview item is located at the specified position.
5891 * [I] infoPtr : valid pointer to the listview structure
5892 * [IO] lpht : hit test information
5893 * [I] subitem : fill out iSubItem.
5894 * [I] select : return the index only if the hit selects the item
5897 * (mm 20001022): We must not allow iSubItem to be touched, for
5898 * an app might pass only a structure with space up to iItem!
5899 * (MS Office 97 does that for instance in the file open dialog)
5902 * SUCCESS : item index
5905 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5907 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5908 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5909 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5910 POINT Origin, Position, opt;
5915 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5919 if (subitem) lpht->iSubItem = 0;
5921 if (infoPtr->rcList.left > lpht->pt.x)
5922 lpht->flags |= LVHT_TOLEFT;
5923 else if (infoPtr->rcList.right < lpht->pt.x)
5924 lpht->flags |= LVHT_TORIGHT;
5926 if (infoPtr->rcList.top > lpht->pt.y)
5927 lpht->flags |= LVHT_ABOVE;
5928 else if (infoPtr->rcList.bottom < lpht->pt.y)
5929 lpht->flags |= LVHT_BELOW;
5931 TRACE("lpht->flags=0x%x\n", lpht->flags);
5932 if (lpht->flags) return -1;
5934 lpht->flags |= LVHT_NOWHERE;
5936 LISTVIEW_GetOrigin(infoPtr, &Origin);
5938 /* first deal with the large items */
5939 rcSearch.left = lpht->pt.x;
5940 rcSearch.top = lpht->pt.y;
5941 rcSearch.right = rcSearch.left + 1;
5942 rcSearch.bottom = rcSearch.top + 1;
5944 iterator_frameditems(&i, infoPtr, &rcSearch);
5945 iterator_next(&i); /* go to first item in the sequence */
5947 iterator_destroy(&i);
5949 TRACE("lpht->iItem=%d\n", iItem);
5950 if (iItem == -1) return -1;
5952 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5953 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5954 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5955 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5956 lvItem.iItem = iItem;
5957 lvItem.iSubItem = 0;
5958 lvItem.pszText = szDispText;
5959 lvItem.cchTextMax = DISP_TEXT_SIZE;
5960 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5961 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5963 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5964 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
5965 opt.x = lpht->pt.x - Position.x - Origin.x;
5966 opt.y = lpht->pt.y - Position.y - Origin.y;
5968 if (uView == LVS_REPORT)
5971 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5972 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5973 if (!PtInRect(&rcBounds, opt)) return -1;
5975 if (PtInRect(&rcIcon, opt))
5976 lpht->flags |= LVHT_ONITEMICON;
5977 else if (PtInRect(&rcLabel, opt))
5978 lpht->flags |= LVHT_ONITEMLABEL;
5979 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5980 lpht->flags |= LVHT_ONITEMSTATEICON;
5981 if (lpht->flags & LVHT_ONITEM)
5982 lpht->flags &= ~LVHT_NOWHERE;
5984 TRACE("lpht->flags=0x%x\n", lpht->flags);
5985 if (uView == LVS_REPORT && subitem)
5989 rcBounds.right = rcBounds.left;
5990 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
5992 rcBounds.left = rcBounds.right;
5993 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5994 if (PtInRect(&rcBounds, opt))
6002 if (select && !(uView == LVS_REPORT &&
6003 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6004 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6006 if (uView == LVS_REPORT)
6008 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6009 UnionRect(&rcBounds, &rcBounds, &rcState);
6011 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6013 return lpht->iItem = iItem;
6017 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6018 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6019 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6020 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6021 their own sort proc. when sending LVM_SORTITEMS.
6024 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6026 LVS_SORTXXX must be specified,
6027 LVS_OWNERDRAW is not set,
6028 <item>.pszText is not LPSTR_TEXTCALLBACK.
6030 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6031 are sorted based on item text..."
6033 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6035 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6036 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6037 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6039 /* if we're sorting descending, negate the return value */
6040 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6045 * Inserts a new item in the listview control.
6048 * [I] infoPtr : valid pointer to the listview structure
6049 * [I] lpLVItem : item information
6050 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6053 * SUCCESS : new item index
6056 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6058 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6063 BOOL is_sorted, has_changed;
6066 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6068 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6070 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6071 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6073 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6075 if ( !(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO))) )
6078 /* insert item in listview control data structure */
6079 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6080 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6082 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6083 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6085 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6086 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6087 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6088 if (nItem == -1) goto fail;
6089 infoPtr->nItemCount++;
6091 /* shift indices first so they don't get tangled */
6092 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6094 /* set the item attributes */
6095 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6097 /* full size structure expected - _WIN32IE >= 0x560 */
6100 else if (lpLVItem->mask & LVIF_INDENT)
6102 /* indent member expected - _WIN32IE >= 0x300 */
6103 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6107 /* minimal structure expected */
6108 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6111 item.state &= ~LVIS_STATEIMAGEMASK;
6112 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6114 /* if we're sorted, sort the list, and update the index */
6117 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6118 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6119 assert(nItem != -1);
6122 /* make room for the position, if we are in the right mode */
6123 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6125 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6127 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6129 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6134 /* send LVN_INSERTITEM notification */
6135 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6137 nmlv.lParam = lpItem->lParam;
6138 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6140 /* align items (set position of each item) */
6141 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6145 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6146 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6148 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6150 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6153 /* now is the invalidation fun */
6154 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6158 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6159 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6160 infoPtr->nItemCount--;
6162 DPA_DeletePtr(hdpaSubItems, 0);
6163 DPA_Destroy (hdpaSubItems);
6170 * Redraws a range of items.
6173 * [I] infoPtr : valid pointer to the listview structure
6174 * [I] nFirst : first item
6175 * [I] nLast : last item
6181 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6185 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6186 max(nFirst, nLast) >= infoPtr->nItemCount)
6189 for (i = nFirst; i <= nLast; i++)
6190 LISTVIEW_InvalidateItem(infoPtr, i);
6197 * Scroll the content of a listview.
6200 * [I] infoPtr : valid pointer to the listview structure
6201 * [I] dx : horizontal scroll amount in pixels
6202 * [I] dy : vertical scroll amount in pixels
6209 * If the control is in report mode (LVS_REPORT) the control can
6210 * be scrolled only in line increments. "dy" will be rounded to the
6211 * nearest number of pixels that are a whole line. Ex: if line height
6212 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6213 * is passed the the scroll will be 0. (per MSDN 7/2002)
6215 * For: (per experimentaion with native control and CSpy ListView)
6216 * LVS_ICON dy=1 = 1 pixel (vertical only)
6218 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6220 * LVS_LIST dx=1 = 1 column (horizontal only)
6221 * but will only scroll 1 column per message
6222 * no matter what the value.
6223 * dy must be 0 or FALSE returned.
6224 * LVS_REPORT dx=1 = 1 pixel
6228 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6230 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6232 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6233 dy /= infoPtr->nItemHeight;
6236 if (dy != 0) return FALSE;
6243 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6244 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6251 * Sets the background color.
6254 * [I] infoPtr : valid pointer to the listview structure
6255 * [I] clrBk : background color
6261 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6263 TRACE("(clrBk=%lx)\n", clrBk);
6265 if(infoPtr->clrBk != clrBk) {
6266 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6267 infoPtr->clrBk = clrBk;
6268 if (clrBk == CLR_NONE)
6269 infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6271 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6272 LISTVIEW_InvalidateList(infoPtr);
6278 /* LISTVIEW_SetBkImage */
6280 /*** Helper for {Insert,Set}ColumnT *only* */
6281 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6283 if (lpColumn->mask & LVCF_FMT)
6285 /* format member is valid */
6286 lphdi->mask |= HDI_FORMAT;
6288 /* set text alignment (leftmost column must be left-aligned) */
6289 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
6290 lphdi->fmt |= HDF_LEFT;
6291 else if (lpColumn->fmt & LVCFMT_RIGHT)
6292 lphdi->fmt |= HDF_RIGHT;
6293 else if (lpColumn->fmt & LVCFMT_CENTER)
6294 lphdi->fmt |= HDF_CENTER;
6296 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6297 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6299 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6301 lphdi->fmt |= HDF_IMAGE;
6302 lphdi->iImage = I_IMAGECALLBACK;
6306 if (lpColumn->mask & LVCF_WIDTH)
6308 lphdi->mask |= HDI_WIDTH;
6309 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6311 /* make it fill the remainder of the controls width */
6315 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6317 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6318 lphdi->cxy += rcHeader.right - rcHeader.left;
6321 /* retrieve the layout of the header */
6322 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6323 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6325 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6328 lphdi->cxy = lpColumn->cx;
6331 if (lpColumn->mask & LVCF_TEXT)
6333 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6334 lphdi->fmt |= HDF_STRING;
6335 lphdi->pszText = lpColumn->pszText;
6336 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6339 if (lpColumn->mask & LVCF_IMAGE)
6341 lphdi->mask |= HDI_IMAGE;
6342 lphdi->iImage = lpColumn->iImage;
6345 if (lpColumn->mask & LVCF_ORDER)
6347 lphdi->mask |= HDI_ORDER;
6348 lphdi->iOrder = lpColumn->iOrder;
6355 * Inserts a new column.
6358 * [I] infoPtr : valid pointer to the listview structure
6359 * [I] nColumn : column index
6360 * [I] lpColumn : column information
6361 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6364 * SUCCESS : new column index
6367 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6368 const LVCOLUMNW *lpColumn, BOOL isW)
6370 COLUMN_INFO *lpColumnInfo;
6374 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6376 if (!lpColumn || nColumn < 0) return -1;
6377 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6379 ZeroMemory(&hdi, sizeof(HDITEMW));
6380 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6382 /* insert item in header control */
6383 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6384 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6385 (WPARAM)nColumn, (LPARAM)&hdi);
6386 if (nNewColumn == -1) return -1;
6387 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6389 /* create our own column info */
6390 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6391 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6393 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6394 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6396 /* now we have to actually adjust the data */
6397 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6399 SUBITEM_INFO *lpSubItem;
6403 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6405 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6406 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6408 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6409 if (lpSubItem->iSubItem >= nNewColumn)
6410 lpSubItem->iSubItem++;
6415 /* make space for the new column */
6416 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6421 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6424 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6432 * Sets the attributes of a header item.
6435 * [I] infoPtr : valid pointer to the listview structure
6436 * [I] nColumn : column index
6437 * [I] lpColumn : column attributes
6438 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6444 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6445 const LVCOLUMNW *lpColumn, BOOL isW)
6447 HDITEMW hdi, hdiget;
6450 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6452 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6454 ZeroMemory(&hdi, sizeof(HDITEMW));
6455 if (lpColumn->mask & LVCF_FMT)
6457 hdi.mask |= HDI_FORMAT;
6458 hdiget.mask = HDI_FORMAT;
6459 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6460 hdi.fmt = hdiget.fmt & HDF_STRING;
6462 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6464 /* set header item attributes */
6465 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6466 if (!bResult) return FALSE;
6468 if (lpColumn->mask & LVCF_FMT)
6470 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6471 int oldFmt = lpColumnInfo->fmt;
6473 lpColumnInfo->fmt = lpColumn->fmt;
6474 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6476 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6477 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6486 * Sets the column order array
6489 * [I] infoPtr : valid pointer to the listview structure
6490 * [I] iCount : number of elements in column order array
6491 * [I] lpiArray : pointer to column order array
6497 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6499 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6510 * Sets the width of a column
6513 * [I] infoPtr : valid pointer to the listview structure
6514 * [I] nColumn : column index
6515 * [I] cx : column width
6521 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6523 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6524 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6528 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6530 /* set column width only if in report or list mode */
6531 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6533 /* take care of invalid cx values */
6534 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6535 else if (uView == LVS_LIST && cx < 1) return FALSE;
6537 /* resize all columns if in LVS_LIST mode */
6538 if(uView == LVS_LIST)
6540 infoPtr->nItemWidth = cx;
6541 LISTVIEW_InvalidateList(infoPtr);
6545 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6547 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6552 lvItem.mask = LVIF_TEXT;
6554 lvItem.iSubItem = nColumn;
6555 lvItem.pszText = szDispText;
6556 lvItem.cchTextMax = DISP_TEXT_SIZE;
6557 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6559 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6560 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6561 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6563 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6564 max_cx += infoPtr->iconSize.cx;
6565 max_cx += TRAILING_LABEL_PADDING;
6568 /* autosize based on listview items width */
6569 if(cx == LVSCW_AUTOSIZE)
6571 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6573 /* if iCol is the last column make it fill the remainder of the controls width */
6574 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6579 LISTVIEW_GetOrigin(infoPtr, &Origin);
6580 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6582 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6586 /* Despite what the MS docs say, if this is not the last
6587 column, then MS resizes the column to the width of the
6588 largest text string in the column, including headers
6589 and items. This is different from LVSCW_AUTOSIZE in that
6590 LVSCW_AUTOSIZE ignores the header string length. */
6593 /* retrieve header text */
6594 hdi.mask = HDI_TEXT;
6595 hdi.cchTextMax = DISP_TEXT_SIZE;
6596 hdi.pszText = szDispText;
6597 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6599 HDC hdc = GetDC(infoPtr->hwndSelf);
6600 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6603 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6604 cx = size.cx + TRAILING_HEADER_PADDING;
6605 /* FIXME: Take into account the header image, if one is present */
6606 SelectObject(hdc, old_font);
6607 ReleaseDC(infoPtr->hwndSelf, hdc);
6609 cx = max (cx, max_cx);
6613 if (cx < 0) return FALSE;
6615 /* call header to update the column change */
6616 hdi.mask = HDI_WIDTH;
6618 TRACE("hdi.cxy=%d\n", hdi.cxy);
6619 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6623 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6626 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6629 HBITMAP hbm_im, hbm_mask, hbm_orig;
6631 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6632 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6635 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6636 ILC_COLOR | ILC_MASK, 2, 2);
6637 hdc_wnd = GetDC(infoPtr->hwndSelf);
6638 hdc = CreateCompatibleDC(hdc_wnd);
6639 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6640 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6641 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6643 rc.left = rc.top = 0;
6644 rc.right = GetSystemMetrics(SM_CXSMICON);
6645 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6647 hbm_orig = SelectObject(hdc, hbm_mask);
6648 FillRect(hdc, &rc, hbr_white);
6649 InflateRect(&rc, -3, -3);
6650 FillRect(hdc, &rc, hbr_black);
6652 SelectObject(hdc, hbm_im);
6653 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6654 SelectObject(hdc, hbm_orig);
6655 ImageList_Add(himl, hbm_im, hbm_mask);
6657 SelectObject(hdc, hbm_im);
6658 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6659 SelectObject(hdc, hbm_orig);
6660 ImageList_Add(himl, hbm_im, hbm_mask);
6662 DeleteObject(hbm_mask);
6663 DeleteObject(hbm_im);
6671 * Sets the extended listview style.
6674 * [I] infoPtr : valid pointer to the listview structure
6676 * [I] dwStyle : style
6679 * SUCCESS : previous style
6682 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6684 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6688 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6690 infoPtr->dwLvExStyle = dwStyle;
6692 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6694 HIMAGELIST himl = 0;
6695 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6696 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6697 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6705 * Sets the new hot cursor used during hot tracking and hover selection.
6708 * [I] infoPtr : valid pointer to the listview structure
6709 * [I} hCurosr : the new hot cursor handle
6712 * Returns the previous hot cursor
6714 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6716 HCURSOR oldCursor = infoPtr->hHotCursor;
6718 infoPtr->hHotCursor = hCursor;
6726 * Sets the hot item index.
6729 * [I] infoPtr : valid pointer to the listview structure
6730 * [I] iIndex : index
6733 * SUCCESS : previous hot item index
6734 * FAILURE : -1 (no hot item)
6736 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6738 INT iOldIndex = infoPtr->nHotItem;
6740 infoPtr->nHotItem = iIndex;
6748 * Sets the amount of time the cursor must hover over an item before it is selected.
6751 * [I] infoPtr : valid pointer to the listview structure
6752 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6755 * Returns the previous hover time
6757 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6759 DWORD oldHoverTime = infoPtr->dwHoverTime;
6761 infoPtr->dwHoverTime = dwHoverTime;
6763 return oldHoverTime;
6768 * Sets spacing for icons of LVS_ICON style.
6771 * [I] infoPtr : valid pointer to the listview structure
6772 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6773 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6776 * MAKELONG(oldcx, oldcy)
6778 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6780 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6781 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6783 TRACE("requested=(%d,%d)\n", cx, cy);
6785 /* this is supported only for LVS_ICON style */
6786 if (uView != LVS_ICON) return oldspacing;
6788 /* set to defaults, if instructed to */
6789 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6790 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6792 /* if 0 then compute width
6793 * FIXME: Should scan each item and determine max width of
6794 * icon or label, then make that the width */
6796 cx = infoPtr->iconSpacing.cx;
6798 /* if 0 then compute height */
6800 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6801 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6804 infoPtr->iconSpacing.cx = cx;
6805 infoPtr->iconSpacing.cy = cy;
6807 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6808 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6809 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6810 infoPtr->ntmHeight);
6812 /* these depend on the iconSpacing */
6813 LISTVIEW_UpdateItemSize(infoPtr);
6818 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6822 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6829 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6830 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6839 * [I] infoPtr : valid pointer to the listview structure
6840 * [I] nType : image list type
6841 * [I] himl : image list handle
6844 * SUCCESS : old image list
6847 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6849 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6850 INT oldHeight = infoPtr->nItemHeight;
6851 HIMAGELIST himlOld = 0;
6853 TRACE("(nType=%d, himl=%p\n", nType, himl);
6858 himlOld = infoPtr->himlNormal;
6859 infoPtr->himlNormal = himl;
6860 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6861 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6865 himlOld = infoPtr->himlSmall;
6866 infoPtr->himlSmall = himl;
6867 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6871 himlOld = infoPtr->himlState;
6872 infoPtr->himlState = himl;
6873 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6874 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6878 ERR("Unknown icon type=%d\n", nType);
6882 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6883 if (infoPtr->nItemHeight != oldHeight)
6884 LISTVIEW_UpdateScroll(infoPtr);
6891 * Preallocates memory (does *not* set the actual count of items !)
6894 * [I] infoPtr : valid pointer to the listview structure
6895 * [I] nItems : item count (projected number of items to allocate)
6896 * [I] dwFlags : update flags
6902 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6904 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6906 if (infoPtr->dwStyle & LVS_OWNERDATA)
6908 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6909 INT nOldCount = infoPtr->nItemCount;
6911 if (nItems < nOldCount)
6913 RANGE range = { nItems, nOldCount };
6914 ranges_del(infoPtr->selectionRanges, range);
6915 if (infoPtr->nFocusedItem >= nItems)
6917 infoPtr->nFocusedItem = -1;
6918 SetRectEmpty(&infoPtr->rcFocus);
6922 infoPtr->nItemCount = nItems;
6923 LISTVIEW_UpdateScroll(infoPtr);
6925 /* the flags are valid only in ownerdata report and list modes */
6926 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6928 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6929 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6931 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6932 LISTVIEW_InvalidateList(infoPtr);
6939 LISTVIEW_GetOrigin(infoPtr, &Origin);
6940 nFrom = min(nOldCount, nItems);
6941 nTo = max(nOldCount, nItems);
6943 if (uView == LVS_REPORT)
6946 rcErase.top = nFrom * infoPtr->nItemHeight;
6947 rcErase.right = infoPtr->nItemWidth;
6948 rcErase.bottom = nTo * infoPtr->nItemHeight;
6949 OffsetRect(&rcErase, Origin.x, Origin.y);
6950 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6951 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6955 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6957 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6958 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6959 rcErase.right = rcErase.left + infoPtr->nItemWidth;
6960 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6961 OffsetRect(&rcErase, Origin.x, Origin.y);
6962 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6963 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6965 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6967 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
6968 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6969 OffsetRect(&rcErase, Origin.x, Origin.y);
6970 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6971 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6977 /* According to MSDN for non-LVS_OWNERDATA this is just
6978 * a performance issue. The control allocates its internal
6979 * data structures for the number of items specified. It
6980 * cuts down on the number of memory allocations. Therefore
6981 * we will just issue a WARN here
6983 WARN("for non-ownerdata performance option not implemented.\n");
6991 * Sets the position of an item.
6994 * [I] infoPtr : valid pointer to the listview structure
6995 * [I] nItem : item index
6996 * [I] pt : coordinate
7002 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7004 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7007 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
7009 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7010 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7012 LISTVIEW_GetOrigin(infoPtr, &Origin);
7014 /* This point value seems to be an undocumented feature.
7015 * The best guess is that it means either at the origin,
7016 * or at true beginning of the list. I will assume the origin. */
7017 if ((pt.x == -1) && (pt.y == -1))
7020 if (uView == LVS_ICON)
7022 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7023 pt.y -= ICON_TOP_PADDING;
7028 infoPtr->bAutoarrange = FALSE;
7030 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7035 * Sets the state of one or many items.
7038 * [I] infoPtr : valid pointer to the listview structure
7039 * [I] nItem : item index
7040 * [I] lpLVItem : item or subitem info
7046 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7048 BOOL bResult = TRUE;
7051 lvItem.iItem = nItem;
7052 lvItem.iSubItem = 0;
7053 lvItem.mask = LVIF_STATE;
7054 lvItem.state = lpLVItem->state;
7055 lvItem.stateMask = lpLVItem->stateMask;
7056 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7060 /* apply to all items */
7061 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7062 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7065 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7068 *update selection mark
7070 * Investigation on windows 2k showed that selection mark was updated
7071 * whenever a new selection was made, but if the selected item was
7072 * unselected it was not updated.
7074 * we are probably still not 100% accurate, but this at least sets the
7075 * proper selection mark when it is needed
7078 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7079 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7082 infoPtr->nSelectionMark = -1;
7083 for (i = 0; i < infoPtr->nItemCount; i++)
7085 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7087 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7089 infoPtr->nSelectionMark = i;
7093 else if (ranges_contain(infoPtr->selectionRanges, i))
7095 infoPtr->nSelectionMark = i;
7106 * Sets the text of an item or subitem.
7109 * [I] hwnd : window handle
7110 * [I] nItem : item index
7111 * [I] lpLVItem : item or subitem info
7112 * [I] isW : TRUE if input is Unicode
7118 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7122 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7124 lvItem.iItem = nItem;
7125 lvItem.iSubItem = lpLVItem->iSubItem;
7126 lvItem.mask = LVIF_TEXT;
7127 lvItem.pszText = lpLVItem->pszText;
7128 lvItem.cchTextMax = lpLVItem->cchTextMax;
7130 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7132 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7137 * Set item index that marks the start of a multiple selection.
7140 * [I] infoPtr : valid pointer to the listview structure
7141 * [I] nIndex : index
7144 * Index number or -1 if there is no selection mark.
7146 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7148 INT nOldIndex = infoPtr->nSelectionMark;
7150 TRACE("(nIndex=%d)\n", nIndex);
7152 infoPtr->nSelectionMark = nIndex;
7159 * Sets the text background color.
7162 * [I] infoPtr : valid pointer to the listview structure
7163 * [I] clrTextBk : text background color
7169 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7171 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7173 if (infoPtr->clrTextBk != clrTextBk)
7175 infoPtr->clrTextBk = clrTextBk;
7176 LISTVIEW_InvalidateList(infoPtr);
7184 * Sets the text foreground color.
7187 * [I] infoPtr : valid pointer to the listview structure
7188 * [I] clrText : text color
7194 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7196 TRACE("(clrText=%lx)\n", clrText);
7198 if (infoPtr->clrText != clrText)
7200 infoPtr->clrText = clrText;
7201 LISTVIEW_InvalidateList(infoPtr);
7209 * Determines which listview item is located at the specified position.
7212 * [I] infoPtr : valid pointer to the listview structure
7213 * [I] hwndNewToolTip : handle to new ToolTip
7218 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7220 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7221 infoPtr->hwndToolTip = hwndNewToolTip;
7222 return hwndOldToolTip;
7225 /* LISTVIEW_SetUnicodeFormat */
7226 /* LISTVIEW_SetWorkAreas */
7230 * Callback internally used by LISTVIEW_SortItems()
7233 * [I] first : pointer to first ITEM_INFO to compare
7234 * [I] second : pointer to second ITEM_INFO to compare
7235 * [I] lParam : HWND of control
7238 * if first comes before second : negative
7239 * if first comes after second : positive
7240 * if first and second are equivalent : zero
7242 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7244 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7245 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7246 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7248 /* Forward the call to the client defined callback */
7249 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7254 * Sorts the listview items.
7257 * [I] infoPtr : valid pointer to the listview structure
7258 * [I] pfnCompare : application-defined value
7259 * [I] lParamSort : pointer to comparision callback
7265 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7267 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7270 LPVOID selectionMarkItem;
7274 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7276 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7278 if (!infoPtr->hdpaItems) return FALSE;
7280 /* if there are 0 or 1 items, there is no need to sort */
7281 if (infoPtr->nItemCount < 2) return TRUE;
7283 if (infoPtr->nFocusedItem >= 0)
7285 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7286 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7287 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7289 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7290 /* clear the lpItem->state for non-selected ones */
7291 /* remove the selection ranges */
7293 infoPtr->pfnCompare = pfnCompare;
7294 infoPtr->lParamSort = lParamSort;
7295 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7297 /* Adjust selections and indices so that they are the way they should
7298 * be after the sort (otherwise, the list items move around, but
7299 * whatever is at the item's previous original position will be
7302 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7303 for (i=0; i < infoPtr->nItemCount; i++)
7305 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7306 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7308 if (lpItem->state & LVIS_SELECTED)
7310 item.state = LVIS_SELECTED;
7311 item.stateMask = LVIS_SELECTED;
7312 LISTVIEW_SetItemState(infoPtr, i, &item);
7314 if (lpItem->state & LVIS_FOCUSED)
7316 infoPtr->nFocusedItem = i;
7317 lpItem->state &= ~LVIS_FOCUSED;
7320 if (selectionMarkItem != NULL)
7321 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7322 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7324 /* refresh the display */
7325 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7326 LISTVIEW_InvalidateList(infoPtr);
7333 * Updates an items or rearranges the listview control.
7336 * [I] infoPtr : valid pointer to the listview structure
7337 * [I] nItem : item index
7343 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7345 TRACE("(nItem=%d)\n", nItem);
7347 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7349 /* rearrange with default alignment style */
7350 if (is_autoarrange(infoPtr))
7351 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7353 LISTVIEW_InvalidateItem(infoPtr, nItem);
7361 * Creates the listview control.
7364 * [I] hwnd : window handle
7365 * [I] lpcs : the create parameters
7371 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7373 LISTVIEW_INFO *infoPtr;
7374 UINT uView = lpcs->style & LVS_TYPEMASK;
7377 TRACE("(lpcs=%p)\n", lpcs);
7379 /* initialize info pointer */
7380 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7381 if (!infoPtr) return -1;
7383 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7385 infoPtr->hwndSelf = hwnd;
7386 infoPtr->dwStyle = lpcs->style;
7387 /* determine the type of structures to use */
7388 infoPtr->hwndNotify = lpcs->hwndParent;
7389 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7390 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7392 /* initialize color information */
7393 infoPtr->clrBk = CLR_NONE;
7394 infoPtr->clrText = comctl32_color.clrWindowText;
7395 infoPtr->clrTextBk = CLR_DEFAULT;
7396 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7398 /* set default values */
7399 infoPtr->nFocusedItem = -1;
7400 infoPtr->nSelectionMark = -1;
7401 infoPtr->nHotItem = -1;
7402 infoPtr->bRedraw = TRUE;
7403 infoPtr->bNoItemMetrics = TRUE;
7404 infoPtr->bDoChangeNotify = TRUE;
7405 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7406 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7407 infoPtr->nEditLabelItem = -1;
7408 infoPtr->dwHoverTime = -1; /* default system hover time */
7410 /* get default font (icon title) */
7411 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7412 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7413 infoPtr->hFont = infoPtr->hDefaultFont;
7414 LISTVIEW_SaveTextMetrics(infoPtr);
7417 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7418 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7419 0, 0, 0, 0, hwnd, NULL,
7420 lpcs->hInstance, NULL);
7421 if (!infoPtr->hwndHeader) goto fail;
7423 /* set header unicode format */
7424 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7426 /* set header font */
7427 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7429 /* allocate memory for the data structure */
7430 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7431 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7432 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7433 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7434 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7436 /* initialize the icon sizes */
7437 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7438 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7440 /* init item size to avoid division by 0 */
7441 LISTVIEW_UpdateItemSize (infoPtr);
7443 if (uView == LVS_REPORT)
7445 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7447 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7451 /* set HDS_HIDDEN flag to hide the header bar */
7452 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7453 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7460 DestroyWindow(infoPtr->hwndHeader);
7461 ranges_destroy(infoPtr->selectionRanges);
7462 DPA_Destroy(infoPtr->hdpaItems);
7463 DPA_Destroy(infoPtr->hdpaPosX);
7464 DPA_Destroy(infoPtr->hdpaPosY);
7465 DPA_Destroy(infoPtr->hdpaColumns);
7472 * Erases the background of the listview control.
7475 * [I] infoPtr : valid pointer to the listview structure
7476 * [I] hdc : device context handle
7482 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7486 TRACE("(hdc=%p)\n", hdc);
7488 if (!GetClipBox(hdc, &rc)) return FALSE;
7490 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7496 * Helper function for LISTVIEW_[HV]Scroll *only*.
7497 * Performs vertical/horizontal scrolling by a give amount.
7500 * [I] infoPtr : valid pointer to the listview structure
7501 * [I] dx : amount of horizontal scroll
7502 * [I] dy : amount of vertical scroll
7504 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7506 /* now we can scroll the list */
7507 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7508 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7509 /* if we have focus, adjust rect */
7510 OffsetRect(&infoPtr->rcFocus, dx, dy);
7511 UpdateWindow(infoPtr->hwndSelf);
7516 * Performs vertical scrolling.
7519 * [I] infoPtr : valid pointer to the listview structure
7520 * [I] nScrollCode : scroll code
7521 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7522 * [I] hScrollWnd : scrollbar control window handle
7528 * SB_LINEUP/SB_LINEDOWN:
7529 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7530 * for LVS_REPORT is 1 line
7531 * for LVS_LIST cannot occur
7534 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7535 INT nScrollDiff, HWND hScrollWnd)
7537 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7538 INT nOldScrollPos, nNewScrollPos;
7539 SCROLLINFO scrollInfo;
7542 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7543 debugscrollcode(nScrollCode), nScrollDiff);
7545 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7547 scrollInfo.cbSize = sizeof(SCROLLINFO);
7548 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7550 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7552 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7554 nOldScrollPos = scrollInfo.nPos;
7555 switch (nScrollCode)
7561 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7565 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7569 nScrollDiff = -scrollInfo.nPage;
7573 nScrollDiff = scrollInfo.nPage;
7576 case SB_THUMBPOSITION:
7578 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7585 /* quit right away if pos isn't changing */
7586 if (nScrollDiff == 0) return 0;
7588 /* calculate new position, and handle overflows */
7589 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7590 if (nScrollDiff > 0) {
7591 if (nNewScrollPos < nOldScrollPos ||
7592 nNewScrollPos > scrollInfo.nMax)
7593 nNewScrollPos = scrollInfo.nMax;
7595 if (nNewScrollPos > nOldScrollPos ||
7596 nNewScrollPos < scrollInfo.nMin)
7597 nNewScrollPos = scrollInfo.nMin;
7600 /* set the new position, and reread in case it changed */
7601 scrollInfo.fMask = SIF_POS;
7602 scrollInfo.nPos = nNewScrollPos;
7603 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7605 /* carry on only if it really changed */
7606 if (nNewScrollPos == nOldScrollPos) return 0;
7608 /* now adjust to client coordinates */
7609 nScrollDiff = nOldScrollPos - nNewScrollPos;
7610 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7612 /* and scroll the window */
7613 scroll_list(infoPtr, 0, nScrollDiff);
7620 * Performs horizontal scrolling.
7623 * [I] infoPtr : valid pointer to the listview structure
7624 * [I] nScrollCode : scroll code
7625 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7626 * [I] hScrollWnd : scrollbar control window handle
7632 * SB_LINELEFT/SB_LINERIGHT:
7633 * for LVS_ICON, LVS_SMALLICON 1 pixel
7634 * for LVS_REPORT is 1 pixel
7635 * for LVS_LIST is 1 column --> which is a 1 because the
7636 * scroll is based on columns not pixels
7639 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7640 INT nScrollDiff, HWND hScrollWnd)
7642 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7643 INT nOldScrollPos, nNewScrollPos;
7644 SCROLLINFO scrollInfo;
7646 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7647 debugscrollcode(nScrollCode), nScrollDiff);
7649 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7651 scrollInfo.cbSize = sizeof(SCROLLINFO);
7652 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7654 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7656 nOldScrollPos = scrollInfo.nPos;
7658 switch (nScrollCode)
7672 nScrollDiff = -scrollInfo.nPage;
7676 nScrollDiff = scrollInfo.nPage;
7679 case SB_THUMBPOSITION:
7681 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7688 /* quit right away if pos isn't changing */
7689 if (nScrollDiff == 0) return 0;
7691 /* calculate new position, and handle overflows */
7692 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7693 if (nScrollDiff > 0) {
7694 if (nNewScrollPos < nOldScrollPos ||
7695 nNewScrollPos > scrollInfo.nMax)
7696 nNewScrollPos = scrollInfo.nMax;
7698 if (nNewScrollPos > nOldScrollPos ||
7699 nNewScrollPos < scrollInfo.nMin)
7700 nNewScrollPos = scrollInfo.nMin;
7703 /* set the new position, and reread in case it changed */
7704 scrollInfo.fMask = SIF_POS;
7705 scrollInfo.nPos = nNewScrollPos;
7706 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7708 /* carry on only if it really changed */
7709 if (nNewScrollPos == nOldScrollPos) return 0;
7711 if(uView == LVS_REPORT)
7712 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7714 /* now adjust to client coordinates */
7715 nScrollDiff = nOldScrollPos - nNewScrollPos;
7716 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7718 /* and scroll the window */
7719 scroll_list(infoPtr, nScrollDiff, 0);
7724 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7726 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7727 INT gcWheelDelta = 0;
7728 UINT pulScrollLines = 3;
7729 SCROLLINFO scrollInfo;
7731 TRACE("(wheelDelta=%d)\n", wheelDelta);
7733 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7734 gcWheelDelta -= wheelDelta;
7736 scrollInfo.cbSize = sizeof(SCROLLINFO);
7737 scrollInfo.fMask = SIF_POS;
7744 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7745 * should be fixed in the future.
7747 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7748 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7752 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7754 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7755 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7756 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7761 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7772 * [I] infoPtr : valid pointer to the listview structure
7773 * [I] nVirtualKey : virtual key
7774 * [I] lKeyData : key data
7779 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7781 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7783 NMLVKEYDOWN nmKeyDown;
7785 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7787 /* send LVN_KEYDOWN notification */
7788 nmKeyDown.wVKey = nVirtualKey;
7789 nmKeyDown.flags = 0;
7790 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7792 switch (nVirtualKey)
7795 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7797 notify(infoPtr, NM_RETURN);
7798 notify(infoPtr, LVN_ITEMACTIVATE);
7803 if (infoPtr->nItemCount > 0)
7808 if (infoPtr->nItemCount > 0)
7809 nItem = infoPtr->nItemCount - 1;
7813 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7817 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7821 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7825 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7829 if (uView == LVS_REPORT)
7830 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7832 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7833 * LISTVIEW_GetCountPerRow(infoPtr);
7834 if(nItem < 0) nItem = 0;
7838 if (uView == LVS_REPORT)
7839 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7841 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7842 * LISTVIEW_GetCountPerRow(infoPtr);
7843 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7847 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7848 LISTVIEW_KeySelection(infoPtr, nItem);
7858 * [I] infoPtr : valid pointer to the listview structure
7863 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7867 /* if we did not have the focus, there's nothing to do */
7868 if (!infoPtr->bFocus) return 0;
7870 /* send NM_KILLFOCUS notification */
7871 notify(infoPtr, NM_KILLFOCUS);
7873 /* if we have a focus rectagle, get rid of it */
7874 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7876 /* set window focus flag */
7877 infoPtr->bFocus = FALSE;
7879 /* invalidate the selected items before reseting focus flag */
7880 LISTVIEW_InvalidateSelectedItems(infoPtr);
7888 * Track mouse/dragging
7891 * [I] infoPtr : valid pointer to the listview structure
7892 * [I] pt : mouse coordinate
7897 static LRESULT LISTVIEW_TrackMouse(LISTVIEW_INFO *infoPtr, POINT pt)
7899 INT cxDrag = GetSystemMetrics(SM_CXDRAG);
7900 INT cyDrag = GetSystemMetrics(SM_CYDRAG);
7906 r.top = pt.y - cyDrag;
7907 r.left = pt.x - cxDrag;
7908 r.bottom = pt.y + cyDrag;
7909 r.right = pt.x + cxDrag;
7911 SetCapture(infoPtr->hwndSelf);
7915 if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
7917 if (msg.message == WM_MOUSEMOVE)
7919 pt.x = (short)LOWORD(msg.lParam);
7920 pt.y = (short)HIWORD(msg.lParam);
7921 if (PtInRect(&r, pt))
7929 else if (msg.message >= WM_LBUTTONDOWN &&
7930 msg.message <= WM_RBUTTONDBLCLK)
7935 DispatchMessageW(&msg);
7938 if (GetCapture() != infoPtr->hwndSelf)
7949 * Processes double click messages (left mouse button).
7952 * [I] infoPtr : valid pointer to the listview structure
7953 * [I] wKey : key flag
7954 * [I] pts : mouse coordinate
7959 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7961 LVHITTESTINFO htInfo;
7963 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7965 /* send NM_RELEASEDCAPTURE notification */
7966 notify(infoPtr, NM_RELEASEDCAPTURE);
7968 htInfo.pt.x = pts.x;
7969 htInfo.pt.y = pts.y;
7971 /* send NM_DBLCLK notification */
7972 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7973 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7975 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7976 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
7983 * Processes mouse down messages (left mouse button).
7986 * [I] infoPtr : valid pointer to the listview structure
7987 * [I] wKey : key flag
7988 * [I] pts : mouse coordinate
7993 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7995 LVHITTESTINFO lvHitTestInfo;
7996 static BOOL bGroupSelect = TRUE;
7997 POINT pt = { pts.x, pts.y };
8000 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8002 /* send NM_RELEASEDCAPTURE notification */
8003 notify(infoPtr, NM_RELEASEDCAPTURE);
8005 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8007 /* set left button down flag */
8008 infoPtr->bLButtonDown = TRUE;
8010 lvHitTestInfo.pt.x = pts.x;
8011 lvHitTestInfo.pt.y = pts.y;
8013 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8014 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
8015 infoPtr->nEditLabelItem = -1;
8016 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8018 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8020 DWORD state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK) >> 12;
8021 if(state == 1 || state == 2)
8025 lvitem.state = state << 12;
8026 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8027 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8031 if (LISTVIEW_TrackMouse(infoPtr, lvHitTestInfo.pt))
8035 ZeroMemory(&nmlv, sizeof(nmlv));
8037 nmlv.ptAction.x = lvHitTestInfo.pt.x;
8038 nmlv.ptAction.y = lvHitTestInfo.pt.y;
8040 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
8045 if (infoPtr->dwStyle & LVS_SINGLESEL)
8047 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8048 infoPtr->nEditLabelItem = nItem;
8050 LISTVIEW_SetSelection(infoPtr, nItem);
8054 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8058 LISTVIEW_AddGroupSelection(infoPtr, nItem);
8059 LISTVIEW_SetItemFocus(infoPtr, nItem);
8060 infoPtr->nSelectionMark = nItem;
8066 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8067 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8069 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8070 infoPtr->nSelectionMark = nItem;
8073 else if (wKey & MK_CONTROL)
8077 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8079 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8080 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8081 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8082 infoPtr->nSelectionMark = nItem;
8084 else if (wKey & MK_SHIFT)
8086 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8090 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8091 infoPtr->nEditLabelItem = nItem;
8093 /* set selection (clears other pre-existing selections) */
8094 LISTVIEW_SetSelection(infoPtr, nItem);
8100 /* remove all selections */
8101 LISTVIEW_DeselectAll(infoPtr);
8110 * Processes mouse up messages (left mouse button).
8113 * [I] infoPtr : valid pointer to the listview structure
8114 * [I] wKey : key flag
8115 * [I] pts : mouse coordinate
8120 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8122 LVHITTESTINFO lvHitTestInfo;
8124 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8126 if (!infoPtr->bLButtonDown) return 0;
8128 lvHitTestInfo.pt.x = pts.x;
8129 lvHitTestInfo.pt.y = pts.y;
8131 /* send NM_CLICK notification */
8132 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8133 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
8135 /* set left button flag */
8136 infoPtr->bLButtonDown = FALSE;
8138 /* if we clicked on a selected item, edit the label */
8139 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8140 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8147 * Destroys the listview control (called after WM_DESTROY).
8150 * [I] infoPtr : valid pointer to the listview structure
8155 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8159 /* delete all items */
8160 LISTVIEW_DeleteAllItems(infoPtr);
8162 /* destroy data structure */
8163 DPA_Destroy(infoPtr->hdpaItems);
8164 DPA_Destroy(infoPtr->hdpaPosX);
8165 DPA_Destroy(infoPtr->hdpaPosY);
8166 DPA_Destroy(infoPtr->hdpaColumns);
8167 ranges_destroy(infoPtr->selectionRanges);
8169 /* destroy image lists */
8170 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8172 if (infoPtr->himlNormal)
8173 ImageList_Destroy(infoPtr->himlNormal);
8174 if (infoPtr->himlSmall)
8175 ImageList_Destroy(infoPtr->himlSmall);
8176 if (infoPtr->himlState)
8177 ImageList_Destroy(infoPtr->himlState);
8180 /* destroy font, bkgnd brush */
8182 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8183 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8185 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
8187 /* free listview info pointer*/
8195 * Handles notifications from header.
8198 * [I] infoPtr : valid pointer to the listview structure
8199 * [I] nCtrlId : control identifier
8200 * [I] lpnmh : notification information
8205 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8207 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8209 TRACE("(lpnmh=%p)\n", lpnmh);
8211 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8213 switch (lpnmh->hdr.code)
8217 case HDN_ITEMCHANGEDW:
8218 case HDN_ITEMCHANGEDA:
8220 COLUMN_INFO *lpColumnInfo;
8223 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8227 hdi.mask = HDI_WIDTH;
8228 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8232 cxy = lpnmh->pitem->cxy;
8234 /* determine how much we change since the last know position */
8235 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8236 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8239 RECT rcCol = lpColumnInfo->rcHeader;
8241 lpColumnInfo->rcHeader.right += dx;
8242 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8243 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8245 /* this trick works for left aligned columns only */
8246 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8248 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
8249 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
8251 rcCol.top = infoPtr->rcList.top;
8252 rcCol.bottom = infoPtr->rcList.bottom;
8253 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8259 case HDN_ITEMCLICKW:
8260 case HDN_ITEMCLICKA:
8262 /* Handle sorting by Header Column */
8265 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8267 nmlv.iSubItem = lpnmh->iItem;
8268 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8278 * Determines the type of structure to use.
8281 * [I] infoPtr : valid pointer to the listview structureof the sender
8282 * [I] hwndFrom : listview window handle
8283 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8288 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8290 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8292 if (nCommand != NF_REQUERY) return 0;
8294 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8301 * Paints/Repaints the listview control.
8304 * [I] infoPtr : valid pointer to the listview structure
8305 * [I] hdc : device context handle
8310 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8312 TRACE("(hdc=%p)\n", hdc);
8314 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8316 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8318 infoPtr->bNoItemMetrics = FALSE;
8319 LISTVIEW_UpdateItemSize(infoPtr);
8320 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8321 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8322 LISTVIEW_UpdateScroll(infoPtr);
8325 LISTVIEW_Refresh(infoPtr, hdc);
8330 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8332 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8333 LISTVIEW_Refresh(infoPtr, hdc);
8334 EndPaint(infoPtr->hwndSelf, &ps);
8342 * Processes double click messages (right mouse button).
8345 * [I] infoPtr : valid pointer to the listview structure
8346 * [I] wKey : key flag
8347 * [I] pts : mouse coordinate
8352 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8354 LVHITTESTINFO lvHitTestInfo;
8356 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8358 /* send NM_RELEASEDCAPTURE notification */
8359 notify(infoPtr, NM_RELEASEDCAPTURE);
8361 /* send NM_RDBLCLK notification */
8362 lvHitTestInfo.pt.x = pts.x;
8363 lvHitTestInfo.pt.y = pts.y;
8364 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8365 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8372 * Processes mouse down messages (right mouse button).
8375 * [I] infoPtr : valid pointer to the listview structure
8376 * [I] wKey : key flag
8377 * [I] pts : mouse coordinate
8382 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8384 LVHITTESTINFO lvHitTestInfo;
8387 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8389 /* send NM_RELEASEDCAPTURE notification */
8390 notify(infoPtr, NM_RELEASEDCAPTURE);
8392 /* make sure the listview control window has the focus */
8393 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8395 /* set right button down flag */
8396 infoPtr->bRButtonDown = TRUE;
8398 /* determine the index of the selected item */
8399 lvHitTestInfo.pt.x = pts.x;
8400 lvHitTestInfo.pt.y = pts.y;
8401 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8403 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8405 LISTVIEW_SetItemFocus(infoPtr, nItem);
8406 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8407 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8408 LISTVIEW_SetSelection(infoPtr, nItem);
8412 LISTVIEW_DeselectAll(infoPtr);
8420 * Processes mouse up messages (right mouse button).
8423 * [I] infoPtr : valid pointer to the listview structure
8424 * [I] wKey : key flag
8425 * [I] pts : mouse coordinate
8430 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8432 LVHITTESTINFO lvHitTestInfo;
8435 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8437 if (!infoPtr->bRButtonDown) return 0;
8439 /* set button flag */
8440 infoPtr->bRButtonDown = FALSE;
8442 /* Send NM_RClICK notification */
8443 lvHitTestInfo.pt.x = pts.x;
8444 lvHitTestInfo.pt.y = pts.y;
8445 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8446 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8448 /* Change to screen coordinate for WM_CONTEXTMENU */
8449 pt = lvHitTestInfo.pt;
8450 ClientToScreen(infoPtr->hwndSelf, &pt);
8452 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8453 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8454 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8465 * [I] infoPtr : valid pointer to the listview structure
8466 * [I] hwnd : window handle of window containing the cursor
8467 * [I] nHittest : hit-test code
8468 * [I] wMouseMsg : ideintifier of the mouse message
8471 * TRUE if cursor is set
8474 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8476 LVHITTESTINFO lvHitTestInfo;
8478 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8480 if(!infoPtr->hHotCursor) return FALSE;
8482 GetCursorPos(&lvHitTestInfo.pt);
8483 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8485 SetCursor(infoPtr->hHotCursor);
8495 * [I] infoPtr : valid pointer to the listview structure
8496 * [I] hwndLoseFocus : handle of previously focused window
8501 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8503 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8505 /* if we have the focus already, there's nothing to do */
8506 if (infoPtr->bFocus) return 0;
8508 /* send NM_SETFOCUS notification */
8509 notify(infoPtr, NM_SETFOCUS);
8511 /* set window focus flag */
8512 infoPtr->bFocus = TRUE;
8514 /* put the focus rect back on */
8515 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8517 /* redraw all visible selected items */
8518 LISTVIEW_InvalidateSelectedItems(infoPtr);
8528 * [I] infoPtr : valid pointer to the listview structure
8529 * [I] fRedraw : font handle
8530 * [I] fRedraw : redraw flag
8535 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8537 HFONT oldFont = infoPtr->hFont;
8539 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8541 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8542 if (infoPtr->hFont == oldFont) return 0;
8544 LISTVIEW_SaveTextMetrics(infoPtr);
8546 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8547 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8549 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8556 * Message handling for WM_SETREDRAW.
8557 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8560 * [I] infoPtr : valid pointer to the listview structure
8561 * [I] bRedraw: state of redraw flag
8564 * DefWinProc return value
8566 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8568 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8570 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8571 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8573 infoPtr->bRedraw = bRedraw;
8575 if(!bRedraw) return 0;
8577 if (is_autoarrange(infoPtr))
8578 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8579 LISTVIEW_UpdateScroll(infoPtr);
8581 /* despite what the WM_SETREDRAW docs says, apps expect us
8582 * to invalidate the listview here... stupid! */
8583 LISTVIEW_InvalidateList(infoPtr);
8590 * Resizes the listview control. This function processes WM_SIZE
8591 * messages. At this time, the width and height are not used.
8594 * [I] infoPtr : valid pointer to the listview structure
8595 * [I] Width : new width
8596 * [I] Height : new height
8601 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8603 RECT rcOld = infoPtr->rcList;
8605 TRACE("(width=%d, height=%d)\n", Width, Height);
8607 LISTVIEW_UpdateSize(infoPtr);
8608 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8610 /* do not bother with display related stuff if we're not redrawing */
8611 if (!is_redrawing(infoPtr)) return 0;
8613 if (is_autoarrange(infoPtr))
8614 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8616 LISTVIEW_UpdateScroll(infoPtr);
8618 /* refresh all only for lists whose height changed significantly */
8619 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8620 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8621 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8622 LISTVIEW_InvalidateList(infoPtr);
8629 * Sets the size information.
8632 * [I] infoPtr : valid pointer to the listview structure
8637 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8639 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8641 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8643 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8645 if (uView == LVS_LIST)
8647 /* Apparently the "LIST" style is supposed to have the same
8648 * number of items in a column even if there is no scroll bar.
8649 * Since if a scroll bar already exists then the bottom is already
8650 * reduced, only reduce if the scroll bar does not currently exist.
8651 * The "2" is there to mimic the native control. I think it may be
8652 * related to either padding or edges. (GLA 7/2002)
8654 if (!(infoPtr->dwStyle & WS_HSCROLL))
8655 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8656 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8658 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8663 hl.prc = &infoPtr->rcList;
8665 Header_Layout(infoPtr->hwndHeader, &hl);
8667 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8669 infoPtr->rcList.top = max(wp.cy, 0);
8672 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8677 * Processes WM_STYLECHANGED messages.
8680 * [I] infoPtr : valid pointer to the listview structure
8681 * [I] wStyleType : window style type (normal or extended)
8682 * [I] lpss : window style information
8687 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8688 const STYLESTRUCT *lpss)
8690 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8691 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8693 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8694 wStyleType, lpss->styleOld, lpss->styleNew);
8696 if (wStyleType != GWL_STYLE) return 0;
8698 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8699 /* what if LVS_OWNERDATA changed? */
8700 /* or LVS_SINGLESEL */
8701 /* or LVS_SORT{AS,DES}CENDING */
8703 infoPtr->dwStyle = lpss->styleNew;
8705 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8706 ((lpss->styleNew & WS_HSCROLL) == 0))
8707 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8709 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8710 ((lpss->styleNew & WS_VSCROLL) == 0))
8711 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8713 if (uNewView != uOldView)
8715 SIZE oldIconSize = infoPtr->iconSize;
8718 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8719 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8721 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8722 SetRectEmpty(&infoPtr->rcFocus);
8724 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8725 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8727 if (uNewView == LVS_ICON)
8729 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8731 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8732 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8733 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8736 else if (uNewView == LVS_REPORT)
8741 hl.prc = &infoPtr->rcList;
8743 Header_Layout(infoPtr->hwndHeader, &hl);
8744 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8747 LISTVIEW_UpdateItemSize(infoPtr);
8750 if (uNewView == LVS_REPORT)
8751 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8753 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8754 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8755 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8757 /* update the size of the client area */
8758 LISTVIEW_UpdateSize(infoPtr);
8760 /* add scrollbars if needed */
8761 LISTVIEW_UpdateScroll(infoPtr);
8763 /* invalidate client area + erase background */
8764 LISTVIEW_InvalidateList(infoPtr);
8771 * Window procedure of the listview control.
8774 static LRESULT WINAPI
8775 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8777 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8779 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8781 if (!infoPtr && (uMsg != WM_CREATE))
8782 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8786 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8791 case LVM_APPROXIMATEVIEWRECT:
8792 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8793 LOWORD(lParam), HIWORD(lParam));
8795 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8797 /* case LVM_CANCELEDITLABEL: */
8799 case LVM_CREATEDRAGIMAGE:
8800 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
8802 case LVM_DELETEALLITEMS:
8803 return LISTVIEW_DeleteAllItems(infoPtr);
8805 case LVM_DELETECOLUMN:
8806 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8808 case LVM_DELETEITEM:
8809 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8811 case LVM_EDITLABELW:
8812 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8814 case LVM_EDITLABELA:
8815 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8817 /* case LVM_ENABLEGROUPVIEW: */
8819 case LVM_ENSUREVISIBLE:
8820 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8823 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8826 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8828 case LVM_GETBKCOLOR:
8829 return infoPtr->clrBk;
8831 /* case LVM_GETBKIMAGE: */
8833 case LVM_GETCALLBACKMASK:
8834 return infoPtr->uCallbackMask;
8836 case LVM_GETCOLUMNA:
8837 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8839 case LVM_GETCOLUMNW:
8840 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8842 case LVM_GETCOLUMNORDERARRAY:
8843 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8845 case LVM_GETCOLUMNWIDTH:
8846 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8848 case LVM_GETCOUNTPERPAGE:
8849 return LISTVIEW_GetCountPerPage(infoPtr);
8851 case LVM_GETEDITCONTROL:
8852 return (LRESULT)infoPtr->hwndEdit;
8854 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8855 return infoPtr->dwLvExStyle;
8857 /* case LVM_GETGROUPINFO: */
8859 /* case LVM_GETGROUPMETRICS: */
8862 return (LRESULT)infoPtr->hwndHeader;
8864 case LVM_GETHOTCURSOR:
8865 return (LRESULT)infoPtr->hHotCursor;
8867 case LVM_GETHOTITEM:
8868 return infoPtr->nHotItem;
8870 case LVM_GETHOVERTIME:
8871 return infoPtr->dwHoverTime;
8873 case LVM_GETIMAGELIST:
8874 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8876 /* case LVM_GETINSERTMARK: */
8878 /* case LVM_GETINSERTMARKCOLOR: */
8880 /* case LVM_GETINSERTMARKRECT: */
8882 case LVM_GETISEARCHSTRINGA:
8883 case LVM_GETISEARCHSTRINGW:
8884 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8888 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8891 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8893 case LVM_GETITEMCOUNT:
8894 return infoPtr->nItemCount;
8896 case LVM_GETITEMPOSITION:
8897 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8899 case LVM_GETITEMRECT:
8900 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8902 case LVM_GETITEMSPACING:
8903 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8905 case LVM_GETITEMSTATE:
8906 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8908 case LVM_GETITEMTEXTA:
8909 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8911 case LVM_GETITEMTEXTW:
8912 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8914 case LVM_GETNEXTITEM:
8915 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8917 case LVM_GETNUMBEROFWORKAREAS:
8918 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8922 if (!lParam) return FALSE;
8923 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8926 /* case LVM_GETOUTLINECOLOR: */
8928 /* case LVM_GETSELECTEDCOLUMN: */
8930 case LVM_GETSELECTEDCOUNT:
8931 return LISTVIEW_GetSelectedCount(infoPtr);
8933 case LVM_GETSELECTIONMARK:
8934 return infoPtr->nSelectionMark;
8936 case LVM_GETSTRINGWIDTHA:
8937 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8939 case LVM_GETSTRINGWIDTHW:
8940 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8942 case LVM_GETSUBITEMRECT:
8943 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8945 case LVM_GETTEXTBKCOLOR:
8946 return infoPtr->clrTextBk;
8948 case LVM_GETTEXTCOLOR:
8949 return infoPtr->clrText;
8951 /* case LVM_GETTILEINFO: */
8953 /* case LVM_GETTILEVIEWINFO: */
8955 case LVM_GETTOOLTIPS:
8956 return (LRESULT)infoPtr->hwndToolTip;
8958 case LVM_GETTOPINDEX:
8959 return LISTVIEW_GetTopIndex(infoPtr);
8961 /*case LVM_GETUNICODEFORMAT:
8962 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8965 /* case LVM_GETVIEW: */
8967 case LVM_GETVIEWRECT:
8968 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8970 case LVM_GETWORKAREAS:
8971 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8974 /* case LVM_HASGROUP: */
8977 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8979 case LVM_INSERTCOLUMNA:
8980 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8982 case LVM_INSERTCOLUMNW:
8983 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8985 /* case LVM_INSERTGROUP: */
8987 /* case LVM_INSERTGROUPSORTED: */
8989 case LVM_INSERTITEMA:
8990 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8992 case LVM_INSERTITEMW:
8993 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8995 /* case LVM_INSERTMARKHITTEST: */
8997 /* case LVM_ISGROUPVIEWENABLED: */
8999 /* case LVM_MAPIDTOINDEX: */
9001 /* case LVM_MAPINDEXTOID: */
9003 /* case LVM_MOVEGROUP: */
9005 /* case LVM_MOVEITEMTOGROUP: */
9007 case LVM_REDRAWITEMS:
9008 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9010 /* case LVM_REMOVEALLGROUPS: */
9012 /* case LVM_REMOVEGROUP: */
9015 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9017 case LVM_SETBKCOLOR:
9018 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9020 /* case LVM_SETBKIMAGE: */
9022 case LVM_SETCALLBACKMASK:
9023 infoPtr->uCallbackMask = (UINT)wParam;
9026 case LVM_SETCOLUMNA:
9027 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9029 case LVM_SETCOLUMNW:
9030 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9032 case LVM_SETCOLUMNORDERARRAY:
9033 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9035 case LVM_SETCOLUMNWIDTH:
9036 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9038 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9039 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9041 /* case LVM_SETGROUPINFO: */
9043 /* case LVM_SETGROUPMETRICS: */
9045 case LVM_SETHOTCURSOR:
9046 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9048 case LVM_SETHOTITEM:
9049 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9051 case LVM_SETHOVERTIME:
9052 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9054 case LVM_SETICONSPACING:
9055 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9057 case LVM_SETIMAGELIST:
9058 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9060 /* case LVM_SETINFOTIP: */
9062 /* case LVM_SETINSERTMARK: */
9064 /* case LVM_SETINSERTMARKCOLOR: */
9067 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9070 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9072 case LVM_SETITEMCOUNT:
9073 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9075 case LVM_SETITEMPOSITION:
9078 pt.x = (short)LOWORD(lParam);
9079 pt.y = (short)HIWORD(lParam);
9080 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9083 case LVM_SETITEMPOSITION32:
9084 if (lParam == 0) return FALSE;
9085 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9087 case LVM_SETITEMSTATE:
9088 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9090 case LVM_SETITEMTEXTA:
9091 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9093 case LVM_SETITEMTEXTW:
9094 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9096 /* case LVM_SETOUTLINECOLOR: */
9098 /* case LVM_SETSELECTEDCOLUMN: */
9100 case LVM_SETSELECTIONMARK:
9101 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9103 case LVM_SETTEXTBKCOLOR:
9104 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9106 case LVM_SETTEXTCOLOR:
9107 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9109 /* case LVM_SETTILEINFO: */
9111 /* case LVM_SETTILEVIEWINFO: */
9113 /* case LVM_SETTILEWIDTH: */
9115 case LVM_SETTOOLTIPS:
9116 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9118 /* case LVM_SETUNICODEFORMAT: */
9120 /* case LVM_SETVIEW: */
9122 /* case LVM_SETWORKAREAS: */
9124 /* case LVM_SORTGROUPS: */
9127 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9129 /* LVM_SORTITEMSEX: */
9131 case LVM_SUBITEMHITTEST:
9132 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9135 return LISTVIEW_Update(infoPtr, (INT)wParam);
9138 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9141 return LISTVIEW_Command(infoPtr, wParam, lParam);
9144 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9147 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9150 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9153 return (LRESULT)infoPtr->hFont;
9156 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9159 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9162 return LISTVIEW_KillFocus(infoPtr);
9164 case WM_LBUTTONDBLCLK:
9165 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9167 case WM_LBUTTONDOWN:
9168 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9171 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9174 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9177 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9180 return LISTVIEW_NCDestroy(infoPtr);
9183 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9184 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9187 case WM_NOTIFYFORMAT:
9188 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9191 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9193 case WM_RBUTTONDBLCLK:
9194 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9196 case WM_RBUTTONDOWN:
9197 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9200 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9203 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9208 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9211 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9214 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9217 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9219 case WM_STYLECHANGED:
9220 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9222 case WM_SYSCOLORCHANGE:
9223 COMCTL32_RefreshSysColors();
9226 /* case WM_TIMER: */
9229 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9232 if (wParam & (MK_SHIFT | MK_CONTROL))
9233 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9234 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9236 case WM_WINDOWPOSCHANGED:
9237 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9239 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9240 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9241 LISTVIEW_UpdateSize(infoPtr);
9242 LISTVIEW_UpdateScroll(infoPtr);
9244 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9246 /* case WM_WININICHANGE: */
9249 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9250 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9253 /* call default window procedure */
9254 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9262 * Registers the window class.
9270 void LISTVIEW_Register(void)
9274 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9275 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9276 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9277 wndClass.cbClsExtra = 0;
9278 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9279 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9280 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9281 wndClass.lpszClassName = WC_LISTVIEWW;
9282 RegisterClassW(&wndClass);
9287 * Unregisters the window class.
9295 void LISTVIEW_Unregister(void)
9297 UnregisterClassW(WC_LISTVIEWW, NULL);
9302 * Handle any WM_COMMAND messages
9305 * [I] infoPtr : valid pointer to the listview structure
9306 * [I] wParam : the first message parameter
9307 * [I] lParam : the second message parameter
9312 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9314 switch (HIWORD(wParam))
9319 * Adjust the edit window size
9322 HDC hdc = GetDC(infoPtr->hwndEdit);
9323 HFONT hFont, hOldFont = 0;
9328 if (!infoPtr->hwndEdit || !hdc) return 0;
9329 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9330 GetWindowRect(infoPtr->hwndEdit, &rect);
9332 /* Select font to get the right dimension of the string */
9333 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9336 hOldFont = SelectObject(hdc, hFont);
9339 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9341 TEXTMETRICW textMetric;
9343 /* Add Extra spacing for the next character */
9344 GetTextMetricsW(hdc, &textMetric);
9345 sz.cx += (textMetric.tmMaxCharWidth * 2);
9353 rect.bottom - rect.top,
9354 SWP_DRAWFRAME|SWP_NOMOVE);
9357 SelectObject(hdc, hOldFont);
9359 ReleaseDC(infoPtr->hwndSelf, hdc);
9365 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9374 * Subclassed edit control windproc function
9377 * [I] hwnd : the edit window handle
9378 * [I] uMsg : the message that is to be processed
9379 * [I] wParam : first message parameter
9380 * [I] lParam : second message parameter
9381 * [I] isW : TRUE if input is Unicode
9386 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9388 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9389 BOOL cancel = FALSE;
9391 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9392 hwnd, uMsg, wParam, lParam, isW);
9397 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9404 WNDPROC editProc = infoPtr->EditWndProc;
9405 infoPtr->EditWndProc = 0;
9406 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9407 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9411 if (VK_ESCAPE == (INT)wParam)
9416 else if (VK_RETURN == (INT)wParam)
9420 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9424 if (infoPtr->hwndEdit)
9426 LPWSTR buffer = NULL;
9428 infoPtr->hwndEdit = 0;
9431 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9435 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9437 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9438 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9442 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9444 if (buffer) Free(buffer);
9448 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9454 * Subclassed edit control Unicode windproc function
9457 * [I] hwnd : the edit window handle
9458 * [I] uMsg : the message that is to be processed
9459 * [I] wParam : first message parameter
9460 * [I] lParam : second message parameter
9464 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9466 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9471 * Subclassed edit control ANSI windproc function
9474 * [I] hwnd : the edit window handle
9475 * [I] uMsg : the message that is to be processed
9476 * [I] wParam : first message parameter
9477 * [I] lParam : second message parameter
9481 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9483 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9488 * Creates a subclassed edit cotrol
9491 * [I] infoPtr : valid pointer to the listview structure
9492 * [I] text : initial text for the edit
9493 * [I] style : the window style
9494 * [I] isW : TRUE if input is Unicode
9498 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9499 INT x, INT y, INT width, INT height, BOOL isW)
9501 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9506 TEXTMETRICW textMetric;
9507 HINSTANCE hinst = (HINSTANCE)GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9509 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9511 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9512 hdc = GetDC(infoPtr->hwndSelf);
9514 /* Select the font to get appropriate metric dimensions */
9515 if(infoPtr->hFont != 0)
9516 hOldFont = SelectObject(hdc, infoPtr->hFont);
9518 /*Get String Length in pixels */
9519 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9521 /*Add Extra spacing for the next character */
9522 GetTextMetricsW(hdc, &textMetric);
9523 sz.cx += (textMetric.tmMaxCharWidth * 2);
9525 if(infoPtr->hFont != 0)
9526 SelectObject(hdc, hOldFont);
9528 ReleaseDC(infoPtr->hwndSelf, hdc);
9530 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9532 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9534 if (!hedit) return 0;
9536 infoPtr->EditWndProc = (WNDPROC)
9537 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9538 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9540 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);