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));
3731 if (cdmode & CDRF_NOTIFYITEMDRAW)
3733 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3734 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3737 if (!(cditemmode & CDRF_SKIPDEFAULT))
3739 prepaint_setup (infoPtr, hdc, &nmlvcd);
3740 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3743 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3744 notify_postpaint(infoPtr, &nmlvcd);
3750 * Draws listview items when in report display mode.
3753 * [I] infoPtr : valid pointer to the listview structure
3754 * [I] hdc : device context handle
3755 * [I] cdmode : custom draw mode
3760 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3763 RECT rcClip, rcItem;
3764 POINT Origin, Position;
3770 /* figure out what to draw */
3771 rgntype = GetClipBox(hdc, &rcClip);
3772 if (rgntype == NULLREGION) return;
3774 /* Get scroll info once before loop */
3775 LISTVIEW_GetOrigin(infoPtr, &Origin);
3777 /* narrow down the columns we need to paint */
3778 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3780 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3781 if (rcItem.right + Origin.x >= rcClip.left) break;
3783 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3785 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3786 if (rcItem.left + Origin.x < rcClip.right) break;
3788 iterator_rangeitems(&j, colRange);
3790 /* in full row select, we _have_ to draw the main item */
3791 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3794 /* iterate through the invalidated rows */
3795 while(iterator_next(i))
3797 /* iterate through the invalidated columns */
3798 while(iterator_next(&j))
3800 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3801 Position.x += Origin.x;
3802 Position.y += Origin.y;
3804 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3806 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3808 rcItem.bottom = infoPtr->nItemHeight;
3809 OffsetRect(&rcItem, Position.x, Position.y);
3810 if (!RectVisible(hdc, &rcItem)) continue;
3813 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3816 iterator_destroy(&j);
3821 * Draws listview items when in list display mode.
3824 * [I] infoPtr : valid pointer to the listview structure
3825 * [I] hdc : device context handle
3826 * [I] cdmode : custom draw mode
3831 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3833 POINT Origin, Position;
3835 /* Get scroll info once before loop */
3836 LISTVIEW_GetOrigin(infoPtr, &Origin);
3838 while(iterator_prev(i))
3840 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3841 Position.x += Origin.x;
3842 Position.y += Origin.y;
3844 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3851 * Draws listview items.
3854 * [I] infoPtr : valid pointer to the listview structure
3855 * [I] hdc : device context handle
3860 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3862 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3863 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3864 NMLVCUSTOMDRAW nmlvcd;
3871 LISTVIEW_DUMP(infoPtr);
3873 infoPtr->bIsDrawing = TRUE;
3875 /* save dc values we're gonna trash while drawing */
3876 hOldFont = SelectObject(hdc, infoPtr->hFont);
3877 oldBkMode = GetBkMode(hdc);
3878 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3879 oldTextColor = GetTextColor(hdc);
3881 oldClrTextBk = infoPtr->clrTextBk;
3882 oldClrText = infoPtr->clrText;
3884 infoPtr->cditemmode = CDRF_DODEFAULT;
3886 GetClientRect(infoPtr->hwndSelf, &rcClient);
3887 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
3888 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3889 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3890 prepaint_setup(infoPtr, hdc, &nmlvcd);
3892 /* Use these colors to draw the items */
3893 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3894 infoPtr->clrText = nmlvcd.clrText;
3896 /* nothing to draw */
3897 if(infoPtr->nItemCount == 0) goto enddraw;
3899 /* figure out what we need to draw */
3900 iterator_visibleitems(&i, infoPtr, hdc);
3902 /* send cache hint notification */
3903 if (infoPtr->dwStyle & LVS_OWNERDATA)
3905 RANGE range = iterator_range(&i);
3908 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
3909 nmlv.iFrom = range.lower;
3910 nmlv.iTo = range.upper - 1;
3911 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3914 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3915 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
3918 if (uView == LVS_REPORT)
3919 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
3920 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3921 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
3923 /* if we have a focus rect, draw it */
3924 if (infoPtr->bFocus)
3925 DrawFocusRect(hdc, &infoPtr->rcFocus);
3927 iterator_destroy(&i);
3930 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3931 notify_postpaint(infoPtr, &nmlvcd);
3933 infoPtr->clrTextBk = oldClrTextBk;
3934 infoPtr->clrText = oldClrText;
3936 SelectObject(hdc, hOldFont);
3937 SetBkMode(hdc, oldBkMode);
3938 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3939 SetTextColor(hdc, oldTextColor);
3940 infoPtr->bIsDrawing = FALSE;
3946 * Calculates the approximate width and height of a given number of items.
3949 * [I] infoPtr : valid pointer to the listview structure
3950 * [I] nItemCount : number of items
3951 * [I] wWidth : width
3952 * [I] wHeight : height
3955 * Returns a DWORD. The width in the low word and the height in high word.
3957 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3958 WORD wWidth, WORD wHeight)
3960 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3961 INT nItemCountPerColumn = 1;
3962 INT nColumnCount = 0;
3963 DWORD dwViewRect = 0;
3965 if (nItemCount == -1)
3966 nItemCount = infoPtr->nItemCount;
3968 if (uView == LVS_LIST)
3970 if (wHeight == 0xFFFF)
3972 /* use current height */
3973 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3976 if (wHeight < infoPtr->nItemHeight)
3977 wHeight = infoPtr->nItemHeight;
3981 if (infoPtr->nItemHeight > 0)
3983 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3984 if (nItemCountPerColumn == 0)
3985 nItemCountPerColumn = 1;
3987 if (nItemCount % nItemCountPerColumn != 0)
3988 nColumnCount = nItemCount / nItemCountPerColumn;
3990 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3994 /* Microsoft padding magic */
3995 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3996 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3998 dwViewRect = MAKELONG(wWidth, wHeight);
4000 else if (uView == LVS_REPORT)
4001 FIXME("uView == LVS_REPORT: not implemented\n");
4002 else if (uView == LVS_SMALLICON)
4003 FIXME("uView == LVS_SMALLICON: not implemented\n");
4004 else if (uView == LVS_ICON)
4005 FIXME("uView == LVS_ICON: not implemented\n");
4013 * Create a drag image list for the specified item.
4016 * [I] infoPtr : valid pointer to the listview structure
4017 * [I] iItem : index of item
4018 * [O] lppt : Upperr-left corner of the image
4021 * Returns a handle to the image list if successful, NULL otherwise.
4023 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4029 HBITMAP hbmp, hOldbmp;
4030 HIMAGELIST dragList = 0;
4031 TRACE("iItem=%d Count=%d \n", iItem, infoPtr->nItemCount);
4033 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4036 rcItem.left = LVIR_BOUNDS;
4037 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4040 lppt->x = rcItem.left;
4041 lppt->y = rcItem.top;
4043 size.cx = rcItem.right - rcItem.left;
4044 size.cy = rcItem.bottom - rcItem.top;
4046 hdcOrig = GetDC(infoPtr->hwndSelf);
4047 hdc = CreateCompatibleDC(hdcOrig);
4048 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4049 hOldbmp = SelectObject(hdc, hbmp);
4051 rcItem.left = rcItem.top = 0;
4052 rcItem.right = size.cx;
4053 rcItem.bottom = size.cy;
4054 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4057 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4059 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4060 SelectObject(hdc, hOldbmp);
4061 ImageList_Add(dragList, hbmp, 0);
4064 SelectObject(hdc, hOldbmp);
4068 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4070 TRACE("ret=%p\n", dragList);
4078 * Removes all listview items and subitems.
4081 * [I] infoPtr : valid pointer to the listview structure
4087 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4090 HDPA hdpaSubItems = NULL;
4097 /* we do it directly, to avoid notifications */
4098 ranges_clear(infoPtr->selectionRanges);
4099 infoPtr->nSelectionMark = -1;
4100 infoPtr->nFocusedItem = -1;
4101 SetRectEmpty(&infoPtr->rcFocus);
4102 /* But we are supposed to leave nHotItem as is! */
4105 /* send LVN_DELETEALLITEMS notification */
4106 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4108 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4110 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4112 /* send LVN_DELETEITEM notification, if not supressed */
4113 if (!bSuppress) notify_deleteitem(infoPtr, i);
4114 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4116 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4117 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4119 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4120 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4123 DPA_Destroy(hdpaSubItems);
4124 DPA_DeletePtr(infoPtr->hdpaItems, i);
4126 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4127 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4128 infoPtr->nItemCount --;
4131 LISTVIEW_UpdateScroll(infoPtr);
4133 LISTVIEW_InvalidateList(infoPtr);
4140 * Scrolls, and updates the columns, when a column is changing width.
4143 * [I] infoPtr : valid pointer to the listview structure
4144 * [I] nColumn : column to scroll
4145 * [I] dx : amount of scroll, in pixels
4150 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4152 COLUMN_INFO *lpColumnInfo;
4156 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4157 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4158 rcCol = lpColumnInfo->rcHeader;
4159 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4160 rcCol.left = rcCol.right;
4162 /* ajust the other columns */
4163 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4165 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4166 lpColumnInfo->rcHeader.left += dx;
4167 lpColumnInfo->rcHeader.right += dx;
4170 /* do not update screen if not in report mode */
4171 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4173 /* if we have a focus, must first erase the focus rect */
4174 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4176 /* Need to reset the item width when inserting a new column */
4177 infoPtr->nItemWidth += dx;
4179 LISTVIEW_UpdateScroll(infoPtr);
4181 /* scroll to cover the deleted column, and invalidate for redraw */
4182 rcOld = infoPtr->rcList;
4183 rcOld.left = rcCol.left;
4184 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4186 /* we can restore focus now */
4187 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4192 * Removes a column from the listview control.
4195 * [I] infoPtr : valid pointer to the listview structure
4196 * [I] nColumn : column index
4202 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4206 TRACE("nColumn=%d\n", nColumn);
4208 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4209 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4211 /* While the MSDN specifically says that column zero should not be deleted,
4212 it does in fact work on WinNT, and at least one app depends on it. On
4213 WinNT, deleting column zero deletes the last column of items but the
4214 first header. Since no app will ever depend on that bizarre behavior,
4215 we just delete the last column including the header.
4218 nColumn = DPA_GetPtrCount(infoPtr->hdpaColumns) - 1;
4220 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4222 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4225 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4226 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4228 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4230 SUBITEM_INFO *lpSubItem, *lpDelItem;
4232 INT nItem, nSubItem, i;
4235 return LISTVIEW_DeleteAllItems(infoPtr);
4237 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4239 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4242 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4244 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4245 if (lpSubItem->iSubItem == nColumn)
4248 lpDelItem = lpSubItem;
4250 else if (lpSubItem->iSubItem > nColumn)
4252 lpSubItem->iSubItem--;
4256 /* if we found our subitem, zapp it */
4260 if (is_textW(lpDelItem->hdr.pszText))
4261 Free(lpDelItem->hdr.pszText);
4266 /* free dpa memory */
4267 DPA_DeletePtr(hdpaSubItems, nSubItem);
4272 /* update the other column info */
4273 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4280 * Invalidates the listview after an item's insertion or deletion.
4283 * [I] infoPtr : valid pointer to the listview structure
4284 * [I] nItem : item index
4285 * [I] dir : -1 if deleting, 1 if inserting
4290 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4292 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4293 INT nPerCol, nItemCol, nItemRow;
4297 /* if we don't refresh, what's the point of scrolling? */
4298 if (!is_redrawing(infoPtr)) return;
4300 assert (abs(dir) == 1);
4302 /* arrange icons if autoarrange is on */
4303 if (is_autoarrange(infoPtr))
4305 BOOL arrange = TRUE;
4306 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4307 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4308 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4311 /* scrollbars need updating */
4312 LISTVIEW_UpdateScroll(infoPtr);
4314 /* figure out the item's position */
4315 if (uView == LVS_REPORT)
4316 nPerCol = infoPtr->nItemCount + 1;
4317 else if (uView == LVS_LIST)
4318 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4319 else /* LVS_ICON, or LVS_SMALLICON */
4322 nItemCol = nItem / nPerCol;
4323 nItemRow = nItem % nPerCol;
4324 LISTVIEW_GetOrigin(infoPtr, &Origin);
4326 /* move the items below up a slot */
4327 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4328 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4329 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4330 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4331 OffsetRect(&rcScroll, Origin.x, Origin.y);
4332 TRACE("rcScroll=%s, dx=%d\n", debugrect(&rcScroll), dir * infoPtr->nItemHeight);
4333 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4335 TRACE("Scrolling rcScroll=%s, rcList=%s\n", debugrect(&rcScroll), debugrect(&infoPtr->rcList));
4336 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4337 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4340 /* report has only that column, so we're done */
4341 if (uView == LVS_REPORT) return;
4343 /* now for LISTs, we have to deal with the columns to the right */
4344 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4346 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4347 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4348 OffsetRect(&rcScroll, Origin.x, Origin.y);
4349 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4350 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4351 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4356 * Removes an item from the listview control.
4359 * [I] infoPtr : valid pointer to the listview structure
4360 * [I] nItem : item index
4366 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4368 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4371 TRACE("(nItem=%d)\n", nItem);
4373 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4375 /* remove selection, and focus */
4377 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4378 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4380 /* send LVN_DELETEITEM notification. */
4381 notify_deleteitem(infoPtr, nItem);
4383 /* we need to do this here, because we'll be deleting stuff */
4384 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4385 LISTVIEW_InvalidateItem(infoPtr, nItem);
4387 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4393 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4394 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4396 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4397 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4400 DPA_Destroy(hdpaSubItems);
4403 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4405 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4406 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4409 infoPtr->nItemCount--;
4410 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4412 /* now is the invalidation fun */
4413 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4420 * Callback implementation for editlabel control
4423 * [I] infoPtr : valid pointer to the listview structure
4424 * [I] pszText : modified text
4425 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4431 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4433 NMLVDISPINFOW dispInfo;
4435 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4437 ZeroMemory(&dispInfo, sizeof(dispInfo));
4438 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4439 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4440 dispInfo.item.iSubItem = 0;
4441 dispInfo.item.stateMask = ~0;
4442 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4443 /* add the text from the edit in */
4444 dispInfo.item.mask |= LVIF_TEXT;
4445 dispInfo.item.pszText = pszText;
4446 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4448 /* Do we need to update the Item Text */
4449 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4450 if (!pszText) return TRUE;
4452 ZeroMemory(&dispInfo, sizeof(dispInfo));
4453 dispInfo.item.mask = LVIF_TEXT;
4454 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4455 dispInfo.item.iSubItem = 0;
4456 dispInfo.item.pszText = pszText;
4457 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4458 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4463 * Begin in place editing of specified list view item
4466 * [I] infoPtr : valid pointer to the listview structure
4467 * [I] nItem : item index
4468 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4474 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4476 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4477 NMLVDISPINFOW dispInfo;
4480 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4482 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4483 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4485 infoPtr->nEditLabelItem = nItem;
4487 /* Is the EditBox still there, if so remove it */
4488 if(infoPtr->hwndEdit != 0)
4490 SetFocus(infoPtr->hwndSelf);
4491 infoPtr->hwndEdit = 0;
4494 LISTVIEW_SetSelection(infoPtr, nItem);
4495 LISTVIEW_SetItemFocus(infoPtr, nItem);
4496 LISTVIEW_InvalidateItem(infoPtr, nItem);
4498 rect.left = LVIR_LABEL;
4499 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4501 ZeroMemory(&dispInfo, sizeof(dispInfo));
4502 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4503 dispInfo.item.iItem = nItem;
4504 dispInfo.item.iSubItem = 0;
4505 dispInfo.item.stateMask = ~0;
4506 dispInfo.item.pszText = szDispText;
4507 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4508 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4510 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4511 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4512 if (!infoPtr->hwndEdit) return 0;
4514 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4516 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4517 infoPtr->hwndEdit = 0;
4521 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4522 SetFocus(infoPtr->hwndEdit);
4523 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4524 return infoPtr->hwndEdit;
4530 * Ensures the specified item is visible, scrolling into view if necessary.
4533 * [I] infoPtr : valid pointer to the listview structure
4534 * [I] nItem : item index
4535 * [I] bPartial : partially or entirely visible
4541 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4543 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4544 INT nScrollPosHeight = 0;
4545 INT nScrollPosWidth = 0;
4546 INT nHorzAdjust = 0;
4547 INT nVertAdjust = 0;
4550 RECT rcItem, rcTemp;
4552 rcItem.left = LVIR_BOUNDS;
4553 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4555 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4557 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4559 /* scroll left/right, but in LVS_REPORT mode */
4560 if (uView == LVS_LIST)
4561 nScrollPosWidth = infoPtr->nItemWidth;
4562 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4563 nScrollPosWidth = 1;
4565 if (rcItem.left < infoPtr->rcList.left)
4568 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4573 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4577 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4579 /* scroll up/down, but not in LVS_LIST mode */
4580 if (uView == LVS_REPORT)
4581 nScrollPosHeight = infoPtr->nItemHeight;
4582 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4583 nScrollPosHeight = 1;
4585 if (rcItem.top < infoPtr->rcList.top)
4588 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4593 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4597 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4599 if (nScrollPosWidth)
4601 INT diff = nHorzDiff / nScrollPosWidth;
4602 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4603 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4606 if (nScrollPosHeight)
4608 INT diff = nVertDiff / nScrollPosHeight;
4609 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4610 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4618 * Searches for an item with specific characteristics.
4621 * [I] hwnd : window handle
4622 * [I] nStart : base item index
4623 * [I] lpFindInfo : item information to look for
4626 * SUCCESS : index of item
4629 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4630 const LVFINDINFOW *lpFindInfo)
4632 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4633 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4634 BOOL bWrap = FALSE, bNearest = FALSE;
4635 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4636 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4637 POINT Position, Destination;
4640 if (!lpFindInfo || nItem < 0) return -1;
4643 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4645 lvItem.mask |= LVIF_TEXT;
4646 lvItem.pszText = szDispText;
4647 lvItem.cchTextMax = DISP_TEXT_SIZE;
4650 if (lpFindInfo->flags & LVFI_WRAP)
4653 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4654 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4659 LISTVIEW_GetOrigin(infoPtr, &Origin);
4660 Destination.x = lpFindInfo->pt.x - Origin.x;
4661 Destination.y = lpFindInfo->pt.y - Origin.y;
4662 switch(lpFindInfo->vkDirection)
4664 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4665 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4666 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4667 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4668 case VK_HOME: Destination.x = Destination.y = 0; break;
4669 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4670 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4672 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4673 Destination.x = rcArea.right;
4674 Destination.y = rcArea.bottom;
4676 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4681 /* if LVFI_PARAM is specified, all other flags are ignored */
4682 if (lpFindInfo->flags & LVFI_PARAM)
4684 lvItem.mask |= LVIF_PARAM;
4686 lvItem.mask &= ~LVIF_TEXT;
4690 for (; nItem < nLast; nItem++)
4692 lvItem.iItem = nItem;
4693 lvItem.iSubItem = 0;
4694 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4696 if (lvItem.mask & LVIF_PARAM)
4698 if (lpFindInfo->lParam == lvItem.lParam)
4704 if (lvItem.mask & LVIF_TEXT)
4706 if (lpFindInfo->flags & LVFI_PARTIAL)
4708 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4712 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4716 if (!bNearest) return nItem;
4718 /* This is very inefficient. To do a good job here,
4719 * we need a sorted array of (x,y) item positions */
4720 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4722 /* compute the distance^2 to the destination */
4723 xdist = Destination.x - Position.x;
4724 ydist = Destination.y - Position.y;
4725 dist = xdist * xdist + ydist * ydist;
4727 /* remember the distance, and item if it's closer */
4731 nNearestItem = nItem;
4738 nLast = min(nStart + 1, infoPtr->nItemCount);
4743 return nNearestItem;
4748 * Searches for an item with specific characteristics.
4751 * [I] hwnd : window handle
4752 * [I] nStart : base item index
4753 * [I] lpFindInfo : item information to look for
4756 * SUCCESS : index of item
4759 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4760 const LVFINDINFOA *lpFindInfo)
4762 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4766 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4767 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4768 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4769 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4775 * Retrieves the background image of the listview control.
4778 * [I] infoPtr : valid pointer to the listview structure
4779 * [O] lpBkImage : background image attributes
4785 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4787 /* FIXME (listview, "empty stub!\n"); */
4793 * Retrieves column attributes.
4796 * [I] infoPtr : valid pointer to the listview structure
4797 * [I] nColumn : column index
4798 * [IO] lpColumn : column information
4799 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4800 * otherwise it is in fact a LPLVCOLUMNA
4806 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4808 COLUMN_INFO *lpColumnInfo;
4811 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4812 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4814 /* initialize memory */
4815 ZeroMemory(&hdi, sizeof(hdi));
4817 if (lpColumn->mask & LVCF_TEXT)
4819 hdi.mask |= HDI_TEXT;
4820 hdi.pszText = lpColumn->pszText;
4821 hdi.cchTextMax = lpColumn->cchTextMax;
4824 if (lpColumn->mask & LVCF_IMAGE)
4825 hdi.mask |= HDI_IMAGE;
4827 if (lpColumn->mask & LVCF_ORDER)
4828 hdi.mask |= HDI_ORDER;
4830 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4832 if (lpColumn->mask & LVCF_FMT)
4833 lpColumn->fmt = lpColumnInfo->fmt;
4835 if (lpColumn->mask & LVCF_WIDTH)
4836 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4838 if (lpColumn->mask & LVCF_IMAGE)
4839 lpColumn->iImage = hdi.iImage;
4841 if (lpColumn->mask & LVCF_ORDER)
4842 lpColumn->iOrder = hdi.iOrder;
4848 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4855 /* FIXME: little hack */
4856 for (i = 0; i < iCount; i++)
4864 * Retrieves the column width.
4867 * [I] infoPtr : valid pointer to the listview structure
4868 * [I] int : column index
4871 * SUCCESS : column width
4874 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4876 INT nColumnWidth = 0;
4879 TRACE("nColumn=%d\n", nColumn);
4881 /* we have a 'column' in LIST and REPORT mode only */
4882 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4885 nColumnWidth = infoPtr->nItemWidth;
4888 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
4889 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
4890 nColumnWidth = rcHeader.right - rcHeader.left;
4894 TRACE("nColumnWidth=%d\n", nColumnWidth);
4895 return nColumnWidth;
4900 * In list or report display mode, retrieves the number of items that can fit
4901 * vertically in the visible area. In icon or small icon display mode,
4902 * retrieves the total number of visible items.
4905 * [I] infoPtr : valid pointer to the listview structure
4908 * Number of fully visible items.
4910 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4912 switch (infoPtr->dwStyle & LVS_TYPEMASK)
4916 return infoPtr->nItemCount;
4918 return LISTVIEW_GetCountPerColumn(infoPtr);
4920 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
4928 * Retrieves an image list handle.
4931 * [I] infoPtr : valid pointer to the listview structure
4932 * [I] nImageList : image list identifier
4935 * SUCCESS : image list handle
4938 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4942 case LVSIL_NORMAL: return infoPtr->himlNormal;
4943 case LVSIL_SMALL: return infoPtr->himlSmall;
4944 case LVSIL_STATE: return infoPtr->himlState;
4949 /* LISTVIEW_GetISearchString */
4953 * Retrieves item attributes.
4956 * [I] hwnd : window handle
4957 * [IO] lpLVItem : item info
4958 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4959 * if FALSE, the lpLVItem is a LPLVITEMA.
4962 * This is the internal 'GetItem' interface -- it tries to
4963 * be smart, and avoids text copies, if possible, by modifing
4964 * lpLVItem->pszText to point to the text string. Please note
4965 * that this is not always possible (e.g. OWNERDATA), so on
4966 * entry you *must* supply valid values for pszText, and cchTextMax.
4967 * The only difference to the documented interface is that upon
4968 * return, you should use *only* the lpLVItem->pszText, rather than
4969 * the buffer pointer you provided on input. Most code already does
4970 * that, so it's not a problem.
4971 * For the two cases when the text must be copied (that is,
4972 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4978 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4980 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
4981 NMLVDISPINFOW dispInfo;
4986 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4988 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4991 if (lpLVItem->mask == 0) return TRUE;
4993 /* a quick optimization if all we're asked is the focus state
4994 * these queries are worth optimising since they are common,
4995 * and can be answered in constant time, without the heavy accesses */
4996 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
4997 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4999 lpLVItem->state = 0;
5000 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5001 lpLVItem->state |= LVIS_FOCUSED;
5005 ZeroMemory(&dispInfo, sizeof(dispInfo));
5007 /* if the app stores all the data, handle it separately */
5008 if (infoPtr->dwStyle & LVS_OWNERDATA)
5010 dispInfo.item.state = 0;
5012 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5013 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5015 /* NOTE: copy only fields which we _know_ are initialized, some apps
5016 * depend on the uninitialized fields being 0 */
5017 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5018 dispInfo.item.iItem = lpLVItem->iItem;
5019 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5020 if (lpLVItem->mask & LVIF_TEXT)
5022 dispInfo.item.pszText = lpLVItem->pszText;
5023 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5025 if (lpLVItem->mask & LVIF_STATE)
5026 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5027 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5028 dispInfo.item.stateMask = lpLVItem->stateMask;
5029 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5031 /* full size structure expected - _WIN32IE >= 0x560 */
5032 *lpLVItem = dispInfo.item;
5034 else if (lpLVItem->mask & LVIF_INDENT)
5036 /* indent member expected - _WIN32IE >= 0x300 */
5037 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5041 /* minimal structure expected */
5042 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5044 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5047 /* make sure lParam is zeroed out */
5048 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5050 /* we store only a little state, so if we're not asked, we're done */
5051 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
5053 /* if focus is handled by us, report it */
5054 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5056 lpLVItem->state &= ~LVIS_FOCUSED;
5057 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5058 lpLVItem->state |= LVIS_FOCUSED;
5061 /* and do the same for selection, if we handle it */
5062 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5064 lpLVItem->state &= ~LVIS_SELECTED;
5065 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5066 lpLVItem->state |= LVIS_SELECTED;
5072 /* find the item and subitem structures before we proceed */
5073 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5074 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5077 if (lpLVItem->iSubItem)
5079 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5080 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5083 pItemHdr = &lpItem->hdr;
5085 /* Do we need to query the state from the app? */
5086 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
5088 dispInfo.item.mask |= LVIF_STATE;
5089 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5092 /* Do we need to enquire about the image? */
5093 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5094 (lpLVItem->iSubItem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5096 dispInfo.item.mask |= LVIF_IMAGE;
5097 dispInfo.item.iImage = I_IMAGECALLBACK;
5100 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5101 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5103 dispInfo.item.mask |= LVIF_TEXT;
5104 dispInfo.item.pszText = lpLVItem->pszText;
5105 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5106 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5107 *dispInfo.item.pszText = '\0';
5110 /* If we don't have all the requested info, query the application */
5111 if (dispInfo.item.mask != 0)
5113 dispInfo.item.iItem = lpLVItem->iItem;
5114 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5115 dispInfo.item.lParam = lpItem->lParam;
5116 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5117 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5120 /* we should not store values for subitems */
5121 if (lpLVItem->iSubItem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5123 /* Now, handle the iImage field */
5124 if (dispInfo.item.mask & LVIF_IMAGE)
5126 lpLVItem->iImage = dispInfo.item.iImage;
5127 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5128 pItemHdr->iImage = dispInfo.item.iImage;
5130 else if (lpLVItem->mask & LVIF_IMAGE)
5132 if(lpLVItem->iSubItem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5133 lpLVItem->iImage = pItemHdr->iImage;
5135 lpLVItem->iImage = 0;
5138 /* The pszText field */
5139 if (dispInfo.item.mask & LVIF_TEXT)
5141 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5142 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5144 lpLVItem->pszText = dispInfo.item.pszText;
5146 else if (lpLVItem->mask & LVIF_TEXT)
5148 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5149 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5152 /* if this is a subitem, we're done */
5153 if (lpLVItem->iSubItem) return TRUE;
5155 /* Next is the lParam field */
5156 if (dispInfo.item.mask & LVIF_PARAM)
5158 lpLVItem->lParam = dispInfo.item.lParam;
5159 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5160 lpItem->lParam = dispInfo.item.lParam;
5162 else if (lpLVItem->mask & LVIF_PARAM)
5163 lpLVItem->lParam = lpItem->lParam;
5165 /* ... the state field (this one is different due to uCallbackmask) */
5166 if (lpLVItem->mask & LVIF_STATE)
5168 lpLVItem->state = lpItem->state;
5169 if (dispInfo.item.mask & LVIF_STATE)
5171 lpLVItem->state &= ~dispInfo.item.stateMask;
5172 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5174 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5176 lpLVItem->state &= ~LVIS_FOCUSED;
5177 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5178 lpLVItem->state |= LVIS_FOCUSED;
5180 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5182 lpLVItem->state &= ~LVIS_SELECTED;
5183 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5184 lpLVItem->state |= LVIS_SELECTED;
5188 /* and last, but not least, the indent field */
5189 if (lpLVItem->mask & LVIF_INDENT)
5190 lpLVItem->iIndent = lpItem->iIndent;
5197 * Retrieves item attributes.
5200 * [I] hwnd : window handle
5201 * [IO] lpLVItem : item info
5202 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5203 * if FALSE, the lpLVItem is a LPLVITEMA.
5206 * This is the external 'GetItem' interface -- it properly copies
5207 * the text in the provided buffer.
5213 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5218 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5221 pszText = lpLVItem->pszText;
5222 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5223 if (bResult && lpLVItem->pszText != pszText)
5224 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5225 lpLVItem->pszText = pszText;
5233 * Retrieves the position (upper-left) of the listview control item.
5234 * Note that for LVS_ICON style, the upper-left is that of the icon
5235 * and not the bounding box.
5238 * [I] infoPtr : valid pointer to the listview structure
5239 * [I] nItem : item index
5240 * [O] lpptPosition : coordinate information
5246 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5248 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5251 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5253 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5255 LISTVIEW_GetOrigin(infoPtr, &Origin);
5256 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5258 if (uView == LVS_ICON)
5260 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5261 lpptPosition->y += ICON_TOP_PADDING;
5263 lpptPosition->x += Origin.x;
5264 lpptPosition->y += Origin.y;
5266 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5273 * Retrieves the bounding rectangle for a listview control item.
5276 * [I] infoPtr : valid pointer to the listview structure
5277 * [I] nItem : item index
5278 * [IO] lprc : bounding rectangle coordinates
5279 * lprc->left specifies the portion of the item for which the bounding
5280 * rectangle will be retrieved.
5282 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5283 * including the icon and label.
5286 * * Experiment shows that native control returns:
5287 * * width = min (48, length of text line)
5288 * * .left = position.x - (width - iconsize.cx)/2
5289 * * .right = .left + width
5290 * * height = #lines of text * ntmHeight + icon height + 8
5291 * * .top = position.y - 2
5292 * * .bottom = .top + height
5293 * * separation between items .y = itemSpacing.cy - height
5294 * * .x = itemSpacing.cx - width
5295 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5298 * * Experiment shows that native control returns:
5299 * * width = iconSize.cx + 16
5300 * * .left = position.x - (width - iconsize.cx)/2
5301 * * .right = .left + width
5302 * * height = iconSize.cy + 4
5303 * * .top = position.y - 2
5304 * * .bottom = .top + height
5305 * * separation between items .y = itemSpacing.cy - height
5306 * * .x = itemSpacing.cx - width
5307 * LVIR_LABEL Returns the bounding rectangle of the item text.
5310 * * Experiment shows that native control returns:
5311 * * width = text length
5312 * * .left = position.x - width/2
5313 * * .right = .left + width
5314 * * height = ntmH * linecount + 2
5315 * * .top = position.y + iconSize.cy + 6
5316 * * .bottom = .top + height
5317 * * separation between items .y = itemSpacing.cy - height
5318 * * .x = itemSpacing.cx - width
5319 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5320 * rectangles, but excludes columns in report view.
5327 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5328 * upon whether the window has the focus currently and on whether the item
5329 * is the one with the focus. Ensure that the control's record of which
5330 * item has the focus agrees with the items' records.
5332 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5334 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5335 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5336 BOOL doLabel = TRUE, oversizedBox = FALSE;
5337 POINT Position, Origin;
5341 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5343 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5345 LISTVIEW_GetOrigin(infoPtr, &Origin);
5346 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5348 /* Be smart and try to figure out the minimum we have to do */
5349 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5350 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5351 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5352 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5353 oversizedBox = TRUE;
5355 /* get what we need from the item before hand, so we make
5356 * only one request. This can speed up things, if data
5357 * is stored on the app side */
5359 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5360 if (doLabel) lvItem.mask |= LVIF_TEXT;
5361 lvItem.iItem = nItem;
5362 lvItem.iSubItem = 0;
5363 lvItem.pszText = szDispText;
5364 lvItem.cchTextMax = DISP_TEXT_SIZE;
5365 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5366 /* we got the state already up, simulate it here, to avoid a reget */
5367 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5369 lvItem.mask |= LVIF_STATE;
5370 lvItem.stateMask = LVIS_FOCUSED;
5371 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5374 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5375 lprc->left = LVIR_BOUNDS;
5379 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5383 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5387 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5390 case LVIR_SELECTBOUNDS:
5391 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5392 UnionRect(lprc, lprc, &label_rect);
5396 WARN("Unknown value: %ld\n", lprc->left);
5400 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5402 TRACE(" rect=%s\n", debugrect(lprc));
5409 * Retrieves the spacing between listview control items.
5412 * [I] infoPtr : valid pointer to the listview structure
5413 * [IO] lprc : rectangle to receive the output
5414 * on input, lprc->top = nSubItem
5415 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5417 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5418 * not only those of the first column.
5419 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5425 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5430 if (!lprc) return FALSE;
5432 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5433 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5435 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5437 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5439 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5442 lvItem.iItem = nItem;
5443 lvItem.iSubItem = lprc->top;
5445 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5449 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5454 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5458 ERR("Unknown bounds=%ld\n", lprc->left);
5462 OffsetRect(lprc, Position.x, Position.y);
5469 * Retrieves the width of a label.
5472 * [I] infoPtr : valid pointer to the listview structure
5475 * SUCCESS : string width (in pixels)
5478 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5480 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5483 TRACE("(nItem=%d)\n", nItem);
5485 lvItem.mask = LVIF_TEXT;
5486 lvItem.iItem = nItem;
5487 lvItem.iSubItem = 0;
5488 lvItem.pszText = szDispText;
5489 lvItem.cchTextMax = DISP_TEXT_SIZE;
5490 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5492 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5497 * Retrieves the spacing between listview control items.
5500 * [I] infoPtr : valid pointer to the listview structure
5501 * [I] bSmall : flag for small or large icon
5504 * Horizontal + vertical spacing
5506 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5512 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5516 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5517 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5519 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5526 * Retrieves the state of a listview control item.
5529 * [I] infoPtr : valid pointer to the listview structure
5530 * [I] nItem : item index
5531 * [I] uMask : state mask
5534 * State specified by the mask.
5536 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5540 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5542 lvItem.iItem = nItem;
5543 lvItem.iSubItem = 0;
5544 lvItem.mask = LVIF_STATE;
5545 lvItem.stateMask = uMask;
5546 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5548 return lvItem.state & uMask;
5553 * Retrieves the text of a listview control item or subitem.
5556 * [I] hwnd : window handle
5557 * [I] nItem : item index
5558 * [IO] lpLVItem : item information
5559 * [I] isW : TRUE if lpLVItem is Unicode
5562 * SUCCESS : string length
5565 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5567 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5569 lpLVItem->mask = LVIF_TEXT;
5570 lpLVItem->iItem = nItem;
5571 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5573 return textlenT(lpLVItem->pszText, isW);
5578 * Searches for an item based on properties + relationships.
5581 * [I] infoPtr : valid pointer to the listview structure
5582 * [I] nItem : item index
5583 * [I] uFlags : relationship flag
5586 * SUCCESS : item index
5589 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5591 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5593 LVFINDINFOW lvFindInfo;
5594 INT nCountPerColumn;
5598 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5599 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5601 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5603 if (uFlags & LVNI_CUT)
5606 if (uFlags & LVNI_DROPHILITED)
5607 uMask |= LVIS_DROPHILITED;
5609 if (uFlags & LVNI_FOCUSED)
5610 uMask |= LVIS_FOCUSED;
5612 if (uFlags & LVNI_SELECTED)
5613 uMask |= LVIS_SELECTED;
5615 /* if we're asked for the focused item, that's only one,
5616 * so it's worth optimizing */
5617 if (uFlags & LVNI_FOCUSED)
5619 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5620 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5623 if (uFlags & LVNI_ABOVE)
5625 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5630 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5636 /* Special case for autoarrange - move 'til the top of a list */
5637 if (is_autoarrange(infoPtr))
5639 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5640 while (nItem - nCountPerRow >= 0)
5642 nItem -= nCountPerRow;
5643 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5648 lvFindInfo.flags = LVFI_NEARESTXY;
5649 lvFindInfo.vkDirection = VK_UP;
5650 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5651 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5653 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5658 else if (uFlags & LVNI_BELOW)
5660 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5662 while (nItem < infoPtr->nItemCount)
5665 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5671 /* Special case for autoarrange - move 'til the bottom of a list */
5672 if (is_autoarrange(infoPtr))
5674 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5675 while (nItem + nCountPerRow < infoPtr->nItemCount )
5677 nItem += nCountPerRow;
5678 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5683 lvFindInfo.flags = LVFI_NEARESTXY;
5684 lvFindInfo.vkDirection = VK_DOWN;
5685 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5686 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5688 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5693 else if (uFlags & LVNI_TOLEFT)
5695 if (uView == LVS_LIST)
5697 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5698 while (nItem - nCountPerColumn >= 0)
5700 nItem -= nCountPerColumn;
5701 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5705 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5707 /* Special case for autoarrange - move 'ti the beginning of a row */
5708 if (is_autoarrange(infoPtr))
5710 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5711 while (nItem % nCountPerRow > 0)
5714 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5719 lvFindInfo.flags = LVFI_NEARESTXY;
5720 lvFindInfo.vkDirection = VK_LEFT;
5721 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5722 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5724 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5729 else if (uFlags & LVNI_TORIGHT)
5731 if (uView == LVS_LIST)
5733 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5734 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5736 nItem += nCountPerColumn;
5737 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5741 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5743 /* Special case for autoarrange - move 'til the end of a row */
5744 if (is_autoarrange(infoPtr))
5746 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5747 while (nItem % nCountPerRow < nCountPerRow - 1 )
5750 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5755 lvFindInfo.flags = LVFI_NEARESTXY;
5756 lvFindInfo.vkDirection = VK_RIGHT;
5757 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5758 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5760 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5769 /* search by index */
5770 for (i = nItem; i < infoPtr->nItemCount; i++)
5772 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5780 /* LISTVIEW_GetNumberOfWorkAreas */
5784 * Retrieves the origin coordinates when in icon or small icon display mode.
5787 * [I] infoPtr : valid pointer to the listview structure
5788 * [O] lpptOrigin : coordinate information
5793 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5795 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5796 INT nHorzPos = 0, nVertPos = 0;
5797 SCROLLINFO scrollInfo;
5799 scrollInfo.cbSize = sizeof(SCROLLINFO);
5800 scrollInfo.fMask = SIF_POS;
5802 if ((infoPtr->dwStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5803 nHorzPos = scrollInfo.nPos;
5804 if ((infoPtr->dwStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5805 nVertPos = scrollInfo.nPos;
5807 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5809 lpptOrigin->x = infoPtr->rcList.left;
5810 lpptOrigin->y = infoPtr->rcList.top;
5811 if (uView == LVS_LIST)
5812 nHorzPos *= infoPtr->nItemWidth;
5813 else if (uView == LVS_REPORT)
5814 nVertPos *= infoPtr->nItemHeight;
5816 lpptOrigin->x -= nHorzPos;
5817 lpptOrigin->y -= nVertPos;
5819 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5824 * Retrieves the width of a string.
5827 * [I] hwnd : window handle
5828 * [I] lpszText : text string to process
5829 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5832 * SUCCESS : string width (in pixels)
5835 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5840 if (is_textT(lpszText, isW))
5842 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5843 HDC hdc = GetDC(infoPtr->hwndSelf);
5844 HFONT hOldFont = SelectObject(hdc, hFont);
5847 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5849 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5850 SelectObject(hdc, hOldFont);
5851 ReleaseDC(infoPtr->hwndSelf, hdc);
5853 return stringSize.cx;
5858 * Determines which listview item is located at the specified position.
5861 * [I] infoPtr : valid pointer to the listview structure
5862 * [IO] lpht : hit test information
5863 * [I] subitem : fill out iSubItem.
5864 * [I] select : return the index only if the hit selects the item
5867 * (mm 20001022): We must not allow iSubItem to be touched, for
5868 * an app might pass only a structure with space up to iItem!
5869 * (MS Office 97 does that for instance in the file open dialog)
5872 * SUCCESS : item index
5875 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5877 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5878 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5879 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5880 POINT Origin, Position, opt;
5885 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5889 if (subitem) lpht->iSubItem = 0;
5891 if (infoPtr->rcList.left > lpht->pt.x)
5892 lpht->flags |= LVHT_TOLEFT;
5893 else if (infoPtr->rcList.right < lpht->pt.x)
5894 lpht->flags |= LVHT_TORIGHT;
5896 if (infoPtr->rcList.top > lpht->pt.y)
5897 lpht->flags |= LVHT_ABOVE;
5898 else if (infoPtr->rcList.bottom < lpht->pt.y)
5899 lpht->flags |= LVHT_BELOW;
5901 TRACE("lpht->flags=0x%x\n", lpht->flags);
5902 if (lpht->flags) return -1;
5904 lpht->flags |= LVHT_NOWHERE;
5906 LISTVIEW_GetOrigin(infoPtr, &Origin);
5908 /* first deal with the large items */
5909 rcSearch.left = lpht->pt.x;
5910 rcSearch.top = lpht->pt.y;
5911 rcSearch.right = rcSearch.left + 1;
5912 rcSearch.bottom = rcSearch.top + 1;
5914 iterator_frameditems(&i, infoPtr, &rcSearch);
5915 iterator_next(&i); /* go to first item in the sequence */
5917 iterator_destroy(&i);
5919 TRACE("lpht->iItem=%d\n", iItem);
5920 if (iItem == -1) return -1;
5922 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5923 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5924 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5925 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5926 lvItem.iItem = iItem;
5927 lvItem.iSubItem = 0;
5928 lvItem.pszText = szDispText;
5929 lvItem.cchTextMax = DISP_TEXT_SIZE;
5930 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5931 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5933 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
5934 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
5935 opt.x = lpht->pt.x - Position.x - Origin.x;
5936 opt.y = lpht->pt.y - Position.y - Origin.y;
5938 if (uView == LVS_REPORT)
5941 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5942 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5943 if (!PtInRect(&rcBounds, opt)) return -1;
5945 if (PtInRect(&rcIcon, opt))
5946 lpht->flags |= LVHT_ONITEMICON;
5947 else if (PtInRect(&rcLabel, opt))
5948 lpht->flags |= LVHT_ONITEMLABEL;
5949 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5950 lpht->flags |= LVHT_ONITEMSTATEICON;
5951 if (lpht->flags & LVHT_ONITEM)
5952 lpht->flags &= ~LVHT_NOWHERE;
5954 TRACE("lpht->flags=0x%x\n", lpht->flags);
5955 if (uView == LVS_REPORT && subitem)
5959 rcBounds.right = rcBounds.left;
5960 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
5962 rcBounds.left = rcBounds.right;
5963 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5964 if (PtInRect(&rcBounds, opt))
5972 if (select && !(uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)))
5974 if (uView == LVS_REPORT)
5976 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5977 UnionRect(&rcBounds, &rcBounds, &rcState);
5979 if (!PtInRect(&rcBounds, opt)) iItem = -1;
5981 return lpht->iItem = iItem;
5985 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5986 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5987 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5988 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5989 their own sort proc. when sending LVM_SORTITEMS.
5992 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5994 LVS_SORTXXX must be specified,
5995 LVS_OWNERDRAW is not set,
5996 <item>.pszText is not LPSTR_TEXTCALLBACK.
5998 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5999 are sorted based on item text..."
6001 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6003 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6004 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6005 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6007 /* if we're sorting descending, negate the return value */
6008 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6013 * Inserts a new item in the listview control.
6016 * [I] infoPtr : valid pointer to the listview structure
6017 * [I] lpLVItem : item information
6018 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6021 * SUCCESS : new item index
6024 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6026 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6031 BOOL is_sorted, has_changed;
6034 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6036 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6038 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6039 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1;
6041 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6043 if ( !(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO))) )
6046 /* insert item in listview control data structure */
6047 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6048 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6050 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6051 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6053 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6054 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6055 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6056 if (nItem == -1) goto fail;
6057 infoPtr->nItemCount++;
6059 /* shift indices first so they don't get tangled */
6060 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6062 /* set the item attributes */
6063 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6065 /* full size structure expected - _WIN32IE >= 0x560 */
6068 else if (lpLVItem->mask & LVIF_INDENT)
6070 /* indent member expected - _WIN32IE >= 0x300 */
6071 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6075 /* minimal structure expected */
6076 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6079 item.state &= ~LVIS_STATEIMAGEMASK;
6080 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6082 /* if we're sorted, sort the list, and update the index */
6085 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6086 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6087 assert(nItem != -1);
6090 /* make room for the position, if we are in the right mode */
6091 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6093 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6095 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6097 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6102 /* send LVN_INSERTITEM notification */
6103 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6105 nmlv.lParam = lpItem->lParam;
6106 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6108 /* align items (set position of each item) */
6109 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6113 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6114 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6116 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6118 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6121 /* now is the invalidation fun */
6122 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6126 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6127 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6128 infoPtr->nItemCount--;
6130 DPA_DeletePtr(hdpaSubItems, 0);
6131 DPA_Destroy (hdpaSubItems);
6138 * Redraws a range of items.
6141 * [I] infoPtr : valid pointer to the listview structure
6142 * [I] nFirst : first item
6143 * [I] nLast : last item
6149 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6153 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6154 max(nFirst, nLast) >= infoPtr->nItemCount)
6157 for (i = nFirst; i <= nLast; i++)
6158 LISTVIEW_InvalidateItem(infoPtr, i);
6165 * Scroll the content of a listview.
6168 * [I] infoPtr : valid pointer to the listview structure
6169 * [I] dx : horizontal scroll amount in pixels
6170 * [I] dy : vertical scroll amount in pixels
6177 * If the control is in report mode (LVS_REPORT) the control can
6178 * be scrolled only in line increments. "dy" will be rounded to the
6179 * nearest number of pixels that are a whole line. Ex: if line height
6180 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6181 * is passed the the scroll will be 0. (per MSDN 7/2002)
6183 * For: (per experimentaion with native control and CSpy ListView)
6184 * LVS_ICON dy=1 = 1 pixel (vertical only)
6186 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6188 * LVS_LIST dx=1 = 1 column (horizontal only)
6189 * but will only scroll 1 column per message
6190 * no matter what the value.
6191 * dy must be 0 or FALSE returned.
6192 * LVS_REPORT dx=1 = 1 pixel
6196 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6198 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6200 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6201 dy /= infoPtr->nItemHeight;
6204 if (dy != 0) return FALSE;
6211 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6212 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6219 * Sets the background color.
6222 * [I] infoPtr : valid pointer to the listview structure
6223 * [I] clrBk : background color
6229 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6231 TRACE("(clrBk=%lx)\n", clrBk);
6233 if(infoPtr->clrBk != clrBk) {
6234 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6235 infoPtr->clrBk = clrBk;
6236 if (clrBk == CLR_NONE)
6237 infoPtr->hBkBrush = (HBRUSH)GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6239 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6240 LISTVIEW_InvalidateList(infoPtr);
6246 /* LISTVIEW_SetBkImage */
6248 /*** Helper for {Insert,Set}ColumnT *only* */
6249 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6251 if (lpColumn->mask & LVCF_FMT)
6253 /* format member is valid */
6254 lphdi->mask |= HDI_FORMAT;
6256 /* set text alignment (leftmost column must be left-aligned) */
6257 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
6258 lphdi->fmt |= HDF_LEFT;
6259 else if (lpColumn->fmt & LVCFMT_RIGHT)
6260 lphdi->fmt |= HDF_RIGHT;
6261 else if (lpColumn->fmt & LVCFMT_CENTER)
6262 lphdi->fmt |= HDF_CENTER;
6264 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6265 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6267 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6269 lphdi->fmt |= HDF_IMAGE;
6270 lphdi->iImage = I_IMAGECALLBACK;
6274 if (lpColumn->mask & LVCF_WIDTH)
6276 lphdi->mask |= HDI_WIDTH;
6277 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6279 /* make it fill the remainder of the controls width */
6283 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6285 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6286 lphdi->cxy += rcHeader.right - rcHeader.left;
6289 /* retrieve the layout of the header */
6290 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6291 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, debugrect(&rcHeader));
6293 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6296 lphdi->cxy = lpColumn->cx;
6299 if (lpColumn->mask & LVCF_TEXT)
6301 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6302 lphdi->fmt |= HDF_STRING;
6303 lphdi->pszText = lpColumn->pszText;
6304 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6307 if (lpColumn->mask & LVCF_IMAGE)
6309 lphdi->mask |= HDI_IMAGE;
6310 lphdi->iImage = lpColumn->iImage;
6313 if (lpColumn->mask & LVCF_ORDER)
6315 lphdi->mask |= HDI_ORDER;
6316 lphdi->iOrder = lpColumn->iOrder;
6323 * Inserts a new column.
6326 * [I] infoPtr : valid pointer to the listview structure
6327 * [I] nColumn : column index
6328 * [I] lpColumn : column information
6329 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6332 * SUCCESS : new column index
6335 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6336 const LVCOLUMNW *lpColumn, BOOL isW)
6338 COLUMN_INFO *lpColumnInfo;
6342 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6344 if (!lpColumn || nColumn < 0) return -1;
6345 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6347 ZeroMemory(&hdi, sizeof(HDITEMW));
6348 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6350 /* insert item in header control */
6351 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6352 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6353 (WPARAM)nColumn, (LPARAM)&hdi);
6354 if (nNewColumn == -1) return -1;
6355 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6357 /* create our own column info */
6358 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6359 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6361 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6362 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6364 /* now we have to actually adjust the data */
6365 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6367 SUBITEM_INFO *lpSubItem;
6371 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6373 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6374 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6376 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6377 if (lpSubItem->iSubItem >= nNewColumn)
6378 lpSubItem->iSubItem++;
6383 /* make space for the new column */
6384 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6389 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6392 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6400 * Sets the attributes of a header item.
6403 * [I] infoPtr : valid pointer to the listview structure
6404 * [I] nColumn : column index
6405 * [I] lpColumn : column attributes
6406 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6412 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6413 const LVCOLUMNW *lpColumn, BOOL isW)
6415 HDITEMW hdi, hdiget;
6418 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6420 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6422 ZeroMemory(&hdi, sizeof(HDITEMW));
6423 if (lpColumn->mask & LVCF_FMT)
6425 hdi.mask |= HDI_FORMAT;
6426 hdiget.mask = HDI_FORMAT;
6427 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6428 hdi.fmt = hdiget.fmt & HDF_STRING;
6430 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6432 /* set header item attributes */
6433 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6434 if (!bResult) return FALSE;
6436 if (lpColumn->mask & LVCF_FMT)
6438 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6439 int oldFmt = lpColumnInfo->fmt;
6441 lpColumnInfo->fmt = lpColumn->fmt;
6442 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6444 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6445 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6454 * Sets the column order array
6457 * [I] infoPtr : valid pointer to the listview structure
6458 * [I] iCount : number of elements in column order array
6459 * [I] lpiArray : pointer to column order array
6465 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6467 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6478 * Sets the width of a column
6481 * [I] infoPtr : valid pointer to the listview structure
6482 * [I] nColumn : column index
6483 * [I] cx : column width
6489 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6491 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6492 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6496 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6498 /* set column width only if in report or list mode */
6499 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6501 /* take care of invalid cx values */
6502 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6503 else if (uView == LVS_LIST && cx < 1) return FALSE;
6505 /* resize all columns if in LVS_LIST mode */
6506 if(uView == LVS_LIST)
6508 infoPtr->nItemWidth = cx;
6509 LISTVIEW_InvalidateList(infoPtr);
6513 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6515 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6520 lvItem.mask = LVIF_TEXT;
6522 lvItem.iSubItem = nColumn;
6523 lvItem.pszText = szDispText;
6524 lvItem.cchTextMax = DISP_TEXT_SIZE;
6525 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6527 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6528 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6529 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6531 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6532 max_cx += infoPtr->iconSize.cx;
6533 max_cx += TRAILING_LABEL_PADDING;
6536 /* autosize based on listview items width */
6537 if(cx == LVSCW_AUTOSIZE)
6539 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6541 /* if iCol is the last column make it fill the remainder of the controls width */
6542 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6547 LISTVIEW_GetOrigin(infoPtr, &Origin);
6548 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6550 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6554 /* Despite what the MS docs say, if this is not the last
6555 column, then MS resizes the column to the width of the
6556 largest text string in the column, including headers
6557 and items. This is different from LVSCW_AUTOSIZE in that
6558 LVSCW_AUTOSIZE ignores the header string length. */
6561 /* retrieve header text */
6562 hdi.mask = HDI_TEXT;
6563 hdi.cchTextMax = DISP_TEXT_SIZE;
6564 hdi.pszText = szDispText;
6565 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi))
6567 HDC hdc = GetDC(infoPtr->hwndSelf);
6568 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6571 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6572 cx = size.cx + TRAILING_HEADER_PADDING;
6573 /* FIXME: Take into account the header image, if one is present */
6574 SelectObject(hdc, old_font);
6575 ReleaseDC(infoPtr->hwndSelf, hdc);
6577 cx = max (cx, max_cx);
6581 if (cx < 0) return FALSE;
6583 /* call header to update the column change */
6584 hdi.mask = HDI_WIDTH;
6586 TRACE("hdi.cxy=%d\n", hdi.cxy);
6587 return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi);
6591 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6594 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6597 HBITMAP hbm_im, hbm_mask, hbm_orig;
6599 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6600 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6603 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6604 ILC_COLOR | ILC_MASK, 2, 2);
6605 hdc_wnd = GetDC(infoPtr->hwndSelf);
6606 hdc = CreateCompatibleDC(hdc_wnd);
6607 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6608 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6609 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6611 rc.left = rc.top = 0;
6612 rc.right = GetSystemMetrics(SM_CXSMICON);
6613 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6615 hbm_orig = SelectObject(hdc, hbm_mask);
6616 FillRect(hdc, &rc, hbr_white);
6617 InflateRect(&rc, -3, -3);
6618 FillRect(hdc, &rc, hbr_black);
6620 SelectObject(hdc, hbm_im);
6621 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6622 SelectObject(hdc, hbm_orig);
6623 ImageList_Add(himl, hbm_im, hbm_mask);
6625 SelectObject(hdc, hbm_im);
6626 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6627 SelectObject(hdc, hbm_orig);
6628 ImageList_Add(himl, hbm_im, hbm_mask);
6630 DeleteObject(hbm_mask);
6631 DeleteObject(hbm_im);
6639 * Sets the extended listview style.
6642 * [I] infoPtr : valid pointer to the listview structure
6644 * [I] dwStyle : style
6647 * SUCCESS : previous style
6650 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6652 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6656 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6658 infoPtr->dwLvExStyle = dwStyle;
6660 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6662 HIMAGELIST himl = 0;
6663 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6664 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6665 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6673 * Sets the new hot cursor used during hot tracking and hover selection.
6676 * [I] infoPtr : valid pointer to the listview structure
6677 * [I} hCurosr : the new hot cursor handle
6680 * Returns the previous hot cursor
6682 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6684 HCURSOR oldCursor = infoPtr->hHotCursor;
6686 infoPtr->hHotCursor = hCursor;
6694 * Sets the hot item index.
6697 * [I] infoPtr : valid pointer to the listview structure
6698 * [I] iIndex : index
6701 * SUCCESS : previous hot item index
6702 * FAILURE : -1 (no hot item)
6704 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6706 INT iOldIndex = infoPtr->nHotItem;
6708 infoPtr->nHotItem = iIndex;
6716 * Sets the amount of time the cursor must hover over an item before it is selected.
6719 * [I] infoPtr : valid pointer to the listview structure
6720 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6723 * Returns the previous hover time
6725 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6727 DWORD oldHoverTime = infoPtr->dwHoverTime;
6729 infoPtr->dwHoverTime = dwHoverTime;
6731 return oldHoverTime;
6736 * Sets spacing for icons of LVS_ICON style.
6739 * [I] infoPtr : valid pointer to the listview structure
6740 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6741 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6744 * MAKELONG(oldcx, oldcy)
6746 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6748 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6749 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6751 TRACE("requested=(%d,%d)\n", cx, cy);
6753 /* this is supported only for LVS_ICON style */
6754 if (uView != LVS_ICON) return oldspacing;
6756 /* set to defaults, if instructed to */
6757 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6758 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6760 /* if 0 then compute width
6761 * FIXME: Should scan each item and determine max width of
6762 * icon or label, then make that the width */
6764 cx = infoPtr->iconSpacing.cx;
6766 /* if 0 then compute height */
6768 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6769 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6772 infoPtr->iconSpacing.cx = cx;
6773 infoPtr->iconSpacing.cy = cy;
6775 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6776 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6777 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6778 infoPtr->ntmHeight);
6780 /* these depend on the iconSpacing */
6781 LISTVIEW_UpdateItemSize(infoPtr);
6786 inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6790 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6797 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6798 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6807 * [I] infoPtr : valid pointer to the listview structure
6808 * [I] nType : image list type
6809 * [I] himl : image list handle
6812 * SUCCESS : old image list
6815 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6817 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6818 INT oldHeight = infoPtr->nItemHeight;
6819 HIMAGELIST himlOld = 0;
6821 TRACE("(nType=%d, himl=%p\n", nType, himl);
6826 himlOld = infoPtr->himlNormal;
6827 infoPtr->himlNormal = himl;
6828 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
6829 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
6833 himlOld = infoPtr->himlSmall;
6834 infoPtr->himlSmall = himl;
6835 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
6839 himlOld = infoPtr->himlState;
6840 infoPtr->himlState = himl;
6841 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
6842 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6846 ERR("Unknown icon type=%d\n", nType);
6850 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
6851 if (infoPtr->nItemHeight != oldHeight)
6852 LISTVIEW_UpdateScroll(infoPtr);
6859 * Preallocates memory (does *not* set the actual count of items !)
6862 * [I] infoPtr : valid pointer to the listview structure
6863 * [I] nItems : item count (projected number of items to allocate)
6864 * [I] dwFlags : update flags
6870 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6872 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6874 if (infoPtr->dwStyle & LVS_OWNERDATA)
6876 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6877 INT nOldCount = infoPtr->nItemCount;
6879 if (nItems < nOldCount)
6881 RANGE range = { nItems, nOldCount };
6882 ranges_del(infoPtr->selectionRanges, range);
6883 if (infoPtr->nFocusedItem >= nItems)
6885 infoPtr->nFocusedItem = -1;
6886 SetRectEmpty(&infoPtr->rcFocus);
6890 infoPtr->nItemCount = nItems;
6891 LISTVIEW_UpdateScroll(infoPtr);
6893 /* the flags are valid only in ownerdata report and list modes */
6894 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
6896 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
6897 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
6899 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
6900 LISTVIEW_InvalidateList(infoPtr);
6907 LISTVIEW_GetOrigin(infoPtr, &Origin);
6908 nFrom = min(nOldCount, nItems);
6909 nTo = max(nOldCount, nItems);
6911 if (uView == LVS_REPORT)
6914 rcErase.top = nFrom * infoPtr->nItemHeight;
6915 rcErase.right = infoPtr->nItemWidth;
6916 rcErase.bottom = nTo * infoPtr->nItemHeight;
6917 OffsetRect(&rcErase, Origin.x, Origin.y);
6918 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6919 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6923 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
6925 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
6926 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
6927 rcErase.right = rcErase.left + infoPtr->nItemWidth;
6928 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6929 OffsetRect(&rcErase, Origin.x, Origin.y);
6930 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6931 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6933 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
6935 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
6936 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
6937 OffsetRect(&rcErase, Origin.x, Origin.y);
6938 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
6939 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
6945 /* According to MSDN for non-LVS_OWNERDATA this is just
6946 * a performance issue. The control allocates its internal
6947 * data structures for the number of items specified. It
6948 * cuts down on the number of memory allocations. Therefore
6949 * we will just issue a WARN here
6951 WARN("for non-ownerdata performance option not implemented.\n");
6959 * Sets the position of an item.
6962 * [I] infoPtr : valid pointer to the listview structure
6963 * [I] nItem : item index
6964 * [I] pt : coordinate
6970 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6972 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6975 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6977 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6978 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6980 LISTVIEW_GetOrigin(infoPtr, &Origin);
6982 /* This point value seems to be an undocumented feature.
6983 * The best guess is that it means either at the origin,
6984 * or at true beginning of the list. I will assume the origin. */
6985 if ((pt.x == -1) && (pt.y == -1))
6988 if (uView == LVS_ICON)
6990 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6991 pt.y -= ICON_TOP_PADDING;
6996 infoPtr->bAutoarrange = FALSE;
6998 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7003 * Sets the state of one or many items.
7006 * [I] infoPtr : valid pointer to the listview structure
7007 * [I] nItem : item index
7008 * [I] lpLVItem : item or subitem info
7014 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7016 BOOL bResult = TRUE;
7019 lvItem.iItem = nItem;
7020 lvItem.iSubItem = 0;
7021 lvItem.mask = LVIF_STATE;
7022 lvItem.state = lpLVItem->state;
7023 lvItem.stateMask = lpLVItem->stateMask;
7024 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7028 /* apply to all items */
7029 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7030 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7033 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7040 * Sets the text of an item or subitem.
7043 * [I] hwnd : window handle
7044 * [I] nItem : item index
7045 * [I] lpLVItem : item or subitem info
7046 * [I] isW : TRUE if input is Unicode
7052 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7056 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7058 lvItem.iItem = nItem;
7059 lvItem.iSubItem = lpLVItem->iSubItem;
7060 lvItem.mask = LVIF_TEXT;
7061 lvItem.pszText = lpLVItem->pszText;
7062 lvItem.cchTextMax = lpLVItem->cchTextMax;
7064 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7066 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7071 * Set item index that marks the start of a multiple selection.
7074 * [I] infoPtr : valid pointer to the listview structure
7075 * [I] nIndex : index
7078 * Index number or -1 if there is no selection mark.
7080 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7082 INT nOldIndex = infoPtr->nSelectionMark;
7084 TRACE("(nIndex=%d)\n", nIndex);
7086 infoPtr->nSelectionMark = nIndex;
7093 * Sets the text background color.
7096 * [I] infoPtr : valid pointer to the listview structure
7097 * [I] clrTextBk : text background color
7103 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7105 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7107 if (infoPtr->clrTextBk != clrTextBk)
7109 infoPtr->clrTextBk = clrTextBk;
7110 LISTVIEW_InvalidateList(infoPtr);
7118 * Sets the text foreground color.
7121 * [I] infoPtr : valid pointer to the listview structure
7122 * [I] clrText : text color
7128 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7130 TRACE("(clrText=%lx)\n", clrText);
7132 if (infoPtr->clrText != clrText)
7134 infoPtr->clrText = clrText;
7135 LISTVIEW_InvalidateList(infoPtr);
7143 * Determines which listview item is located at the specified position.
7146 * [I] infoPtr : valid pointer to the listview structure
7147 * [I] hwndNewToolTip : handle to new ToolTip
7152 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7154 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7155 infoPtr->hwndToolTip = hwndNewToolTip;
7156 return hwndOldToolTip;
7159 /* LISTVIEW_SetUnicodeFormat */
7160 /* LISTVIEW_SetWorkAreas */
7164 * Callback internally used by LISTVIEW_SortItems()
7167 * [I] first : pointer to first ITEM_INFO to compare
7168 * [I] second : pointer to second ITEM_INFO to compare
7169 * [I] lParam : HWND of control
7172 * if first comes before second : negative
7173 * if first comes after second : positive
7174 * if first and second are equivalent : zero
7176 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7178 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7179 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7180 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7182 /* Forward the call to the client defined callback */
7183 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7188 * Sorts the listview items.
7191 * [I] infoPtr : valid pointer to the listview structure
7192 * [I] pfnCompare : application-defined value
7193 * [I] lParamSort : pointer to comparision callback
7199 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7201 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7204 LPVOID selectionMarkItem;
7208 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7210 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7212 if (!infoPtr->hdpaItems) return FALSE;
7214 /* if there are 0 or 1 items, there is no need to sort */
7215 if (infoPtr->nItemCount < 2) return TRUE;
7217 if (infoPtr->nFocusedItem >= 0)
7219 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7220 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7221 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7223 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7224 /* clear the lpItem->state for non-selected ones */
7225 /* remove the selection ranges */
7227 infoPtr->pfnCompare = pfnCompare;
7228 infoPtr->lParamSort = lParamSort;
7229 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
7231 /* Adjust selections and indices so that they are the way they should
7232 * be after the sort (otherwise, the list items move around, but
7233 * whatever is at the item's previous original position will be
7236 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7237 for (i=0; i < infoPtr->nItemCount; i++)
7239 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7240 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7242 if (lpItem->state & LVIS_SELECTED)
7244 item.state = LVIS_SELECTED;
7245 item.stateMask = LVIS_SELECTED;
7246 LISTVIEW_SetItemState(infoPtr, i, &item);
7248 if (lpItem->state & LVIS_FOCUSED)
7250 infoPtr->nFocusedItem = i;
7251 lpItem->state &= ~LVIS_FOCUSED;
7254 if (selectionMarkItem != NULL)
7255 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7256 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7258 /* refresh the display */
7259 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7260 LISTVIEW_InvalidateList(infoPtr);
7267 * Updates an items or rearranges the listview control.
7270 * [I] infoPtr : valid pointer to the listview structure
7271 * [I] nItem : item index
7277 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7279 TRACE("(nItem=%d)\n", nItem);
7281 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7283 /* rearrange with default alignment style */
7284 if (is_autoarrange(infoPtr))
7285 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7287 LISTVIEW_InvalidateItem(infoPtr, nItem);
7295 * Creates the listview control.
7298 * [I] hwnd : window handle
7299 * [I] lpcs : the create parameters
7305 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7307 LISTVIEW_INFO *infoPtr;
7308 UINT uView = lpcs->style & LVS_TYPEMASK;
7311 TRACE("(lpcs=%p)\n", lpcs);
7313 /* initialize info pointer */
7314 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7315 if (!infoPtr) return -1;
7317 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7319 infoPtr->hwndSelf = hwnd;
7320 infoPtr->dwStyle = lpcs->style;
7321 /* determine the type of structures to use */
7322 infoPtr->hwndNotify = lpcs->hwndParent;
7323 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7324 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7326 /* initialize color information */
7327 infoPtr->clrBk = CLR_NONE;
7328 infoPtr->clrText = comctl32_color.clrWindowText;
7329 infoPtr->clrTextBk = CLR_DEFAULT;
7330 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7332 /* set default values */
7333 infoPtr->nFocusedItem = -1;
7334 infoPtr->nSelectionMark = -1;
7335 infoPtr->nHotItem = -1;
7336 infoPtr->bRedraw = TRUE;
7337 infoPtr->bNoItemMetrics = TRUE;
7338 infoPtr->bDoChangeNotify = TRUE;
7339 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7340 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7341 infoPtr->nEditLabelItem = -1;
7342 infoPtr->dwHoverTime = -1; /* default system hover time */
7344 /* get default font (icon title) */
7345 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7346 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7347 infoPtr->hFont = infoPtr->hDefaultFont;
7348 LISTVIEW_SaveTextMetrics(infoPtr);
7351 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7352 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7353 0, 0, 0, 0, hwnd, NULL,
7354 lpcs->hInstance, NULL);
7355 if (!infoPtr->hwndHeader) goto fail;
7357 /* set header unicode format */
7358 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7360 /* set header font */
7361 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7363 /* allocate memory for the data structure */
7364 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7365 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7366 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7367 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7368 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7370 /* initialize the icon sizes */
7371 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7372 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7374 /* init item size to avoid division by 0 */
7375 LISTVIEW_UpdateItemSize (infoPtr);
7377 if (uView == LVS_REPORT)
7379 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7381 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7385 /* set HDS_HIDDEN flag to hide the header bar */
7386 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7387 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7394 DestroyWindow(infoPtr->hwndHeader);
7395 ranges_destroy(infoPtr->selectionRanges);
7396 DPA_Destroy(infoPtr->hdpaItems);
7397 DPA_Destroy(infoPtr->hdpaPosX);
7398 DPA_Destroy(infoPtr->hdpaPosY);
7399 DPA_Destroy(infoPtr->hdpaColumns);
7406 * Erases the background of the listview control.
7409 * [I] infoPtr : valid pointer to the listview structure
7410 * [I] hdc : device context handle
7416 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7420 TRACE("(hdc=%p)\n", hdc);
7422 if (!GetClipBox(hdc, &rc)) return FALSE;
7424 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7430 * Helper function for LISTVIEW_[HV]Scroll *only*.
7431 * Performs vertical/horizontal scrolling by a give amount.
7434 * [I] infoPtr : valid pointer to the listview structure
7435 * [I] dx : amount of horizontal scroll
7436 * [I] dy : amount of vertical scroll
7438 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7440 /* now we can scroll the list */
7441 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7442 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7443 /* if we have focus, adjust rect */
7444 OffsetRect(&infoPtr->rcFocus, dx, dy);
7445 UpdateWindow(infoPtr->hwndSelf);
7450 * Performs vertical scrolling.
7453 * [I] infoPtr : valid pointer to the listview structure
7454 * [I] nScrollCode : scroll code
7455 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7456 * [I] hScrollWnd : scrollbar control window handle
7462 * SB_LINEUP/SB_LINEDOWN:
7463 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7464 * for LVS_REPORT is 1 line
7465 * for LVS_LIST cannot occur
7468 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7469 INT nScrollDiff, HWND hScrollWnd)
7471 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7472 INT nOldScrollPos, nNewScrollPos;
7473 SCROLLINFO scrollInfo;
7476 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7477 debugscrollcode(nScrollCode), nScrollDiff);
7479 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7481 scrollInfo.cbSize = sizeof(SCROLLINFO);
7482 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7484 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7486 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7488 nOldScrollPos = scrollInfo.nPos;
7489 switch (nScrollCode)
7495 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7499 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7503 nScrollDiff = -scrollInfo.nPage;
7507 nScrollDiff = scrollInfo.nPage;
7510 case SB_THUMBPOSITION:
7512 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7519 /* quit right away if pos isn't changing */
7520 if (nScrollDiff == 0) return 0;
7522 /* calculate new position, and handle overflows */
7523 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7524 if (nScrollDiff > 0) {
7525 if (nNewScrollPos < nOldScrollPos ||
7526 nNewScrollPos > scrollInfo.nMax)
7527 nNewScrollPos = scrollInfo.nMax;
7529 if (nNewScrollPos > nOldScrollPos ||
7530 nNewScrollPos < scrollInfo.nMin)
7531 nNewScrollPos = scrollInfo.nMin;
7534 /* set the new position, and reread in case it changed */
7535 scrollInfo.fMask = SIF_POS;
7536 scrollInfo.nPos = nNewScrollPos;
7537 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7539 /* carry on only if it really changed */
7540 if (nNewScrollPos == nOldScrollPos) return 0;
7542 /* now adjust to client coordinates */
7543 nScrollDiff = nOldScrollPos - nNewScrollPos;
7544 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7546 /* and scroll the window */
7547 scroll_list(infoPtr, 0, nScrollDiff);
7554 * Performs horizontal scrolling.
7557 * [I] infoPtr : valid pointer to the listview structure
7558 * [I] nScrollCode : scroll code
7559 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7560 * [I] hScrollWnd : scrollbar control window handle
7566 * SB_LINELEFT/SB_LINERIGHT:
7567 * for LVS_ICON, LVS_SMALLICON 1 pixel
7568 * for LVS_REPORT is 1 pixel
7569 * for LVS_LIST is 1 column --> which is a 1 because the
7570 * scroll is based on columns not pixels
7573 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7574 INT nScrollDiff, HWND hScrollWnd)
7576 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7577 INT nOldScrollPos, nNewScrollPos;
7578 SCROLLINFO scrollInfo;
7580 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7581 debugscrollcode(nScrollCode), nScrollDiff);
7583 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7585 scrollInfo.cbSize = sizeof(SCROLLINFO);
7586 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7588 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7590 nOldScrollPos = scrollInfo.nPos;
7592 switch (nScrollCode)
7606 nScrollDiff = -scrollInfo.nPage;
7610 nScrollDiff = scrollInfo.nPage;
7613 case SB_THUMBPOSITION:
7615 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7622 /* quit right away if pos isn't changing */
7623 if (nScrollDiff == 0) return 0;
7625 /* calculate new position, and handle overflows */
7626 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7627 if (nScrollDiff > 0) {
7628 if (nNewScrollPos < nOldScrollPos ||
7629 nNewScrollPos > scrollInfo.nMax)
7630 nNewScrollPos = scrollInfo.nMax;
7632 if (nNewScrollPos > nOldScrollPos ||
7633 nNewScrollPos < scrollInfo.nMin)
7634 nNewScrollPos = scrollInfo.nMin;
7637 /* set the new position, and reread in case it changed */
7638 scrollInfo.fMask = SIF_POS;
7639 scrollInfo.nPos = nNewScrollPos;
7640 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7642 /* carry on only if it really changed */
7643 if (nNewScrollPos == nOldScrollPos) return 0;
7645 if(uView == LVS_REPORT)
7646 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7648 /* now adjust to client coordinates */
7649 nScrollDiff = nOldScrollPos - nNewScrollPos;
7650 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7652 /* and scroll the window */
7653 scroll_list(infoPtr, nScrollDiff, 0);
7658 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7660 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7661 INT gcWheelDelta = 0;
7662 UINT pulScrollLines = 3;
7663 SCROLLINFO scrollInfo;
7665 TRACE("(wheelDelta=%d)\n", wheelDelta);
7667 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7668 gcWheelDelta -= wheelDelta;
7670 scrollInfo.cbSize = sizeof(SCROLLINFO);
7671 scrollInfo.fMask = SIF_POS;
7678 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7679 * should be fixed in the future.
7681 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7682 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7686 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7688 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7689 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7690 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
7695 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7706 * [I] infoPtr : valid pointer to the listview structure
7707 * [I] nVirtualKey : virtual key
7708 * [I] lKeyData : key data
7713 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7715 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7717 NMLVKEYDOWN nmKeyDown;
7719 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7721 /* send LVN_KEYDOWN notification */
7722 nmKeyDown.wVKey = nVirtualKey;
7723 nmKeyDown.flags = 0;
7724 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7726 switch (nVirtualKey)
7729 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7731 notify(infoPtr, NM_RETURN);
7732 notify(infoPtr, LVN_ITEMACTIVATE);
7737 if (infoPtr->nItemCount > 0)
7742 if (infoPtr->nItemCount > 0)
7743 nItem = infoPtr->nItemCount - 1;
7747 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7751 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7755 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7759 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7763 if (uView == LVS_REPORT)
7764 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7766 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7767 * LISTVIEW_GetCountPerRow(infoPtr);
7768 if(nItem < 0) nItem = 0;
7772 if (uView == LVS_REPORT)
7773 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7775 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7776 * LISTVIEW_GetCountPerRow(infoPtr);
7777 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7781 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7782 LISTVIEW_KeySelection(infoPtr, nItem);
7792 * [I] infoPtr : valid pointer to the listview structure
7797 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7801 /* if we did not have the focus, there's nothing to do */
7802 if (!infoPtr->bFocus) return 0;
7804 /* send NM_KILLFOCUS notification */
7805 notify(infoPtr, NM_KILLFOCUS);
7807 /* if we have a focus rectagle, get rid of it */
7808 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7810 /* set window focus flag */
7811 infoPtr->bFocus = FALSE;
7813 /* invalidate the selected items before reseting focus flag */
7814 LISTVIEW_InvalidateSelectedItems(infoPtr);
7822 * Track mouse/dragging
7825 * [I] infoPtr : valid pointer to the listview structure
7826 * [I] pt : mouse coordinate
7831 static LRESULT LISTVIEW_TrackMouse(LISTVIEW_INFO *infoPtr, POINT pt)
7833 INT cxDrag = GetSystemMetrics(SM_CXDRAG);
7834 INT cyDrag = GetSystemMetrics(SM_CYDRAG);
7840 r.top = pt.y - cyDrag;
7841 r.left = pt.x - cxDrag;
7842 r.bottom = pt.y + cyDrag;
7843 r.right = pt.x + cxDrag;
7845 SetCapture(infoPtr->hwndSelf);
7849 if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD))
7851 if (msg.message == WM_MOUSEMOVE)
7853 pt.x = (short)LOWORD(msg.lParam);
7854 pt.y = (short)HIWORD(msg.lParam);
7855 if (PtInRect(&r, pt))
7863 else if (msg.message >= WM_LBUTTONDOWN &&
7864 msg.message <= WM_RBUTTONDBLCLK)
7869 DispatchMessageW(&msg);
7872 if (GetCapture() != infoPtr->hwndSelf)
7883 * Processes double click messages (left mouse button).
7886 * [I] infoPtr : valid pointer to the listview structure
7887 * [I] wKey : key flag
7888 * [I] pts : mouse coordinate
7893 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7895 LVHITTESTINFO htInfo;
7897 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7899 /* send NM_RELEASEDCAPTURE notification */
7900 notify(infoPtr, NM_RELEASEDCAPTURE);
7902 htInfo.pt.x = pts.x;
7903 htInfo.pt.y = pts.y;
7905 /* send NM_DBLCLK notification */
7906 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7907 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7909 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7910 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
7917 * Processes mouse down messages (left mouse button).
7920 * [I] infoPtr : valid pointer to the listview structure
7921 * [I] wKey : key flag
7922 * [I] pts : mouse coordinate
7927 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7929 LVHITTESTINFO lvHitTestInfo;
7930 static BOOL bGroupSelect = TRUE;
7931 POINT pt = { pts.x, pts.y };
7934 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7936 /* send NM_RELEASEDCAPTURE notification */
7937 notify(infoPtr, NM_RELEASEDCAPTURE);
7939 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7941 /* set left button down flag */
7942 infoPtr->bLButtonDown = TRUE;
7944 lvHitTestInfo.pt.x = pts.x;
7945 lvHitTestInfo.pt.y = pts.y;
7947 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7948 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7949 infoPtr->nEditLabelItem = -1;
7950 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7952 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
7954 DWORD state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK) >> 12;
7955 if(state == 1 || state == 2)
7959 lvitem.state = state << 12;
7960 lvitem.stateMask = LVIS_STATEIMAGEMASK;
7961 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
7965 if (LISTVIEW_TrackMouse(infoPtr, lvHitTestInfo.pt))
7969 ZeroMemory(&nmlv, sizeof(nmlv));
7971 nmlv.ptAction.x = lvHitTestInfo.pt.x;
7972 nmlv.ptAction.y = lvHitTestInfo.pt.y;
7974 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
7979 if (infoPtr->dwStyle & LVS_SINGLESEL)
7981 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7982 infoPtr->nEditLabelItem = nItem;
7984 LISTVIEW_SetSelection(infoPtr, nItem);
7988 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7992 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7993 LISTVIEW_SetItemFocus(infoPtr, nItem);
7994 infoPtr->nSelectionMark = nItem;
8000 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8001 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8003 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8004 infoPtr->nSelectionMark = nItem;
8007 else if (wKey & MK_CONTROL)
8011 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8013 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8014 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8015 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8016 infoPtr->nSelectionMark = nItem;
8018 else if (wKey & MK_SHIFT)
8020 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8024 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8025 infoPtr->nEditLabelItem = nItem;
8027 /* set selection (clears other pre-existing selections) */
8028 LISTVIEW_SetSelection(infoPtr, nItem);
8034 /* remove all selections */
8035 LISTVIEW_DeselectAll(infoPtr);
8044 * Processes mouse up messages (left mouse button).
8047 * [I] infoPtr : valid pointer to the listview structure
8048 * [I] wKey : key flag
8049 * [I] pts : mouse coordinate
8054 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8056 LVHITTESTINFO lvHitTestInfo;
8058 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
8060 if (!infoPtr->bLButtonDown) return 0;
8062 lvHitTestInfo.pt.x = pts.x;
8063 lvHitTestInfo.pt.y = pts.y;
8065 /* send NM_CLICK notification */
8066 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8067 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
8069 /* set left button flag */
8070 infoPtr->bLButtonDown = FALSE;
8072 /* if we clicked on a selected item, edit the label */
8073 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8074 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8081 * Destroys the listview control (called after WM_DESTROY).
8084 * [I] infoPtr : valid pointer to the listview structure
8089 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8093 /* delete all items */
8094 LISTVIEW_DeleteAllItems(infoPtr);
8096 /* destroy data structure */
8097 DPA_Destroy(infoPtr->hdpaItems);
8098 DPA_Destroy(infoPtr->hdpaPosX);
8099 DPA_Destroy(infoPtr->hdpaPosY);
8100 DPA_Destroy(infoPtr->hdpaColumns);
8101 ranges_destroy(infoPtr->selectionRanges);
8103 /* destroy image lists */
8104 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8106 if (infoPtr->himlNormal)
8107 ImageList_Destroy(infoPtr->himlNormal);
8108 if (infoPtr->himlSmall)
8109 ImageList_Destroy(infoPtr->himlSmall);
8110 if (infoPtr->himlState)
8111 ImageList_Destroy(infoPtr->himlState);
8114 /* destroy font, bkgnd brush */
8116 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8117 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8119 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
8121 /* free listview info pointer*/
8129 * Handles notifications from header.
8132 * [I] infoPtr : valid pointer to the listview structure
8133 * [I] nCtrlId : control identifier
8134 * [I] lpnmh : notification information
8139 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8141 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8143 TRACE("(lpnmh=%p)\n", lpnmh);
8145 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8147 switch (lpnmh->hdr.code)
8151 case HDN_ITEMCHANGEDW:
8152 case HDN_ITEMCHANGEDA:
8154 COLUMN_INFO *lpColumnInfo;
8157 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8161 hdi.mask = HDI_WIDTH;
8162 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0;
8166 cxy = lpnmh->pitem->cxy;
8168 /* determine how much we change since the last know position */
8169 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8170 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8173 RECT rcCol = lpColumnInfo->rcHeader;
8175 lpColumnInfo->rcHeader.right += dx;
8176 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8177 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8179 /* this trick works for left aligned columns only */
8180 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8182 rcCol.right = min (rcCol.right, lpColumnInfo->rcHeader.right);
8183 rcCol.left = max (rcCol.left, rcCol.right - 3 * infoPtr->ntmAveCharWidth);
8185 rcCol.top = infoPtr->rcList.top;
8186 rcCol.bottom = infoPtr->rcList.bottom;
8187 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8193 case HDN_ITEMCLICKW:
8194 case HDN_ITEMCLICKA:
8196 /* Handle sorting by Header Column */
8199 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8201 nmlv.iSubItem = lpnmh->iItem;
8202 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8212 * Determines the type of structure to use.
8215 * [I] infoPtr : valid pointer to the listview structureof the sender
8216 * [I] hwndFrom : listview window handle
8217 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8222 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8224 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8226 if (nCommand != NF_REQUERY) return 0;
8228 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8235 * Paints/Repaints the listview control.
8238 * [I] infoPtr : valid pointer to the listview structure
8239 * [I] hdc : device context handle
8244 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8246 TRACE("(hdc=%p)\n", hdc);
8248 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8250 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8252 infoPtr->bNoItemMetrics = FALSE;
8253 LISTVIEW_UpdateItemSize(infoPtr);
8254 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8255 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8256 LISTVIEW_UpdateScroll(infoPtr);
8259 LISTVIEW_Refresh(infoPtr, hdc);
8264 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8266 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8267 LISTVIEW_Refresh(infoPtr, hdc);
8268 EndPaint(infoPtr->hwndSelf, &ps);
8276 * Processes double click messages (right mouse button).
8279 * [I] infoPtr : valid pointer to the listview structure
8280 * [I] wKey : key flag
8281 * [I] pts : mouse coordinate
8286 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8288 LVHITTESTINFO lvHitTestInfo;
8290 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8292 /* send NM_RELEASEDCAPTURE notification */
8293 notify(infoPtr, NM_RELEASEDCAPTURE);
8295 /* send NM_RDBLCLK notification */
8296 lvHitTestInfo.pt.x = pts.x;
8297 lvHitTestInfo.pt.y = pts.y;
8298 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8299 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8306 * Processes mouse down messages (right mouse button).
8309 * [I] infoPtr : valid pointer to the listview structure
8310 * [I] wKey : key flag
8311 * [I] pts : mouse coordinate
8316 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8318 LVHITTESTINFO lvHitTestInfo;
8321 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8323 /* send NM_RELEASEDCAPTURE notification */
8324 notify(infoPtr, NM_RELEASEDCAPTURE);
8326 /* make sure the listview control window has the focus */
8327 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8329 /* set right button down flag */
8330 infoPtr->bRButtonDown = TRUE;
8332 /* determine the index of the selected item */
8333 lvHitTestInfo.pt.x = pts.x;
8334 lvHitTestInfo.pt.y = pts.y;
8335 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8337 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8339 LISTVIEW_SetItemFocus(infoPtr, nItem);
8340 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8341 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8342 LISTVIEW_SetSelection(infoPtr, nItem);
8346 LISTVIEW_DeselectAll(infoPtr);
8354 * Processes mouse up messages (right mouse button).
8357 * [I] infoPtr : valid pointer to the listview structure
8358 * [I] wKey : key flag
8359 * [I] pts : mouse coordinate
8364 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8366 LVHITTESTINFO lvHitTestInfo;
8369 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8371 if (!infoPtr->bRButtonDown) return 0;
8373 /* set button flag */
8374 infoPtr->bRButtonDown = FALSE;
8376 /* Send NM_RClICK notification */
8377 lvHitTestInfo.pt.x = pts.x;
8378 lvHitTestInfo.pt.y = pts.y;
8379 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8380 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
8382 /* Change to screen coordinate for WM_CONTEXTMENU */
8383 pt = lvHitTestInfo.pt;
8384 ClientToScreen(infoPtr->hwndSelf, &pt);
8386 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8387 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8388 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8399 * [I] infoPtr : valid pointer to the listview structure
8400 * [I] hwnd : window handle of window containing the cursor
8401 * [I] nHittest : hit-test code
8402 * [I] wMouseMsg : ideintifier of the mouse message
8405 * TRUE if cursor is set
8408 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8410 LVHITTESTINFO lvHitTestInfo;
8412 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8414 if(!infoPtr->hHotCursor) return FALSE;
8416 GetCursorPos(&lvHitTestInfo.pt);
8417 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8419 SetCursor(infoPtr->hHotCursor);
8429 * [I] infoPtr : valid pointer to the listview structure
8430 * [I] hwndLoseFocus : handle of previously focused window
8435 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8437 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8439 /* if we have the focus already, there's nothing to do */
8440 if (infoPtr->bFocus) return 0;
8442 /* send NM_SETFOCUS notification */
8443 notify(infoPtr, NM_SETFOCUS);
8445 /* set window focus flag */
8446 infoPtr->bFocus = TRUE;
8448 /* put the focus rect back on */
8449 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8451 /* redraw all visible selected items */
8452 LISTVIEW_InvalidateSelectedItems(infoPtr);
8462 * [I] infoPtr : valid pointer to the listview structure
8463 * [I] fRedraw : font handle
8464 * [I] fRedraw : redraw flag
8469 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8471 HFONT oldFont = infoPtr->hFont;
8473 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8475 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8476 if (infoPtr->hFont == oldFont) return 0;
8478 LISTVIEW_SaveTextMetrics(infoPtr);
8480 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8481 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8483 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8490 * Message handling for WM_SETREDRAW.
8491 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8494 * [I] infoPtr : valid pointer to the listview structure
8495 * [I] bRedraw: state of redraw flag
8498 * DefWinProc return value
8500 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8502 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8504 /* we can not use straight equality here because _any_ non-zero value is TRUE */
8505 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8507 infoPtr->bRedraw = bRedraw;
8509 if(!bRedraw) return 0;
8511 if (is_autoarrange(infoPtr))
8512 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8513 LISTVIEW_UpdateScroll(infoPtr);
8515 /* despite what the WM_SETREDRAW docs says, apps expect us
8516 * to invalidate the listview here... stupid! */
8517 LISTVIEW_InvalidateList(infoPtr);
8524 * Resizes the listview control. This function processes WM_SIZE
8525 * messages. At this time, the width and height are not used.
8528 * [I] infoPtr : valid pointer to the listview structure
8529 * [I] Width : new width
8530 * [I] Height : new height
8535 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8537 RECT rcOld = infoPtr->rcList;
8539 TRACE("(width=%d, height=%d)\n", Width, Height);
8541 LISTVIEW_UpdateSize(infoPtr);
8542 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8544 /* do not bother with display related stuff if we're not redrawing */
8545 if (!is_redrawing(infoPtr)) return 0;
8547 if (is_autoarrange(infoPtr))
8548 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8550 LISTVIEW_UpdateScroll(infoPtr);
8552 /* refresh all only for lists whose height changed significantly */
8553 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8554 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8555 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8556 LISTVIEW_InvalidateList(infoPtr);
8563 * Sets the size information.
8566 * [I] infoPtr : valid pointer to the listview structure
8571 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8573 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8575 TRACE("uView=%d, rcList(old)=%s\n", uView, debugrect(&infoPtr->rcList));
8577 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8579 if (uView == LVS_LIST)
8581 /* Apparently the "LIST" style is supposed to have the same
8582 * number of items in a column even if there is no scroll bar.
8583 * Since if a scroll bar already exists then the bottom is already
8584 * reduced, only reduce if the scroll bar does not currently exist.
8585 * The "2" is there to mimic the native control. I think it may be
8586 * related to either padding or edges. (GLA 7/2002)
8588 if (!(infoPtr->dwStyle & WS_HSCROLL))
8589 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8590 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8592 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8597 hl.prc = &infoPtr->rcList;
8599 Header_Layout(infoPtr->hwndHeader, &hl);
8601 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8603 infoPtr->rcList.top = max(wp.cy, 0);
8606 TRACE(" rcList=%s\n", debugrect(&infoPtr->rcList));
8611 * Processes WM_STYLECHANGED messages.
8614 * [I] infoPtr : valid pointer to the listview structure
8615 * [I] wStyleType : window style type (normal or extended)
8616 * [I] lpss : window style information
8621 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8622 const STYLESTRUCT *lpss)
8624 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8625 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8627 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8628 wStyleType, lpss->styleOld, lpss->styleNew);
8630 if (wStyleType != GWL_STYLE) return 0;
8632 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8633 /* what if LVS_OWNERDATA changed? */
8634 /* or LVS_SINGLESEL */
8635 /* or LVS_SORT{AS,DES}CENDING */
8637 infoPtr->dwStyle = lpss->styleNew;
8639 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8640 ((lpss->styleNew & WS_HSCROLL) == 0))
8641 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8643 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8644 ((lpss->styleNew & WS_VSCROLL) == 0))
8645 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8647 if (uNewView != uOldView)
8649 SIZE oldIconSize = infoPtr->iconSize;
8652 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8653 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8655 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8656 SetRectEmpty(&infoPtr->rcFocus);
8658 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8659 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
8661 if (uNewView == LVS_ICON)
8663 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8665 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
8666 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8667 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8670 else if (uNewView == LVS_REPORT)
8675 hl.prc = &infoPtr->rcList;
8677 Header_Layout(infoPtr->hwndHeader, &hl);
8678 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8681 LISTVIEW_UpdateItemSize(infoPtr);
8684 if (uNewView == LVS_REPORT)
8685 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
8687 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
8688 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
8689 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8691 /* update the size of the client area */
8692 LISTVIEW_UpdateSize(infoPtr);
8694 /* add scrollbars if needed */
8695 LISTVIEW_UpdateScroll(infoPtr);
8697 /* invalidate client area + erase background */
8698 LISTVIEW_InvalidateList(infoPtr);
8705 * Window procedure of the listview control.
8708 static LRESULT WINAPI
8709 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8711 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8713 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8715 if (!infoPtr && (uMsg != WM_CREATE))
8716 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8720 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8725 case LVM_APPROXIMATEVIEWRECT:
8726 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8727 LOWORD(lParam), HIWORD(lParam));
8729 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8731 /* case LVM_CANCELEDITLABEL: */
8733 case LVM_CREATEDRAGIMAGE:
8734 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
8736 case LVM_DELETEALLITEMS:
8737 return LISTVIEW_DeleteAllItems(infoPtr);
8739 case LVM_DELETECOLUMN:
8740 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8742 case LVM_DELETEITEM:
8743 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8745 case LVM_EDITLABELW:
8746 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8748 case LVM_EDITLABELA:
8749 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8751 /* case LVM_ENABLEGROUPVIEW: */
8753 case LVM_ENSUREVISIBLE:
8754 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8757 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8760 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8762 case LVM_GETBKCOLOR:
8763 return infoPtr->clrBk;
8765 /* case LVM_GETBKIMAGE: */
8767 case LVM_GETCALLBACKMASK:
8768 return infoPtr->uCallbackMask;
8770 case LVM_GETCOLUMNA:
8771 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8773 case LVM_GETCOLUMNW:
8774 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8776 case LVM_GETCOLUMNORDERARRAY:
8777 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8779 case LVM_GETCOLUMNWIDTH:
8780 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8782 case LVM_GETCOUNTPERPAGE:
8783 return LISTVIEW_GetCountPerPage(infoPtr);
8785 case LVM_GETEDITCONTROL:
8786 return (LRESULT)infoPtr->hwndEdit;
8788 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8789 return infoPtr->dwLvExStyle;
8791 /* case LVM_GETGROUPINFO: */
8793 /* case LVM_GETGROUPMETRICS: */
8796 return (LRESULT)infoPtr->hwndHeader;
8798 case LVM_GETHOTCURSOR:
8799 return (LRESULT)infoPtr->hHotCursor;
8801 case LVM_GETHOTITEM:
8802 return infoPtr->nHotItem;
8804 case LVM_GETHOVERTIME:
8805 return infoPtr->dwHoverTime;
8807 case LVM_GETIMAGELIST:
8808 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8810 /* case LVM_GETINSERTMARK: */
8812 /* case LVM_GETINSERTMARKCOLOR: */
8814 /* case LVM_GETINSERTMARKRECT: */
8816 case LVM_GETISEARCHSTRINGA:
8817 case LVM_GETISEARCHSTRINGW:
8818 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8822 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8825 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8827 case LVM_GETITEMCOUNT:
8828 return infoPtr->nItemCount;
8830 case LVM_GETITEMPOSITION:
8831 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8833 case LVM_GETITEMRECT:
8834 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8836 case LVM_GETITEMSPACING:
8837 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8839 case LVM_GETITEMSTATE:
8840 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8842 case LVM_GETITEMTEXTA:
8843 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8845 case LVM_GETITEMTEXTW:
8846 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8848 case LVM_GETNEXTITEM:
8849 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8851 case LVM_GETNUMBEROFWORKAREAS:
8852 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8856 if (!lParam) return FALSE;
8857 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8860 /* case LVM_GETOUTLINECOLOR: */
8862 /* case LVM_GETSELECTEDCOLUMN: */
8864 case LVM_GETSELECTEDCOUNT:
8865 return LISTVIEW_GetSelectedCount(infoPtr);
8867 case LVM_GETSELECTIONMARK:
8868 return infoPtr->nSelectionMark;
8870 case LVM_GETSTRINGWIDTHA:
8871 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8873 case LVM_GETSTRINGWIDTHW:
8874 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8876 case LVM_GETSUBITEMRECT:
8877 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8879 case LVM_GETTEXTBKCOLOR:
8880 return infoPtr->clrTextBk;
8882 case LVM_GETTEXTCOLOR:
8883 return infoPtr->clrText;
8885 /* case LVM_GETTILEINFO: */
8887 /* case LVM_GETTILEVIEWINFO: */
8889 case LVM_GETTOOLTIPS:
8890 return (LRESULT)infoPtr->hwndToolTip;
8892 case LVM_GETTOPINDEX:
8893 return LISTVIEW_GetTopIndex(infoPtr);
8895 /*case LVM_GETUNICODEFORMAT:
8896 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8899 /* case LVM_GETVIEW: */
8901 case LVM_GETVIEWRECT:
8902 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8904 case LVM_GETWORKAREAS:
8905 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8908 /* case LVM_HASGROUP: */
8911 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8913 case LVM_INSERTCOLUMNA:
8914 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8916 case LVM_INSERTCOLUMNW:
8917 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8919 /* case LVM_INSERTGROUP: */
8921 /* case LVM_INSERTGROUPSORTED: */
8923 case LVM_INSERTITEMA:
8924 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8926 case LVM_INSERTITEMW:
8927 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8929 /* case LVM_INSERTMARKHITTEST: */
8931 /* case LVM_ISGROUPVIEWENABLED: */
8933 /* case LVM_MAPIDTOINDEX: */
8935 /* case LVM_MAPINDEXTOID: */
8937 /* case LVM_MOVEGROUP: */
8939 /* case LVM_MOVEITEMTOGROUP: */
8941 case LVM_REDRAWITEMS:
8942 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8944 /* case LVM_REMOVEALLGROUPS: */
8946 /* case LVM_REMOVEGROUP: */
8949 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8951 case LVM_SETBKCOLOR:
8952 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8954 /* case LVM_SETBKIMAGE: */
8956 case LVM_SETCALLBACKMASK:
8957 infoPtr->uCallbackMask = (UINT)wParam;
8960 case LVM_SETCOLUMNA:
8961 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8963 case LVM_SETCOLUMNW:
8964 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8966 case LVM_SETCOLUMNORDERARRAY:
8967 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8969 case LVM_SETCOLUMNWIDTH:
8970 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
8972 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8973 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8975 /* case LVM_SETGROUPINFO: */
8977 /* case LVM_SETGROUPMETRICS: */
8979 case LVM_SETHOTCURSOR:
8980 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8982 case LVM_SETHOTITEM:
8983 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8985 case LVM_SETHOVERTIME:
8986 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8988 case LVM_SETICONSPACING:
8989 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
8991 case LVM_SETIMAGELIST:
8992 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8994 /* case LVM_SETINFOTIP: */
8996 /* case LVM_SETINSERTMARK: */
8998 /* case LVM_SETINSERTMARKCOLOR: */
9001 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9004 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9006 case LVM_SETITEMCOUNT:
9007 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9009 case LVM_SETITEMPOSITION:
9012 pt.x = (short)LOWORD(lParam);
9013 pt.y = (short)HIWORD(lParam);
9014 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9017 case LVM_SETITEMPOSITION32:
9018 if (lParam == 0) return FALSE;
9019 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9021 case LVM_SETITEMSTATE:
9022 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9024 case LVM_SETITEMTEXTA:
9025 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9027 case LVM_SETITEMTEXTW:
9028 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9030 /* case LVM_SETOUTLINECOLOR: */
9032 /* case LVM_SETSELECTEDCOLUMN: */
9034 case LVM_SETSELECTIONMARK:
9035 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9037 case LVM_SETTEXTBKCOLOR:
9038 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9040 case LVM_SETTEXTCOLOR:
9041 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9043 /* case LVM_SETTILEINFO: */
9045 /* case LVM_SETTILEVIEWINFO: */
9047 /* case LVM_SETTILEWIDTH: */
9049 case LVM_SETTOOLTIPS:
9050 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9052 /* case LVM_SETUNICODEFORMAT: */
9054 /* case LVM_SETVIEW: */
9056 /* case LVM_SETWORKAREAS: */
9058 /* case LVM_SORTGROUPS: */
9061 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9063 /* LVM_SORTITEMSEX: */
9065 case LVM_SUBITEMHITTEST:
9066 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9069 return LISTVIEW_Update(infoPtr, (INT)wParam);
9072 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9075 return LISTVIEW_Command(infoPtr, wParam, lParam);
9078 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9081 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9084 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9087 return (LRESULT)infoPtr->hFont;
9090 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9093 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9096 return LISTVIEW_KillFocus(infoPtr);
9098 case WM_LBUTTONDBLCLK:
9099 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9101 case WM_LBUTTONDOWN:
9102 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9105 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9108 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9111 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9114 return LISTVIEW_NCDestroy(infoPtr);
9117 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9118 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9121 case WM_NOTIFYFORMAT:
9122 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9125 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9127 case WM_RBUTTONDBLCLK:
9128 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9130 case WM_RBUTTONDOWN:
9131 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9134 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
9137 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9142 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9145 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9148 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9151 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9153 case WM_STYLECHANGED:
9154 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9156 case WM_SYSCOLORCHANGE:
9157 COMCTL32_RefreshSysColors();
9160 /* case WM_TIMER: */
9163 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9166 if (wParam & (MK_SHIFT | MK_CONTROL))
9167 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9168 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9170 case WM_WINDOWPOSCHANGED:
9171 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9173 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9174 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9175 LISTVIEW_UpdateSize(infoPtr);
9176 LISTVIEW_UpdateScroll(infoPtr);
9178 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9180 /* case WM_WININICHANGE: */
9183 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9184 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9187 /* call default window procedure */
9188 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9196 * Registers the window class.
9204 void LISTVIEW_Register(void)
9208 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9209 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9210 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9211 wndClass.cbClsExtra = 0;
9212 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9213 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9214 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9215 wndClass.lpszClassName = WC_LISTVIEWW;
9216 RegisterClassW(&wndClass);
9221 * Unregisters the window class.
9229 void LISTVIEW_Unregister(void)
9231 UnregisterClassW(WC_LISTVIEWW, NULL);
9236 * Handle any WM_COMMAND messages
9239 * [I] infoPtr : valid pointer to the listview structure
9240 * [I] wParam : the first message parameter
9241 * [I] lParam : the second message parameter
9246 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9248 switch (HIWORD(wParam))
9253 * Adjust the edit window size
9256 HDC hdc = GetDC(infoPtr->hwndEdit);
9257 HFONT hFont, hOldFont = 0;
9262 if (!infoPtr->hwndEdit || !hdc) return 0;
9263 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9264 GetWindowRect(infoPtr->hwndEdit, &rect);
9266 /* Select font to get the right dimension of the string */
9267 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9270 hOldFont = SelectObject(hdc, hFont);
9273 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9275 TEXTMETRICW textMetric;
9277 /* Add Extra spacing for the next character */
9278 GetTextMetricsW(hdc, &textMetric);
9279 sz.cx += (textMetric.tmMaxCharWidth * 2);
9287 rect.bottom - rect.top,
9288 SWP_DRAWFRAME|SWP_NOMOVE);
9291 SelectObject(hdc, hOldFont);
9293 ReleaseDC(infoPtr->hwndSelf, hdc);
9299 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9308 * Subclassed edit control windproc function
9311 * [I] hwnd : the edit window handle
9312 * [I] uMsg : the message that is to be processed
9313 * [I] wParam : first message parameter
9314 * [I] lParam : second message parameter
9315 * [I] isW : TRUE if input is Unicode
9320 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9322 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9323 BOOL cancel = FALSE;
9325 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9326 hwnd, uMsg, wParam, lParam, isW);
9331 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9338 WNDPROC editProc = infoPtr->EditWndProc;
9339 infoPtr->EditWndProc = 0;
9340 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9341 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9345 if (VK_ESCAPE == (INT)wParam)
9350 else if (VK_RETURN == (INT)wParam)
9354 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9358 if (infoPtr->hwndEdit)
9360 LPWSTR buffer = NULL;
9362 infoPtr->hwndEdit = 0;
9365 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9369 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9371 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9372 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9376 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9378 if (buffer) Free(buffer);
9382 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9388 * Subclassed edit control Unicode windproc function
9391 * [I] hwnd : the edit window handle
9392 * [I] uMsg : the message that is to be processed
9393 * [I] wParam : first message parameter
9394 * [I] lParam : second message parameter
9398 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9400 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9405 * Subclassed edit control ANSI windproc function
9408 * [I] hwnd : the edit window handle
9409 * [I] uMsg : the message that is to be processed
9410 * [I] wParam : first message parameter
9411 * [I] lParam : second message parameter
9415 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9417 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9422 * Creates a subclassed edit cotrol
9425 * [I] infoPtr : valid pointer to the listview structure
9426 * [I] text : initial text for the edit
9427 * [I] style : the window style
9428 * [I] isW : TRUE if input is Unicode
9432 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9433 INT x, INT y, INT width, INT height, BOOL isW)
9435 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9440 TEXTMETRICW textMetric;
9441 HINSTANCE hinst = (HINSTANCE)GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9443 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9445 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9446 hdc = GetDC(infoPtr->hwndSelf);
9448 /* Select the font to get appropriate metric dimensions */
9449 if(infoPtr->hFont != 0)
9450 hOldFont = SelectObject(hdc, infoPtr->hFont);
9452 /*Get String Length in pixels */
9453 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9455 /*Add Extra spacing for the next character */
9456 GetTextMetricsW(hdc, &textMetric);
9457 sz.cx += (textMetric.tmMaxCharWidth * 2);
9459 if(infoPtr->hFont != 0)
9460 SelectObject(hdc, hOldFont);
9462 ReleaseDC(infoPtr->hwndSelf, hdc);
9464 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9466 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9468 if (!hedit) return 0;
9470 infoPtr->EditWndProc = (WNDPROC)
9471 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9472 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9474 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);