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
25 * Listview control implementation.
28 * -- Drawing optimizations.
29 * -- Hot item handling.
32 * LISTVIEW_Notify : most notifications from children (editbox and header)
35 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
37 * Advanced functionality:
38 * LISTVIEW_GetNumberOfWorkAreas : not implemented
39 * LISTVIEW_GetISearchString : not implemented
40 * LISTVIEW_GetBkImage : not implemented
41 * LISTVIEW_SetBkImage : not implemented
42 * LISTVIEW_GetColumnOrderArray : simple hack only
43 * LISTVIEW_SetColumnOrderArray : simple hack only
44 * LISTVIEW_Arrange : empty stub
45 * LISTVIEW_ApproximateViewRect : incomplete
46 * LISTVIEW_Update : not completed
48 * Known differences in message stream from native control (not known if
49 * these differences cause problems):
50 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
51 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
52 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
53 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
54 * does *not* invoke DefWindowProc
55 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
56 * processing for "USEDOUBLECLICKTIME".
61 #include "wine/port.h"
73 #include "wine/debug.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(listview);
77 /* Some definitions for inline edit control */
79 typedef struct tagCOLUMNCACHE
83 } COLUMNCACHE, *LPCOLUMNCACHE;
85 typedef struct tagITEMHDR
89 } ITEMHDR, *LPITEMHDR;
91 typedef struct tagLISTVIEW_SUBITEM
97 typedef struct tagLISTVIEW_ITEM
106 typedef struct tagRANGE
112 typedef struct tagLISTVIEW_INFO
119 HIMAGELIST himlNormal;
120 HIMAGELIST himlSmall;
121 HIMAGELIST himlState;
126 HDPA hdpaSelectionRanges;
128 BOOL bRemovingAllSelections;
131 RECT rcList; /* This rectangle is really the window
132 * client rectangle possibly reduced by the
133 * horizontal scroll bar and/or header - see
134 * LISTVIEW_UpdateSize. This rectangle offset
135 * by the LISTVIEW_GetOrigin value is in
136 * client coordinates */
137 RECT rcView; /* This rectangle contains all items -
138 * contructed in LISTVIEW_AlignTop and
139 * LISTVIEW_AlignLeft */
148 INT ntmHeight; /* from GetTextMetrics from above font */
149 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
153 DWORD dwStyle; /* the cached window GWL_STYLE */
154 DWORD dwLvExStyle; /* extended listview style */
157 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
158 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
159 PFNLVCOMPARE pfnCompare;
167 DWORD lastKeyPressTimestamp;
169 INT nSearchParamLength;
170 WCHAR szSearchParam[ MAX_PATH ];
174 DEFINE_COMMON_NOTIFICATIONS(LISTVIEW_INFO, hwndSelf);
179 /* How many we debug buffer to allocate */
180 #define DEBUG_BUFFERS 20
181 /* The size of a single debug bbuffer */
182 #define DEBUG_BUFFER_SIZE 256
184 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
185 #define SB_INTERNAL -1
187 /* maximum size of a label */
188 #define DISP_TEXT_SIZE 512
190 /* padding for items in list and small icon display modes */
191 #define WIDTH_PADDING 12
193 /* padding for items in list, report and small icon display modes */
194 #define HEIGHT_PADDING 1
196 /* offset of items in report display mode */
197 #define REPORT_MARGINX 2
199 /* padding for icon in large icon display mode
200 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
201 * that HITTEST will see.
202 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
203 * ICON_TOP_PADDING - sum of the two above.
204 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
205 * LABEL_VERT_PADDING - between bottom of text and end of box
207 * ICON_LR_PADDING - additional width above icon size.
208 * ICON_LR_HALF - half of the above value
210 #define ICON_TOP_PADDING_NOTHITABLE 2
211 #define ICON_TOP_PADDING_HITABLE 2
212 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
213 #define ICON_BOTTOM_PADDING 4
214 #define LABEL_VERT_PADDING 7
215 #define ICON_LR_PADDING 16
216 #define ICON_LR_HALF (ICON_LR_PADDING/2)
218 /* default label width for items in list and small icon display modes */
219 #define DEFAULT_LABEL_WIDTH 40
221 /* default column width for items in list display mode */
222 #define DEFAULT_COLUMN_WIDTH 128
224 /* Size of "line" scroll for V & H scrolls */
225 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
227 /* Padding betwen image and label */
228 #define IMAGE_PADDING 2
230 /* Padding behind the label */
231 #define TRAILING_PADDING 5
233 /* Border for the icon caption */
234 #define CAPTION_BORDER 2
236 /* Standard DrawText flags for LISTVIEW_UpdateLargeItemLabelRect and LISTVIEW_DrawLargeItem */
237 #define LISTVIEW_DTFLAGS DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_EDITCONTROL
239 /* Dump the LISTVIEW_INFO structure to the debug channel */
240 #define LISTVIEW_DUMP(iP) do { \
241 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
242 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
243 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
244 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
245 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
246 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
247 (iP->bFocus) ? "true" : "false"); \
248 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
249 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
250 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
251 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
253 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
254 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
259 * forward declarations
261 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
262 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
263 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
264 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
265 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
266 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *);
267 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
268 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
269 static INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *);
270 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *, INT, LPRECT);
271 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
272 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *, INT);
273 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
274 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
275 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
276 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
277 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, POINT);
278 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
279 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
280 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
281 static void LISTVIEW_UnsupportedStyles(LONG);
282 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
283 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
284 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
285 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
286 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *, WPARAM, LPARAM);
287 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
288 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
289 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
290 static BOOL LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *, int, RECT*);
291 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
292 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
293 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
294 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
295 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
296 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
298 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
299 #define KEY_DELAY 450
301 /******** Text handling functions *************************************/
303 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
304 * text string. The string may be ANSI or Unicode, in which case
305 * the boolean isW tells us the type of the string.
307 * The name of the function tell what type of strings it expects:
308 * W: Unicode, T: ANSI/Unicode - function of isW
311 static inline BOOL is_textW(LPCWSTR text)
313 return text != NULL && text != LPSTR_TEXTCALLBACKW;
316 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
318 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
319 return is_textW(text);
322 static inline int textlenT(LPCWSTR text, BOOL isW)
324 return !is_textT(text, isW) ? 0 :
325 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
328 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
331 if (isSrcW) lstrcpynW(dest, src, max);
332 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
334 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
335 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
338 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
340 LPWSTR wstr = (LPWSTR)text;
342 if (!isW && is_textT(text, isW))
344 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
345 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
346 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
348 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
352 static inline void textfreeT(LPWSTR wstr, BOOL isW)
354 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
358 * dest is a pointer to a Unicode string
359 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
361 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
365 if (src == LPSTR_TEXTCALLBACKW)
367 if (is_textW(*dest)) COMCTL32_Free(*dest);
368 *dest = LPSTR_TEXTCALLBACKW;
372 LPWSTR pszText = textdupTtoW(src, isW);
373 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
374 bResult = Str_SetPtrW(dest, pszText);
375 textfreeT(pszText, isW);
381 * compares a Unicode to a Unicode/ANSI text string
383 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
385 if (!aw) return bt ? -1 : 0;
386 if (!bt) return aw ? 1 : 0;
387 if (aw == LPSTR_TEXTCALLBACKW)
388 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
389 if (bt != LPSTR_TEXTCALLBACKW)
391 LPWSTR bw = textdupTtoW(bt, isW);
392 int r = bw ? lstrcmpW(aw, bw) : 1;
400 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
404 n = min(min(n, strlenW(s1)), strlenW(s2));
405 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
406 return res ? res - sizeof(WCHAR) : res;
409 /******** Debugging functions *****************************************/
411 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
413 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
414 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
417 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
419 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
420 n = min(textlenT(text, isW), n);
421 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
424 static char* debug_getbuf()
426 static int index = 0;
427 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
428 return buffers[index++ % DEBUG_BUFFERS];
431 static inline char* debugpoint(const POINT* lppt)
435 char* buf = debug_getbuf();
436 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
438 } else return "(null)";
441 static inline char* debugrect(const RECT* rect)
445 char* buf = debug_getbuf();
446 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
447 rect->left, rect->top, rect->right, rect->bottom);
449 } else return "(null)";
452 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
454 char* buf = debug_getbuf(), *text = buf;
455 int len, size = DEBUG_BUFFER_SIZE;
457 if (lpLVItem == NULL) return "(null)";
458 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
459 if (len == -1) goto end; buf += len; size -= len;
460 if (lpLVItem->mask & LVIF_STATE)
461 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
463 if (len == -1) goto end; buf += len; size -= len;
464 if (lpLVItem->mask & LVIF_TEXT)
465 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
467 if (len == -1) goto end; buf += len; size -= len;
468 if (lpLVItem->mask & LVIF_IMAGE)
469 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
471 if (len == -1) goto end; buf += len; size -= len;
472 if (lpLVItem->mask & LVIF_PARAM)
473 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
475 if (len == -1) goto end; buf += len; size -= len;
476 if (lpLVItem->mask & LVIF_INDENT)
477 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
479 if (len == -1) goto end; buf += len; size -= len;
482 buf = text + strlen(text);
484 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
488 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
490 char* buf = debug_getbuf(), *text = buf;
491 int len, size = DEBUG_BUFFER_SIZE;
493 if (lpColumn == NULL) return "(null)";
494 len = snprintf(buf, size, "{");
495 if (len == -1) goto end; buf += len; size -= len;
496 if (lpColumn->mask & LVCF_SUBITEM)
497 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
499 if (len == -1) goto end; buf += len; size -= len;
500 if (lpColumn->mask & LVCF_FMT)
501 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
503 if (len == -1) goto end; buf += len; size -= len;
504 if (lpColumn->mask & LVCF_WIDTH)
505 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
507 if (len == -1) goto end; buf += len; size -= len;
508 if (lpColumn->mask & LVCF_TEXT)
509 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
511 if (len == -1) goto end; buf += len; size -= len;
512 if (lpColumn->mask & LVCF_IMAGE)
513 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
515 if (len == -1) goto end; buf += len; size -= len;
516 if (lpColumn->mask & LVCF_ORDER)
517 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
519 if (len == -1) goto end; buf += len; size -= len;
522 buf = text + strlen(text);
524 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
529 /******** Notification functions i************************************/
531 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
533 pnmh->hwndFrom = infoPtr->hwndSelf;
534 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
536 return (BOOL)SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
537 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
540 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
543 notify(infoPtr, LVN_ITEMACTIVATE, &nmh);
546 static inline BOOL notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
548 return notify(infoPtr, code, (LPNMHDR)plvnm);
551 static int get_ansi_notification(INT unicodeNotificationCode)
553 switch (unicodeNotificationCode)
555 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
556 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
557 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
558 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
559 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
560 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
562 ERR("unknown notification %x\n", unicodeNotificationCode);
563 return unicodeNotificationCode;
567 Send notification. depends on dispinfoW having same
568 structure as dispinfoA.
569 infoPtr : listview struct
570 notificationCode : *Unicode* notification code
571 pdi : dispinfo structure (can be unicode or ansi)
572 isW : TRUE if dispinfo is Unicode
574 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
576 BOOL bResult = FALSE;
577 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
579 INT cchTempBufMax = 0, savCchTextMax = 0;
580 LPWSTR pszTempBuf = NULL, savPszText = NULL;
582 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
584 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
585 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
588 if (convertToAnsi || convertToUnicode)
590 if (notificationCode != LVN_GETDISPINFOW)
592 cchTempBufMax = convertToUnicode ?
593 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
594 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
598 cchTempBufMax = pdi->item.cchTextMax;
599 pdi->item.pszText = 0; /* make sure we don't process garbage */
602 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
603 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
604 if (!pszTempBuf) return FALSE;
605 if (convertToUnicode)
606 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
607 pszTempBuf, cchTempBufMax);
609 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
610 cchTempBufMax, NULL, NULL);
611 savCchTextMax = pdi->item.cchTextMax;
612 savPszText = pdi->item.pszText;
613 pdi->item.pszText = pszTempBuf;
614 pdi->item.cchTextMax = cchTempBufMax;
617 if (infoPtr->notifyFormat == NFR_ANSI)
618 realNotifCode = get_ansi_notification(notificationCode);
620 realNotifCode = notificationCode;
621 bResult = notify(infoPtr, realNotifCode, (LPNMHDR)pdi);
623 if (convertToUnicode || convertToAnsi)
625 if (convertToUnicode) /* note : pointer can be changed by app ! */
626 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
627 savCchTextMax, NULL, NULL);
629 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
630 savPszText, savCchTextMax);
631 pdi->item.pszText = savPszText; /* restores our buffer */
632 pdi->item.cchTextMax = savCchTextMax;
633 HeapFree(GetProcessHeap(), 0, pszTempBuf);
638 static inline void notify_odcachehint(LISTVIEW_INFO *infoPtr, INT iFrom, INT iTo)
644 notify(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
647 static BOOL notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, HDC hdc, RECT rc)
649 NMLVCUSTOMDRAW nmlvcd;
651 TRACE("(dwDrawStage=%lx, hdc=%x, rc=?)\n", dwDrawStage, hdc);
653 nmlvcd.nmcd.dwDrawStage = dwDrawStage;
654 nmlvcd.nmcd.hdc = hdc;
656 nmlvcd.nmcd.dwItemSpec = 0;
657 nmlvcd.nmcd.uItemState = 0;
658 nmlvcd.nmcd.lItemlParam = 0;
659 nmlvcd.clrText = infoPtr->clrText;
660 nmlvcd.clrTextBk = infoPtr->clrBk;
662 return (BOOL)notify(infoPtr, NM_CUSTOMDRAW, &nmlvcd.nmcd.hdr);
665 /* FIXME: we should inline this where it's called somehow
666 * I think we need to pass in the structure
668 static BOOL notify_customdrawitem (LISTVIEW_INFO *infoPtr, HDC hdc, UINT iItem, UINT iSubItem, UINT uItemDrawState)
670 NMLVCUSTOMDRAW nmlvcd;
678 item.mask = LVIF_PARAM;
679 if (!LISTVIEW_GetItemT(infoPtr, &item, TRUE)) return FALSE;
683 if (LISTVIEW_GetItemState(infoPtr, iItem, LVIS_SELECTED)) uItemState |= CDIS_SELECTED;
684 if (LISTVIEW_GetItemState(infoPtr, iItem, LVIS_FOCUSED)) uItemState |= CDIS_FOCUS;
685 if (iItem == infoPtr->nHotItem) uItemState |= CDIS_HOT;
687 itemRect.left = LVIR_BOUNDS;
688 LISTVIEW_GetItemRect(infoPtr, iItem, &itemRect);
690 nmlvcd.nmcd.dwDrawStage = CDDS_ITEM | uItemDrawState;
691 nmlvcd.nmcd.hdc = hdc;
692 nmlvcd.nmcd.rc = itemRect;
693 nmlvcd.nmcd.dwItemSpec = iItem;
694 nmlvcd.nmcd.uItemState = uItemState;
695 nmlvcd.nmcd.lItemlParam = item.lParam;
696 nmlvcd.clrText = infoPtr->clrText;
697 nmlvcd.clrTextBk = infoPtr->clrBk;
698 nmlvcd.iSubItem = iSubItem;
700 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
701 nmlvcd.nmcd.dwDrawStage, nmlvcd.nmcd.hdc, nmlvcd.nmcd.dwItemSpec,
702 nmlvcd.nmcd.uItemState, nmlvcd.nmcd.lItemlParam);
704 bReturn = notify(infoPtr, NM_CUSTOMDRAW, &nmlvcd.nmcd.hdr);
706 infoPtr->clrText = nmlvcd.clrText;
707 infoPtr->clrBk = nmlvcd.clrTextBk;
712 /******** Misc helper functions ************************************/
714 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
715 WPARAM wParam, LPARAM lParam, BOOL isW)
717 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
718 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
721 /******** Internal API functions ************************************/
723 /* The Invalidate* are macros, so we preserve the caller location */
724 #define LISTVIEW_InvalidateRect(infoPtr, rect) do { \
725 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
726 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
729 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
731 rcItem.left = LVIR_BOUNDS; \
732 if(LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) \
733 LISTVIEW_InvalidateRect(infoPtr, &rcItem); \
736 #define LISTVIEW_InvalidateList(infoPtr)\
737 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->rcList)
739 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
741 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
744 static inline int LISTVIEW_GetType(LISTVIEW_INFO *infoPtr)
746 return infoPtr->dwStyle & LVS_TYPEMASK;
751 * Retrieves the number of items that can fit vertically in the client area.
754 * [I] infoPtr : valid pointer to the listview structure
757 * Number of items per row.
759 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
761 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
763 return max(nListWidth/infoPtr->nItemWidth, 1);
768 * Retrieves the number of items that can fit horizontally in the client
772 * [I] infoPtr : valid pointer to the listview structure
775 * Number of items per column.
777 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
779 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
781 return max(nListHeight / infoPtr->nItemHeight, 1);
786 * Retrieves the range of visible items. Note that the upper limit
787 * may be a bit larger than the actual last visible item.
790 * [I] infoPtr : valid pointer to the listview structure
793 * maximum range of visible items
795 static RANGE LISTVIEW_GetVisibleRange(LISTVIEW_INFO *infoPtr)
797 UINT uView = LISTVIEW_GetType(infoPtr);
798 INT nPerCol, nPerRow;
801 visrange.lower = LISTVIEW_GetTopIndex(infoPtr);
803 if (uView == LVS_REPORT)
805 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
808 else if (uView == LVS_LIST)
810 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
811 nPerRow = LISTVIEW_GetCountPerRow(infoPtr);
815 /* FIXME: this is correct only in autoarrange mode */
816 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
817 nPerRow = LISTVIEW_GetCountPerRow(infoPtr) + 1;
820 visrange.upper = visrange.lower + nPerCol * nPerRow;
821 if (visrange.upper > infoPtr->nItemCount)
822 visrange.upper = infoPtr->nItemCount;
824 TRACE("range=(%d, %d)\n", visrange.lower, visrange.upper);
830 /*************************************************************************
831 * LISTVIEW_ProcessLetterKeys
833 * Processes keyboard messages generated by pressing the letter keys
835 * What this does is perform a case insensitive search from the
836 * current position with the following quirks:
837 * - If two chars or more are pressed in quick succession we search
838 * for the corresponding string (e.g. 'abc').
839 * - If there is a delay we wipe away the current search string and
840 * restart with just that char.
841 * - If the user keeps pressing the same character, whether slowly or
842 * fast, so that the search string is entirely composed of this
843 * character ('aaaaa' for instance), then we search for first item
844 * that starting with that character.
845 * - If the user types the above character in quick succession, then
846 * we must also search for the corresponding string ('aaaaa'), and
847 * go to that string if there is a match.
850 * [I] hwnd : handle to the window
851 * [I] charCode : the character code, the actual character
852 * [I] keyData : key data
860 * - The current implementation has a list of characters it will
861 * accept and it ignores averything else. In particular it will
862 * ignore accentuated characters which seems to match what
863 * Windows does. But I'm not sure it makes sense to follow
865 * - We don't sound a beep when the search fails.
869 * TREEVIEW_ProcessLetterKeys
871 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
876 WCHAR buffer[MAX_PATH];
877 DWORD timestamp,elapsed;
879 /* simple parameter checking */
880 if (!charCode || !keyData) return 0;
882 /* only allow the valid WM_CHARs through */
883 if (!isalnum(charCode) &&
884 charCode != '.' && charCode != '`' && charCode != '!' &&
885 charCode != '@' && charCode != '#' && charCode != '$' &&
886 charCode != '%' && charCode != '^' && charCode != '&' &&
887 charCode != '*' && charCode != '(' && charCode != ')' &&
888 charCode != '-' && charCode != '_' && charCode != '+' &&
889 charCode != '=' && charCode != '\\'&& charCode != ']' &&
890 charCode != '}' && charCode != '[' && charCode != '{' &&
891 charCode != '/' && charCode != '?' && charCode != '>' &&
892 charCode != '<' && charCode != ',' && charCode != '~')
895 /* if there's one item or less, there is no where to go */
896 if (infoPtr->nItemCount <= 1) return 0;
898 /* compute how much time elapsed since last keypress */
899 timestamp=GetTickCount();
900 if (timestamp > infoPtr->lastKeyPressTimestamp) {
901 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
903 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
906 /* update the search parameters */
907 infoPtr->lastKeyPressTimestamp=timestamp;
908 if (elapsed < KEY_DELAY) {
909 if (infoPtr->nSearchParamLength < MAX_PATH) {
910 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
912 if (infoPtr->charCode != charCode) {
913 infoPtr->charCode=charCode=0;
916 infoPtr->charCode=charCode;
917 infoPtr->szSearchParam[0]=charCode;
918 infoPtr->nSearchParamLength=1;
919 /* Redundant with the 1 char string */
923 /* and search from the current position */
925 if (infoPtr->nFocusedItem >= 0) {
926 endidx=infoPtr->nFocusedItem;
928 /* if looking for single character match,
929 * then we must always move forward
931 if (infoPtr->nSearchParamLength == 1)
934 endidx=infoPtr->nItemCount;
938 if (idx == infoPtr->nItemCount) {
939 if (endidx == infoPtr->nItemCount || endidx == 0)
945 item.mask = LVIF_TEXT;
948 item.pszText = buffer;
949 item.cchTextMax = MAX_PATH;
950 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
952 /* check for a match */
953 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
956 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
957 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
958 /* This would work but we must keep looking for a longer match */
962 } while (idx != endidx);
965 LISTVIEW_KeySelection(infoPtr, nItem);
970 /*************************************************************************
971 * LISTVIEW_UpdateHeaderSize [Internal]
973 * Function to resize the header control
976 * hwnd [I] handle to a window
977 * nNewScrollPos [I] Scroll Pos to Set
984 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
989 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
991 GetWindowRect(infoPtr->hwndHeader, &winRect);
992 point[0].x = winRect.left;
993 point[0].y = winRect.top;
994 point[1].x = winRect.right;
995 point[1].y = winRect.bottom;
997 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
998 point[0].x = -nNewScrollPos;
999 point[1].x += nNewScrollPos;
1001 SetWindowPos(infoPtr->hwndHeader,0,
1002 point[0].x,point[0].y,point[1].x,point[1].y,
1003 SWP_NOZORDER | SWP_NOACTIVATE);
1008 * Update the scrollbars. This functions should be called whenever
1009 * the content, size or view changes.
1012 * [I] infoPtr : valid pointer to the listview structure
1017 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1019 LONG lStyle = infoPtr->dwStyle;
1020 UINT uView = lStyle & LVS_TYPEMASK;
1021 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1022 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1023 SCROLLINFO scrollInfo;
1025 if (lStyle & LVS_NOSCROLL) return;
1027 scrollInfo.cbSize = sizeof(SCROLLINFO);
1029 if (uView == LVS_LIST)
1031 /* update horizontal scrollbar */
1032 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1033 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1035 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1036 infoPtr->nItemCount, nCountPerColumn, nCountPerRow);
1038 scrollInfo.nMin = 0;
1039 scrollInfo.nMax = infoPtr->nItemCount / nCountPerColumn;
1040 if((infoPtr->nItemCount % nCountPerColumn) == 0)
1042 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1043 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1044 scrollInfo.nPage = nCountPerRow;
1045 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1046 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1047 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1049 else if (uView == LVS_REPORT)
1053 /* update vertical scrollbar */
1054 scrollInfo.nMin = 0;
1055 scrollInfo.nMax = infoPtr->nItemCount - 1;
1056 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1057 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1058 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1059 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1060 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1061 scrollInfo.nMax, scrollInfo.nPage, test);
1062 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1063 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1065 /* update horizontal scrollbar */
1066 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1067 scrollInfo.fMask = SIF_POS;
1068 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1069 || infoPtr->nItemCount == 0)
1071 scrollInfo.nPos = 0;
1073 scrollInfo.nMin = 0;
1074 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1075 scrollInfo.nPage = nListWidth;
1076 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1077 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1078 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1079 scrollInfo.nMax, scrollInfo.nPage, test);
1080 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1081 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1083 /* Update the Header Control */
1084 scrollInfo.fMask = SIF_POS;
1085 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1086 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1093 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1095 INT nViewWidth = rcView.right - rcView.left;
1096 INT nViewHeight = rcView.bottom - rcView.top;
1098 /* Update Horizontal Scrollbar */
1099 scrollInfo.fMask = SIF_POS;
1100 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1101 || infoPtr->nItemCount == 0)
1103 scrollInfo.nPos = 0;
1105 scrollInfo.nMin = 0;
1106 scrollInfo.nMax = max(nViewWidth, 0)-1;
1107 scrollInfo.nPage = nListWidth;
1108 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1109 TRACE("LVS_ICON/SMALLICON Horz.\n");
1110 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1112 /* Update Vertical Scrollbar */
1113 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1114 scrollInfo.fMask = SIF_POS;
1115 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1116 || infoPtr->nItemCount == 0)
1118 scrollInfo.nPos = 0;
1120 scrollInfo.nMin = 0;
1121 scrollInfo.nMax = max(nViewHeight,0)-1;
1122 scrollInfo.nPage = nListHeight;
1123 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1124 TRACE("LVS_ICON/SMALLICON Vert.\n");
1125 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1133 * Shows/hides the focus rectangle.
1136 * [I] infoPtr : valid pointer to the listview structure
1137 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1142 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, INT nItem, BOOL fShow)
1146 TRACE("fShow=%d, nItem=%d\n", fShow, nItem);
1148 if (nItem < 0 || nItem >= infoPtr->nItemCount) return;
1150 rcItem.left = LVIR_BOUNDS;
1152 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
1153 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
1154 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
1156 /* this little optimization eliminates some nasty flicker */
1157 if (!LISTVIEW_GetSubItemRect(infoPtr, nItem, &rcItem)) return;
1161 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
1164 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
1172 item.mask = LVIF_PARAM;
1173 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto invalidate;
1175 if (!(hdc = GetDC(infoPtr->hwndSelf))) goto invalidate;
1176 ZeroMemory(&dis, sizeof(dis));
1177 dis.CtlType = ODT_LISTVIEW;
1178 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1180 dis.itemAction = ODA_FOCUS;
1181 if (fShow) dis.itemState |= ODS_FOCUS;
1182 dis.hwndItem = infoPtr->hwndSelf;
1184 dis.rcItem = rcItem;
1185 dis.itemData = item.lParam;
1187 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1188 ReleaseDC(infoPtr->hwndSelf, hdc);
1193 /* Here we are inneficient. We could, in theory, simply DrawFocusRect
1194 * to erase/show the focus, without all this heavy duty redraw.
1195 * Note that there are cases where we can not do that: when the list
1196 * is in ICON mode, and the item is large, we must to invalidate it.
1197 * Moreover, in the vast majority of cases, the selection status of
1198 * the item changes anyway, and so the item is invalidated already,
1199 * so not too much harm is done. If we do notice any flicker, we should
1200 * refine this method. */
1202 LISTVIEW_InvalidateRect(infoPtr, &rcItem);
1207 * Invalidates all visible selected items.
1209 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1214 visrange = LISTVIEW_GetVisibleRange(infoPtr);
1215 for (i = visrange.lower; i <= visrange.upper; i++)
1217 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
1218 LISTVIEW_InvalidateItem(infoPtr, i);
1225 * Prints a message for unsupported window styles.
1226 * A kind of TODO list for window styles.
1229 * [I] LONG : window style
1234 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1236 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1237 FIXME(" LVS_NOSCROLL\n");
1239 if (lStyle & LVS_NOLABELWRAP)
1240 FIXME(" LVS_NOLABELWRAP\n");
1242 if (lStyle & LVS_SORTASCENDING)
1243 FIXME(" LVS_SORTASCENDING\n");
1245 if (lStyle & LVS_SORTDESCENDING)
1246 FIXME(" LVS_SORTDESCENDING\n");
1250 * DESCRIPTION: [INTERNAL]
1251 * Compute the rectangles of an item. This is to localize all
1252 * the computations in one place. If you are not interested in some
1253 * of these values, simply pass in a NULL -- the fucntion is smart
1254 * enough to compute only what's necessary. The function computes
1255 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1256 * one, the BOX rectangle. This rectangle is very cheap to compute,
1257 * and is guaranteed to contain all the other rectangles. Computing
1258 * the ICON rect is also cheap, but all the others are potentaily
1259 * expensive. This gives an easy and effective optimization when
1260 * searching (like point inclusion, or rectangle intersection):
1261 * first test against the BOX, and if TRUE, test agains the desired
1262 * rectangle. These optimizations are coded in:
1263 * LISTVIEW_PtInRect, and LISTVIEW_IntersectRect
1264 * use them wherever is appropriate.
1267 * [I] infoPtr : valid pointer to the listview structure
1268 * [I] nItem : item number
1269 * [O] lprcBox : ptr to Box rectangle
1270 * The internal LVIR_BOX rectangle
1271 * [O] lprcBounds : ptr to Bounds rectangle
1272 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1273 * [O] lprcIcon : ptr to Icon rectangle
1274 * Same as LVM_GETITEMRECT with LVIR_ICON
1275 * [O] lprcLabel : ptr to Label rectangle
1276 * Same as LVM_GETITEMRECT with LVIR_LABEL
1279 * TRUE if computations OK
1282 static BOOL LISTVIEW_GetItemMeasures(LISTVIEW_INFO *infoPtr, INT nItem,
1283 LPRECT lprcBox, LPRECT lprcBounds,
1284 LPRECT lprcIcon, LPRECT lprcLabel)
1286 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1287 BOOL doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1288 RECT Box, Icon, Label;
1291 /* This should be very cheap to compute */
1292 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
1294 /* Be smart and try to figure out the minimum we have to do */
1297 if (uView == LVS_REPORT) doIcon = TRUE;
1298 else doLabel = TRUE;
1300 if (uView == LVS_ICON && infoPtr->bFocus &&
1301 LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1302 oversizedBox = doLabel = TRUE;
1303 if (lprcLabel) doLabel = TRUE;
1304 if (doLabel || lprcIcon) doIcon = TRUE;
1306 /************************************************************/
1307 /* compute the box rectangle (it should be cheap to do) */
1308 /************************************************************/
1309 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1311 Box.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1312 Box.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1314 else if (uView == LVS_LIST)
1316 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1317 Box.left = nItem / nCountPerColumn * infoPtr->nItemWidth;
1318 Box.top = nItem % nCountPerColumn * infoPtr->nItemHeight;
1320 else /* LVS_REPORT */
1322 Box.left = REPORT_MARGINX;
1323 Box.top = nItem * infoPtr->nItemHeight;
1325 Box.left += Origin.x;
1326 Box.top += Origin.y;
1327 Box.right = Box.left + infoPtr->nItemWidth;
1328 Box.bottom = Box.top + infoPtr->nItemHeight;
1330 /************************************************************/
1331 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1332 /************************************************************/
1333 if (!doIcon) goto noicon;
1334 if (uView == LVS_ICON)
1336 Icon.left = Box.left;
1337 if (infoPtr->himlNormal)
1338 Icon.left += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2 - ICON_LR_HALF;
1340 Icon.right = Icon.left;
1341 Icon.bottom = Icon.top;
1342 if (infoPtr->himlNormal)
1344 Icon.right += infoPtr->iconSize.cx + ICON_LR_PADDING;
1345 Icon.bottom += infoPtr->iconSize.cy + ICON_TOP_PADDING;
1348 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1351 Icon.left = Box.left;
1352 if (uView == LVS_REPORT)
1356 lvItem.mask = LVIF_INDENT;
1357 lvItem.iItem = nItem;
1358 lvItem.iSubItem = 0;
1359 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
1360 Icon.left += infoPtr->iconSize.cx * lvItem.iIndent;
1362 if (infoPtr->himlState) Icon.left += infoPtr->iconStateSize.cx;
1364 Icon.right = Icon.left;
1365 if (infoPtr->himlSmall) Icon.right += infoPtr->iconSize.cx;
1366 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1368 if(lprcIcon) *lprcIcon = Icon;
1369 TRACE("hwnd=%x, item=%d, icon=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Icon));
1372 /************************************************************/
1373 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1374 /************************************************************/
1375 if (!doLabel) goto nolabel;
1376 if (uView == LVS_ICON)
1380 Label.left = Box.left;
1381 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1382 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1384 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1385 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
1387 Label.left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
1388 Label.right = Label.left + nLabelWidth;
1389 Label.bottom = Label.top + infoPtr->ntmHeight + 1;
1390 Label.bottom += HEIGHT_PADDING;
1394 Label.right = Label.left + infoPtr->nItemWidth;
1395 Label.bottom = Label.top + infoPtr->nItemHeight + HEIGHT_PADDING;
1396 LISTVIEW_UpdateLargeItemLabelRect (infoPtr, nItem, &Label);
1399 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1403 Label.left = Icon.right;
1404 Label.top = Box.top;
1405 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1406 nLabelWidth += TRAILING_PADDING;
1407 if (infoPtr->himlSmall) nLabelWidth += IMAGE_PADDING;
1408 Label.right = min(Label.left + nLabelWidth, Box.right);
1409 Label.bottom = Label.top + infoPtr->nItemHeight;
1412 if (lprcLabel) *lprcLabel = Label;
1413 TRACE("hwnd=%x, item=%d, label=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Label));
1416 /***********************************************************/
1417 /* compute bounds box for the item (ala LVM_GETITEMRECT) */
1418 /***********************************************************/
1419 if (!lprcBounds) goto nobounds;
1420 if (uView == LVS_REPORT)
1422 lprcBounds->left = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT ? Box.left : Icon.left;
1423 lprcBounds->top = Box.top;
1424 lprcBounds->right = min(lprcBounds->left + infoPtr->nItemWidth, Box.right) - REPORT_MARGINX;
1425 if (lprcBounds->right < lprcBounds->left) lprcBounds->right = lprcBounds->left;
1426 lprcBounds->bottom = lprcBounds->top + infoPtr->nItemHeight;
1430 UnionRect(lprcBounds, &Icon, &Label);
1432 TRACE("hwnd=%x, item=%d, bounds=%s\n", infoPtr->hwndSelf, nItem, debugrect(lprcBounds));
1435 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1436 else if (lprcBox) *lprcBox = Box;
1437 TRACE("hwnd=%x, item=%d, box=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Box));
1444 * Aligns the items with the top edge of the window.
1447 * [I] infoPtr : valid pointer to the listview structure
1452 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1454 UINT uView = LISTVIEW_GetType(infoPtr);
1455 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1458 INT i, off_x=0, off_y=0;
1460 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1462 /* Since SetItemPosition uses upper-left of icon, and for
1463 style=LVS_ICON the icon is not left adjusted, get the offset */
1464 if (uView == LVS_ICON)
1466 off_y = ICON_TOP_PADDING;
1467 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1471 ZeroMemory(&rcView, sizeof(RECT));
1472 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1474 infoPtr->rcList.left, infoPtr->rcList.right);
1476 if (nListWidth > infoPtr->nItemWidth)
1478 for (i = 0; i < infoPtr->nItemCount; i++)
1480 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1483 ptItem.y += infoPtr->nItemHeight;
1486 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1487 ptItem.x += infoPtr->nItemWidth;
1488 rcView.right = max(rcView.right, ptItem.x);
1491 rcView.right -= off_x;
1492 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1496 for (i = 0; i < infoPtr->nItemCount; i++)
1498 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1499 ptItem.y += infoPtr->nItemHeight;
1502 rcView.right = infoPtr->nItemWidth;
1503 rcView.bottom = ptItem.y-off_y;
1506 infoPtr->rcView = rcView;
1512 * Aligns the items with the left edge of the window.
1515 * [I] infoPtr : valid pointer to the listview structure
1520 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1522 UINT uView = LISTVIEW_GetType(infoPtr);
1523 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1526 INT i, off_x=0, off_y=0;
1528 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1530 /* Since SetItemPosition uses upper-left of icon, and for
1531 style=LVS_ICON the icon is not left adjusted, get the offset */
1532 if (uView == LVS_ICON)
1534 off_y = ICON_TOP_PADDING;
1535 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1539 ZeroMemory(&rcView, sizeof(RECT));
1540 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1542 if (nListHeight > infoPtr->nItemHeight)
1544 for (i = 0; i < infoPtr->nItemCount; i++)
1546 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1549 ptItem.x += infoPtr->nItemWidth;
1552 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1553 ptItem.y += infoPtr->nItemHeight;
1554 rcView.bottom = max(rcView.bottom, ptItem.y);
1557 rcView.right = ptItem.x + infoPtr->nItemWidth;
1561 for (i = 0; i < infoPtr->nItemCount; i++)
1563 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1564 ptItem.x += infoPtr->nItemWidth;
1567 rcView.bottom = infoPtr->nItemHeight;
1568 rcView.right = ptItem.x;
1571 infoPtr->rcView = rcView;
1578 * Retrieves the bounding rectangle of all the items.
1581 * [I] infoPtr : valid pointer to the listview structure
1582 * [O] lprcView : bounding rectangle
1588 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1592 TRACE("(lprcView=%p)\n", lprcView);
1594 if (!lprcView) return FALSE;
1596 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return FALSE;
1598 *lprcView = infoPtr->rcView;
1599 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
1601 TRACE("lprcView=%s\n", debugrect(lprcView));
1608 * Retrieves the subitem pointer associated with the subitem index.
1611 * [I] HDPA : DPA handle for a specific item
1612 * [I] INT : index of subitem
1615 * SUCCESS : subitem pointer
1618 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1621 LISTVIEW_SUBITEM *lpSubItem;
1624 /* we should binary search here if need be */
1625 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1627 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1628 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
1638 * Calculates the width of a specific item.
1641 * [I] infoPtr : valid pointer to the listview structure
1642 * [I] nItem : item to calculate width, or -1 for max of all
1645 * Returns the width of an item width an item.
1647 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr, INT nItem)
1649 UINT uView = LISTVIEW_GetType(infoPtr);
1650 INT nItemWidth = 0, i;
1652 if (uView == LVS_ICON)
1653 nItemWidth = infoPtr->iconSpacing.cx;
1654 else if (uView == LVS_REPORT)
1656 INT nHeaderItemCount;
1659 /* calculate width of header */
1660 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1661 for (i = 0; i < nHeaderItemCount; i++)
1662 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem))
1663 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1669 if (infoPtr->nItemCount == 0) return DEFAULT_COLUMN_WIDTH;
1671 /* get width of string */
1674 for (i = 0; i < infoPtr->nItemCount; i++)
1676 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
1677 nItemWidth = max(nItemWidth, nLabelWidth);
1681 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1682 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
1683 nItemWidth += WIDTH_PADDING;
1684 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
1685 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
1686 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1689 return max(nItemWidth, 1);
1694 * Calculates the max width of any item in the list.
1697 * [I] infoPtr : valid pointer to the listview structure
1698 * [I] LONG : window style
1701 * Returns item width.
1703 static inline INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *infoPtr)
1705 return LISTVIEW_CalculateItemWidth(infoPtr, -1);
1710 * Retrieves and saves important text metrics info for the current
1714 * [I] infoPtr : valid pointer to the listview structure
1717 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
1720 HDC hdc = GetDC(infoPtr->hwndSelf);
1721 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1722 INT oldHeight, oldACW;
1724 GetTextMetricsW(hdc, &tm);
1726 oldHeight = infoPtr->ntmHeight;
1727 oldACW = infoPtr->ntmAveCharWidth;
1728 infoPtr->ntmHeight = tm.tmHeight;
1729 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1731 SelectObject(hdc, hOldFont);
1732 ReleaseDC(infoPtr->hwndSelf, hdc);
1733 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1734 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1740 * Calculates the height of an item.
1743 * [I] infoPtr : valid pointer to the listview structure
1746 * Returns item height.
1748 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *infoPtr)
1752 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
1753 nItemHeight = infoPtr->iconSpacing.cy;
1756 nItemHeight = infoPtr->ntmHeight;
1757 if (infoPtr->himlState)
1758 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
1759 if (infoPtr->himlSmall)
1760 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
1761 if (infoPtr->himlState || infoPtr->himlSmall)
1762 nItemHeight += HEIGHT_PADDING;
1768 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
1772 ERR("Selections are:\n");
1773 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
1775 RANGE *selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
1776 ERR(" [%d - %d]\n", selection->lower, selection->upper);
1783 * A compare function for selection ranges
1786 * [I] range1 : pointer to selection range 1;
1787 * [I] range2 : pointer to selection range 2;
1791 * >0 : if Item 1 > Item 2
1792 * <0 : if Item 2 > Item 1
1793 * 0 : if Item 1 == Item 2
1795 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2, LPARAM flags)
1797 if (((RANGE*)range1)->upper < ((RANGE*)range2)->lower)
1799 if (((RANGE*)range2)->upper < ((RANGE*)range1)->lower)
1805 * Helper function for LISTVIEW_AddSelectionRange, and LISTVIEW_SetItem.
1807 static BOOL add_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
1813 TRACE("range (%i - %i)\n", lower, upper);
1815 /* try find overlapping selections first */
1816 selection.lower = lower - 1;
1817 selection.upper = upper + 1;
1818 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1819 LISTVIEW_CompareSelectionRanges, 0, 0);
1825 /* create the brand new selection to insert */
1826 newsel = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
1827 if(!newsel) return FALSE;
1828 newsel->lower = lower;
1829 newsel->upper = upper;
1831 /* figure out where to insert it */
1832 index = DPA_Search(infoPtr->hdpaSelectionRanges, newsel, 0,
1833 LISTVIEW_CompareSelectionRanges, 0, DPAS_INSERTAFTER);
1834 if (index == -1) index = 0;
1836 /* and get it over with */
1837 DPA_InsertPtr(infoPtr->hdpaSelectionRanges, index, newsel);
1841 RANGE *chksel, *mrgsel;
1842 INT fromindex, mergeindex;
1844 chksel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index);
1845 if (!chksel) return FALSE;
1846 TRACE("Merge with index %i (%d - %d)\n",
1847 index, chksel->lower, chksel->upper);
1849 chksel->lower = min(lower, chksel->lower);
1850 chksel->upper = max(upper, chksel->upper);
1852 TRACE("New range %i (%d - %d)\n",
1853 index, chksel->lower, chksel->upper);
1855 /* merge now common selection ranges */
1857 selection.lower = chksel->lower - 1;
1858 selection.upper = chksel->upper + 1;
1862 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, fromindex,
1863 LISTVIEW_CompareSelectionRanges, 0, 0);
1864 if (mergeindex == -1) break;
1865 if (mergeindex == index)
1867 fromindex = index + 1;
1871 TRACE("Merge with index %i\n", mergeindex);
1873 mrgsel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, mergeindex);
1874 if (!mrgsel) return FALSE;
1876 chksel->lower = min(chksel->lower, mrgsel->lower);
1877 chksel->upper = max(chksel->upper, mrgsel->upper);
1878 COMCTL32_Free(mrgsel);
1879 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, mergeindex);
1880 if (mergeindex < index) index --;
1884 /*DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);*/
1886 if (adj_sel_only) return TRUE;
1888 /* set the selection on items */
1889 lvItem.state = LVIS_SELECTED;
1890 lvItem.stateMask = LVIS_SELECTED;
1891 for(i = lower; i <= upper; i++)
1892 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
1898 * Helper function for LISTVIEW_RemoveSelectionRange, and LISTVIEW_SetItem.
1900 static BOOL remove_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
1902 RANGE remsel, tmpsel, *chksel;
1908 lvItem.stateMask = LVIS_SELECTED;
1910 remsel.lower = lower;
1911 remsel.upper = upper;
1913 TRACE("range: (%d - %d)\n", remsel.lower, remsel.upper);
1917 index = DPA_Search(infoPtr->hdpaSelectionRanges, &remsel, 0,
1918 LISTVIEW_CompareSelectionRanges, 0, 0);
1919 if (index == -1) return TRUE;
1921 chksel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index);
1922 if (!chksel) return FALSE;
1924 TRACE("Matches range index %i (%d - %d)\n",
1925 index, chksel->lower, chksel->upper);
1927 /* case 1: Same range */
1928 if ( (chksel->upper == remsel.upper) &&
1929 (chksel->lower == remsel.lower) )
1931 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, index);
1934 /* case 2: engulf */
1935 else if ( (chksel->upper <= remsel.upper) &&
1936 (chksel->lower >= remsel.lower) )
1938 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, index);
1940 /* case 3: overlap upper */
1941 else if ( (chksel->upper < remsel.upper) &&
1942 (chksel->lower < remsel.lower) )
1944 chksel->upper = remsel.lower - 1;
1946 /* case 4: overlap lower */
1947 else if ( (chksel->upper > remsel.upper) &&
1948 (chksel->lower > remsel.lower) )
1950 chksel->lower = remsel.upper + 1;
1952 /* case 5: fully internal */
1956 (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
1957 if (!newsel) return FALSE;
1959 newsel->lower = chksel->lower;
1960 newsel->upper = remsel.lower - 1;
1961 chksel->lower = remsel.upper + 1;
1962 DPA_InsertPtr(infoPtr->hdpaSelectionRanges, index, newsel);
1963 /*DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);*/
1967 if (adj_sel_only) continue;
1969 /* here, chksel holds the selection to delete */
1970 for (i = chksel->lower; i <= chksel->upper; i++)
1971 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
1980 * Adds a selection range.
1983 * [I] infoPtr : valid pointer to the listview structure
1984 * [I] lower : lower item index
1985 * [I] upper : upper item index
1991 static inline BOOL LISTVIEW_AddSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
1993 return add_selection_range(infoPtr, lower, upper, FALSE);
1998 * Removes a range selections.
2001 * [I] infoPtr : valid pointer to the listview structure
2002 * [I] lower : lower item index
2003 * [I] upper : upper item index
2009 static inline BOOL LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2011 return remove_selection_range(infoPtr, lower, upper, FALSE);
2016 * Removes all selection ranges
2019 * [I] infoPtr : valid pointer to the listview structure
2025 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr)
2029 if (infoPtr->bRemovingAllSelections) return TRUE;
2031 infoPtr->bRemovingAllSelections = TRUE;
2037 sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, 0);
2038 if (sel) LISTVIEW_RemoveSelectionRange(infoPtr, sel->lower, sel->upper);
2040 while (infoPtr->hdpaSelectionRanges->nItemCount > 0);
2042 infoPtr->bRemovingAllSelections = FALSE;
2049 * Retrieves the number of items that are marked as selected.
2052 * [I] infoPtr : valid pointer to the listview structure
2055 * Number of items selected.
2057 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2059 INT i, nSelectedCount = 0;
2061 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2064 for (i = 0; i < infoPtr->nItemCount; i++)
2066 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2072 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
2074 RANGE *sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
2075 nSelectedCount += sel->upper - sel->lower + 1;
2079 TRACE("nSelectedCount=%d\n", nSelectedCount);
2080 return nSelectedCount;
2085 * Manages the item focus.
2088 * [I] infoPtr : valid pointer to the listview structure
2089 * [I] INT : item index
2092 * TRUE : focused item changed
2093 * FALSE : focused item has NOT changed
2095 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2097 INT oldFocus = infoPtr->nFocusedItem;
2100 lvItem.state = LVIS_FOCUSED;
2101 lvItem.stateMask = LVIS_FOCUSED;
2102 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2104 return oldFocus != infoPtr->nFocusedItem;
2109 * Updates the various indices after an item has been inserted or deleted.
2112 * [I] infoPtr : valid pointer to the listview structure
2113 * [I] nItem : item index
2114 * [I] direction : Direction of shift, +1 or -1.
2119 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2121 RANGE selection,*checkselection;
2124 TRACE("Shifting %iu, %i steps\n",nItem,direction);
2126 selection.upper = nItem;
2127 selection.lower = nItem;
2129 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
2130 LISTVIEW_CompareSelectionRanges,
2131 0,DPAS_SORTED|DPAS_INSERTAFTER);
2133 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
2135 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
2136 if ((checkselection->lower >= nItem)&&
2137 ((int)(checkselection->lower + direction) >= 0))
2138 checkselection->lower += direction;
2139 if ((checkselection->upper >= nItem)&&
2140 ((int)(checkselection->upper + direction) >= 0))
2141 checkselection->upper += direction;
2145 /* Note that the following will fail if direction != +1 and -1 */
2146 if (infoPtr->nSelectionMark > nItem)
2147 infoPtr->nSelectionMark += direction;
2148 else if (infoPtr->nSelectionMark == nItem)
2151 infoPtr->nSelectionMark += direction;
2152 else if (infoPtr->nSelectionMark >= infoPtr->nItemCount)
2153 infoPtr->nSelectionMark = infoPtr->nItemCount - 1;
2156 if (infoPtr->nFocusedItem > nItem)
2157 infoPtr->nFocusedItem += direction;
2158 else if (infoPtr->nFocusedItem == nItem)
2161 infoPtr->nFocusedItem += direction;
2164 if (infoPtr->nFocusedItem >= infoPtr->nItemCount)
2165 infoPtr->nFocusedItem = infoPtr->nItemCount - 1;
2166 if (infoPtr->nFocusedItem >= 0)
2167 LISTVIEW_SetItemFocus(infoPtr, infoPtr->nFocusedItem);
2170 /* But we are not supposed to modify nHotItem! */
2176 * Adds a block of selections.
2179 * [I] infoPtr : valid pointer to the listview structure
2180 * [I] INT : item index
2185 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2187 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2188 INT nLast = max(infoPtr->nSelectionMark, nItem);
2195 item.state = LVIS_SELECTED;
2196 item.stateMask = LVIS_SELECTED;
2198 /* FIXME: this is not correct LVS_OWNERDATA
2199 * See docu for LVN_ITEMCHANGED. Is there something similar for
2200 * RemoveGroupSelection (is there such a thing?)?
2202 for (i = nFirst; i <= nLast; i++)
2203 LISTVIEW_SetItemState(infoPtr,i,&item);
2205 LISTVIEW_SetItemFocus(infoPtr, nItem);
2206 infoPtr->nSelectionMark = nItem;
2212 * Sets a single group selection.
2215 * [I] infoPtr : valid pointer to the listview structure
2216 * [I] INT : item index
2221 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2223 UINT uView = LISTVIEW_GetType(infoPtr);
2229 LISTVIEW_RemoveAllSelections(infoPtr);
2231 item.state = LVIS_SELECTED;
2232 item.stateMask = LVIS_SELECTED;
2234 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2238 if (infoPtr->nSelectionMark == -1)
2239 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2242 nFirst = min(infoPtr->nSelectionMark, nItem);
2243 nLast = max(infoPtr->nSelectionMark, nItem);
2245 for (i = nFirst; i <= nLast; i++)
2246 LISTVIEW_SetItemState(infoPtr, i, &item);
2250 RECT rcItem, rcSelMark;
2252 rcItem.left = LVIR_BOUNDS;
2253 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2254 rcSelMark.left = LVIR_BOUNDS;
2255 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2256 UnionRect(&rcSel, &rcItem, &rcSelMark);
2257 for (i = 0; i <= infoPtr->nItemCount; i++)
2259 LISTVIEW_GetItemPosition(infoPtr, i, &ptItem);
2260 if (PtInRect(&rcSel, ptItem))
2261 LISTVIEW_SetItemState(infoPtr, i, &item);
2265 LISTVIEW_SetItemFocus(infoPtr, nItem);
2270 * Sets a single selection.
2273 * [I] infoPtr : valid pointer to the listview structure
2274 * [I] INT : item index
2279 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2283 TRACE("nItem=%d\n", nItem);
2285 LISTVIEW_RemoveAllSelections(infoPtr);
2287 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2288 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2289 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2291 infoPtr->nSelectionMark = nItem;
2296 * Set selection(s) with keyboard.
2299 * [I] infoPtr : valid pointer to the listview structure
2300 * [I] INT : item index
2303 * SUCCESS : TRUE (needs to be repainted)
2304 * FAILURE : FALSE (nothing has changed)
2306 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2308 /* FIXME: pass in the state */
2309 LONG lStyle = infoPtr->dwStyle;
2310 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2311 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2312 BOOL bResult = FALSE;
2314 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2316 if (lStyle & LVS_SINGLESEL)
2319 LISTVIEW_SetSelection(infoPtr, nItem);
2320 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2327 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2331 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2336 LISTVIEW_SetSelection(infoPtr, nItem);
2337 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2342 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2348 * Selects an item based on coordinates.
2351 * [I] infoPtr : valid pointer to the listview structure
2352 * [I] pt : mouse click ccordinates
2355 * SUCCESS : item index
2358 static INT LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, POINT pt)
2364 visrange = LISTVIEW_GetVisibleRange(infoPtr);
2365 for (i = visrange.lower; i <= visrange.upper; i++)
2367 rcItem.left = LVIR_SELECTBOUNDS;
2368 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
2370 TRACE("i=%d, rcItem=%s\n", i, debugrect(&rcItem));
2371 if (PtInRect(&rcItem, pt)) return i;
2379 * Called when the mouse is being actively tracked and has hovered for a specified
2383 * [I] infoPtr : valid pointer to the listview structure
2384 * [I] fwKeys : key indicator
2385 * [I] pts : mouse position
2388 * 0 if the message was processed, non-zero if there was an error
2391 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2392 * over the item for a certain period of time.
2395 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2397 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2398 /* FIXME: select the item!!! */
2399 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2406 * Called whenever WM_MOUSEMOVE is received.
2409 * [I] infoPtr : valid pointer to the listview structure
2410 * [I] fwKeys : key indicator
2411 * [I] pts : mouse position
2414 * 0 if the message is processed, non-zero if there was an error
2416 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2418 TRACKMOUSEEVENT trackinfo;
2420 /* see if we are supposed to be tracking mouse hovering */
2421 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2422 /* fill in the trackinfo struct */
2423 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2424 trackinfo.dwFlags = TME_QUERY;
2425 trackinfo.hwndTrack = infoPtr->hwndSelf;
2426 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2428 /* see if we are already tracking this hwnd */
2429 _TrackMouseEvent(&trackinfo);
2431 if(!(trackinfo.dwFlags & TME_HOVER)) {
2432 trackinfo.dwFlags = TME_HOVER;
2434 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2435 _TrackMouseEvent(&trackinfo);
2444 * Tests wheather the item is assignable to a list with style lStyle
2446 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2448 if ( (lpLVItem->mask & LVIF_TEXT) &&
2449 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2450 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2457 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2460 * [I] infoPtr : valid pointer to the listview structure
2461 * [I] lpLVItem : valid pointer to new item atttributes
2462 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2468 static BOOL set_owner_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2470 LONG lStyle = infoPtr->dwStyle;
2474 /* a virtual listview stores only the state for the main item */
2475 if (lpLVItem->iSubItem || !(lpLVItem->mask & LVIF_STATE)) return FALSE;
2477 oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, LVIS_FOCUSED | LVIS_SELECTED);
2478 TRACE("oldState=%x, newState=%x, uCallbackMask=%x\n",
2479 oldState, lpLVItem->state, infoPtr->uCallbackMask);
2481 /* we're done if we don't need to change anything we handle */
2482 if ( !((oldState ^ lpLVItem->state) & lpLVItem->stateMask &
2483 ~infoPtr->uCallbackMask & (LVIS_FOCUSED | LVIS_SELECTED))) return TRUE;
2486 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2487 * by LVS_OWERNDATA list controls
2490 /* if we handle the focus, and we're asked to change it, do it now */
2491 if ( lpLVItem->stateMask & LVIS_FOCUSED )
2493 if (lpLVItem->state & LVIS_FOCUSED)
2494 infoPtr->nFocusedItem = lpLVItem->iItem;
2495 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2496 infoPtr->nFocusedItem = -1;
2499 /* and the selection is the only other state a virtual list may hold */
2500 if (lpLVItem->stateMask & LVIS_SELECTED)
2502 if (lpLVItem->state & LVIS_SELECTED)
2504 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2505 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2508 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2511 /* notify the parent now that things have changed */
2512 ZeroMemory(&nmlv, sizeof(nmlv));
2513 nmlv.iItem = lpLVItem->iItem;
2514 nmlv.uNewState = lpLVItem->state;
2515 nmlv.uOldState = oldState;
2516 nmlv.uChanged = LVIF_STATE;
2517 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2524 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2527 * [I] infoPtr : valid pointer to the listview structure
2528 * [I] lpLVItem : valid pointer to new item atttributes
2529 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2535 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2537 LONG lStyle = infoPtr->dwStyle;
2538 UINT uView = lStyle & LVS_TYPEMASK;
2540 LISTVIEW_ITEM *lpItem;
2544 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2545 if (!hdpaSubItems && hdpaSubItems != (HDPA)-1) return FALSE;
2547 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2548 if (!lpItem) return FALSE;
2550 /* determine what fields will change */
2551 if ((lpLVItem->mask & LVIF_STATE) &&
2552 ((lpItem->state ^ lpLVItem->state) & lpLVItem->stateMask))
2553 uChanged |= LVIF_STATE;
2555 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2556 uChanged |= LVIF_IMAGE;
2558 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2559 uChanged |= LVIF_PARAM;
2561 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2562 uChanged |= LVIF_INDENT;
2564 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2565 uChanged |= LVIF_TEXT;
2567 if (!uChanged) return TRUE;
2569 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2570 nmlv.iItem = lpLVItem->iItem;
2571 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2572 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2573 nmlv.uChanged = uChanged;
2574 nmlv.lParam = lpItem->lParam;
2576 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2577 if(lpItem->valid && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
2580 /* copy information */
2581 if (lpLVItem->mask & LVIF_TEXT)
2582 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
2584 if (lpLVItem->mask & LVIF_IMAGE)
2585 lpItem->hdr.iImage = lpLVItem->iImage;
2587 if (lpLVItem->mask & LVIF_PARAM)
2588 lpItem->lParam = lpLVItem->lParam;
2590 if (lpLVItem->mask & LVIF_INDENT)
2591 lpItem->iIndent = lpLVItem->iIndent;
2593 if (uChanged & LVIF_STATE)
2595 lpItem->state &= ~lpLVItem->stateMask;
2596 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2597 if (nmlv.uNewState & LVIS_SELECTED)
2599 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2600 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2602 else if (lpLVItem->stateMask & LVIS_SELECTED)
2603 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2605 /* if we are asked to change focus, and we manage it, do it */
2606 if (nmlv.uNewState & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
2608 if (lpLVItem->state & LVIS_FOCUSED)
2610 infoPtr->nFocusedItem = lpLVItem->iItem;
2611 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
2613 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2614 infoPtr->nFocusedItem = -1;
2618 /* if LVS_LIST or LVS_SMALLICON, update the width of the items */
2619 if((uChanged & LVIF_TEXT) && ((uView == LVS_LIST) || (uView == LVS_SMALLICON)))
2621 int item_width = LISTVIEW_CalculateItemWidth(infoPtr, lpLVItem->iItem);
2622 if(item_width > infoPtr->nItemWidth) infoPtr->nItemWidth = item_width;
2625 /* if we're inserting the item, we're done */
2626 if (!lpItem->valid) return TRUE;
2628 /* send LVN_ITEMCHANGED notification */
2629 nmlv.lParam = lpItem->lParam;
2630 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2637 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2640 * [I] infoPtr : valid pointer to the listview structure
2641 * [I] lpLVItem : valid pointer to new subitem atttributes
2642 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2648 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2651 LISTVIEW_SUBITEM *lpSubItem;
2652 BOOL bModified = FALSE;
2654 /* set subitem only if column is present */
2655 if (Header_GetItemCount(infoPtr->hwndHeader) <= lpLVItem->iSubItem)
2658 /* First do some sanity checks */
2659 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
2660 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
2662 /* get the subitem structure, and create it if not there */
2663 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2664 if (!hdpaSubItems) return FALSE;
2666 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
2669 LISTVIEW_SUBITEM *tmpSubItem;
2672 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2673 if (!lpSubItem) return FALSE;
2674 /* we could binary search here, if need be...*/
2675 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2677 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2678 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
2680 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
2682 COMCTL32_Free(lpSubItem);
2685 lpSubItem->iSubItem = lpLVItem->iSubItem;
2689 if (lpLVItem->mask & LVIF_IMAGE)
2690 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
2692 lpSubItem->hdr.iImage = lpLVItem->iImage;
2696 if (lpLVItem->mask & LVIF_TEXT)
2697 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
2699 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
2703 if (bModified && !infoPtr->bIsDrawing)
2707 rect.left = LVIR_BOUNDS;
2708 rect.top = lpLVItem->iSubItem;
2709 /* GetSubItemRect will fail in non-report mode, so there's
2710 * gonna be no invalidation then, yay! */
2711 if (LISTVIEW_GetSubItemRect(infoPtr, lpLVItem->iItem, &rect))
2712 LISTVIEW_InvalidateRect(infoPtr, &rect);
2720 * Sets item attributes.
2723 * [I] infoPtr : valid pointer to the listview structure
2724 * [I] LPLVITEM : new item atttributes
2725 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2731 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2733 INT oldFocus = infoPtr->nFocusedItem;
2734 LPWSTR pszText = NULL;
2737 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2739 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
2742 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
2743 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
2745 pszText = lpLVItem->pszText;
2746 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
2749 /* actually set the fields */
2750 if (infoPtr->dwStyle & LVS_OWNERDATA)
2751 bResult = set_owner_item(infoPtr, lpLVItem, TRUE);
2754 /* sanity checks first */
2755 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
2757 if (lpLVItem->iSubItem)
2758 bResult = set_sub_item(infoPtr, lpLVItem, TRUE);
2760 bResult = set_main_item(infoPtr, lpLVItem, TRUE);
2763 /* redraw item, if necessary */
2764 if (bResult && !infoPtr->bIsDrawing && lpLVItem->iSubItem == 0)
2766 if (oldFocus != infoPtr->nFocusedItem && infoPtr->bFocus)
2767 LISTVIEW_ShowFocusRect(infoPtr, oldFocus, FALSE);
2769 /* this little optimization eliminates some nasty flicker */
2770 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
2771 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
2772 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
2776 rect.left = LVIR_BOUNDS;
2778 if (LISTVIEW_GetSubItemRect(infoPtr, lpLVItem->iItem, &rect))
2779 LISTVIEW_InvalidateRect(infoPtr, &rect);
2782 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
2787 textfreeT(lpLVItem->pszText, isW);
2788 lpLVItem->pszText = pszText;
2796 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2799 * [I] infoPtr : valid pointer to the listview structure
2804 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
2806 LONG lStyle = infoPtr->dwStyle;
2807 UINT uView = lStyle & LVS_TYPEMASK;
2809 SCROLLINFO scrollInfo;
2811 scrollInfo.cbSize = sizeof(SCROLLINFO);
2812 scrollInfo.fMask = SIF_POS;
2814 if (uView == LVS_LIST)
2816 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
2817 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
2819 else if (uView == LVS_REPORT)
2821 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2822 nItem = scrollInfo.nPos;
2826 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2827 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
2830 TRACE("nItem=%d\n", nItem);
2835 /* used by the drawing code */
2836 typedef struct tagTEXTATTR
2843 /* helper function for the drawing code */
2844 static inline void set_text_attr(HDC hdc, TEXTATTR *ta)
2846 ta->bkMode = SetBkMode(hdc, ta->bkMode);
2847 ta->bkColor = SetBkColor(hdc, ta->bkColor);
2848 ta->fgColor = SetTextColor(hdc, ta->fgColor);
2851 /* helper function for the drawing code */
2852 static void select_text_attr(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL isSelected, TEXTATTR *ta)
2854 ta->bkMode = OPAQUE;
2856 if (isSelected && infoPtr->bFocus)
2858 ta->bkColor = comctl32_color.clrHighlight;
2859 ta->fgColor = comctl32_color.clrHighlightText;
2861 else if (isSelected && (infoPtr->dwStyle & LVS_SHOWSELALWAYS))
2863 ta->bkColor = comctl32_color.clr3dFace;
2864 ta->fgColor = comctl32_color.clrBtnText;
2866 else if ( (infoPtr->clrTextBk != CLR_DEFAULT) && (infoPtr->clrTextBk != CLR_NONE) )
2868 ta->bkColor = infoPtr->clrTextBk;
2869 ta->fgColor = infoPtr->clrText;
2873 ta->bkMode = TRANSPARENT;
2874 ta->bkColor = GetBkColor(hdc);
2875 ta->fgColor = infoPtr->clrText;
2878 set_text_attr(hdc, ta);
2883 * Erases the background of the given rectangle
2886 * [I] infoPtr : valid pointer to the listview structure
2887 * [I] hdc : device context handle
2888 * [I] lprcBox : clipping rectangle
2894 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
2896 if (!infoPtr->hBkBrush) return FALSE;
2898 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
2900 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
2908 * [I] infoPtr : valid pointer to the listview structure
2909 * [I] HDC : device context handle
2910 * [I] INT : item index
2911 * [I] INT : subitem index
2912 * [I] RECT * : clipping rectangle
2918 static BOOL LISTVIEW_DrawSubItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem,
2919 INT nSubItem, RECT rcItem, UINT align)
2921 WCHAR szDispText[DISP_TEXT_SIZE];
2924 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, rcItem=%s)\n",
2925 hdc, nItem, nSubItem, debugrect(&rcItem));
2927 /* get information needed for drawing the item */
2928 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
2929 lvItem.iItem = nItem;
2930 lvItem.iSubItem = nSubItem;
2931 lvItem.cchTextMax = DISP_TEXT_SIZE;
2932 lvItem.pszText = szDispText;
2933 *lvItem.pszText = '\0';
2934 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
2936 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
2938 if (lvItem.iImage) FIXME("Draw the image for the subitem\n");
2940 DrawTextW(hdc, lvItem.pszText, -1, &rcItem,
2941 DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS | align);
2952 * [I] infoPtr : valid pointer to the listview structure
2953 * [I] hdc : device context handle
2954 * [I] nItem : item index
2955 * [I] rcItem : item rectangle
2958 * TRUE: if item is focused
2961 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem)
2963 WCHAR szDispText[DISP_TEXT_SIZE];
2964 INT nLabelWidth, imagePadding = 0;
2965 RECT* lprcFocus, rcOrig = rcItem;
2969 TRACE("(hdc=%x, nItem=%d)\n", hdc, nItem);
2971 /* get information needed for drawing the item */
2972 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
2973 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
2974 lvItem.iItem = nItem;
2975 lvItem.iSubItem = 0;
2976 lvItem.cchTextMax = DISP_TEXT_SIZE;
2977 lvItem.pszText = szDispText;
2978 *lvItem.pszText = '\0';
2979 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
2980 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
2982 /* now check if we need to update the focus rectangle */
2983 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
2984 if (lprcFocus) SetRectEmpty(lprcFocus);
2987 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
2990 if (infoPtr->himlState)
2992 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
2995 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
2996 rcItem.left, rcItem.top, ILD_NORMAL);
2998 rcItem.left += infoPtr->iconStateSize.cx;
2999 imagePadding = IMAGE_PADDING;
3003 if (infoPtr->himlSmall)
3005 if (lvItem.iImage >= 0)
3007 UINT mode = (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ?
3008 ILD_SELECTED : ILD_NORMAL;
3009 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3010 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc,
3011 rcItem.left, rcItem.top, mode);
3013 rcItem.left += infoPtr->iconSize.cx;
3014 imagePadding = IMAGE_PADDING;
3017 /* Don't bother painting item being edited */
3018 if (infoPtr->bEditing && lprcFocus)
3021 select_text_attr(infoPtr, hdc, lvItem.state & LVIS_SELECTED, &ta);
3023 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
3024 rcItem.left += imagePadding;
3025 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3026 if (rcItem.right > rcOrig.right) rcItem.right = rcOrig.right;
3030 TRACE("drawing text=%s, in rect=%s\n", debugstr_w(lvItem.pszText), debugrect(&rcItem));
3031 if(lprcFocus) *lprcFocus = rcItem;
3032 if (lvItem.state & LVIS_SELECTED)
3033 ExtTextOutW(hdc, rcItem.left, rcItem.top, ETO_OPAQUE, &rcItem, 0, 0, 0);
3034 DrawTextW(hdc, lvItem.pszText, -1, &rcItem,
3035 DT_SINGLELINE | DT_VCENTER | DT_WORD_ELLIPSIS | DT_CENTER);
3038 set_text_attr(hdc, &ta);
3039 return lprcFocus != NULL;
3044 * Draws an item when in large icon display mode.
3047 * [I] infoPtr : valid pointer to the listview structure
3048 * [I] hdc : device context handle
3049 * [I] nItem : item index
3050 * [I] rcItem : clipping rectangle
3053 * TRUE: if item is focused
3056 static BOOL LISTVIEW_DrawLargeItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem)
3058 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3060 UINT uFormat = LISTVIEW_DTFLAGS;
3061 RECT rcIcon, rcFocus, rcLabel, *lprcFocus;
3063 TRACE("(hdc=%x, nItem=%d, rcItem=%s)\n", hdc, nItem, debugrect(&rcItem));
3065 /* get information needed for drawing the item */
3066 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3067 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3068 lvItem.iItem = nItem;
3069 lvItem.iSubItem = 0;
3070 lvItem.cchTextMax = DISP_TEXT_SIZE;
3071 lvItem.pszText = szDispText;
3072 *lvItem.pszText = '\0';
3073 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3074 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3076 /* now check if we need to update the focus rectangle */
3077 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3079 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, &rcIcon, &rcLabel)) return FALSE;
3081 /* Set the item to the boundary box for now */
3082 TRACE("rcIcon=%s, rcLabel=%s\n", debugrect(&rcIcon), debugrect(&rcLabel));
3084 /* Figure out text colours etc. depending on state
3085 * At least the following states exist; there may be more.
3086 * Many items may be selected
3087 * At most one item may have the focus
3088 * The application may not actually be active currently
3089 * 1. The item is not selected in any way
3090 * 2. The cursor is flying over the icon or text and the text is being
3091 * expanded because it is not fully displayed currently.
3092 * 3. The item is selected and is focussed, i.e. the user has not clicked
3093 * in the blank area of the window, and the window (or application?)
3094 * still has the focus.
3095 * 4. As 3 except that a different window has the focus
3096 * 5. The item is the selected item of all the items, but the user has
3097 * clicked somewhere else on the window.
3098 * Only a few of these are handled currently. In particular 2 is not yet
3099 * handled since we do not support the functionality currently (or at least
3100 * we didn't when I wrote this)
3103 if (lvItem.state & LVIS_SELECTED)
3105 /* set item colors */
3106 SetBkColor(hdc, comctl32_color.clrHighlight);
3107 SetTextColor(hdc, comctl32_color.clrHighlightText);
3108 SetBkMode (hdc, OPAQUE);
3109 /* set raster mode */
3110 SetROP2(hdc, R2_XORPEN);
3111 /* When exactly is it in XOR? while being dragged? */
3115 /* set item colors */
3116 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3118 SetBkMode(hdc, TRANSPARENT);
3122 SetBkMode(hdc, OPAQUE);
3123 SetBkColor(hdc, infoPtr->clrTextBk);
3126 SetTextColor(hdc, infoPtr->clrText);
3127 /* set raster mode */
3128 SetROP2(hdc, R2_COPYPEN);
3131 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3132 * wrapping and long words split.
3133 * In cases 1 and 4 only a portion of the text is displayed with word
3134 * wrapping and both word and end ellipsis. (I don't yet know about path
3137 uFormat |= lprcFocus ? DT_NOCLIP : DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3140 if (infoPtr->himlState != NULL)
3142 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3145 x = rcIcon.left - infoPtr->iconStateSize.cx + 10;
3146 y = rcIcon.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
3147 if (uStateImage > 0)
3148 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, x, y, ILD_NORMAL);
3152 if (infoPtr->himlNormal != NULL)
3154 if (lvItem.iImage >= 0)
3155 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc,
3156 rcIcon.left + ICON_LR_HALF, rcIcon.top + ICON_TOP_PADDING,
3157 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3160 /* Draw the text below the icon */
3162 /* Don't bother painting item being edited */
3163 if ((infoPtr->bEditing && lprcFocus) || !lvItem.pszText || !lstrlenW(lvItem.pszText))
3165 if(lprcFocus) SetRectEmpty(lprcFocus);
3171 /* I am sure of most of the uFormat values. However I am not sure about
3172 * whether we need or do not need the following:
3173 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3174 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3175 * We certainly do not need
3176 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3177 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3180 /* If the text is being drawn without clipping (i.e. the full text) then we
3181 * need to jump through a few hoops to ensure that it all gets displayed and
3182 * that the background is complete
3184 rcFocus = rcLabel; /* save for focus */
3185 if (lvItem.state & LVIS_SELECTED)
3186 ExtTextOutW(hdc, rcLabel.left, rcLabel.top, ETO_OPAQUE, &rcLabel, 0, 0, 0);
3187 /* else ? What if we are losing the focus? will we not get a complete
3191 DrawTextW (hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3192 TRACE("text at rcLabel=%s is %s\n", debugrect(&rcLabel), debugstr_w(lvItem.pszText));
3194 if(lprcFocus) CopyRect(lprcFocus, &rcFocus);
3196 return lprcFocus != NULL;
3201 * Draws listview items when in report display mode.
3204 * [I] infoPtr : valid pointer to the listview structure
3205 * [I] HDC : device context handle
3210 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3212 INT rgntype, nDrawPosY, j;
3213 INT nTop, nItem, nLast, nUpdateHeight, nUpdateWidth;
3214 INT nColumnCount, nFirstCol, nLastCol;
3215 RECT rcItem, rcClip, rcFullSelect;
3216 BOOL bFullSelected, isFocused;
3217 DWORD cditemmode = CDRF_DODEFAULT;
3218 LONG lStyle = infoPtr->dwStyle;
3219 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3220 TEXTATTR tmpTa, oldTa;
3221 COLUMNCACHE *lpCols;
3228 /* figure out what to draw */
3229 rgntype = GetClipBox(hdc, &rcClip);
3230 if (rgntype == NULLREGION) return;
3231 nUpdateHeight = rcClip.bottom - rcClip.top + 1;
3232 nUpdateWidth = rcClip.right - rcClip.left;
3233 nTop = LISTVIEW_GetTopIndex(infoPtr);
3234 nItem = nTop + (rcClip.top - infoPtr->rcList.top) / infoPtr->nItemHeight;
3237 nLast = nItem + nUpdateHeight / infoPtr->nItemHeight;
3238 if (nUpdateHeight % infoPtr->nItemHeight) nLast++;
3239 if (nLast > infoPtr->nItemCount)
3240 nLast = infoPtr->nItemCount;
3242 /* send cache hint notification */
3243 if (lStyle & LVS_OWNERDATA)
3244 notify_odcachehint(infoPtr, nItem, nLast);
3246 /* cache column info */
3247 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3248 lpCols = COMCTL32_Alloc(nColumnCount * sizeof(COLUMNCACHE));
3249 if (!lpCols) return;
3250 for (j = 0; j < nColumnCount; j++)
3252 Header_GetItemRect(infoPtr->hwndHeader, j, &lpCols[j].rc);
3253 TRACE("lpCols[%d].rc=%s\n", j, debugrect(&lpCols[j].rc));
3256 /* Get scroll info once before loop */
3257 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrig)) return;
3259 /* we now narrow the columns as well */
3260 nLastCol = nColumnCount - 1;
3261 for(nFirstCol = 0; nFirstCol < nColumnCount; nFirstCol++)
3262 if (lpCols[nFirstCol].rc.right + ptOrig.x >= rcClip.left) break;
3263 for(nLastCol = nColumnCount - 1; nLastCol >= 0; nLastCol--)
3264 if (lpCols[nLastCol].rc.left + ptOrig.x < rcClip.right) break;
3266 /* cache the per-column information before we start drawing */
3267 for (j = nFirstCol; j <= nLastCol; j++)
3269 lvColumn.mask = LVCF_FMT;
3270 LISTVIEW_GetColumnT(infoPtr, j, &lvColumn, TRUE);
3271 TRACE("lvColumn=%s\n", debuglvcolumn_t(&lvColumn, TRUE));
3272 lpCols[j].align = DT_LEFT;
3273 if (lvColumn.fmt & LVCFMT_RIGHT)
3274 lpCols[j].align = DT_RIGHT;
3275 else if (lvColumn.fmt & LVCFMT_CENTER)
3276 lpCols[j].align = DT_CENTER;
3279 /* a last few bits before we start drawing */
3280 TRACE("nTop=%d, nItem=%d, nLast=%d, nFirstCol=%d, nLastCol=%d\n",
3281 nTop, nItem, nLast, nFirstCol, nLastCol);
3282 bFullSelected = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT;
3283 nDrawPosY = infoPtr->rcList.top + (nItem - nTop) * infoPtr->nItemHeight;
3285 /* save dc values we're gonna trash while drawing */
3286 oldTa.bkMode = GetBkMode(hdc);
3287 oldTa.bkColor = GetBkColor(hdc);
3288 oldTa.fgColor = GetTextColor(hdc);
3290 /* iterate through the invalidated rows */
3291 for (; nItem < nLast; nItem++, nDrawPosY += infoPtr->nItemHeight)
3293 /* if owner wants to take a first stab at it, have it his way... */
3294 if (lStyle & LVS_OWNERDRAWFIXED)
3298 TRACE("Owner Drawn\n");
3302 item.mask = LVIF_PARAM | LVIF_STATE;
3303 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3304 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3306 ZeroMemory(&dis, sizeof(dis));
3307 dis.CtlType = ODT_LISTVIEW;
3310 dis.itemAction = ODA_DRAWENTIRE;
3311 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3312 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3313 dis.hwndItem = infoPtr->hwndSelf;
3315 dis.rcItem.left = lpCols[0].rc.left;
3316 dis.rcItem.right = lpCols[nColumnCount - 1].rc.right;
3317 dis.rcItem.top = nDrawPosY;
3318 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3319 OffsetRect(&dis.rcItem, ptOrig.x, 0);
3320 dis.itemData = item.lParam;
3322 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3323 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3324 /* In theory we should do the default drawing if WM_DRAWITEM
3325 * returns FALSE but, in the words of Larry McVoy, in practice
3326 * theory is different than practice, and hence there are
3327 * important apps out there that depend on no default drawing
3328 * in LVS_OWNERDRAWFIXED. So we always skip to the next item. */
3332 /* compute the full select rectangle, if needed */
3335 item.mask = LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3336 item.stateMask = LVIS_SELECTED;
3339 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3341 rcFullSelect.left = lpCols[0].rc.left + REPORT_MARGINX +
3342 infoPtr->iconSize.cx * item.iIndent +
3343 (infoPtr->himlSmall ? infoPtr->iconSize.cx : 0);
3344 rcFullSelect.right = max(rcFullSelect.left, lpCols[nColumnCount - 1].rc.right - REPORT_MARGINX);
3345 rcFullSelect.top = nDrawPosY;
3346 rcFullSelect.bottom = rcFullSelect.top + infoPtr->nItemHeight;
3347 OffsetRect(&rcFullSelect, ptOrig.x, 0);
3350 /* draw the background of the selection rectangle, if need be */
3351 select_text_attr(infoPtr, hdc, bFullSelected && (item.state & LVIS_SELECTED), &tmpTa);
3352 if (bFullSelected && (item.state & LVIS_SELECTED))
3353 ExtTextOutW(hdc, rcFullSelect.left, rcFullSelect.top, ETO_OPAQUE, &rcFullSelect, 0, 0, 0);
3355 /* iterate through the invalidated columns */
3357 for (j = nFirstCol; j <= nLastCol; j++)
3359 if (cdmode & CDRF_NOTIFYITEMDRAW)
3360 cditemmode = notify_customdrawitem (infoPtr, hdc, nItem, j, CDDS_ITEMPREPAINT);
3361 if (cditemmode & CDRF_SKIPDEFAULT) continue;
3363 rcItem = lpCols[j].rc;
3364 rcItem.left += REPORT_MARGINX;
3365 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3366 rcItem.top = nDrawPosY;
3367 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3369 /* Offset the Scroll Bar Pos */
3370 OffsetRect(&rcItem, ptOrig.x, 0);
3373 isFocused = LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem);
3375 LISTVIEW_DrawSubItem(infoPtr, hdc, nItem, j, rcItem, lpCols[j].align);
3377 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3378 notify_customdrawitem(infoPtr, hdc, nItem, 0, CDDS_ITEMPOSTPAINT);
3381 /* Adjust focus if we have it, and we are in full select */
3382 if (bFullSelected && isFocused) infoPtr->rcFocus = rcFullSelect;
3385 /* cleanup the mess */
3386 set_text_attr(hdc, &oldTa);
3387 COMCTL32_Free(lpCols);
3392 * Draws listview items when in list display mode.
3395 * [I] infoPtr : valid pointer to the listview structure
3396 * [I] HDC : device context handle
3401 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3407 INT nCountPerColumn;
3408 INT nItemWidth = infoPtr->nItemWidth;
3409 INT nItemHeight = infoPtr->nItemHeight;
3410 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3411 DWORD cditemmode = CDRF_DODEFAULT;
3413 /* get number of fully visible columns */
3414 nColumnCount = nListWidth / nItemWidth;
3415 if (nListWidth % nItemWidth) nColumnCount++;
3416 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
3417 nItem = ListView_GetTopIndex(infoPtr->hwndSelf);
3418 TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3419 nColumnCount, nCountPerColumn, nItem);
3421 for (i = 0; i < nColumnCount; i++)
3423 for (j = 0; j < nCountPerColumn; j++, nItem++)
3425 if (nItem >= infoPtr->nItemCount)
3428 if (cdmode & CDRF_NOTIFYITEMDRAW)
3429 cditemmode = notify_customdrawitem (infoPtr, hdc, nItem, 0, CDDS_ITEMPREPAINT);
3430 if (cditemmode & CDRF_SKIPDEFAULT)
3433 rcItem.top = j * nItemHeight;
3434 rcItem.left = i * nItemWidth;
3435 rcItem.bottom = rcItem.top + nItemHeight;
3436 rcItem.right = rcItem.left + nItemWidth;
3438 LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem);
3440 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3441 notify_customdrawitem(infoPtr, hdc, nItem, 0, CDDS_ITEMPOSTPAINT);
3449 * Draws listview items when in icon or small icon display mode.
3452 * [I] infoPtr : valid pointer to the listview structure
3453 * [I] HDC : device context handle
3458 static void LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL bSmall, DWORD cdmode)
3461 RECT rcItem, rcClip, rcTemp;
3463 DWORD cditemmode = CDRF_DODEFAULT;
3467 GetClipBox(hdc, &rcClip);
3469 /* Draw the visible non-selected items */
3470 for (i = 0; i < infoPtr->nItemCount; i++)
3472 if (LISTVIEW_GetItemState(infoPtr,i,LVIS_SELECTED))
3475 rcItem.left = LVIR_BOUNDS;
3476 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
3477 if (!IntersectRect(&rcTemp, &rcItem, &rcClip))
3480 if (cdmode & CDRF_NOTIFYITEMDRAW)
3481 cditemmode = notify_customdrawitem (infoPtr, hdc, i, 0, CDDS_ITEMPREPAINT);
3482 if (cditemmode & CDRF_SKIPDEFAULT)
3485 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3487 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3489 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3491 if (ptPosition.y < infoPtr->rcList.bottom)
3493 if (ptPosition.x < infoPtr->rcList.right)
3495 rcItem.top = ptPosition.y;
3496 rcItem.left = ptPosition.x;
3497 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3498 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3501 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem);
3503 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem);
3508 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3509 notify_customdrawitem(infoPtr, hdc, i, 0, CDDS_ITEMPOSTPAINT);
3512 /* Draw the visible selected items */
3513 for (i = 0; i < infoPtr->nItemCount; i++)
3515 if (!LISTVIEW_GetItemState(infoPtr,i,LVIS_SELECTED))
3518 rcItem.left = LVIR_BOUNDS;
3519 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
3520 if (!IntersectRect(&rcTemp, &rcItem, &rcClip))
3523 if (cdmode & CDRF_NOTIFYITEMDRAW)
3524 cditemmode = notify_customdrawitem (infoPtr, hdc, i, 0, CDDS_ITEMPREPAINT);
3525 if (cditemmode & CDRF_SKIPDEFAULT)
3528 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3530 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3532 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3534 if (ptPosition.y < infoPtr->rcList.bottom)
3536 if (ptPosition.x < infoPtr->rcList.right)
3538 rcItem.top = ptPosition.y;
3539 rcItem.left = ptPosition.x;
3540 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3541 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3544 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem);
3546 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem);
3551 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3552 notify_customdrawitem(infoPtr, hdc, i, 0, CDDS_ITEMPOSTPAINT);
3558 * Draws listview items.
3561 * [I] infoPtr : valid pointer to the listview structure
3562 * [I] HDC : device context handle
3567 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3569 UINT uView = LISTVIEW_GetType(infoPtr);
3574 LISTVIEW_DUMP(infoPtr);
3576 GetClientRect(infoPtr->hwndSelf, &rcClient);
3578 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, hdc, rcClient);
3579 if (cdmode == CDRF_SKIPDEFAULT) return;
3581 infoPtr->bIsDrawing = TRUE;
3583 /* nothing to draw */
3584 if(infoPtr->nItemCount == 0) goto enddraw;
3587 hOldFont = SelectObject(hdc, infoPtr->hFont);
3589 if (uView == LVS_LIST)
3590 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3591 else if (uView == LVS_REPORT)
3592 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3594 LISTVIEW_RefreshIcon(infoPtr, hdc, uView == LVS_SMALLICON, cdmode);
3596 /* if we have a focus rect, draw it */
3597 if (infoPtr->bFocus && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
3598 DrawFocusRect(hdc, &infoPtr->rcFocus);
3600 /* unselect objects */
3601 SelectObject(hdc, hOldFont);
3604 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3605 notify_customdraw(infoPtr, CDDS_POSTPAINT, hdc, rcClient);
3607 infoPtr->bIsDrawing = FALSE;
3613 * Calculates the approximate width and height of a given number of items.
3616 * [I] infoPtr : valid pointer to the listview structure
3617 * [I] INT : number of items
3622 * Returns a DWORD. The width in the low word and the height in high word.
3624 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3625 WORD wWidth, WORD wHeight)
3627 UINT uView = LISTVIEW_GetType(infoPtr);
3628 INT nItemCountPerColumn = 1;
3629 INT nColumnCount = 0;
3630 DWORD dwViewRect = 0;
3632 if (nItemCount == -1)
3633 nItemCount = infoPtr->nItemCount;
3635 if (uView == LVS_LIST)
3637 if (wHeight == 0xFFFF)
3639 /* use current height */
3640 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3643 if (wHeight < infoPtr->nItemHeight)
3644 wHeight = infoPtr->nItemHeight;
3648 if (infoPtr->nItemHeight > 0)
3650 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3651 if (nItemCountPerColumn == 0)
3652 nItemCountPerColumn = 1;
3654 if (nItemCount % nItemCountPerColumn != 0)
3655 nColumnCount = nItemCount / nItemCountPerColumn;
3657 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3661 /* Microsoft padding magic */
3662 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3663 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3665 dwViewRect = MAKELONG(wWidth, wHeight);
3667 else if (uView == LVS_REPORT)
3668 FIXME("uView == LVS_REPORT: not implemented\n");
3669 else if (uView == LVS_SMALLICON)
3670 FIXME("uView == LVS_SMALLICON: not implemented\n");
3671 else if (uView == LVS_ICON)
3672 FIXME("uView == LVS_ICON: not implemented\n");
3679 * Arranges listview items in icon display mode.
3682 * [I] infoPtr : valid pointer to the listview structure
3683 * [I] INT : alignment code
3689 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3691 UINT uView = LISTVIEW_GetType(infoPtr);
3692 BOOL bResult = FALSE;
3694 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3699 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3702 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3705 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3707 case LVA_SNAPTOGRID:
3708 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3716 /* << LISTVIEW_CreateDragImage >> */
3721 * Removes all listview items and subitems.
3724 * [I] infoPtr : valid pointer to the listview structure
3730 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3732 LONG lStyle = infoPtr->dwStyle;
3733 UINT uView = lStyle & LVS_TYPEMASK;
3734 LISTVIEW_ITEM *lpItem;
3735 LISTVIEW_SUBITEM *lpSubItem;
3738 BOOL bResult = FALSE;
3743 LISTVIEW_RemoveAllSelections(infoPtr);
3744 infoPtr->nSelectionMark=-1;
3745 infoPtr->nFocusedItem=-1;
3746 /* But we are supposed to leave nHotItem as is! */
3748 if (lStyle & LVS_OWNERDATA)
3750 infoPtr->nItemCount = 0;
3751 LISTVIEW_InvalidateList(infoPtr);
3755 if (infoPtr->nItemCount > 0)
3759 /* send LVN_DELETEALLITEMS notification */
3760 /* verify if subsequent LVN_DELETEITEM notifications should be
3762 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3764 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3766 for (i = 0; i < infoPtr->nItemCount; i++)
3768 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3769 if (hdpaSubItems != NULL)
3771 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3773 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3774 if (lpSubItem != NULL)
3776 /* free subitem string */
3777 if (is_textW(lpSubItem->hdr.pszText))
3778 COMCTL32_Free(lpSubItem->hdr.pszText);
3781 COMCTL32_Free(lpSubItem);
3785 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3790 /* send LVN_DELETEITEM notification */
3792 nmlv.lParam = lpItem->lParam;
3793 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3796 /* free item string */
3797 if (is_textW(lpItem->hdr.pszText))
3798 COMCTL32_Free(lpItem->hdr.pszText);
3801 COMCTL32_Free(lpItem);
3804 DPA_Destroy(hdpaSubItems);
3808 /* reinitialize listview memory */
3809 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3810 infoPtr->nItemCount = 0;
3811 DPA_DeleteAllPtrs(infoPtr->hdpaPosX);
3812 DPA_DeleteAllPtrs(infoPtr->hdpaPosY);
3814 /* align items (set position of each item) */
3815 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3817 if (lStyle & LVS_ALIGNLEFT)
3819 LISTVIEW_AlignLeft(infoPtr);
3823 LISTVIEW_AlignTop(infoPtr);
3827 LISTVIEW_UpdateScroll(infoPtr);
3829 LISTVIEW_InvalidateList(infoPtr);
3837 * Removes a column from the listview control.
3840 * [I] infoPtr : valid pointer to the listview structure
3841 * [I] INT : column index
3847 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3849 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3852 TRACE("nColumn=%d\n", nColumn);
3854 if (nColumn <= 0) return FALSE;
3856 if (uView == LVS_REPORT)
3858 if (!Header_GetItemRect(infoPtr->hwndHeader, nColumn, &rcCol))
3861 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3865 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3867 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3869 INT nItem, nSubItem, i;
3871 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3873 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3874 if (!hdpaSubItems) continue;
3877 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3879 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3880 if (!lpSubItem) break;
3881 if (lpSubItem->iSubItem == nColumn)
3884 lpDelItem = lpSubItem;
3886 else if (lpSubItem->iSubItem > nColumn)
3888 lpSubItem->iSubItem--;
3892 /* if we found our subitem, zapp it */
3896 if (is_textW(lpDelItem->hdr.pszText))
3897 COMCTL32_Free(lpDelItem->hdr.pszText);
3900 COMCTL32_Free(lpDelItem);
3902 /* free dpa memory */
3903 DPA_DeletePtr(hdpaSubItems, nSubItem);
3908 /* we need to worry about display issues in report mode only */
3909 if (uView != LVS_REPORT) return TRUE;
3911 /* if we have a focus, must first erase the focus rect */
3912 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, infoPtr->nFocusedItem, FALSE);
3914 /* Need to reset the item width when deleting a column */
3915 infoPtr->nItemWidth -= rcCol.right - rcCol.left;
3917 /* update scrollbar(s) */
3918 LISTVIEW_UpdateScroll(infoPtr);
3920 /* scroll to cover the deleted column, and invalidate for redraw */
3921 rcOld = infoPtr->rcList;
3922 rcOld.left = rcCol.left;
3923 ScrollWindowEx(infoPtr->hwndSelf, -(rcCol.right - rcCol.left), 0,
3924 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3926 /* we can restore focus now */
3927 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, infoPtr->nFocusedItem, TRUE);
3934 * Removes an item from the listview control.
3937 * [I] infoPtr : valid pointer to the listview structure
3938 * [I] INT : item index
3944 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
3946 LONG lStyle = infoPtr->dwStyle;
3947 UINT uView = lStyle & LVS_TYPEMASK;
3948 LONG lCtrlId = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3950 BOOL bResult = FALSE;
3952 LISTVIEW_ITEM *lpItem;
3953 LISTVIEW_SUBITEM *lpSubItem;
3957 TRACE("(nItem=%d)\n", nItem);
3960 /* First, send LVN_DELETEITEM notification. */
3961 memset(&nmlv, 0, sizeof (NMLISTVIEW));
3962 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
3963 nmlv.hdr.idFrom = lCtrlId;
3964 nmlv.hdr.code = LVN_DELETEITEM;
3966 SendMessageW((infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)lCtrlId,
3970 /* remove it from the selection range */
3971 item.state = LVIS_SELECTED;
3972 item.stateMask = LVIS_SELECTED;
3973 LISTVIEW_SetItemState(infoPtr,nItem,&item);
3975 if (lStyle & LVS_OWNERDATA)
3977 infoPtr->nItemCount--;
3978 LISTVIEW_InvalidateList(infoPtr); /*FIXME: optimize */
3982 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3984 /* initialize memory */
3985 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3987 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
3988 if (hdpaSubItems != NULL)
3990 infoPtr->nItemCount--;
3991 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3993 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3994 if (lpSubItem != NULL)
3996 /* free item string */
3997 if (is_textW(lpSubItem->hdr.pszText))
3998 COMCTL32_Free(lpSubItem->hdr.pszText);
4001 COMCTL32_Free(lpSubItem);
4005 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4008 /* free item string */
4009 if (is_textW(lpItem->hdr.pszText))
4010 COMCTL32_Free(lpItem->hdr.pszText);
4013 COMCTL32_Free(lpItem);
4016 bResult = DPA_Destroy(hdpaSubItems);
4017 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4018 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4021 LISTVIEW_ShiftIndices(infoPtr,nItem,-1);
4023 /* align items (set position of each item) */
4024 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4026 if (lStyle & LVS_ALIGNLEFT)
4027 LISTVIEW_AlignLeft(infoPtr);
4029 LISTVIEW_AlignTop(infoPtr);
4032 LISTVIEW_UpdateScroll(infoPtr);
4034 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
4043 * Callback implementation for editlabel control
4046 * [I] infoPtr : valid pointer to the listview structure
4047 * [I] pszText : modified text
4048 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4054 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4056 NMLVDISPINFOW dispInfo;
4058 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4060 infoPtr->bEditing = FALSE;
4062 ZeroMemory(&dispInfo, sizeof(dispInfo));
4063 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4064 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4065 dispInfo.item.iSubItem = 0;
4066 dispInfo.item.stateMask = ~0;
4067 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4068 dispInfo.item.pszText = pszText;
4069 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4071 /* Do we need to update the Item Text */
4072 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4073 if (!pszText) return TRUE;
4075 ZeroMemory(&dispInfo, sizeof(dispInfo));
4076 dispInfo.item.mask = LVIF_TEXT;
4077 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4078 dispInfo.item.iSubItem = 0;
4079 dispInfo.item.pszText = pszText;
4080 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4081 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4086 * Begin in place editing of specified list view item
4089 * [I] infoPtr : valid pointer to the listview structure
4090 * [I] INT : item index
4091 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4097 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4099 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4100 NMLVDISPINFOW dispInfo;
4103 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4105 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4107 infoPtr->nEditLabelItem = nItem;
4109 /* Is the EditBox still there, if so remove it */
4110 if(infoPtr->hwndEdit != 0)
4112 SetFocus(infoPtr->hwndSelf);
4113 infoPtr->hwndEdit = 0;
4116 LISTVIEW_SetSelection(infoPtr, nItem);
4117 LISTVIEW_SetItemFocus(infoPtr, nItem);
4119 rect.left = LVIR_LABEL;
4120 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4122 ZeroMemory(&dispInfo, sizeof(dispInfo));
4123 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4124 dispInfo.item.iItem = nItem;
4125 dispInfo.item.iSubItem = 0;
4126 dispInfo.item.stateMask = ~0;
4127 dispInfo.item.pszText = szDispText;
4128 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4129 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4131 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4132 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4133 if (!infoPtr->hwndEdit) return 0;
4135 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4137 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4138 infoPtr->hwndEdit = 0;
4142 infoPtr->bEditing = TRUE;
4143 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4144 SetFocus(infoPtr->hwndEdit);
4145 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4146 return infoPtr->hwndEdit;
4152 * Ensures the specified item is visible, scrolling into view if necessary.
4155 * [I] infoPtr : valid pointer to the listview structure
4156 * [I] nItem : item index
4157 * [I] bPartial : partially or entirely visible
4163 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4165 UINT uView = LISTVIEW_GetType(infoPtr);
4166 INT nScrollPosHeight = 0;
4167 INT nScrollPosWidth = 0;
4168 INT nHorzAdjust = 0;
4169 INT nVertAdjust = 0;
4174 /* FIXME: ALWAYS bPartial == FALSE, FOR NOW! */
4176 rcItem.left = LVIR_BOUNDS;
4177 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4179 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4181 /* scroll left/right, but in LVS_REPORT mode */
4182 if (uView == LVS_LIST)
4183 nScrollPosWidth = infoPtr->nItemWidth;
4184 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4185 nScrollPosWidth = 1;
4187 if (rcItem.left < infoPtr->rcList.left)
4190 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4195 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4199 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4201 /* scroll up/down, but not in LVS_LIST mode */
4202 if (uView == LVS_REPORT)
4203 nScrollPosHeight = infoPtr->nItemHeight;
4204 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4205 nScrollPosHeight = 1;
4207 if (rcItem.top < infoPtr->rcList.top)
4210 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4215 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4219 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4221 if (nScrollPosWidth)
4223 INT diff = nHorzDiff / nScrollPosWidth;
4224 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4225 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4228 if (nScrollPosHeight)
4230 INT diff = nVertDiff / nScrollPosHeight;
4231 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4232 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4240 * Retrieves the nearest item, given a position and a direction.
4243 * [I] infoPtr : valid pointer to the listview structure
4244 * [I] POINT : start position
4245 * [I] UINT : direction
4248 * Item index if successdful, -1 otherwise.
4250 static INT LISTVIEW_GetNearestItem(LISTVIEW_INFO *infoPtr, POINT pt, UINT vkDirection)
4255 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4256 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4257 ((vkDirection == VK_UP) ? "VK_UP" :
4258 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4260 if (!LISTVIEW_GetViewRect(infoPtr, &rcView)) return -1;
4262 if (!LISTVIEW_GetOrigin(infoPtr, &ht.pt)) return -1;
4267 if (vkDirection == VK_DOWN) ht.pt.y += infoPtr->nItemHeight;
4268 else if (vkDirection == VK_UP) ht.pt.y -= infoPtr->nItemHeight;
4269 else if (vkDirection == VK_LEFT) ht.pt.x -= infoPtr->nItemWidth;
4270 else if (vkDirection == VK_RIGHT) ht.pt.x += infoPtr->nItemWidth;
4272 if (!PtInRect(&rcView, ht.pt)) return -1;
4274 return LISTVIEW_SuperHitTestItem(infoPtr, &ht, TRUE, TRUE);
4279 * Searches for an item with specific characteristics.
4282 * [I] hwnd : window handle
4283 * [I] nStart : base item index
4284 * [I] lpFindInfo : item information to look for
4287 * SUCCESS : index of item
4290 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4291 LPLVFINDINFOW lpFindInfo)
4294 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4298 INT nLast = infoPtr->nItemCount;
4300 if ((nItem >= -1) && (lpFindInfo != NULL))
4303 if (lpFindInfo->flags & LVFI_PARAM)
4305 lvItem.mask |= LVIF_PARAM;
4308 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4310 lvItem.mask |= LVIF_TEXT;
4311 lvItem.pszText = szDispText;
4312 lvItem.cchTextMax = DISP_TEXT_SIZE;
4315 if (lpFindInfo->flags & LVFI_WRAP)
4318 if (lpFindInfo->flags & LVFI_NEARESTXY)
4320 ptItem.x = lpFindInfo->pt.x;
4321 ptItem.y = lpFindInfo->pt.y;
4326 while (nItem < nLast)
4328 if (lpFindInfo->flags & LVFI_NEARESTXY)
4330 nItem = LISTVIEW_GetNearestItem(infoPtr, ptItem,
4331 lpFindInfo->vkDirection);
4334 /* get position of the new item index */
4335 if (!ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &ptItem))
4346 lvItem.iItem = nItem;
4347 lvItem.iSubItem = 0;
4348 if (LISTVIEW_GetItemW(infoPtr, &lvItem))
4350 if (lvItem.mask & LVIF_TEXT)
4352 if (lpFindInfo->flags & LVFI_PARTIAL)
4354 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4359 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4364 if (lvItem.mask & LVIF_PARAM)
4366 if (lpFindInfo->lParam != lvItem.lParam)
4392 * Searches for an item with specific characteristics.
4395 * [I] hwnd : window handle
4396 * [I] nStart : base item index
4397 * [I] lpFindInfo : item information to look for
4400 * SUCCESS : index of item
4403 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4404 LPLVFINDINFOA lpFindInfo)
4406 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4410 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4411 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4412 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4413 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4419 * Retrieves the background image of the listview control.
4422 * [I] infoPtr : valid pointer to the listview structure
4423 * [O] LPLVMKBIMAGE : background image attributes
4429 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4431 /* FIXME (listview, "empty stub!\n"); */
4437 * Retrieves column attributes.
4440 * [I] infoPtr : valid pointer to the listview structure
4441 * [I] INT : column index
4442 * [IO] LPLVCOLUMNW : column information
4443 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4444 * otherwise it is in fact a LPLVCOLUMNA
4450 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4453 BOOL bResult = FALSE;
4455 if (lpColumn != NULL)
4458 /* initialize memory */
4459 ZeroMemory(&hdi, sizeof(hdi));
4461 if (lpColumn->mask & LVCF_FMT)
4462 hdi.mask |= HDI_FORMAT;
4464 if (lpColumn->mask & LVCF_WIDTH)
4465 hdi.mask |= HDI_WIDTH;
4467 if (lpColumn->mask & LVCF_TEXT)
4469 hdi.mask |= HDI_TEXT;
4470 hdi.cchTextMax = lpColumn->cchTextMax;
4471 hdi.pszText = lpColumn->pszText;
4474 if (lpColumn->mask & LVCF_IMAGE)
4475 hdi.mask |= HDI_IMAGE;
4477 if (lpColumn->mask & LVCF_ORDER)
4478 hdi.mask |= HDI_ORDER;
4481 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4483 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4487 if (lpColumn->mask & LVCF_FMT)
4491 if (hdi.fmt & HDF_LEFT)
4492 lpColumn->fmt |= LVCFMT_LEFT;
4493 else if (hdi.fmt & HDF_RIGHT)
4494 lpColumn->fmt |= LVCFMT_RIGHT;
4495 else if (hdi.fmt & HDF_CENTER)
4496 lpColumn->fmt |= LVCFMT_CENTER;
4498 if (hdi.fmt & HDF_IMAGE)
4499 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4501 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4502 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4505 if (lpColumn->mask & LVCF_WIDTH)
4506 lpColumn->cx = hdi.cxy;
4508 if (lpColumn->mask & LVCF_IMAGE)
4509 lpColumn->iImage = hdi.iImage;
4511 if (lpColumn->mask & LVCF_ORDER)
4512 lpColumn->iOrder = hdi.iOrder;
4514 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4515 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4524 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4531 /* FIXME: little hack */
4532 for (i = 0; i < iCount; i++)
4540 * Retrieves the column width.
4543 * [I] infoPtr : valid pointer to the listview structure
4544 * [I] int : column index
4547 * SUCCESS : column width
4550 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4552 INT nColumnWidth = 0;
4555 TRACE("nColumn=%d\n", nColumn);
4557 switch(LISTVIEW_GetType(infoPtr))
4560 nColumnWidth = infoPtr->nItemWidth;
4563 hdi.mask = HDI_WIDTH;
4564 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4565 nColumnWidth = hdi.cxy;
4568 /* we don't have a 'column' in [SMALL]ICON mode */
4571 TRACE("nColumnWidth=%d\n", nColumnWidth);
4572 return nColumnWidth;
4577 * In list or report display mode, retrieves the number of items that can fit
4578 * vertically in the visible area. In icon or small icon display mode,
4579 * retrieves the total number of visible items.
4582 * [I] infoPtr : valid pointer to the listview structure
4585 * Number of fully visible items.
4587 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4589 UINT uView = LISTVIEW_GetType(infoPtr);
4592 if (uView == LVS_LIST)
4594 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4596 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4597 LISTVIEW_GetCountPerColumn(infoPtr);
4600 else if (uView == LVS_REPORT)
4602 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4606 nItemCount = infoPtr->nItemCount;
4615 * Retrieves an image list handle.
4618 * [I] infoPtr : valid pointer to the listview structure
4619 * [I] nImageList : image list identifier
4622 * SUCCESS : image list handle
4625 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4627 HIMAGELIST himl = NULL;
4632 himl = infoPtr->himlNormal;
4635 himl = infoPtr->himlSmall;
4638 himl = infoPtr->himlState;
4642 return (LRESULT)himl;
4645 /* LISTVIEW_GetISearchString */
4648 * Helper function for LISTVIEW_GetItemT *only*. Tests if an item is selected.
4649 * It is important that no other functions call this because of callbacks.
4651 static inline BOOL is_item_selected(LISTVIEW_INFO *infoPtr, INT nItem)
4653 RANGE selection = { nItem, nItem };
4655 return DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
4656 LISTVIEW_CompareSelectionRanges, 0, DPAS_SORTED) != -1;
4661 * Retrieves item attributes.
4664 * [I] hwnd : window handle
4665 * [IO] lpLVItem : item info
4666 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4667 * if FALSE, the lpLVItem is a LPLVITEMA.
4670 * This is the internal 'GetItem' interface -- it tries to
4671 * be smart, and avoids text copies, if possible, by modifing
4672 * lpLVItem->pszText to point to the text string. Please note
4673 * that this is not always possible (e.g. OWNERDATA), so on
4674 * entry you *must* supply valid values for pszText, and cchTextMax.
4675 * The only difference to the documented interface is that upon
4676 * return, you should use *only* the lpLVItem->pszText, rather than
4677 * the buffer pointer you provided on input. Most code already does
4678 * that, so it's not a problem.
4679 * For the two cases when the text must be copied (that is,
4680 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4686 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4688 NMLVDISPINFOW dispInfo;
4689 LISTVIEW_ITEM *lpItem;
4693 /* In the following:
4694 * lpLVItem describes the information requested by the user
4695 * lpItem is what we have
4696 * dispInfo is a structure we use to request the missing
4697 * information from the application
4700 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4702 if (!lpLVItem || (lpLVItem->iItem < 0) ||
4703 (lpLVItem->iItem >= infoPtr->nItemCount))
4706 /* a quick optimization if all we're asked is the focus state
4707 * these queries are worth optimising since they are common,
4708 * and can be answered in constant time, without the heavy accesses */
4709 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIF_STATE) &&
4710 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4712 lpLVItem->state = 0;
4713 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4714 lpLVItem->state |= LVIS_FOCUSED;
4718 ZeroMemory(&dispInfo, sizeof(dispInfo));
4720 /* if the app stores all the data, handle it separately */
4721 if (infoPtr->dwStyle & LVS_OWNERDATA)
4723 dispInfo.item.state = 0;
4725 /* if we need to callback, do it now */
4726 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4728 /* NOTE: copy only fields which we _know_ are initialized, some apps
4729 * depend on the uninitialized fields being 0 */
4730 dispInfo.item.mask = lpLVItem->mask;
4731 dispInfo.item.iItem = lpLVItem->iItem;
4732 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4733 if (lpLVItem->mask & LVIF_TEXT)
4735 dispInfo.item.pszText = lpLVItem->pszText;
4736 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4738 if (lpLVItem->mask & LVIF_STATE)
4739 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4740 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4741 dispInfo.item.stateMask = lpLVItem->stateMask;
4742 *lpLVItem = dispInfo.item;
4743 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4746 /* we store only a little state, so if we're not asked, we're done */
4747 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4749 /* if focus is handled by us, report it */
4750 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4752 lpLVItem->state &= ~LVIS_FOCUSED;
4753 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4754 lpLVItem->state |= LVIS_FOCUSED;
4757 /* and do the same for selection, if we handle it */
4758 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4760 lpLVItem->state &= ~LVIS_SELECTED;
4761 if (is_item_selected(infoPtr, lpLVItem->iItem))
4762 lpLVItem->state |= LVIS_SELECTED;
4768 /* find the item and subitem structures before we proceed */
4769 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4770 if (hdpaSubItems == NULL) return FALSE;
4772 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4775 if (lpLVItem->iSubItem)
4777 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4778 if(!lpSubItem) return FALSE;
4779 pItemHdr = &lpSubItem->hdr;
4782 pItemHdr = &lpItem->hdr;
4784 /* Do we need to query the state from the app? */
4785 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4787 dispInfo.item.mask |= LVIF_STATE;
4788 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4791 /* Do we need to enquire about the image? */
4792 if ((lpLVItem->mask & LVIF_IMAGE) && (pItemHdr->iImage==I_IMAGECALLBACK))
4793 dispInfo.item.mask |= LVIF_IMAGE;
4795 /* Do we need to enquire about the text? */
4796 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4798 dispInfo.item.mask |= LVIF_TEXT;
4799 dispInfo.item.pszText = lpLVItem->pszText;
4800 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4801 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4802 *dispInfo.item.pszText = '\0';
4805 /* If we don't have all the requested info, query the application */
4806 if (dispInfo.item.mask != 0)
4808 dispInfo.item.iItem = lpLVItem->iItem;
4809 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4810 dispInfo.item.lParam = lpItem->lParam;
4811 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4812 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4815 /* Now, handle the iImage field */
4816 if (dispInfo.item.mask & LVIF_IMAGE)
4818 lpLVItem->iImage = dispInfo.item.iImage;
4819 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (pItemHdr->iImage==I_IMAGECALLBACK))
4820 pItemHdr->iImage = dispInfo.item.iImage;
4822 else if (lpLVItem->mask & LVIF_IMAGE)
4823 lpLVItem->iImage = pItemHdr->iImage;
4825 /* The pszText field */
4826 if (dispInfo.item.mask & LVIF_TEXT)
4828 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4829 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4831 lpLVItem->pszText = dispInfo.item.pszText;
4833 else if (lpLVItem->mask & LVIF_TEXT)
4835 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4836 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4839 /* if this is a subitem, we're done*/
4840 if (lpLVItem->iSubItem) return TRUE;
4842 /* Next is the lParam field */
4843 if (dispInfo.item.mask & LVIF_PARAM)
4845 lpLVItem->lParam = dispInfo.item.lParam;
4846 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4847 lpItem->lParam = dispInfo.item.lParam;
4849 else if (lpLVItem->mask & LVIF_PARAM)
4850 lpLVItem->lParam = lpItem->lParam;
4852 /* ... the state field (this one is different due to uCallbackmask) */
4853 if (lpLVItem->mask & LVIF_STATE)
4855 lpLVItem->state = lpItem->state;
4856 if (dispInfo.item.mask & LVIF_STATE)
4858 lpLVItem->state &= ~dispInfo.item.stateMask;
4859 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4861 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4863 lpLVItem->state &= ~LVIS_FOCUSED;
4864 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4865 lpLVItem->state |= LVIS_FOCUSED;
4867 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4869 lpLVItem->state &= ~LVIS_SELECTED;
4870 if (is_item_selected(infoPtr, lpLVItem->iItem))
4871 lpLVItem->state |= LVIS_SELECTED;
4875 /* and last, but not least, the indent field */
4876 if (lpLVItem->mask & LVIF_INDENT)
4877 lpLVItem->iIndent = lpItem->iIndent;
4884 * Retrieves item attributes.
4887 * [I] hwnd : window handle
4888 * [IO] lpLVItem : item info
4889 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4890 * if FALSE, the lpLVItem is a LPLVITEMA.
4893 * This is the external 'GetItem' interface -- it properly copies
4894 * the text in the provided buffer.
4900 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4905 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4908 pszText = lpLVItem->pszText;
4909 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4910 if (bResult && lpLVItem->pszText != pszText)
4911 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4912 lpLVItem->pszText = pszText;
4920 * Retrieves the position (upper-left) of the listview control item.
4921 * Note that for LVS_ICON style, the upper-left is that of the icon
4922 * and not the bounding box.
4925 * [I] infoPtr : valid pointer to the listview structure
4926 * [I] nItem : item index
4927 * [O] lpptPosition : coordinate information
4933 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4935 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4938 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4940 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4942 /* These should be very cheap to compute */
4943 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, &Box, NULL, NULL, NULL)) return FALSE;
4945 lpptPosition->x = Box.left;
4946 lpptPosition->y = Box.top;
4947 if (uView == LVS_ICON)
4949 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
4950 lpptPosition->y += ICON_TOP_PADDING;
4953 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
4959 * DESCRIPTION: [INTERNAL]
4960 * Update the bounding rectangle around the text under a large icon.
4961 * This depends on whether it has the focus or not.
4962 * On entry the rectangle's top, left and right should be set.
4963 * On return the bottom will also be set and the width may have been
4967 * [I] infoPtr : pointer to the listview structure
4968 * [I] nItem : the item for which we are calculating this
4969 * [I/O] rect : the rectangle to be updated
4971 * This appears to be weird, even in the Microsoft implementation.
4973 static BOOL LISTVIEW_UpdateLargeItemLabelRect (LISTVIEW_INFO *infoPtr, int nItem, RECT *rect)
4975 HDC hdc = GetDC (infoPtr->hwndSelf);
4976 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
4977 UINT uFormat = LISTVIEW_DTFLAGS | DT_CALCRECT;
4978 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4979 RECT rcText = *rect;
4980 RECT rcBack = *rect;
4981 BOOL focused, selected;
4982 int dx, dy, old_wid, new_wid;
4985 TRACE("%s, focus item=%d, cur item=%d\n",
4986 (infoPtr->bFocus) ? "Window has focus" : "Window not focused",
4987 infoPtr->nFocusedItem, nItem);
4990 focused = infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED);
4991 selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
4993 uFormat |= (focused) ? DT_NOCLIP : DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
4995 /* We (aim to) display the full text. In Windows 95 it appears to
4996 * calculate the size assuming the specified font and then it draws
4997 * the text in that region with the specified font except scaled to
4998 * 10 point (or the height of the system font or ...). Thus if the
4999 * window has 24 point Helvetica the highlit rectangle will be
5000 * taller than the text and if it is 7 point Helvetica then the text
5002 * For now we will simply say that it is the correct size to display
5003 * the text in the specified font.
5005 lvItem.mask = LVIF_TEXT;
5006 lvItem.iItem = nItem;
5007 lvItem.iSubItem = 0;
5008 lvItem.pszText = szDispText;
5009 lvItem.cchTextMax = DISP_TEXT_SIZE;
5010 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5012 InflateRect(&rcText, -2, 0);
5013 DrawTextW (hdc, lvItem.pszText, -1, &rcText, uFormat);
5014 /* Microsoft, in their great wisdom, have decided that the rectangle
5015 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
5016 * not the location. So we have to do the centring ourselves (and take
5017 * responsibility for agreeing off-by-one consistency with them).
5020 old_wid = rcText.right - rcText.left;
5021 new_wid = rcBack.right - rcBack.left;
5022 dx = rcBack.left - rcText.left + (new_wid-old_wid)/2;
5023 dy = rcBack.top - rcText.top;
5024 OffsetRect (&rcText, dx, dy);
5029 InflateRect(&rcText, 2, 0);
5031 else /* not focused, may or may not be selected */
5034 * We need to have the bottom to be an intergal number of
5035 * text lines (ntmHeight) below text top that is less than
5036 * or equal to the nItemHeight.
5038 INT lh = infoPtr->nItemHeight - infoPtr->iconSize.cy -
5039 ICON_TOP_PADDING - ICON_BOTTOM_PADDING;
5040 INT ih = (lh / infoPtr->ntmHeight) * infoPtr->ntmHeight;
5041 rcText.bottom = min(rcText.bottom, rcText.top + ih);
5046 TRACE("%s and %s, bounding rect=(%d,%d)-(%d,%d)\n",
5047 (focused) ? "focused(full text)" : "not focused",
5048 (selected) ? "selected" : "not selected",
5049 rect->left, rect->top, rect->right, rect->bottom);
5051 SelectObject (hdc, hOldFont);
5052 ReleaseDC (infoPtr->hwndSelf, hdc);
5059 * Retrieves the bounding rectangle for a listview control item.
5062 * [I] infoPtr : valid pointer to the listview structure
5063 * [I] nItem : item index
5064 * [IO] lprc : bounding rectangle coordinates
5065 * lprc->left specifies the portion of the item for which the bounding
5066 * rectangle will be retrieved.
5068 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5069 * including the icon and label.
5072 * * Experiment shows that native control returns:
5073 * * width = min (48, length of text line)
5074 * * .left = position.x - (width - iconsize.cx)/2
5075 * * .right = .left + width
5076 * * height = #lines of text * ntmHeight + icon height + 8
5077 * * .top = position.y - 2
5078 * * .bottom = .top + height
5079 * * separation between items .y = itemSpacing.cy - height
5080 * * .x = itemSpacing.cx - width
5081 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5084 * * Experiment shows that native control returns:
5085 * * width = iconSize.cx + 16
5086 * * .left = position.x - (width - iconsize.cx)/2
5087 * * .right = .left + width
5088 * * height = iconSize.cy + 4
5089 * * .top = position.y - 2
5090 * * .bottom = .top + height
5091 * * separation between items .y = itemSpacing.cy - height
5092 * * .x = itemSpacing.cx - width
5093 * LVIR_LABEL Returns the bounding rectangle of the item text.
5096 * * Experiment shows that native control returns:
5097 * * width = text length
5098 * * .left = position.x - width/2
5099 * * .right = .left + width
5100 * * height = ntmH * linecount + 2
5101 * * .top = position.y + iconSize.cy + 6
5102 * * .bottom = .top + height
5103 * * separation between items .y = itemSpacing.cy - height
5104 * * .x = itemSpacing.cx - width
5105 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5106 * rectangles, but excludes columns in report view.
5113 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5114 * upon whether the window has the focus currently and on whether the item
5115 * is the one with the focus. Ensure that the control's record of which
5116 * item has the focus agrees with the items' records.
5118 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5122 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5124 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5129 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, lprc, NULL)) return FALSE;
5133 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, NULL, lprc)) return FALSE;
5137 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, lprc, NULL, NULL)) return FALSE;
5140 case LVIR_SELECTBOUNDS:
5141 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, lprc, NULL, &label_rect)) return FALSE;
5142 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
5143 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) )
5144 lprc->right = label_rect.right;
5148 WARN("Unknown value: %d\n", lprc->left);
5152 TRACE(" rect=%s\n", debugrect(lprc));
5159 * Retrieves the spacing between listview control items.
5162 * [I] infoPtr : valid pointer to the listview structure
5163 * [IO] lprc : rectangle to receive the output
5164 * on input, lprc->top = nSubItem
5165 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5167 * NOTE: this call is succeeds only for REPORT style listviews.
5168 * Because we can calculate things much faster in report mode,
5169 * we're gonna do the calculations inline here, instead of
5170 * calling functions that do heavy lifting.
5176 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5179 INT nSubItem, flags;
5181 if (!lprc || LISTVIEW_GetType(infoPtr) != LVS_REPORT) return FALSE;
5183 nSubItem = lprc->top;
5186 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, nSubItem);
5188 if (!Header_GetItemRect(infoPtr->hwndHeader, nSubItem, lprc)) return FALSE;
5189 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptPosition)) return FALSE;
5190 lprc->top = ptPosition.y;
5191 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5196 FIXME("Unimplemented LVIR_ICON\n");
5200 /* nothing to do here, we're done */
5203 ERR("Unknown bounds=%d\n", lprc->left);
5212 * Retrieves the width of a label.
5215 * [I] infoPtr : valid pointer to the listview structure
5218 * SUCCESS : string width (in pixels)
5221 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5223 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5226 TRACE("(nItem=%d)\n", nItem);
5228 lvItem.mask = LVIF_TEXT;
5229 lvItem.iItem = nItem;
5230 lvItem.iSubItem = 0;
5231 lvItem.pszText = szDispText;
5232 lvItem.cchTextMax = DISP_TEXT_SIZE;
5233 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5235 /* FIXME: is this right? What if the label is very long? */
5236 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5241 * Retrieves the spacing between listview control items.
5244 * [I] infoPtr : valid pointer to the listview structure
5245 * [I] BOOL : flag for small or large icon
5248 * Horizontal + vertical spacing
5250 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5256 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5260 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
5261 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5263 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5270 * Retrieves the state of a listview control item.
5273 * [I] infoPtr : valid pointer to the listview structure
5274 * [I] nItem : item index
5275 * [I] uMask : state mask
5278 * State specified by the mask.
5280 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5284 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5286 lvItem.iItem = nItem;
5287 lvItem.iSubItem = 0;
5288 lvItem.mask = LVIF_STATE;
5289 lvItem.stateMask = uMask;
5290 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5292 return lvItem.state & uMask;
5297 * Retrieves the text of a listview control item or subitem.
5300 * [I] hwnd : window handle
5301 * [I] nItem : item index
5302 * [IO] lpLVItem : item information
5303 * [I] isW : TRUE if lpLVItem is Unicode
5306 * SUCCESS : string length
5309 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5311 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5313 lpLVItem->mask = LVIF_TEXT;
5314 lpLVItem->iItem = nItem;
5315 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5317 return textlenT(lpLVItem->pszText, isW);
5322 * Searches for an item based on properties + relationships.
5325 * [I] infoPtr : valid pointer to the listview structure
5326 * [I] nItem : item index
5327 * [I] uFlags : relationship flag
5330 * This function is ver, very inefficient! Needs work.
5333 * SUCCESS : item index
5336 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5338 UINT uView = LISTVIEW_GetType(infoPtr);
5340 LVFINDINFOW lvFindInfo;
5341 INT nCountPerColumn;
5344 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5345 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5347 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5349 if (uFlags & LVNI_CUT)
5352 if (uFlags & LVNI_DROPHILITED)
5353 uMask |= LVIS_DROPHILITED;
5355 if (uFlags & LVNI_FOCUSED)
5356 uMask |= LVIS_FOCUSED;
5358 if (uFlags & LVNI_SELECTED)
5359 uMask |= LVIS_SELECTED;
5361 /* if we're asked for the focused item, that's only one,
5362 * so it's worth optimizing */
5363 if (uFlags & LVNI_FOCUSED)
5365 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5366 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5369 if (uFlags & LVNI_ABOVE)
5371 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5376 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5382 lvFindInfo.flags = LVFI_NEARESTXY;
5383 lvFindInfo.vkDirection = VK_UP;
5384 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5385 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5387 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5392 else if (uFlags & LVNI_BELOW)
5394 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5396 while (nItem < infoPtr->nItemCount)
5399 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5405 lvFindInfo.flags = LVFI_NEARESTXY;
5406 lvFindInfo.vkDirection = VK_DOWN;
5407 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5408 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5410 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5415 else if (uFlags & LVNI_TOLEFT)
5417 if (uView == LVS_LIST)
5419 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5420 while (nItem - nCountPerColumn >= 0)
5422 nItem -= nCountPerColumn;
5423 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5427 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5429 lvFindInfo.flags = LVFI_NEARESTXY;
5430 lvFindInfo.vkDirection = VK_LEFT;
5431 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5432 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5434 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5439 else if (uFlags & LVNI_TORIGHT)
5441 if (uView == LVS_LIST)
5443 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5444 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5446 nItem += nCountPerColumn;
5447 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5451 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5453 lvFindInfo.flags = LVFI_NEARESTXY;
5454 lvFindInfo.vkDirection = VK_RIGHT;
5455 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5456 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5458 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5467 /* search by index */
5468 for (i = nItem; i < infoPtr->nItemCount; i++)
5470 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5478 /* LISTVIEW_GetNumberOfWorkAreas */
5482 * Retrieves the origin coordinates when in icon or small icon display mode.
5485 * [I] infoPtr : valid pointer to the listview structure
5486 * [O] lpptOrigin : coordinate information
5492 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5494 DWORD lStyle = infoPtr->dwStyle;
5495 UINT uView = lStyle & LVS_TYPEMASK;
5496 INT nHorzPos = 0, nVertPos = 0;
5497 SCROLLINFO scrollInfo;
5499 if (!lpptOrigin) return FALSE;
5501 scrollInfo.cbSize = sizeof(SCROLLINFO);
5502 scrollInfo.fMask = SIF_POS;
5504 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5505 nHorzPos = scrollInfo.nPos;
5506 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5507 nVertPos = scrollInfo.nPos;
5509 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5511 lpptOrigin->x = infoPtr->rcList.left;
5512 lpptOrigin->y = infoPtr->rcList.top;
5513 if (uView == LVS_LIST)
5515 nHorzPos *= LISTVIEW_GetCountPerColumn(infoPtr);
5518 else if (uView == LVS_REPORT)
5520 nVertPos *= infoPtr->nItemHeight;
5523 lpptOrigin->x -= nHorzPos;
5524 lpptOrigin->y -= nVertPos;
5526 TRACE("(pt=(%ld,%ld))\n", lpptOrigin->x, lpptOrigin->y);
5533 * Retrieves the width of a string.
5536 * [I] hwnd : window handle
5537 * [I] lpszText : text string to process
5538 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5541 * SUCCESS : string width (in pixels)
5544 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5549 if (is_textT(lpszText, isW))
5551 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5552 HDC hdc = GetDC(infoPtr->hwndSelf);
5553 HFONT hOldFont = SelectObject(hdc, hFont);
5556 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5558 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5559 SelectObject(hdc, hOldFont);
5560 ReleaseDC(infoPtr->hwndSelf, hdc);
5562 return stringSize.cx;
5568 * Determines item if a hit or closest if not
5571 * [I] infoPtr : valid pointer to the listview structure
5572 * [IO] lpht : hit test information
5573 * [I] subitem : fill out iSubItem.
5574 * [I] bNearItem : return the nearest item
5577 * SUCCESS : item index of hit
5580 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL bNearItem)
5582 LONG lStyle = infoPtr->dwStyle;
5583 UINT uView = lStyle & LVS_TYPEMASK;
5584 INT i,j,topindex,bottomindex,nearestItem;
5585 RECT rcItem,rcSubItem;
5586 DWORD xterm, yterm, dist, mindist;
5588 TRACE("(lpht->pt=%s, subitem=%d\n", debugpoint(&lpht->pt), subitem);
5593 /* FIXME: get the visible range */
5594 topindex = LISTVIEW_GetTopIndex(infoPtr);
5595 if (uView == LVS_REPORT)
5597 bottomindex = topindex + LISTVIEW_GetCountPerColumn(infoPtr) + 1;
5598 bottomindex = min(bottomindex,infoPtr->nItemCount);
5602 bottomindex = infoPtr->nItemCount;
5605 for (i = topindex; i < bottomindex; i++)
5607 rcItem.left = LVIR_BOUNDS;
5608 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5610 if (PtInRect(&rcItem, lpht->pt))
5613 rcItem.left = LVIR_ICON;
5614 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5616 if (PtInRect(&rcItem, lpht->pt))
5618 lpht->flags = LVHT_ONITEMICON;
5624 rcItem.left = LVIR_LABEL;
5625 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5627 if (PtInRect(&rcItem, lpht->pt))
5629 lpht->flags = LVHT_ONITEMLABEL;
5635 lpht->flags = LVHT_ONITEMSTATEICON;
5640 INT nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5642 rcSubItem.right = rcSubItem.left;
5643 for (j = 0; j < nColumnCount; j++)
5645 rcSubItem.left = rcSubItem.right;
5646 rcSubItem.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5647 if (PtInRect(&rcSubItem, lpht->pt))
5654 TRACE("hit on item %d\n", i);
5660 * Now compute distance from point to center of boundary
5661 * box. Since we are only interested in the relative
5662 * distance, we can skip the nasty square root operation
5664 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpht->pt.x;
5665 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpht->pt.y;
5666 dist = xterm * xterm + yterm * yterm;
5667 if (mindist < 0 || dist < mindist)
5676 lpht->flags = LVHT_NOWHERE;
5678 return bNearItem ? nearestItem : -1;
5684 * Determines which listview item is located at the specified position.
5687 * [I] infoPtr : valid pointer to the listview structure
5688 * [IO] lpht : hit test information
5689 * [I] subitem : fill out iSubItem.
5692 * SUCCESS : item index
5695 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem)
5697 TRACE("(x=%ld, y=%ld)\n", lpht->pt.x, lpht->pt.y);
5701 if (subitem) lpht->iSubItem = 0;
5703 if (infoPtr->rcList.left > lpht->pt.x)
5704 lpht->flags |= LVHT_TOLEFT;
5705 else if (infoPtr->rcList.right < lpht->pt.x)
5706 lpht->flags |= LVHT_TORIGHT;
5708 if (infoPtr->rcList.top > lpht->pt.y)
5709 lpht->flags |= LVHT_ABOVE;
5710 else if (infoPtr->rcList.bottom < lpht->pt.y)
5711 lpht->flags |= LVHT_BELOW;
5713 if (lpht->flags) return -1;
5715 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
5716 * an app might pass only a structure with space up to iItem!
5717 * (MS Office 97 does that for instance in the file open dialog)
5719 return LISTVIEW_SuperHitTestItem(infoPtr, lpht, subitem, FALSE);
5725 * Inserts a new column.
5728 * [I] infoPtr : valid pointer to the listview structure
5729 * [I] INT : column index
5730 * [I] LPLVCOLUMNW : column information
5733 * SUCCESS : new column index
5736 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5737 LPLVCOLUMNW lpColumn, BOOL isW)
5743 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5745 if (!lpColumn) return -1;
5747 hdi.mask = hdi.fmt = 0;
5748 if (lpColumn->mask & LVCF_FMT)
5750 /* format member is valid */
5751 hdi.mask |= HDI_FORMAT;
5753 /* set text alignment (leftmost column must be left-aligned) */
5754 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5755 hdi.fmt |= HDF_LEFT;
5756 else if (lpColumn->fmt & LVCFMT_RIGHT)
5757 hdi.fmt |= HDF_RIGHT;
5758 else if (lpColumn->fmt & LVCFMT_CENTER)
5759 hdi.fmt |= HDF_CENTER;
5761 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5762 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5764 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5766 hdi.fmt |= HDF_IMAGE;
5767 hdi.iImage = I_IMAGECALLBACK;
5770 if (lpColumn->fmt & LVCFMT_IMAGE)
5771 ; /* FIXME: enable images for *(sub)items* this column */
5774 if (lpColumn->mask & LVCF_WIDTH)
5776 hdi.mask |= HDI_WIDTH;
5777 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5779 /* make it fill the remainder of the controls width */
5784 /* get the width of every item except the current one */
5785 hdit.mask = HDI_WIDTH;
5788 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5789 if (Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit)))
5790 hdi.cxy += hdit.cxy;
5792 /* retrieve the layout of the header */
5793 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5794 TRACE("start cxy=%d rcHeader=%s\n", hdi.cxy, debugrect(&rcHeader));
5796 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
5799 hdi.cxy = lpColumn->cx;
5802 if (lpColumn->mask & LVCF_TEXT)
5804 hdi.mask |= HDI_TEXT | HDI_FORMAT;
5805 hdi.fmt |= HDF_STRING;
5806 hdi.pszText = lpColumn->pszText;
5807 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
5810 if (lpColumn->mask & LVCF_IMAGE)
5812 hdi.mask |= HDI_IMAGE;
5813 hdi.iImage = lpColumn->iImage;
5816 if (lpColumn->mask & LVCF_ORDER)
5818 hdi.mask |= HDI_ORDER;
5819 hdi.iOrder = lpColumn->iOrder;
5822 /* insert item in header control */
5823 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5824 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5825 (WPARAM)nColumn, (LPARAM)&hdi);
5826 if (nNewColumn == -1) return -1;
5827 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &rcCol)) return -1;
5829 /* now we have to actually adjust the data */
5830 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
5832 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
5836 /* preallocate memory, so we can fail gracefully */
5837 if (nNewColumn == 0)
5839 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->nItemCount);
5840 if (!lpNewItems) return -1;
5841 for (i = 0; i < infoPtr->nItemCount; i++)
5842 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
5843 if (i != infoPtr->nItemCount)
5845 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5846 COMCTL32_Free(lpNewItems);
5851 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5853 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5854 if (!hdpaSubItems) continue;
5855 for (i = 1; i < hdpaSubItems->nItemCount; i++)
5857 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
5858 if (!lpSubItem) break;
5859 if (lpSubItem->iSubItem >= nNewColumn)
5860 lpSubItem->iSubItem++;
5863 /* if we found our subitem, zapp it */
5864 if (nNewColumn == 0)
5866 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
5867 lpSubItem = lpNewItems[nItem];
5868 lpSubItem->hdr = lpMainItem->hdr;
5869 lpSubItem->iSubItem = 1;
5870 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
5871 lpMainItem->iSubItem = 0;
5872 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
5876 COMCTL32_Free(lpNewItems);
5879 /* we don't have to worry abiut display issues in non-report mode */
5880 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return nNewColumn;
5882 /* if we have a focus, must first erase the focus rect */
5883 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, infoPtr->nFocusedItem, FALSE);
5885 /* Need to reset the item width when inserting a new column */
5886 infoPtr->nItemWidth += rcCol.right - rcCol.left;
5888 LISTVIEW_UpdateScroll(infoPtr);
5890 /* scroll to cover the deleted column, and invalidate for redraw */
5891 rcOld = infoPtr->rcList;
5892 rcOld.left = rcCol.left;
5893 ScrollWindowEx(infoPtr->hwndSelf, rcCol.right - rcCol.left, 0,
5894 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5896 /* we can restore focus now */
5897 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, infoPtr->nFocusedItem, TRUE);
5902 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5903 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5904 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5905 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5906 their own sort proc. when sending LVM_SORTITEMS.
5909 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5911 LVS_SORTXXX must be specified,
5912 LVS_OWNERDRAW is not set,
5913 <item>.pszText is not LPSTR_TEXTCALLBACK.
5915 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5916 are sorted based on item text..."
5918 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5920 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5921 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5922 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5924 /* if we're sorting descending, negate the return value */
5925 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5930 * Inserts a new item in the listview control.
5933 * [I] infoPtr : valid pointer to the listview structure
5934 * [I] lpLVItem : item information
5935 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5938 * SUCCESS : new item index
5941 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5943 LONG lStyle = infoPtr->dwStyle;
5944 UINT uView = lStyle & LVS_TYPEMASK;
5948 LISTVIEW_ITEM *lpItem;
5951 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5953 if (lStyle & LVS_OWNERDATA)
5955 nItem = infoPtr->nItemCount;
5956 infoPtr->nItemCount++;
5960 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5961 if (!lpLVItem || lpLVItem->iSubItem) return -1;
5963 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5965 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5968 /* insert item in listview control data structure */
5969 if ( (hdpaSubItems = DPA_Create(8)) )
5970 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5971 if (nItem == -1) goto fail;
5973 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5974 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5976 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
5977 is_sorted ? infoPtr->nItemCount + 1 : lpLVItem->iItem,
5979 if (nItem == -1) goto fail;
5980 infoPtr->nItemCount++;
5982 if (!LISTVIEW_SetItemT(infoPtr, lpLVItem, isW))
5985 /* if we're sorted, sort the list, and update the index */
5988 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5989 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5992 ERR("We can't find the item we just inserted, possible memory corruption.");
5993 /* we can't remove it from the list if we can't find it, so just fail */
5994 /* we don't deallocate memory here, as it will probably cause more problems */
5999 /* make room for the position, if we are in the right mode */
6000 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6002 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6004 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6006 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6011 /* Add the subitem list to the items array. Do this last in case we go to
6012 * fail during the above.
6014 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6016 lpItem->valid = TRUE;
6018 /* send LVN_INSERTITEM notification */
6019 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6021 nmlv.lParam = lpItem->lParam;
6022 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6024 /* align items (set position of each item) */
6025 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6027 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
6028 else LISTVIEW_AlignTop(infoPtr);
6031 LISTVIEW_UpdateScroll(infoPtr);
6033 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6035 TRACE(" <- %d\n", nItem);
6039 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6040 infoPtr->nItemCount--;
6042 DPA_DeletePtr(hdpaSubItems, 0);
6043 DPA_Destroy (hdpaSubItems);
6044 COMCTL32_Free (lpItem);
6050 * Redraws a range of items.
6053 * [I] infoPtr : valid pointer to the listview structure
6054 * [I] INT : first item
6055 * [I] INT : last item
6061 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6065 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6066 max(nFirst, nLast) >= infoPtr->nItemCount)
6069 for (i = nFirst; i <= nLast; i++)
6070 LISTVIEW_InvalidateItem(infoPtr, i);
6077 * Scroll the content of a listview.
6080 * [I] infoPtr : valid pointer to the listview structure
6081 * [I] INT : horizontal scroll amount in pixels
6082 * [I] INT : vertical scroll amount in pixels
6089 * If the control is in report mode (LVS_REPORT) the control can
6090 * be scrolled only in line increments. "dy" will be rounded to the
6091 * nearest number of pixels that are a whole line. Ex: if line height
6092 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6093 * is passed the the scroll will be 0. (per MSDN 7/2002)
6095 * For: (per experimentaion with native control and CSpy ListView)
6096 * LVS_ICON dy=1 = 1 pixel (vertical only)
6098 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6100 * LVS_LIST dx=1 = 1 column (horizontal only)
6101 * but will only scroll 1 column per message
6102 * no matter what the value.
6103 * dy must be 0 or FALSE returned.
6104 * LVS_REPORT dx=1 = 1 pixel
6108 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6110 switch(LISTVIEW_GetType(infoPtr)) {
6112 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6113 dy /= infoPtr->nItemHeight;
6116 if (dy != 0) return FALSE;
6123 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6124 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6131 * Sets the background color.
6134 * [I] infoPtr : valid pointer to the listview structure
6135 * [I] COLORREF : background color
6141 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6143 TRACE("(clrBk=%lx)\n", clrBk);
6145 if(infoPtr->clrBk != clrBk) {
6146 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6147 infoPtr->clrBk = clrBk;
6148 if (clrBk == CLR_NONE)
6149 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6151 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6152 LISTVIEW_InvalidateList(infoPtr);
6158 /* LISTVIEW_SetBkImage */
6162 * Sets the attributes of a header item.
6165 * [I] infoPtr : valid pointer to the listview structure
6166 * [I] INT : column index
6167 * [I] LPLVCOLUMNW : column attributes
6168 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6169 * otherwise it is in fact a LPLVCOLUMNA
6175 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6176 LPLVCOLUMNW lpColumn, BOOL isW)
6178 BOOL bResult = FALSE;
6179 HDITEMW hdi, hdiget;
6181 if ((lpColumn != NULL) && (nColumn >= 0) &&
6182 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6184 /* initialize memory */
6185 ZeroMemory(&hdi, sizeof(hdi));
6187 if (lpColumn->mask & LVCF_FMT)
6189 /* format member is valid */
6190 hdi.mask |= HDI_FORMAT;
6192 /* get current format first */
6193 hdiget.mask = HDI_FORMAT;
6194 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6195 /* preserve HDF_STRING if present */
6196 hdi.fmt = hdiget.fmt & HDF_STRING;
6198 /* set text alignment (leftmost column must be left-aligned) */
6201 hdi.fmt |= HDF_LEFT;
6205 if (lpColumn->fmt & LVCFMT_LEFT)
6206 hdi.fmt |= HDF_LEFT;
6207 else if (lpColumn->fmt & LVCFMT_RIGHT)
6208 hdi.fmt |= HDF_RIGHT;
6209 else if (lpColumn->fmt & LVCFMT_CENTER)
6210 hdi.fmt |= HDF_CENTER;
6213 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6214 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6216 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6217 hdi.fmt |= HDF_IMAGE;
6219 if (lpColumn->fmt & LVCFMT_IMAGE)
6221 hdi.fmt |= HDF_IMAGE;
6222 hdi.iImage = I_IMAGECALLBACK;
6226 if (lpColumn->mask & LVCF_WIDTH)
6228 hdi.mask |= HDI_WIDTH;
6229 hdi.cxy = lpColumn->cx;
6232 if (lpColumn->mask & LVCF_TEXT)
6234 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6235 hdi.pszText = lpColumn->pszText;
6236 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6237 hdi.fmt |= HDF_STRING;
6240 if (lpColumn->mask & LVCF_IMAGE)
6242 hdi.mask |= HDI_IMAGE;
6243 hdi.iImage = lpColumn->iImage;
6246 if (lpColumn->mask & LVCF_ORDER)
6248 hdi.mask |= HDI_ORDER;
6249 hdi.iOrder = lpColumn->iOrder;
6252 /* set header item attributes */
6254 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6256 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6264 * Sets the column order array
6267 * [I] infoPtr : valid pointer to the listview structure
6268 * [I] INT : number of elements in column order array
6269 * [I] INT : pointer to column order array
6275 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6277 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6288 * Sets the width of a column
6291 * [I] infoPtr : valid pointer to the listview structure
6292 * [I] INT : column index
6293 * [I] INT : column width
6299 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6303 LONG lStyle = infoPtr->dwStyle;
6304 UINT uView = lStyle & LVS_TYPEMASK;
6309 WCHAR text_buffer[DISP_TEXT_SIZE];
6310 INT header_item_count;
6315 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6317 if (!infoPtr->hwndHeader) /* make sure we have a header */
6320 /* set column width only if in report or list mode */
6321 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6324 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6326 /* take care of invalid cx values */
6327 if((uView == LVS_REPORT) && (cx < -2))
6328 cx = LVSCW_AUTOSIZE;
6329 else if (uView == LVS_LIST && (cx < 1))
6332 /* resize all columns if in LVS_LIST mode */
6333 if(uView == LVS_LIST) {
6334 infoPtr->nItemWidth = cx;
6335 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6339 /* autosize based on listview items width */
6340 if(cx == LVSCW_AUTOSIZE)
6342 /* set the width of the column to the width of the widest item */
6343 if (iCol == 0 || uView == LVS_LIST)
6346 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6348 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6349 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6351 if (infoPtr->himlSmall)
6352 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6356 lvItem.iSubItem = iCol;
6357 lvItem.mask = LVIF_TEXT;
6358 lvItem.pszText = szDispText;
6359 lvItem.cchTextMax = DISP_TEXT_SIZE;
6361 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6363 lvItem.iItem = item_index;
6364 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6365 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6366 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6369 cx += TRAILING_PADDING;
6370 } /* autosize based on listview header width */
6371 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6373 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6375 /* if iCol is the last column make it fill the remainder of the controls width */
6376 if(iCol == (header_item_count - 1)) {
6377 /* get the width of every item except the current one */
6378 hdi.mask = HDI_WIDTH;
6381 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6382 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6386 /* retrieve the layout of the header */
6387 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6389 cx = (rcHeader.right - rcHeader.left) - cx;
6393 /* Despite what the MS docs say, if this is not the last
6394 column, then MS resizes the column to the width of the
6395 largest text string in the column, including headers
6396 and items. This is different from LVSCW_AUTOSIZE in that
6397 LVSCW_AUTOSIZE ignores the header string length.
6400 /* retrieve header font */
6401 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6403 /* retrieve header text */
6404 hdi.mask = HDI_TEXT;
6405 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6406 hdi.pszText = text_buffer;
6408 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6410 /* determine the width of the text in the header */
6411 hdc = GetDC(infoPtr->hwndSelf);
6412 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6414 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6416 SelectObject(hdc, old_font); /* restore the old font */
6417 ReleaseDC(infoPtr->hwndSelf, hdc);
6419 lvItem.iSubItem = iCol;
6420 lvItem.mask = LVIF_TEXT;
6421 lvItem.pszText = szDispText;
6422 lvItem.cchTextMax = DISP_TEXT_SIZE;
6424 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6426 lvItem.iItem = item_index;
6427 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6428 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6429 nLabelWidth += TRAILING_PADDING;
6430 /* While it is possible for subitems to have icons, even MS messes
6431 up the positioning, so I suspect no applications actually use
6433 if (item_index == 0 && infoPtr->himlSmall)
6434 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6435 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6440 /* call header to update the column change */
6441 hdi.mask = HDI_WIDTH;
6444 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6446 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6453 * Sets the extended listview style.
6456 * [I] infoPtr : valid pointer to the listview structure
6461 * SUCCESS : previous style
6464 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6466 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6470 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6472 infoPtr->dwLvExStyle = dwStyle;
6479 * Sets the new hot cursor used during hot tracking and hover selection.
6482 * [I] infoPtr : valid pointer to the listview structure
6483 * [I} hCurosr : the new hot cursor handle
6486 * Returns the previous hot cursor
6488 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6490 HCURSOR oldCursor = infoPtr->hHotCursor;
6491 infoPtr->hHotCursor = hCursor;
6498 * Sets the hot item index.
6501 * [I] infoPtr : valid pointer to the listview structure
6505 * SUCCESS : previous hot item index
6506 * FAILURE : -1 (no hot item)
6508 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6510 INT iOldIndex = infoPtr->nHotItem;
6511 infoPtr->nHotItem = iIndex;
6518 * Sets the amount of time the cursor must hover over an item before it is selected.
6521 * [I] infoPtr : valid pointer to the listview structure
6522 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6525 * Returns the previous hover time
6527 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6529 DWORD oldHoverTime = infoPtr->dwHoverTime;
6530 infoPtr->dwHoverTime = dwHoverTime;
6531 return oldHoverTime;
6536 * Sets spacing for icons of LVS_ICON style.
6539 * [I] infoPtr : valid pointer to the listview structure
6540 * [I] DWORD : MAKELONG(cx, cy)
6543 * MAKELONG(oldcx, oldcy)
6545 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6547 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6548 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6549 LONG lStyle = infoPtr->dwStyle;
6550 UINT uView = lStyle & LVS_TYPEMASK;
6552 TRACE("requested=(%d,%d)\n", cx, cy);
6554 /* this is supported only for LVS_ICON style */
6555 if (uView != LVS_ICON) return oldspacing;
6557 /* set to defaults, if instructed to */
6558 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6559 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6561 /* if 0 then compute width
6562 * FIXME: Should scan each item and determine max width of
6563 * icon or label, then make that the width */
6565 cx = infoPtr->iconSpacing.cx;
6567 /* if 0 then compute height */
6569 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6570 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6573 infoPtr->iconSpacing.cx = cx;
6574 infoPtr->iconSpacing.cy = cy;
6576 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6577 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6578 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6579 infoPtr->ntmHeight);
6581 /* these depend on the iconSpacing */
6582 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6583 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6588 inline void update_icon_size(HIMAGELIST himl, SIZE *size)
6592 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6598 size->cx = size->cy = 0;
6606 * [I] infoPtr : valid pointer to the listview structure
6607 * [I] INT : image list type
6608 * [I] HIMAGELIST : image list handle
6611 * SUCCESS : old image list
6614 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6616 UINT uView = LISTVIEW_GetType(infoPtr);
6617 INT oldHeight = infoPtr->nItemHeight;
6618 HIMAGELIST himlOld = 0;
6623 himlOld = infoPtr->himlNormal;
6624 infoPtr->himlNormal = himl;
6625 if (uView == LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6626 LISTVIEW_SetIconSpacing(infoPtr, 0);
6630 himlOld = infoPtr->himlSmall;
6631 infoPtr->himlSmall = himl;
6632 if (uView != LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6636 himlOld = infoPtr->himlState;
6637 infoPtr->himlState = himl;
6638 update_icon_size(himl, &infoPtr->iconStateSize);
6639 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6643 ERR("Unknown icon type=%d\n", nType);
6647 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6648 if (infoPtr->nItemHeight != oldHeight)
6649 LISTVIEW_UpdateScroll(infoPtr);
6656 * Preallocates memory (does *not* set the actual count of items !)
6659 * [I] infoPtr : valid pointer to the listview structure
6660 * [I] INT : item count (projected number of items to allocate)
6661 * [I] DWORD : update flags
6667 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6669 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6671 if (infoPtr->dwStyle & LVS_OWNERDATA)
6673 int precount,topvisible;
6675 TRACE("LVS_OWNERDATA is set!\n");
6676 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6677 FIXME("flags %s %s not implemented\n",
6678 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6680 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6682 LISTVIEW_RemoveAllSelections(infoPtr);
6684 precount = infoPtr->nItemCount;
6685 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6686 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6688 infoPtr->nItemCount = nItems;
6689 infoPtr->nItemWidth = max(LISTVIEW_CalculateMaxWidth(infoPtr),
6690 DEFAULT_COLUMN_WIDTH);
6692 LISTVIEW_UpdateSize(infoPtr);
6693 LISTVIEW_UpdateScroll(infoPtr);
6695 if (min(precount,infoPtr->nItemCount) < topvisible)
6696 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6700 /* According to MSDN for non-LVS_OWNERDATA this is just
6701 * a performance issue. The control allocates its internal
6702 * data structures for the number of items specified. It
6703 * cuts down on the number of memory allocations. Therefore
6704 * we will just issue a WARN here
6706 WARN("for non-ownerdata performance option not implemented.\n");
6714 * Sets the position of an item.
6717 * [I] infoPtr : valid pointer to the listview structure
6718 * [I] nItem : item index
6719 * [I] pt : coordinate
6725 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6727 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6730 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6732 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6733 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6735 /* This point value seems to be an undocumented feature.
6736 * The best guess is that it means either at the origin,
6737 * or at true beginning of the list. I will assume the origin. */
6738 if ((pt.x == -1) && (pt.y == -1))
6739 LISTVIEW_GetOrigin(infoPtr, &pt);
6740 else if (uView == LVS_ICON)
6742 pt.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
6743 pt.y -= ICON_TOP_PADDING;
6746 /* save the old position */
6747 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
6748 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
6750 /* Is the position changing? */
6751 if (pt.x == old.x && pt.y == old.y) return TRUE;
6753 /* FIXME: shouldn't we invalidate, as the item moved? */
6755 /* Allocating a POINTER for every item is too resource intensive,
6756 * so we'll keep the (x,y) in different arrays */
6757 if (DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)pt.x) &&
6758 DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)pt.y) )
6761 ERR("We should never fail here (nItem=%d, pt=%s), please report.\n",
6762 nItem, debugpoint(&pt));
6768 * Sets the state of one or many items.
6771 * [I] infoPtr : valid pointer to the listview structure
6772 * [I]INT : item index
6773 * [I] LPLVITEM : item or subitem info
6779 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6781 BOOL bResult = TRUE;
6784 lvItem.iItem = nItem;
6785 lvItem.iSubItem = 0;
6786 lvItem.mask = LVIF_STATE;
6787 lvItem.state = lpLVItem->state;
6788 lvItem.stateMask = lpLVItem->stateMask;
6789 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6793 /* apply to all items */
6794 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6795 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6798 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6805 * Sets the text of an item or subitem.
6808 * [I] hwnd : window handle
6809 * [I] nItem : item index
6810 * [I] lpLVItem : item or subitem info
6811 * [I] isW : TRUE if input is Unicode
6817 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6821 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6823 lvItem.iItem = nItem;
6824 lvItem.iSubItem = lpLVItem->iSubItem;
6825 lvItem.mask = LVIF_TEXT;
6826 lvItem.pszText = lpLVItem->pszText;
6827 lvItem.cchTextMax = lpLVItem->cchTextMax;
6829 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6831 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6836 * Set item index that marks the start of a multiple selection.
6839 * [I] infoPtr : valid pointer to the listview structure
6843 * Index number or -1 if there is no selection mark.
6845 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6847 INT nOldIndex = infoPtr->nSelectionMark;
6849 TRACE("(nIndex=%d)\n", nIndex);
6851 infoPtr->nSelectionMark = nIndex;
6858 * Sets the text background color.
6861 * [I] infoPtr : valid pointer to the listview structure
6862 * [I] COLORREF : text background color
6868 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6870 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6872 if (infoPtr->clrTextBk != clrTextBk)
6874 infoPtr->clrTextBk = clrTextBk;
6875 LISTVIEW_InvalidateList(infoPtr);
6883 * Sets the text foreground color.
6886 * [I] infoPtr : valid pointer to the listview structure
6887 * [I] COLORREF : text color
6893 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6895 TRACE("(clrText=%lx)\n", clrText);
6897 if (infoPtr->clrText != clrText)
6899 infoPtr->clrText = clrText;
6900 LISTVIEW_InvalidateList(infoPtr);
6906 /* LISTVIEW_SetToolTips */
6907 /* LISTVIEW_SetUnicodeFormat */
6908 /* LISTVIEW_SetWorkAreas */
6912 * Callback internally used by LISTVIEW_SortItems()
6915 * [I] LPVOID : first LISTVIEW_ITEM to compare
6916 * [I] LPVOID : second LISTVIEW_ITEM to compare
6917 * [I] LPARAM : HWND of control
6920 * if first comes before second : negative
6921 * if first comes after second : positive
6922 * if first and second are equivalent : zero
6924 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6926 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6927 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6928 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6930 /* Forward the call to the client defined callback */
6931 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6936 * Sorts the listview items.
6939 * [I] infoPtr : valid pointer to the listview structure
6940 * [I] WPARAM : application-defined value
6941 * [I] LPARAM : pointer to comparision callback
6947 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6949 UINT lStyle = infoPtr->dwStyle;
6951 LISTVIEW_ITEM *lpItem;
6952 LPVOID selectionMarkItem;
6955 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6957 if (lStyle & LVS_OWNERDATA) return FALSE;
6959 if (!infoPtr->hdpaItems) return FALSE;
6961 /* if there are 0 or 1 items, there is no need to sort */
6962 if (infoPtr->nItemCount < 2) return TRUE;
6964 if (infoPtr->nFocusedItem >= 0)
6966 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6967 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6968 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6971 infoPtr->pfnCompare = pfnCompare;
6972 infoPtr->lParamSort = lParamSort;
6973 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6975 /* Adjust selections and indices so that they are the way they should
6976 * be after the sort (otherwise, the list items move around, but
6977 * whatever is at the item's previous original position will be
6980 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6981 for (i=0; i < infoPtr->nItemCount; i++)
6983 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6984 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6986 if (lpItem->state & LVIS_SELECTED)
6987 LISTVIEW_AddSelectionRange(infoPtr, i, i);
6989 LISTVIEW_RemoveSelectionRange(infoPtr, i, i);
6990 if (lpItem->state & LVIS_FOCUSED)
6992 infoPtr->nFocusedItem = i;
6993 lpItem->state &= ~LVIS_FOCUSED;
6996 if (selectionMarkItem != NULL)
6997 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6998 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7000 /* align the items */
7001 LISTVIEW_AlignTop(infoPtr);
7003 /* refresh the display */
7004 LISTVIEW_InvalidateList(infoPtr); /* FIXME: display should not change for [SMALL]ICON view */
7011 * Updates an items or rearranges the listview control.
7014 * [I] infoPtr : valid pointer to the listview structure
7015 * [I] INT : item index
7021 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7023 LONG lStyle = infoPtr->dwStyle;
7024 UINT uView = lStyle & LVS_TYPEMASK;
7026 TRACE("(nItem=%d)\n", nItem);
7028 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7030 /* rearrange with default alignment style */
7031 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
7032 LISTVIEW_Arrange(infoPtr, 0);
7034 LISTVIEW_InvalidateItem(infoPtr, nItem);
7042 * Creates the listview control.
7045 * [I] hwnd : window handle
7046 * [I] lpcs : the create parameters
7052 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7054 LISTVIEW_INFO *infoPtr;
7055 UINT uView = lpcs->style & LVS_TYPEMASK;
7058 TRACE("(lpcs=%p)\n", lpcs);
7060 /* initialize info pointer */
7061 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7062 if (!infoPtr) return -1;
7064 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7066 infoPtr->hwndSelf = hwnd;
7067 infoPtr->dwStyle = lpcs->style;
7068 /* determine the type of structures to use */
7069 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7070 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7072 /* initialize color information */
7073 infoPtr->clrBk = CLR_NONE;
7074 infoPtr->clrText = comctl32_color.clrWindowText;
7075 infoPtr->clrTextBk = CLR_DEFAULT;
7076 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7078 /* set default values */
7079 infoPtr->nFocusedItem = -1;
7080 infoPtr->nSelectionMark = -1;
7081 infoPtr->nHotItem = -1;
7082 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7083 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7084 infoPtr->nEditLabelItem = -1;
7086 /* get default font (icon title) */
7087 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7088 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7089 infoPtr->hFont = infoPtr->hDefaultFont;
7090 LISTVIEW_SaveTextMetrics(infoPtr);
7093 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7094 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7095 0, 0, 0, 0, hwnd, (HMENU)0,
7096 lpcs->hInstance, NULL);
7098 /* set header unicode format */
7099 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
7101 /* set header font */
7102 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7105 if (uView == LVS_ICON)
7107 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7108 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7110 else if (uView == LVS_REPORT)
7112 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7114 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7118 /* set HDS_HIDDEN flag to hide the header bar */
7119 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7120 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7124 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7125 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7129 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7130 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7133 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
7134 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
7136 /* display unsupported listview window styles */
7137 LISTVIEW_UnsupportedStyles(lpcs->style);
7139 /* allocate memory for the data structure */
7140 infoPtr->hdpaItems = DPA_Create(10);
7141 infoPtr->hdpaPosX = DPA_Create(10);
7142 infoPtr->hdpaPosY = DPA_Create(10);
7144 /* allocate memory for the selection ranges */
7145 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7147 /* initialize size of items */
7148 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7149 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
7151 /* initialize the hover time to -1(indicating the default system hover time) */
7152 infoPtr->dwHoverTime = -1;
7159 * Erases the background of the listview control.
7162 * [I] infoPtr : valid pointer to the listview structure
7163 * [I] hdc : device context handle
7169 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7173 TRACE("(hdc=%x)\n", hdc);
7175 if (!GetClipBox(hdc, &rc)) return FALSE;
7177 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7183 * Helper function for LISTVIEW_[HV]Scroll *only*.
7184 * Performs vertical/horizontal scrolling by a give amount.
7187 * [I] infoPtr : valid pointer to the listview structure
7188 * [I] dx : amount of horizontal scroll
7189 * [I] dy : amount of vertical scroll
7191 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7193 /* now we can scroll the list */
7194 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7195 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7196 /* if we have focus, adjust rect */
7197 OffsetRect(&infoPtr->rcFocus, dx, dy);
7198 UpdateWindow(infoPtr->hwndSelf);
7203 * Performs vertical scrolling.
7206 * [I] infoPtr : valid pointer to the listview structure
7207 * [I] nScrollCode : scroll code
7208 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7209 * [I] hScrollWnd : scrollbar control window handle
7215 * SB_LINEUP/SB_LINEDOWN:
7216 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7217 * for LVS_REPORT is 1 line
7218 * for LVS_LIST cannot occur
7221 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7222 INT nScrollDiff, HWND hScrollWnd)
7224 UINT uView = LISTVIEW_GetType(infoPtr);
7225 INT nOldScrollPos, nNewScrollPos;
7226 SCROLLINFO scrollInfo;
7229 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7231 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7233 scrollInfo.cbSize = sizeof(SCROLLINFO);
7234 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7236 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7238 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7240 nOldScrollPos = scrollInfo.nPos;
7241 switch (nScrollCode)
7247 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7251 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7255 nScrollDiff = -scrollInfo.nPage;
7259 nScrollDiff = scrollInfo.nPage;
7262 case SB_THUMBPOSITION:
7264 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7271 /* quit right away if pos isn't changing */
7272 if (nScrollDiff == 0) return 0;
7274 /* calculate new position, and handle overflows */
7275 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7276 if (nScrollDiff > 0) {
7277 if (nNewScrollPos < nOldScrollPos ||
7278 nNewScrollPos > scrollInfo.nMax)
7279 nNewScrollPos = scrollInfo.nMax;
7281 if (nNewScrollPos > nOldScrollPos ||
7282 nNewScrollPos < scrollInfo.nMin)
7283 nNewScrollPos = scrollInfo.nMin;
7286 /* set the new position, and reread in case it changed */
7287 scrollInfo.fMask = SIF_POS;
7288 scrollInfo.nPos = nNewScrollPos;
7289 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7291 /* carry on only if it really changed */
7292 if (nNewScrollPos == nOldScrollPos) return 0;
7294 /* now adjust to client coordinates */
7295 nScrollDiff = nOldScrollPos - nNewScrollPos;
7296 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7298 /* and scroll the window */
7299 scroll_list(infoPtr, 0, nScrollDiff);
7306 * Performs horizontal scrolling.
7309 * [I] infoPtr : valid pointer to the listview structure
7310 * [I] nScrollCode : scroll code
7311 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7312 * [I] hScrollWnd : scrollbar control window handle
7318 * SB_LINELEFT/SB_LINERIGHT:
7319 * for LVS_ICON, LVS_SMALLICON 1 pixel
7320 * for LVS_REPORT is 1 pixel
7321 * for LVS_LIST is 1 column --> which is a 1 because the
7322 * scroll is based on columns not pixels
7325 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7326 INT nScrollDiff, HWND hScrollWnd)
7328 UINT uView = LISTVIEW_GetType(infoPtr);
7329 INT nOldScrollPos, nNewScrollPos;
7330 SCROLLINFO scrollInfo;
7332 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7334 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7336 scrollInfo.cbSize = sizeof(SCROLLINFO);
7337 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7339 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7341 nOldScrollPos = scrollInfo.nPos;
7343 switch (nScrollCode)
7357 nScrollDiff = -scrollInfo.nPage;
7361 nScrollDiff = scrollInfo.nPage;
7364 case SB_THUMBPOSITION:
7366 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7373 /* quit right away if pos isn't changing */
7374 if (nScrollDiff == 0) return 0;
7376 /* calculate new position, and handle overflows */
7377 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7378 if (nScrollDiff > 0) {
7379 if (nNewScrollPos < nOldScrollPos ||
7380 nNewScrollPos > scrollInfo.nMax)
7381 nNewScrollPos = scrollInfo.nMax;
7383 if (nNewScrollPos > nOldScrollPos ||
7384 nNewScrollPos < scrollInfo.nMin)
7385 nNewScrollPos = scrollInfo.nMin;
7388 /* set the new position, and reread in case it changed */
7389 scrollInfo.fMask = SIF_POS;
7390 scrollInfo.nPos = nNewScrollPos;
7391 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7393 /* carry on only if it really changed */
7394 if (nNewScrollPos == nOldScrollPos) return 0;
7396 if(uView == LVS_REPORT)
7397 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7399 /* now adjust to client coordinates */
7400 nScrollDiff = nOldScrollPos - nNewScrollPos;
7401 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7403 /* and scroll the window */
7404 scroll_list(infoPtr, nScrollDiff, 0);
7409 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7411 UINT uView = LISTVIEW_GetType(infoPtr);
7412 INT gcWheelDelta = 0;
7413 UINT pulScrollLines = 3;
7414 SCROLLINFO scrollInfo;
7416 TRACE("(wheelDelta=%d)\n", wheelDelta);
7418 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7419 gcWheelDelta -= wheelDelta;
7421 scrollInfo.cbSize = sizeof(SCROLLINFO);
7422 scrollInfo.fMask = SIF_POS;
7429 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7430 * should be fixed in the future.
7432 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7433 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7434 scrollInfo.nPos + (gcWheelDelta < 0) ?
7435 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7436 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7440 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7442 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7444 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7445 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7446 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7452 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7463 * [I] infoPtr : valid pointer to the listview structure
7464 * [I] INT : virtual key
7465 * [I] LONG : key data
7470 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7472 UINT uView = LISTVIEW_GetType(infoPtr);
7474 NMLVKEYDOWN nmKeyDown;
7476 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7478 /* send LVN_KEYDOWN notification */
7479 nmKeyDown.wVKey = nVirtualKey;
7480 nmKeyDown.flags = 0;
7481 notify(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7483 switch (nVirtualKey)
7486 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7488 notify_return(infoPtr);
7489 notify_itemactivate(infoPtr);
7494 if (infoPtr->nItemCount > 0)
7499 if (infoPtr->nItemCount > 0)
7500 nItem = infoPtr->nItemCount - 1;
7504 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7508 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7512 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7516 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7520 if (uView == LVS_REPORT)
7521 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7523 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7524 * LISTVIEW_GetCountPerRow(infoPtr);
7525 if(nItem < 0) nItem = 0;
7529 if (uView == LVS_REPORT)
7530 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7532 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7533 * LISTVIEW_GetCountPerRow(infoPtr);
7534 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7538 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7539 LISTVIEW_KeySelection(infoPtr, nItem);
7549 * [I] infoPtr : valid pointer to the listview structure
7554 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7558 /* if we did not have the focus, there's nothing to do */
7559 if (!infoPtr->bFocus) return 0;
7561 /* send NM_KILLFOCUS notification */
7562 notify_killfocus(infoPtr);
7564 /* if we have a focus rectagle, get rid of it */
7565 LISTVIEW_ShowFocusRect(infoPtr, infoPtr->nFocusedItem, FALSE);
7567 /* set window focus flag */
7568 infoPtr->bFocus = FALSE;
7570 /* invalidate the selected items before reseting focus flag */
7571 LISTVIEW_InvalidateSelectedItems(infoPtr);
7578 * Processes double click messages (left mouse button).
7581 * [I] infoPtr : valid pointer to the listview structure
7582 * [I] wKey : key flag
7583 * [I] pts : mouse coordinate
7588 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7590 LVHITTESTINFO htInfo;
7593 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7595 htInfo.pt.x = pts.x;
7596 htInfo.pt.y = pts.y;
7598 /* send NM_DBLCLK notification */
7599 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7600 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE);
7601 nmlv.iItem = htInfo.iItem;
7602 nmlv.iSubItem = htInfo.iSubItem;
7603 nmlv.ptAction = htInfo.pt;
7604 notify_listview(infoPtr, NM_DBLCLK, &nmlv);
7606 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7607 if(nmlv.iItem != -1)
7608 notify_itemactivate(infoPtr);
7615 * Processes mouse down messages (left mouse button).
7618 * [I] infoPtr : valid pointer to the listview structure
7619 * [I] wKey : key flag
7620 * [I] pts : mouse coordinate
7625 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7627 LONG lStyle = infoPtr->dwStyle;
7628 static BOOL bGroupSelect = TRUE;
7629 POINT pt = { pts.x, pts.y };
7632 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7634 /* FIXME: NM_CLICK */
7636 /* send NM_RELEASEDCAPTURE notification */
7637 notify_releasedcapture(infoPtr);
7639 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7641 /* set left button down flag */
7642 infoPtr->bLButtonDown = TRUE;
7644 nItem = LISTVIEW_GetItemAtPt(infoPtr, pt);
7645 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7646 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7648 if (lStyle & LVS_SINGLESEL)
7650 if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7651 && infoPtr->nEditLabelItem == -1)
7652 infoPtr->nEditLabelItem = nItem;
7654 LISTVIEW_SetSelection(infoPtr, nItem);
7658 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7661 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7666 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7667 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7669 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7670 infoPtr->nSelectionMark = nItem;
7673 else if (wKey & MK_CONTROL)
7677 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7679 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7680 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7681 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7682 infoPtr->nSelectionMark = nItem;
7684 else if (wKey & MK_SHIFT)
7686 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7690 BOOL was_selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
7692 /* set selection (clears other pre-existing selections) */
7693 LISTVIEW_SetSelection(infoPtr, nItem);
7695 if (was_selected && infoPtr->nEditLabelItem == -1)
7696 infoPtr->nEditLabelItem = nItem;
7702 /* remove all selections */
7703 LISTVIEW_RemoveAllSelections(infoPtr);
7711 * Processes mouse up messages (left mouse button).
7714 * [I] infoPtr : valid pointer to the listview structure
7715 * [I] wKey : key flag
7716 * [I] pts : mouse coordinate
7721 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7723 LVHITTESTINFO lvHitTestInfo;
7726 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7728 if (!infoPtr->bLButtonDown) return 0;
7730 lvHitTestInfo.pt.x = pts.x;
7731 lvHitTestInfo.pt.y = pts.y;
7733 /* send NM_CLICK notification */
7734 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7735 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE);
7736 nmlv.iItem = lvHitTestInfo.iItem;
7737 nmlv.iSubItem = lvHitTestInfo.iSubItem;
7738 nmlv.ptAction = lvHitTestInfo.pt;
7739 notify_listview(infoPtr, NM_CLICK, &nmlv);
7741 /* set left button flag */
7742 infoPtr->bLButtonDown = FALSE;
7744 if(infoPtr->nEditLabelItem != -1)
7746 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem &&
7747 (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7748 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7749 infoPtr->nEditLabelItem = -1;
7757 * Destroys the listview control (called after WM_DESTROY).
7760 * [I] infoPtr : valid pointer to the listview structure
7765 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7767 LONG lStyle = infoPtr->dwStyle;
7771 /* delete all items */
7772 LISTVIEW_DeleteAllItems(infoPtr);
7774 /* destroy data structure */
7775 DPA_Destroy(infoPtr->hdpaItems);
7776 DPA_Destroy(infoPtr->hdpaSelectionRanges);
7778 /* destroy image lists */
7779 if (!(lStyle & LVS_SHAREIMAGELISTS))
7781 /* FIXME: If the caller does a ImageList_Destroy and then we
7782 * do this code the area will be freed twice. Currently
7783 * this generates an "err:heap:HEAP_ValidateInUseArena
7784 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
7785 * has PREV_FREE flag" sometimes.
7787 * We will leak the memory till we figure out how to fix
7789 if (infoPtr->himlNormal)
7790 ImageList_Destroy(infoPtr->himlNormal);
7791 if (infoPtr->himlSmall)
7792 ImageList_Destroy(infoPtr->himlSmall);
7793 if (infoPtr->himlState)
7794 ImageList_Destroy(infoPtr->himlState);
7797 /* destroy font, bkgnd brush */
7799 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7800 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7802 /* free listview info pointer*/
7803 COMCTL32_Free(infoPtr);
7805 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7811 * Handles notifications from children.
7814 * [I] infoPtr : valid pointer to the listview structure
7815 * [I] INT : control identifier
7816 * [I] LPNMHDR : notification information
7821 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7823 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7825 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7827 /* handle notification from header control */
7828 if (lpnmh->code == HDN_ENDTRACKW)
7830 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7831 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7833 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7835 /* Handle sorting by Header Column */
7838 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7840 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
7841 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7843 else if(lpnmh->code == NM_RELEASEDCAPTURE)
7845 /* Idealy this should be done in HDN_ENDTRACKA
7846 * but since SetItemBounds in Header.c is called after
7847 * the notification is sent, it is neccessary to handle the
7848 * update of the scroll bar here (Header.c works fine as it is,
7849 * no need to disturb it)
7851 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7852 LISTVIEW_UpdateScroll(infoPtr);
7853 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7863 * Determines the type of structure to use.
7866 * [I] infoPtr : valid pointer to the listview structureof the sender
7867 * [I] HWND : listview window handle
7868 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
7873 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7875 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7877 if (nCommand == NF_REQUERY)
7878 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7879 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7885 * Paints/Repaints the listview control.
7888 * [I] infoPtr : valid pointer to the listview structure
7889 * [I] HDC : device context handle
7894 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7896 TRACE("(hdc=%x)\n", hdc);
7899 LISTVIEW_Refresh(infoPtr, hdc);
7904 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7906 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7907 LISTVIEW_Refresh(infoPtr, hdc);
7908 EndPaint(infoPtr->hwndSelf, &ps);
7916 * Processes double click messages (right mouse button).
7919 * [I] infoPtr : valid pointer to the listview structure
7920 * [I] wKey : key flag
7921 * [I] pts : mouse coordinate
7926 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7928 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7930 /* send NM_RELEASEDCAPTURE notification */
7931 notify_releasedcapture(infoPtr);
7933 /* send NM_RDBLCLK notification */
7934 notify_rdblclk(infoPtr);
7941 * Processes mouse down messages (right mouse button).
7944 * [I] infoPtr : valid pointer to the listview structure
7945 * [I] wKey : key flag
7946 * [I] pts : mouse coordinate
7951 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7953 LVHITTESTINFO lvHitTestInfo;
7957 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7959 /* FIXME: NM_CLICK */
7961 /* send NM_RELEASEDCAPTURE notification */
7962 notify_releasedcapture(infoPtr);
7964 /* make sure the listview control window has the focus */
7965 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7967 /* set right button down flag */
7968 infoPtr->bRButtonDown = TRUE;
7970 /* determine the index of the selected item */
7971 lvHitTestInfo.pt.x = pts.x;
7972 lvHitTestInfo.pt.y = pts.y;
7973 nItem = LISTVIEW_GetItemAtPt(infoPtr, lvHitTestInfo.pt);
7975 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7977 LISTVIEW_SetItemFocus(infoPtr,nItem);
7978 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7979 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7980 LISTVIEW_SetSelection(infoPtr, nItem);
7984 LISTVIEW_RemoveAllSelections(infoPtr);
7988 /* Send NM_RClICK notification */
7989 ZeroMemory(&nmlv, sizeof(nmlv));
7990 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE);
7991 nmlv.iItem = lvHitTestInfo.iItem;
7992 nmlv.iSubItem = lvHitTestInfo.iSubItem;
7993 nmlv.ptAction = lvHitTestInfo.pt;
7994 notify_listview(infoPtr, NM_RCLICK, &nmlv);
8001 * Processes mouse up messages (right mouse button).
8004 * [I] infoPtr : valid pointer to the listview structure
8005 * [I] wKey : key flag
8006 * [I] pts : mouse coordinate
8011 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8013 POINT pt = { pts.x, pts.y };
8015 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8017 if (!infoPtr->bRButtonDown) return 0;
8019 /* set button flag */
8020 infoPtr->bRButtonDown = FALSE;
8022 /* Change to screen coordinate for WM_CONTEXTMENU */
8023 ClientToScreen(infoPtr->hwndSelf, &pt);
8025 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8026 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8027 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8038 * [I] infoPtr : valid pointer to the listview structure
8039 * [I] hwnd : window handle of window containing the cursor
8040 * [I] nHittest : hit-test code
8041 * [I] wMouseMsg : ideintifier of the mouse message
8044 * TRUE if cursor is set
8047 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8051 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8053 if(!infoPtr->hHotCursor) return FALSE;
8056 if (LISTVIEW_GetItemAtPt(infoPtr, pt) < 0) return FALSE;
8058 SetCursor(infoPtr->hHotCursor);
8068 * [I] infoPtr : valid pointer to the listview structure
8069 * [I] infoPtr : handle of previously focused window
8074 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8076 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
8078 /* if we have the focus already, there's nothing to do */
8079 if (infoPtr->bFocus) return 0;
8081 /* send NM_SETFOCUS notification */
8082 notify_setfocus(infoPtr);
8084 /* set window focus flag */
8085 infoPtr->bFocus = TRUE;
8087 /* put the focus rect back on */
8088 LISTVIEW_ShowFocusRect(infoPtr, infoPtr->nFocusedItem, TRUE);
8090 /* redraw all visible selected items */
8091 LISTVIEW_InvalidateSelectedItems(infoPtr);
8101 * [I] infoPtr : valid pointer to the listview structure
8102 * [I] HFONT : font handle
8103 * [I] WORD : redraw flag
8108 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8110 HFONT oldFont = infoPtr->hFont;
8112 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
8114 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8115 if (infoPtr->hFont == oldFont) return 0;
8117 LISTVIEW_SaveTextMetrics(infoPtr);
8119 if (LISTVIEW_GetType(infoPtr) == LVS_REPORT)
8120 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8122 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8129 * Message handling for WM_SETREDRAW.
8130 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8133 * [I] infoPtr : valid pointer to the listview structure
8134 * [I] bRedraw: state of redraw flag
8137 * DefWinProc return value
8139 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8141 /* FIXME: this is bogus */
8142 LRESULT lResult = DefWindowProcW(infoPtr->hwndSelf, WM_SETREDRAW, bRedraw, 0);
8144 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
8145 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8151 * Resizes the listview control. This function processes WM_SIZE
8152 * messages. At this time, the width and height are not used.
8155 * [I] infoPtr : valid pointer to the listview structure
8156 * [I] WORD : new width
8157 * [I] WORD : new height
8162 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8164 LONG lStyle = infoPtr->dwStyle;
8165 UINT uView = lStyle & LVS_TYPEMASK;
8167 TRACE("(width=%d, height=%d)\n", Width, Height);
8169 if (LISTVIEW_UpdateSize(infoPtr))
8171 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8173 if (lStyle & LVS_ALIGNLEFT)
8174 LISTVIEW_AlignLeft(infoPtr);
8176 LISTVIEW_AlignTop(infoPtr);
8179 LISTVIEW_UpdateScroll(infoPtr);
8181 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8189 * Sets the size information.
8192 * [I] infoPtr : valid pointer to the listview structure
8195 * Zero if no size change
8198 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8200 LONG lStyle = infoPtr->dwStyle;
8201 UINT uView = lStyle & LVS_TYPEMASK;
8205 GetClientRect(infoPtr->hwndSelf, &rcList);
8206 CopyRect(&rcOld,&(infoPtr->rcList));
8207 infoPtr->rcList.left = 0;
8208 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8209 infoPtr->rcList.top = 0;
8210 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8212 if (uView == LVS_LIST)
8214 /* Apparently the "LIST" style is supposed to have the same
8215 * number of items in a column even if there is no scroll bar.
8216 * Since if a scroll bar already exists then the bottom is already
8217 * reduced, only reduce if the scroll bar does not currently exist.
8218 * The "2" is there to mimic the native control. I think it may be
8219 * related to either padding or edges. (GLA 7/2002)
8221 if (!(lStyle & WS_HSCROLL))
8223 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8224 if (infoPtr->rcList.bottom > nHScrollHeight)
8225 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8229 if (infoPtr->rcList.bottom > 2)
8230 infoPtr->rcList.bottom -= 2;
8233 else if (uView == LVS_REPORT)
8240 Header_Layout(infoPtr->hwndHeader, &hl);
8242 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8244 if (!(LVS_NOCOLUMNHEADER & lStyle))
8245 infoPtr->rcList.top = max(wp.cy, 0);
8247 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8252 * Processes WM_STYLECHANGED messages.
8255 * [I] infoPtr : valid pointer to the listview structure
8256 * [I] WPARAM : window style type (normal or extended)
8257 * [I] LPSTYLESTRUCT : window style information
8262 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8265 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8266 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8267 RECT rcList = infoPtr->rcList;
8269 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8270 wStyleType, lpss->styleOld, lpss->styleNew);
8272 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8274 if (wStyleType == GWL_STYLE)
8276 infoPtr->dwStyle = lpss->styleNew;
8278 if (uOldView == LVS_REPORT)
8279 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8281 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8282 ((lpss->styleNew & WS_HSCROLL) == 0))
8283 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8285 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8286 ((lpss->styleNew & WS_VSCROLL) == 0))
8287 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8289 /* If switching modes, then start with no scroll bars and then
8292 if (uNewView != uOldView)
8293 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8295 if (uNewView == LVS_ICON)
8299 /* First readjust the iconSize and if necessary the iconSpacing */
8300 oldcx = infoPtr->iconSize.cx;
8301 oldcy = infoPtr->iconSize.cy;
8302 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8303 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8304 if (infoPtr->himlNormal != NULL)
8307 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
8308 infoPtr->iconSize.cx = cx;
8309 infoPtr->iconSize.cy = cy;
8311 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
8313 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8314 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8315 LISTVIEW_SetIconSpacing(infoPtr,0);
8318 /* Now update the full item width and height */
8319 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8320 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8321 if (lpss->styleNew & LVS_ALIGNLEFT)
8322 LISTVIEW_AlignLeft(infoPtr);
8324 LISTVIEW_AlignTop(infoPtr);
8326 else if (uNewView == LVS_REPORT)
8333 Header_Layout(infoPtr->hwndHeader, &hl);
8334 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8336 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8337 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8339 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8340 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8341 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8342 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8344 else if (uNewView == LVS_LIST)
8346 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8347 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8348 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8349 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8353 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8354 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8355 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8356 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8357 if (lpss->styleNew & LVS_ALIGNLEFT)
8358 LISTVIEW_AlignLeft(infoPtr);
8360 LISTVIEW_AlignTop(infoPtr);
8363 /* update the size of the client area */
8364 LISTVIEW_UpdateSize(infoPtr);
8366 /* add scrollbars if needed */
8367 LISTVIEW_UpdateScroll(infoPtr);
8369 /* invalidate client area + erase background */
8370 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8372 /* print the list of unsupported window styles */
8373 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8376 /* If they change the view and we have an active edit control
8377 we will need to kill the control since the redraw will
8378 misplace the edit control.
8380 if (infoPtr->bEditing &&
8381 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8382 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8384 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8392 * Window procedure of the listview control.
8395 static LRESULT WINAPI
8396 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8398 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8400 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8402 if (!infoPtr && (uMsg != WM_CREATE))
8403 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8407 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8412 case LVM_APPROXIMATEVIEWRECT:
8413 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8414 LOWORD(lParam), HIWORD(lParam));
8416 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8418 /* case LVN_CANCELEDITLABEL */
8420 /* case LVM_CREATEDRAGIMAGE: */
8422 case LVM_DELETEALLITEMS:
8423 return LISTVIEW_DeleteAllItems(infoPtr);
8425 case LVM_DELETECOLUMN:
8426 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8428 case LVM_DELETEITEM:
8429 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8431 case LVM_EDITLABELW:
8432 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8434 case LVM_EDITLABELA:
8435 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8437 /* case LVN_ENABLEGROUPVIEW: */
8439 case LVM_ENSUREVISIBLE:
8440 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8443 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8446 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8448 case LVM_GETBKCOLOR:
8449 return infoPtr->clrBk;
8451 /* case LVM_GETBKIMAGE: */
8453 case LVM_GETCALLBACKMASK:
8454 return infoPtr->uCallbackMask;
8456 case LVM_GETCOLUMNA:
8457 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8459 case LVM_GETCOLUMNW:
8460 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8462 case LVM_GETCOLUMNORDERARRAY:
8463 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8465 case LVM_GETCOLUMNWIDTH:
8466 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8468 case LVM_GETCOUNTPERPAGE:
8469 return LISTVIEW_GetCountPerPage(infoPtr);
8471 case LVM_GETEDITCONTROL:
8472 return (LRESULT)infoPtr->hwndEdit;
8474 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8475 return infoPtr->dwLvExStyle;
8478 return (LRESULT)infoPtr->hwndHeader;
8480 case LVM_GETHOTCURSOR:
8481 return infoPtr->hHotCursor;
8483 case LVM_GETHOTITEM:
8484 return infoPtr->nHotItem;
8486 case LVM_GETHOVERTIME:
8487 return infoPtr->dwHoverTime;
8489 case LVM_GETIMAGELIST:
8490 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8492 /* case LVN_GETINSERTMARK: */
8494 /* case LVN_GETINSERTMARKCOLOR: */
8496 /* case LVN_GETINSERTMARKRECT: */
8498 case LVM_GETISEARCHSTRINGA:
8499 case LVM_GETISEARCHSTRINGW:
8500 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8504 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8507 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8509 case LVM_GETITEMCOUNT:
8510 return infoPtr->nItemCount;
8512 case LVM_GETITEMPOSITION:
8513 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8515 case LVM_GETITEMRECT:
8516 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8518 case LVM_GETITEMSPACING:
8519 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8521 case LVM_GETITEMSTATE:
8522 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8524 case LVM_GETITEMTEXTA:
8525 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8527 case LVM_GETITEMTEXTW:
8528 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8530 case LVM_GETNEXTITEM:
8531 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8533 case LVM_GETNUMBEROFWORKAREAS:
8534 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8538 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8540 /* case LVN_GETOUTLINECOLOR: */
8542 /* case LVM_GETSELECTEDCOLUMN: */
8544 case LVM_GETSELECTEDCOUNT:
8545 return LISTVIEW_GetSelectedCount(infoPtr);
8547 case LVM_GETSELECTIONMARK:
8548 return infoPtr->nSelectionMark;
8550 case LVM_GETSTRINGWIDTHA:
8551 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8553 case LVM_GETSTRINGWIDTHW:
8554 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8556 case LVM_GETSUBITEMRECT:
8557 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8559 case LVM_GETTEXTBKCOLOR:
8560 return infoPtr->clrTextBk;
8562 case LVM_GETTEXTCOLOR:
8563 return infoPtr->clrText;
8565 /* case LVN_GETTILEINFO: */
8567 /* case LVN_GETTILEVIEWINFO: */
8569 case LVM_GETTOOLTIPS:
8570 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8573 case LVM_GETTOPINDEX:
8574 return LISTVIEW_GetTopIndex(infoPtr);
8576 /*case LVM_GETUNICODEFORMAT:
8577 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8580 case LVM_GETVIEWRECT:
8581 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8583 case LVM_GETWORKAREAS:
8584 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8587 /* case LVN_HASGROUP: */
8590 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE);
8592 case LVM_INSERTCOLUMNA:
8593 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8595 case LVM_INSERTCOLUMNW:
8596 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8598 /* case LVN_INSERTGROUP: */
8600 /* case LVN_INSERTGROUPSORTED: */
8602 case LVM_INSERTITEMA:
8603 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8605 case LVM_INSERTITEMW:
8606 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8608 /* case LVN_INSERTMARKHITTEST: */
8610 /* case LVN_ISGROUPVIEWENABLED: */
8612 /* case LVN_MAPIDTOINDEX: */
8614 /* case LVN_INEDXTOID: */
8616 /* case LVN_MOVEGROUP: */
8618 /* case LVN_MOVEITEMTOGROUP: */
8620 case LVM_REDRAWITEMS:
8621 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8623 /* case LVN_REMOVEALLGROUPS: */
8625 /* case LVN_REMOVEGROUP: */
8628 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8630 case LVM_SETBKCOLOR:
8631 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8633 /* case LVM_SETBKIMAGE: */
8635 case LVM_SETCALLBACKMASK:
8636 infoPtr->uCallbackMask = (UINT)wParam;
8639 case LVM_SETCOLUMNA:
8640 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8642 case LVM_SETCOLUMNW:
8643 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8645 case LVM_SETCOLUMNORDERARRAY:
8646 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8648 case LVM_SETCOLUMNWIDTH:
8649 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8651 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8652 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8654 /* case LVN_SETGROUPINFO: */
8656 /* case LVN_SETGROUPMETRICS: */
8658 case LVM_SETHOTCURSOR:
8659 return LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8661 case LVM_SETHOTITEM:
8662 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8664 case LVM_SETHOVERTIME:
8665 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8667 case LVM_SETICONSPACING:
8668 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8670 case LVM_SETIMAGELIST:
8671 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8673 /* case LVN_SETINFOTIP: */
8675 /* case LVN_SETINSERTMARK: */
8677 /* case LVN_SETINSERTMARKCOLOR: */
8680 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8683 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8685 case LVM_SETITEMCOUNT:
8686 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8688 case LVM_SETITEMPOSITION:
8690 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8691 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8694 case LVM_SETITEMPOSITION32:
8695 if (lParam == 0) return FALSE;
8696 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8698 case LVM_SETITEMSTATE:
8699 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8701 case LVM_SETITEMTEXTA:
8702 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8704 case LVM_SETITEMTEXTW:
8705 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8707 /* case LVN_SETOUTLINECOLOR: */
8709 /* case LVN_SETSELECTEDCOLUMN: */
8711 case LVM_SETSELECTIONMARK:
8712 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8714 case LVM_SETTEXTBKCOLOR:
8715 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8717 case LVM_SETTEXTCOLOR:
8718 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8720 /* case LVN_SETTILEINFO: */
8722 /* case LVN_SETTILEVIEWINFO: */
8724 /* case LVN_SETTILEWIDTH: */
8726 /* case LVM_SETTOOLTIPS: */
8728 /* case LVM_SETUNICODEFORMAT: */
8730 /* case LVN_SETVIEW: */
8732 /* case LVM_SETWORKAREAS: */
8734 /* case LVN_SORTGROUPS: */
8737 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8739 case LVM_SUBITEMHITTEST:
8740 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE);
8743 return LISTVIEW_Update(infoPtr, (INT)wParam);
8746 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8749 return LISTVIEW_Command(infoPtr, wParam, lParam);
8752 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8755 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8758 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8761 return infoPtr->hFont;
8764 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8767 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8770 return LISTVIEW_KillFocus(infoPtr);
8772 case WM_LBUTTONDBLCLK:
8773 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8775 case WM_LBUTTONDOWN:
8776 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8779 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8782 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8785 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8788 return LISTVIEW_NCDestroy(infoPtr);
8791 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8793 case WM_NOTIFYFORMAT:
8794 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8797 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8799 case WM_RBUTTONDBLCLK:
8800 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8802 case WM_RBUTTONDOWN:
8803 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8806 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8809 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8814 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8817 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8820 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8823 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8825 case WM_STYLECHANGED:
8826 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8828 case WM_SYSCOLORCHANGE:
8829 COMCTL32_RefreshSysColors();
8832 /* case WM_TIMER: */
8835 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8838 if (wParam & (MK_SHIFT | MK_CONTROL))
8839 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8840 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8842 case WM_WINDOWPOSCHANGED:
8843 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8844 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8845 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8846 LISTVIEW_UpdateSize(infoPtr);
8847 LISTVIEW_UpdateScroll(infoPtr);
8849 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8851 /* case WM_WININICHANGE: */
8854 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8855 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8858 /* call default window procedure */
8859 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8867 * Registers the window class.
8875 void LISTVIEW_Register(void)
8879 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8880 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8881 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8882 wndClass.cbClsExtra = 0;
8883 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8884 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8885 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8886 wndClass.lpszClassName = WC_LISTVIEWW;
8887 RegisterClassW(&wndClass);
8892 * Unregisters the window class.
8900 void LISTVIEW_Unregister(void)
8902 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8907 * Handle any WM_COMMAND messages
8913 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8915 switch (HIWORD(wParam))
8920 * Adjust the edit window size
8923 HDC hdc = GetDC(infoPtr->hwndEdit);
8924 HFONT hFont, hOldFont = 0;
8929 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8930 GetWindowRect(infoPtr->hwndEdit, &rect);
8932 /* Select font to get the right dimension of the string */
8933 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8936 hOldFont = SelectObject(hdc, hFont);
8939 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8941 TEXTMETRICW textMetric;
8943 /* Add Extra spacing for the next character */
8944 GetTextMetricsW(hdc, &textMetric);
8945 sz.cx += (textMetric.tmMaxCharWidth * 2);
8953 rect.bottom - rect.top,
8954 SWP_DRAWFRAME|SWP_NOMOVE);
8957 SelectObject(hdc, hOldFont);
8959 ReleaseDC(infoPtr->hwndSelf, hdc);
8965 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8974 * Subclassed edit control windproc function
8980 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
8981 WPARAM wParam, LPARAM lParam, BOOL isW)
8983 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8984 static BOOL bIgnoreKillFocus = FALSE;
8985 BOOL cancel = FALSE;
8987 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8988 hwnd, uMsg, wParam, lParam, isW);
8993 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8996 if(bIgnoreKillFocus) return TRUE;
9001 WNDPROC editProc = infoPtr->EditWndProc;
9002 infoPtr->EditWndProc = 0;
9003 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9004 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9008 if (VK_ESCAPE == (INT)wParam)
9013 else if (VK_RETURN == (INT)wParam)
9017 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9020 if (infoPtr->bEditing)
9022 LPWSTR buffer = NULL;
9026 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9030 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9032 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9033 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9037 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9038 /* eg. Using a messagebox */
9039 bIgnoreKillFocus = TRUE;
9040 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9042 if (buffer) COMCTL32_Free(buffer);
9044 bIgnoreKillFocus = FALSE;
9047 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9053 * Subclassed edit control windproc function
9059 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9061 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9066 * Subclassed edit control windproc function
9072 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9074 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9079 * Creates a subclassed edit cotrol
9085 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9086 INT x, INT y, INT width, INT height, BOOL isW)
9088 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9093 TEXTMETRICW textMetric;
9094 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9096 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9098 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9099 hdc = GetDC(infoPtr->hwndSelf);
9101 /* Select the font to get appropriate metric dimensions */
9102 if(infoPtr->hFont != 0)
9103 hOldFont = SelectObject(hdc, infoPtr->hFont);
9105 /*Get String Lenght in pixels */
9106 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9108 /*Add Extra spacing for the next character */
9109 GetTextMetricsW(hdc, &textMetric);
9110 sz.cx += (textMetric.tmMaxCharWidth * 2);
9112 if(infoPtr->hFont != 0)
9113 SelectObject(hdc, hOldFont);
9115 ReleaseDC(infoPtr->hwndSelf, hdc);
9117 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9119 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9121 if (!hedit) return 0;
9123 infoPtr->EditWndProc = (WNDPROC)
9124 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9125 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9127 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);