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.
30 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
31 * -- Support CustonDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs)
34 * LISTVIEW_Notify : most notifications from children (editbox and header)
37 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
39 * Advanced functionality:
40 * LISTVIEW_GetNumberOfWorkAreas : not implemented
41 * LISTVIEW_GetISearchString : not implemented
42 * LISTVIEW_GetBkImage : not implemented
43 * LISTVIEW_SetBkImage : not implemented
44 * LISTVIEW_GetColumnOrderArray : simple hack only
45 * LISTVIEW_SetColumnOrderArray : simple hack only
46 * LISTVIEW_Arrange : empty stub
47 * LISTVIEW_ApproximateViewRect : incomplete
48 * LISTVIEW_Update : not completed
50 * Known differences in message stream from native control (not known if
51 * these differences cause problems):
52 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
53 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
54 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
55 * processing for "USEDOUBLECLICKTIME".
60 #include "wine/port.h"
73 #include "wine/debug.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(listview);
77 typedef struct tagCOLUMNCACHE
81 } COLUMNCACHE, *LPCOLUMNCACHE;
83 typedef struct tagITEMHDR
87 } ITEMHDR, *LPITEMHDR;
89 typedef struct tagLISTVIEW_SUBITEM
95 typedef struct tagLISTVIEW_ITEM
104 typedef struct tagRANGE
110 typedef struct tagITERATOR
119 typedef struct tagLISTVIEW_INFO
126 COLORREF clrTextBkDefault;
127 HIMAGELIST himlNormal;
128 HIMAGELIST himlSmall;
129 HIMAGELIST himlState;
134 HDPA hdpaSelectionRanges;
136 BOOL bRemovingAllSelections;
139 RECT rcList; /* This rectangle is really the window
140 * client rectangle possibly reduced by the
141 * horizontal scroll bar and/or header - see
142 * LISTVIEW_UpdateSize. This rectangle offset
143 * by the LISTVIEW_GetOrigin value is in
144 * client coordinates */
145 RECT rcView; /* This rectangle contains all items -
146 * contructed in LISTVIEW_AlignTop and
147 * LISTVIEW_AlignLeft */
156 INT ntmHeight; /* from GetTextMetrics from above font */
161 DWORD dwStyle; /* the cached window GWL_STYLE */
162 DWORD dwLvExStyle; /* extended listview style */
165 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
166 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
167 PFNLVCOMPARE pfnCompare;
174 DWORD lastKeyPressTimestamp;
176 INT nSearchParamLength;
177 WCHAR szSearchParam[ MAX_PATH ];
184 /* How many we debug buffer to allocate */
185 #define DEBUG_BUFFERS 20
186 /* The size of a single debug bbuffer */
187 #define DEBUG_BUFFER_SIZE 256
189 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
190 #define SB_INTERNAL -1
192 /* maximum size of a label */
193 #define DISP_TEXT_SIZE 512
195 /* padding for items in list and small icon display modes */
196 #define WIDTH_PADDING 12
198 /* padding for items in list, report and small icon display modes */
199 #define HEIGHT_PADDING 1
201 /* offset of items in report display mode */
202 #define REPORT_MARGINX 2
204 /* padding for icon in large icon display mode
205 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
206 * that HITTEST will see.
207 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
208 * ICON_TOP_PADDING - sum of the two above.
209 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
210 * LABEL_VERT_PADDING - between bottom of text and end of box
212 * ICON_LR_PADDING - additional width above icon size.
213 * ICON_LR_HALF - half of the above value
215 #define ICON_TOP_PADDING_NOTHITABLE 2
216 #define ICON_TOP_PADDING_HITABLE 2
217 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
218 #define ICON_BOTTOM_PADDING 4
219 #define LABEL_VERT_PADDING 7
220 #define ICON_LR_PADDING 16
221 #define ICON_LR_HALF (ICON_LR_PADDING/2)
223 /* default label width for items in list and small icon display modes */
224 #define DEFAULT_LABEL_WIDTH 40
226 /* default column width for items in list display mode */
227 #define DEFAULT_COLUMN_WIDTH 128
229 /* Size of "line" scroll for V & H scrolls */
230 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
232 /* Padding betwen image and label */
233 #define IMAGE_PADDING 2
235 /* Padding behind the label */
236 #define TRAILING_PADDING 5
238 /* Border for the icon caption */
239 #define CAPTION_BORDER 2
241 /* Standard DrawText flags */
242 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
243 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
244 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
246 /* The time in milisecods to reset the search in the list */
247 #define KEY_DELAY 450
249 /* Dump the LISTVIEW_INFO structure to the debug channel */
250 #define LISTVIEW_DUMP(iP) do { \
251 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
252 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
253 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
254 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
255 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
256 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
257 (iP->bFocus) ? "true" : "false"); \
258 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
259 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
260 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
261 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
263 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
264 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
269 * forward declarations
271 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
272 static BOOL LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
273 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
274 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
275 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
276 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *);
277 static BOOL LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
278 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
279 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
280 static INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *);
281 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
282 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *, INT);
283 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
284 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
285 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
286 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
287 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, POINT);
288 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
289 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
290 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
291 static void LISTVIEW_UnsupportedStyles(LONG);
292 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
293 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
294 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
295 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
296 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
297 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
298 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
299 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
300 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
301 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
302 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
303 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
304 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
306 /******** Text handling functions *************************************/
308 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
309 * text string. The string may be ANSI or Unicode, in which case
310 * the boolean isW tells us the type of the string.
312 * The name of the function tell what type of strings it expects:
313 * W: Unicode, T: ANSI/Unicode - function of isW
316 static inline BOOL is_textW(LPCWSTR text)
318 return text != NULL && text != LPSTR_TEXTCALLBACKW;
321 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
323 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
324 return is_textW(text);
327 static inline int textlenT(LPCWSTR text, BOOL isW)
329 return !is_textT(text, isW) ? 0 :
330 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
333 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
336 if (isSrcW) lstrcpynW(dest, src, max);
337 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
339 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
340 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
343 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
345 LPWSTR wstr = (LPWSTR)text;
347 if (!isW && is_textT(text, isW))
349 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
350 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
351 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
353 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
357 static inline void textfreeT(LPWSTR wstr, BOOL isW)
359 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
363 * dest is a pointer to a Unicode string
364 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
366 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
370 if (src == LPSTR_TEXTCALLBACKW)
372 if (is_textW(*dest)) COMCTL32_Free(*dest);
373 *dest = LPSTR_TEXTCALLBACKW;
377 LPWSTR pszText = textdupTtoW(src, isW);
378 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
379 bResult = Str_SetPtrW(dest, pszText);
380 textfreeT(pszText, isW);
386 * compares a Unicode to a Unicode/ANSI text string
388 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
390 if (!aw) return bt ? -1 : 0;
391 if (!bt) return aw ? 1 : 0;
392 if (aw == LPSTR_TEXTCALLBACKW)
393 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
394 if (bt != LPSTR_TEXTCALLBACKW)
396 LPWSTR bw = textdupTtoW(bt, isW);
397 int r = bw ? lstrcmpW(aw, bw) : 1;
405 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
409 n = min(min(n, strlenW(s1)), strlenW(s2));
410 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
411 return res ? res - sizeof(WCHAR) : res;
414 /******** Debugging functions *****************************************/
416 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
418 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
419 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
422 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
424 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
425 n = min(textlenT(text, isW), n);
426 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
429 static char* debug_getbuf()
431 static int index = 0;
432 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
433 return buffers[index++ % DEBUG_BUFFERS];
436 static inline char* debugpoint(const POINT* lppt)
440 char* buf = debug_getbuf();
441 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
443 } else return "(null)";
446 static inline char* debugrect(const RECT* rect)
450 char* buf = debug_getbuf();
451 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
452 rect->left, rect->top, rect->right, rect->bottom);
454 } else return "(null)";
457 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
459 char* buf = debug_getbuf(), *text = buf;
460 int len, size = DEBUG_BUFFER_SIZE;
462 if (lpLVItem == NULL) return "(null)";
463 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
464 if (len == -1) goto end; buf += len; size -= len;
465 if (lpLVItem->mask & LVIF_STATE)
466 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
468 if (len == -1) goto end; buf += len; size -= len;
469 if (lpLVItem->mask & LVIF_TEXT)
470 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
472 if (len == -1) goto end; buf += len; size -= len;
473 if (lpLVItem->mask & LVIF_IMAGE)
474 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
476 if (len == -1) goto end; buf += len; size -= len;
477 if (lpLVItem->mask & LVIF_PARAM)
478 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
480 if (len == -1) goto end; buf += len; size -= len;
481 if (lpLVItem->mask & LVIF_INDENT)
482 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
484 if (len == -1) goto end; buf += len; size -= len;
487 buf = text + strlen(text);
489 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
493 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
495 char* buf = debug_getbuf(), *text = buf;
496 int len, size = DEBUG_BUFFER_SIZE;
498 if (lpColumn == NULL) return "(null)";
499 len = snprintf(buf, size, "{");
500 if (len == -1) goto end; buf += len; size -= len;
501 if (lpColumn->mask & LVCF_SUBITEM)
502 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
504 if (len == -1) goto end; buf += len; size -= len;
505 if (lpColumn->mask & LVCF_FMT)
506 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
508 if (len == -1) goto end; buf += len; size -= len;
509 if (lpColumn->mask & LVCF_WIDTH)
510 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
512 if (len == -1) goto end; buf += len; size -= len;
513 if (lpColumn->mask & LVCF_TEXT)
514 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
516 if (len == -1) goto end; buf += len; size -= len;
517 if (lpColumn->mask & LVCF_IMAGE)
518 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
520 if (len == -1) goto end; buf += len; size -= len;
521 if (lpColumn->mask & LVCF_ORDER)
522 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
524 if (len == -1) goto end; buf += len; size -= len;
527 buf = text + strlen(text);
529 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
534 /******** Notification functions i************************************/
536 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
540 TRACE("(code=%d)\n", code);
542 pnmh->hwndFrom = infoPtr->hwndSelf;
543 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
545 result = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
546 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
548 TRACE(" <= %ld\n", result);
553 static inline LRESULT notify(LISTVIEW_INFO *infoPtr, INT code)
556 return notify_hdr(infoPtr, code, &nmh);
559 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
561 notify(infoPtr, LVN_ITEMACTIVATE);
564 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
566 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
569 static LRESULT notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
573 ZeroMemory(&nmlv, sizeof(nmlv));
574 nmlv.iItem = lvht->iItem;
575 nmlv.iSubItem = lvht->iSubItem;
576 nmlv.ptAction = lvht->pt;
577 return notify_listview(infoPtr, code, &nmlv);
580 static int get_ansi_notification(INT unicodeNotificationCode)
582 switch (unicodeNotificationCode)
584 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
585 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
586 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
587 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
588 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
589 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
591 ERR("unknown notification %x\n", unicodeNotificationCode);
592 return unicodeNotificationCode;
596 Send notification. depends on dispinfoW having same
597 structure as dispinfoA.
598 infoPtr : listview struct
599 notificationCode : *Unicode* notification code
600 pdi : dispinfo structure (can be unicode or ansi)
601 isW : TRUE if dispinfo is Unicode
603 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
605 BOOL bResult = FALSE;
606 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
608 INT cchTempBufMax = 0, savCchTextMax = 0;
609 LPWSTR pszTempBuf = NULL, savPszText = NULL;
611 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
613 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
614 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
617 if (convertToAnsi || convertToUnicode)
619 if (notificationCode != LVN_GETDISPINFOW)
621 cchTempBufMax = convertToUnicode ?
622 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
623 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
627 cchTempBufMax = pdi->item.cchTextMax;
628 *pdi->item.pszText = 0; /* make sure we don't process garbage */
631 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
632 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
633 if (!pszTempBuf) return FALSE;
634 if (convertToUnicode)
635 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
636 pszTempBuf, cchTempBufMax);
638 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
639 cchTempBufMax, NULL, NULL);
640 savCchTextMax = pdi->item.cchTextMax;
641 savPszText = pdi->item.pszText;
642 pdi->item.pszText = pszTempBuf;
643 pdi->item.cchTextMax = cchTempBufMax;
646 if (infoPtr->notifyFormat == NFR_ANSI)
647 realNotifCode = get_ansi_notification(notificationCode);
649 realNotifCode = notificationCode;
650 bResult = notify_hdr(infoPtr, realNotifCode, (LPNMHDR)pdi);
652 if (convertToUnicode || convertToAnsi)
654 if (convertToUnicode) /* note : pointer can be changed by app ! */
655 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
656 savCchTextMax, NULL, NULL);
658 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
659 savPszText, savCchTextMax);
660 pdi->item.pszText = savPszText; /* restores our buffer */
661 pdi->item.cchTextMax = savCchTextMax;
662 HeapFree(GetProcessHeap(), 0, pszTempBuf);
667 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc, LPRECT rcBounds)
669 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
670 lpnmlvcd->nmcd.hdc = hdc;
671 lpnmlvcd->nmcd.rc = *rcBounds;
672 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
673 lpnmlvcd->clrText = infoPtr->clrText;
676 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
678 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
679 return notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
682 /******** Item iterator functions **********************************/
684 static BOOL ranges_add(HDPA ranges, RANGE range);
685 static BOOL ranges_del(HDPA ranges, RANGE range);
686 static void ranges_dump(HDPA ranges);
689 * ITERATOR DOCUMENTATION
691 * The iterator functions allow for easy, and convenient iteration
692 * over items of iterest in the list. Typically, you create a
693 * iterator, use it, and destroy it, as such:
696 * iterator_xxxitems(&i, ...);
697 * while (iterator_{prev,next}(&i)
699 * //code which uses i.nItem
701 * iterator_destroy(&i);
703 * where xxx is either: framed, or visible.
704 * Note that it is important that the code destroys the iterator
705 * after it's done with it, as the creation of the iterator may
706 * allocate memory, which thus needs to be freed.
708 * You can iterate both forwards, and backwards through the list,
709 * by using iterator_next or iterator_prev respectively.
711 * Lower numbered items are draw on top of higher number items in
712 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
713 * items may overlap). So, to test items, you should use
715 * which lists the items top to bottom (in Z-order).
716 * For drawing items, you should use
718 * which lists the items bottom to top (in Z-order).
719 * If you keep iterating over the items after the end-of-items
720 * marker (-1) is returned, the iterator will start from the
721 * beginning. Typically, you don't need to test for -1,
722 * because iterator_{next,prev} will return TRUE if more items
723 * are to be iterated over, or FALSE otherwise.
725 * Note: the iterator is defined to be bidirectional. That is,
726 * any number of prev followed by any number of next, or
727 * five versa, should leave the iterator at the same item:
728 * prev * n, next * n = next * n, prev * n
730 * The iterator has a notion of a out-of-order, special item,
731 * which sits at the start of the list. This is used in
732 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
733 * which needs to be first, as it may overlap other items.
735 * The code is a bit messy because we have:
736 * - a special item to deal with
737 * - simple range, or composite range
739 * If find bugs, or want to add features, please make sure you
740 * always check/modify *both* iterator_prev, and iterator_next.
744 * This function iterates through the items in increasing order,
745 * but prefixed by the special item, then -1. That is:
746 * special, 1, 2, 3, ..., n, -1.
747 * Each item is listed only once.
749 static inline BOOL iterator_next(ITERATOR* i)
753 i->nItem = i->nSpecial;
754 if (i->nItem != -1) return TRUE;
756 if (i->nItem == i->nSpecial)
758 if (i->ranges) i->index = 0;
764 if (i->nItem == i->nSpecial) i->nItem++;
765 if (i->nItem <= i->range.upper) return TRUE;
770 if (i->index < i->ranges->nItemCount)
771 i->range = *(RANGE*)DPA_GetPtr(i->ranges, i->index++);
774 else if (i->nItem > i->range.upper) goto end;
776 i->nItem = i->range.lower;
777 if (i->nItem >= 0) goto testitem;
784 * This function iterates through the items in decreasing order,
785 * followed by the special item, then -1. That is:
786 * n, n-1, ..., 3, 2, 1, special, -1.
787 * Each item is listed only once.
789 static inline BOOL iterator_prev(ITERATOR* i)
796 if (i->ranges) i->index = i->ranges->nItemCount;
799 if (i->nItem == i->nSpecial)
807 if (i->nItem == i->nSpecial) i->nItem--;
808 if (i->nItem >= i->range.lower) return TRUE;
814 i->range = *(RANGE*)DPA_GetPtr(i->ranges, --i->index);
817 else if (!start && i->nItem < i->range.lower) goto end;
819 i->nItem = i->range.upper;
820 if (i->nItem >= 0) goto testitem;
822 return (i->nItem = i->nSpecial) != -1;
825 static RANGE iterator_range(ITERATOR* i)
829 if (!i->ranges) return i->range;
831 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges, 0)).lower;
832 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges, i->ranges->nItemCount - 1)).upper;
837 * Releases resources associated with this ierator.
839 static inline void iterator_destroy(ITERATOR* i)
841 if (i->ranges) DPA_Destroy(i->ranges);
845 * Create an empty iterator.
847 static inline BOOL iterator_empty(ITERATOR* i)
849 ZeroMemory(i, sizeof(*i));
850 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
855 * Creates an iterator over the items which intersect lprc.
857 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
859 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
860 RECT frame = *lprc, rcItem, rcTemp;
863 /* in case we fail, we want to return an empty iterator */
864 if (!iterator_empty(i)) return FALSE;
866 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
868 OffsetRect(&frame, -Origin.x, -Origin.y);
870 if (uView == LVS_ICON || uView == LVS_SMALLICON)
874 if (uView == LVS_ICON)
876 if (LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem) && IntersectRect(&rcTemp, &rcItem, lprc))
877 i->nSpecial = infoPtr->nFocusedItem;
879 if (!(i->ranges = DPA_Create(50))) return FALSE;
880 /* to do better here, we need to have PosX, and PosY sorted */
881 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
883 rcItem.left = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
884 rcItem.top = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
885 rcItem.right = rcItem.left + infoPtr->nItemWidth;
886 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
887 if (IntersectRect(&rcTemp, &rcItem, &frame))
889 RANGE item_range = { nItem, nItem };
890 ranges_add(i->ranges, item_range);
891 TRACE(" icon=%d\n", nItem);
896 else if (uView == LVS_REPORT)
900 if (frame.left >= infoPtr->nItemWidth) return TRUE;
901 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
903 lower = max(frame.top / infoPtr->nItemHeight, 0);
904 upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1);
905 if (upper < lower) return TRUE;
906 i->range.lower = lower;
907 i->range.upper = upper;
908 TRACE(" report=[%d,%d]\n", lower, upper);
912 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
913 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
914 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
915 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
916 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
917 INT lower = nFirstCol * nPerCol + nFirstRow;
921 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
922 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
924 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
926 if (!(i->ranges = DPA_Create(nLastCol - nFirstCol + 1))) return FALSE;
927 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
929 item_range.lower = nCol * nPerCol + nFirstRow;
930 if(item_range.lower >= infoPtr->nItemCount) break;
931 item_range.upper = min(nCol * nPerCol + nLastRow, infoPtr->nItemCount - 1);
932 TRACE(" list=[%d,%d]\n", item_range.lower, item_range.upper);
933 ranges_add(i->ranges, item_range);
941 * Creates an iterator over the items which intersect the visible region of hdc.
943 static BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
945 POINT Origin, Position;
949 rgntype = GetClipBox(hdc, &rcClip);
950 if (rgntype == NULLREGION) return iterator_empty(i);
951 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
952 if (rgntype == SIMPLEREGION) return TRUE;
954 /* first deal with the special item */
955 if (LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem) && !RectVisible(hdc, &rcItem))
958 /* if we can't deal with the region, we'll just go with the simple range */
959 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return TRUE;
962 if (!(i->ranges = DPA_Create(50))) return TRUE;
963 if (!ranges_add(i->ranges, i->range))
965 DPA_Destroy(i->ranges);
971 /* now delete the invisible items from the list */
972 while(iterator_next(i))
974 if (!LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position)) continue;
975 rcItem.left = Position.x + Origin.x;
976 rcItem.top = Position.y + Origin.y;
977 rcItem.right = rcItem.left + infoPtr->nItemWidth;
978 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
979 if (!RectVisible(hdc, &rcItem))
981 RANGE item_range = { i->nItem, i->nItem };
982 ranges_del(i->ranges, item_range);
985 /* the iterator should restart on the next iterator_next */
990 /******** Misc helper functions ************************************/
992 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
993 WPARAM wParam, LPARAM lParam, BOOL isW)
995 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
996 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
999 /******** Internal API functions ************************************/
1001 /* The Invalidate* are macros, so we preserve the caller location */
1002 #define LISTVIEW_InvalidateRect(infoPtr, rect) while(infoPtr->bRedraw) { \
1003 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
1004 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
1008 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
1010 if(LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox)) \
1011 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
1014 #define LISTVIEW_InvalidateSubItem(infoPtr, nItem, nSubItem) do { \
1015 POINT Origin, Position; \
1017 if (LISTVIEW_GetOrigin(infoPtr, &Origin) && \
1018 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position) && \
1019 Header_GetItemRect(infoPtr->hwndHeader, nSubItem, &rcBox)) { \
1020 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y); \
1021 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
1025 #define LISTVIEW_InvalidateList(infoPtr)\
1026 LISTVIEW_InvalidateRect(infoPtr, NULL)
1028 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1030 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1035 * Retrieves the number of items that can fit vertically in the client area.
1038 * [I] infoPtr : valid pointer to the listview structure
1041 * Number of items per row.
1043 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1045 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1047 return max(nListWidth/infoPtr->nItemWidth, 1);
1052 * Retrieves the number of items that can fit horizontally in the client
1056 * [I] infoPtr : valid pointer to the listview structure
1059 * Number of items per column.
1061 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1063 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1065 return max(nListHeight / infoPtr->nItemHeight, 1);
1069 /*************************************************************************
1070 * LISTVIEW_ProcessLetterKeys
1072 * Processes keyboard messages generated by pressing the letter keys
1074 * What this does is perform a case insensitive search from the
1075 * current position with the following quirks:
1076 * - If two chars or more are pressed in quick succession we search
1077 * for the corresponding string (e.g. 'abc').
1078 * - If there is a delay we wipe away the current search string and
1079 * restart with just that char.
1080 * - If the user keeps pressing the same character, whether slowly or
1081 * fast, so that the search string is entirely composed of this
1082 * character ('aaaaa' for instance), then we search for first item
1083 * that starting with that character.
1084 * - If the user types the above character in quick succession, then
1085 * we must also search for the corresponding string ('aaaaa'), and
1086 * go to that string if there is a match.
1089 * [I] hwnd : handle to the window
1090 * [I] charCode : the character code, the actual character
1091 * [I] keyData : key data
1099 * - The current implementation has a list of characters it will
1100 * accept and it ignores averything else. In particular it will
1101 * ignore accentuated characters which seems to match what
1102 * Windows does. But I'm not sure it makes sense to follow
1104 * - We don't sound a beep when the search fails.
1108 * TREEVIEW_ProcessLetterKeys
1110 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1115 WCHAR buffer[MAX_PATH];
1116 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1118 /* simple parameter checking */
1119 if (!charCode || !keyData) return 0;
1121 /* only allow the valid WM_CHARs through */
1122 if (!isalnum(charCode) &&
1123 charCode != '.' && charCode != '`' && charCode != '!' &&
1124 charCode != '@' && charCode != '#' && charCode != '$' &&
1125 charCode != '%' && charCode != '^' && charCode != '&' &&
1126 charCode != '*' && charCode != '(' && charCode != ')' &&
1127 charCode != '-' && charCode != '_' && charCode != '+' &&
1128 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1129 charCode != '}' && charCode != '[' && charCode != '{' &&
1130 charCode != '/' && charCode != '?' && charCode != '>' &&
1131 charCode != '<' && charCode != ',' && charCode != '~')
1134 /* if there's one item or less, there is no where to go */
1135 if (infoPtr->nItemCount <= 1) return 0;
1137 /* update the search parameters */
1138 infoPtr->lastKeyPressTimestamp = GetTickCount();
1139 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1140 if (infoPtr->nSearchParamLength < MAX_PATH)
1141 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1142 if (infoPtr->charCode != charCode)
1143 infoPtr->charCode = charCode = 0;
1145 infoPtr->charCode=charCode;
1146 infoPtr->szSearchParam[0]=charCode;
1147 infoPtr->nSearchParamLength=1;
1148 /* Redundant with the 1 char string */
1152 /* and search from the current position */
1154 if (infoPtr->nFocusedItem >= 0) {
1155 endidx=infoPtr->nFocusedItem;
1157 /* if looking for single character match,
1158 * then we must always move forward
1160 if (infoPtr->nSearchParamLength == 1)
1163 endidx=infoPtr->nItemCount;
1167 if (idx == infoPtr->nItemCount) {
1168 if (endidx == infoPtr->nItemCount || endidx == 0)
1174 item.mask = LVIF_TEXT;
1177 item.pszText = buffer;
1178 item.cchTextMax = MAX_PATH;
1179 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1181 /* check for a match */
1182 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1185 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1186 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1187 /* This would work but we must keep looking for a longer match */
1191 } while (idx != endidx);
1194 LISTVIEW_KeySelection(infoPtr, nItem);
1199 /*************************************************************************
1200 * LISTVIEW_UpdateHeaderSize [Internal]
1202 * Function to resize the header control
1205 * hwnd [I] handle to a window
1206 * nNewScrollPos [I] Scroll Pos to Set
1213 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1218 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1220 GetWindowRect(infoPtr->hwndHeader, &winRect);
1221 point[0].x = winRect.left;
1222 point[0].y = winRect.top;
1223 point[1].x = winRect.right;
1224 point[1].y = winRect.bottom;
1226 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1227 point[0].x = -nNewScrollPos;
1228 point[1].x += nNewScrollPos;
1230 SetWindowPos(infoPtr->hwndHeader,0,
1231 point[0].x,point[0].y,point[1].x,point[1].y,
1232 SWP_NOZORDER | SWP_NOACTIVATE);
1237 * Update the scrollbars. This functions should be called whenever
1238 * the content, size or view changes.
1241 * [I] infoPtr : valid pointer to the listview structure
1246 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1248 LONG lStyle = infoPtr->dwStyle;
1249 UINT uView = lStyle & LVS_TYPEMASK;
1250 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1251 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1252 SCROLLINFO scrollInfo;
1254 if (lStyle & LVS_NOSCROLL) return;
1256 scrollInfo.cbSize = sizeof(SCROLLINFO);
1258 if (uView == LVS_LIST)
1260 /* update horizontal scrollbar */
1261 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1262 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1264 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1265 infoPtr->nItemCount, nCountPerColumn, nCountPerRow);
1267 scrollInfo.nMin = 0;
1268 scrollInfo.nMax = infoPtr->nItemCount / nCountPerColumn;
1269 if((infoPtr->nItemCount % nCountPerColumn) == 0)
1271 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1272 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1273 scrollInfo.nPage = nCountPerRow;
1274 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1275 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1276 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1278 else if (uView == LVS_REPORT)
1282 /* update vertical scrollbar */
1283 scrollInfo.nMin = 0;
1284 scrollInfo.nMax = infoPtr->nItemCount - 1;
1285 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1286 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1287 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1288 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1289 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1290 scrollInfo.nMax, scrollInfo.nPage, test);
1291 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1292 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1294 /* update horizontal scrollbar */
1295 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1296 scrollInfo.fMask = SIF_POS;
1297 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1298 || infoPtr->nItemCount == 0)
1300 scrollInfo.nPos = 0;
1302 scrollInfo.nMin = 0;
1303 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1304 scrollInfo.nPage = nListWidth;
1305 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1306 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1307 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1308 scrollInfo.nMax, scrollInfo.nPage, test);
1309 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1310 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1312 /* Update the Header Control */
1313 scrollInfo.fMask = SIF_POS;
1314 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1315 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1322 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1324 INT nViewWidth = rcView.right - rcView.left;
1325 INT nViewHeight = rcView.bottom - rcView.top;
1327 /* Update Horizontal Scrollbar */
1328 scrollInfo.fMask = SIF_POS;
1329 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1330 || infoPtr->nItemCount == 0)
1332 scrollInfo.nPos = 0;
1334 scrollInfo.nMin = 0;
1335 scrollInfo.nMax = max(nViewWidth, 0)-1;
1336 scrollInfo.nPage = nListWidth;
1337 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1338 TRACE("LVS_ICON/SMALLICON Horz.\n");
1339 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1341 /* Update Vertical Scrollbar */
1342 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1343 scrollInfo.fMask = SIF_POS;
1344 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1345 || infoPtr->nItemCount == 0)
1347 scrollInfo.nPos = 0;
1349 scrollInfo.nMin = 0;
1350 scrollInfo.nMax = max(nViewHeight,0)-1;
1351 scrollInfo.nPage = nListHeight;
1352 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1353 TRACE("LVS_ICON/SMALLICON Vert.\n");
1354 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1362 * Shows/hides the focus rectangle.
1365 * [I] infoPtr : valid pointer to the listview structure
1366 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1371 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1373 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1376 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1378 if (infoPtr->nFocusedItem < 0) return;
1380 /* we need some gymnastics in ICON mode to handle large items */
1381 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1385 if (!LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox))
1387 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1389 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1394 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1396 /* for some reason, owner draw should work only in report mode */
1397 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1402 item.iItem = infoPtr->nFocusedItem;
1404 item.mask = LVIF_PARAM;
1405 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1407 ZeroMemory(&dis, sizeof(dis));
1408 dis.CtlType = ODT_LISTVIEW;
1409 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1410 dis.itemID = item.iItem;
1411 dis.itemAction = ODA_FOCUS;
1412 if (fShow) dis.itemState |= ODS_FOCUS;
1413 dis.hwndItem = infoPtr->hwndSelf;
1415 if (!LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem)) goto done;
1416 dis.itemData = item.lParam;
1418 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1422 DrawFocusRect(hdc, &infoPtr->rcFocus);
1425 ReleaseDC(infoPtr->hwndSelf, hdc);
1429 * Invalidates all visible selected items.
1431 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1435 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1436 while(iterator_next(&i))
1438 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1439 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1441 iterator_destroy(&i);
1447 * Prints a message for unsupported window styles.
1448 * A kind of TODO list for window styles.
1451 * [I] LONG : window style
1456 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1458 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1459 FIXME(" LVS_NOSCROLL\n");
1461 if (lStyle & LVS_NOLABELWRAP)
1462 FIXME(" LVS_NOLABELWRAP\n");
1464 if (lStyle & LVS_SORTASCENDING)
1465 FIXME(" LVS_SORTASCENDING\n");
1467 if (lStyle & LVS_SORTDESCENDING)
1468 FIXME(" LVS_SORTDESCENDING\n");
1473 * DESCRIPTION: [INTERNAL]
1474 * Computes an item's (left,top) corner, relative to rcView.
1475 * That is, the position has NOT been made relative to the Origin.
1476 * This is deliberate, to avoid computing the Origin over, and
1477 * over again, when this function is call in a loop. Instead,
1478 * one ca factor the computation of the Origin before the loop,
1479 * and offset the value retured by this function, on every iteration.
1482 * [I] infoPtr : valid pointer to the listview structure
1483 * [I] nItem : item number
1484 * [O] lpptOrig : item top, left corner
1487 * TRUE if computations OK
1490 static BOOL LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1492 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1494 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
1496 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1498 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1499 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1501 else if (uView == LVS_LIST)
1503 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1504 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1505 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1507 else /* LVS_REPORT */
1509 lpptPosition->x = REPORT_MARGINX;
1510 lpptPosition->y = nItem * infoPtr->nItemHeight;
1517 * DESCRIPTION: [INTERNAL]
1518 * Compute the rectangles of an item. This is to localize all
1519 * the computations in one place. If you are not interested in some
1520 * of these values, simply pass in a NULL -- the fucntion is smart
1521 * enough to compute only what's necessary. The function computes
1522 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1523 * one, the BOX rectangle. This rectangle is very cheap to compute,
1524 * and is guaranteed to contain all the other rectangles. Computing
1525 * the ICON rect is also cheap, but all the others are potentaily
1526 * expensive. This gives an easy and effective optimization when
1527 * searching (like point inclusion, or rectangle intersection):
1528 * first test against the BOX, and if TRUE, test agains the desired
1530 * If the function does not have all the necessary information
1531 * to computed the requested rectangles, will crash with a
1532 * failed assertion. This is done so we catch all programming
1533 * errors, given that the function is called only from our code.
1535 * We have the following 'special' meanings for a few fields:
1536 * * If LVIS_FOCUSED is set, we assume the item has the focus
1537 * This is important in ICON mode, where it might get a larger
1538 * then usual rectange
1540 * Please note that subitem support works only in REPORT mode.
1543 * [I] infoPtr : valid pointer to the listview structure
1544 * [I] lpLVItem : item to compute the measures for
1545 * [O] lprcBox : ptr to Box rectangle
1546 * The internal LVIR_BOX rectangle
1547 * [0] lprcState : ptr to State icon rectangle
1548 * The internal LVIR_STATE rectangle
1549 * [O] lprcIcon : ptr to Icon rectangle
1550 * Same as LVM_GETITEMRECT with LVIR_ICON
1551 * [O] lprcLabel : ptr to Label rectangle
1552 * Same as LVM_GETITEMRECT with LVIR_LABEL
1555 * TRUE if computations OK
1558 static BOOL LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem,
1559 LPRECT lprcBox, LPRECT lprcState,
1560 LPRECT lprcIcon, LPRECT lprcLabel)
1562 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1563 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1564 RECT Box, State, Icon, Label;
1566 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1568 /* Be smart and try to figure out the minimum we have to do */
1569 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1570 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1572 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1573 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1575 if (lprcLabel) doLabel = TRUE;
1576 if (doLabel || lprcIcon) doIcon = TRUE;
1577 if (doIcon || lprcState) doState = TRUE;
1579 /************************************************************/
1580 /* compute the box rectangle (it should be cheap to do) */
1581 /************************************************************/
1582 if (lpLVItem->iSubItem)
1584 if (!Header_GetItemRect(infoPtr->hwndHeader, lpLVItem->iSubItem, &Box)) return FALSE;
1589 Box.right = infoPtr->nItemWidth;
1592 Box.bottom = infoPtr->nItemHeight;
1594 /************************************************************/
1595 /* compute STATEICON bounding box */
1596 /************************************************************/
1599 if (uView == LVS_ICON)
1601 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1602 if (infoPtr->himlNormal)
1603 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1604 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1608 /* we need the ident in report mode, if we don't have it, we fail */
1609 State.left = Box.left;
1610 if (uView == LVS_REPORT)
1612 State.left += REPORT_MARGINX;
1613 if (lpLVItem->iSubItem == 0)
1615 assert(lpLVItem->mask & LVIF_INDENT);
1616 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1619 State.top = Box.top;
1621 State.right = State.left;
1622 State.bottom = State.top;
1623 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1625 State.right += infoPtr->iconStateSize.cx;
1626 State.bottom += infoPtr->iconStateSize.cy;
1628 if (lprcState) *lprcState = State;
1629 TRACE(" - state=%s\n", debugrect(&State));
1632 /************************************************************/
1633 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1634 /************************************************************/
1637 if (uView == LVS_ICON)
1639 Icon.left = Box.left;
1640 if (infoPtr->himlNormal)
1641 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1642 Icon.top = Box.top + ICON_TOP_PADDING;
1643 Icon.right = Icon.left;
1644 Icon.bottom = Icon.top;
1645 if (infoPtr->himlNormal)
1647 Icon.right += infoPtr->iconSize.cx;
1648 Icon.bottom += infoPtr->iconSize.cy;
1651 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1653 Icon.left = State.right;
1654 if (!IsRectEmpty(&State)) Icon.left += IMAGE_PADDING;
1656 Icon.right = Icon.left;
1657 /* FIXME: add suport for icons for subitems */
1658 if (infoPtr->himlSmall && lpLVItem->iSubItem == 0) Icon.right += infoPtr->iconSize.cx;
1659 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1661 if(lprcIcon) *lprcIcon = Icon;
1662 TRACE(" - icon=%s\n", debugrect(&Icon));
1665 /************************************************************/
1666 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1667 /************************************************************/
1670 SIZE labelSize = { 0, 0 };
1672 /* calculate how far to the right can the label strech */
1673 Label.right = Box.right;
1674 if (uView == LVS_REPORT)
1676 if (lpLVItem->iSubItem == 0 && !Header_GetItemRect(infoPtr->hwndHeader, 0, &Label)) return FALSE;
1677 Label.right -= REPORT_MARGINX;
1680 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
1682 labelSize.cx = infoPtr->nItemWidth;
1683 labelSize.cy = infoPtr->nItemHeight;
1687 /* we need the text in non owner draw mode */
1688 assert(lpLVItem->mask & LVIF_TEXT);
1689 if (is_textT(lpLVItem->pszText, TRUE))
1691 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1692 HDC hdc = GetDC(infoPtr->hwndSelf);
1693 HFONT hOldFont = SelectObject(hdc, hFont);
1697 /* compute rough rectangle where the label will go */
1698 SetRectEmpty(&rcText);
1699 rcText.right = infoPtr->nItemWidth - TRAILING_PADDING;
1700 rcText.bottom = infoPtr->nItemHeight;
1701 if (uView == LVS_ICON)
1702 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1704 /* now figure out the flags */
1705 if (uView == LVS_ICON)
1706 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1708 uFormat = LV_SL_DT_FLAGS;
1710 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1712 labelSize.cx = min(rcText.right - rcText.left + TRAILING_PADDING, infoPtr->nItemWidth);
1713 labelSize.cy = rcText.bottom - rcText.top;
1715 SelectObject(hdc, hOldFont);
1716 ReleaseDC(infoPtr->hwndSelf, hdc);
1720 if (uView == LVS_ICON)
1722 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1723 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1724 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1725 Label.right = Label.left + labelSize.cx;
1726 Label.bottom = Label.top + infoPtr->nItemHeight;
1727 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1729 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1730 labelSize.cy /= infoPtr->ntmHeight;
1731 labelSize.cy = max(labelSize.cy, 1);
1732 labelSize.cy *= infoPtr->ntmHeight;
1734 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1736 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1738 Label.left = Icon.right;
1739 if (!IsRectEmpty(&Icon) || !IsRectEmpty(&State)) Label.left += IMAGE_PADDING;
1740 Label.top = Box.top;
1741 Label.right = min(Label.left + labelSize.cx, Label.right);
1742 Label.bottom = Label.top + infoPtr->nItemHeight;
1745 if (lprcLabel) *lprcLabel = Label;
1746 TRACE(" - label=%s\n", debugrect(&Label));
1749 /* Fix the Box if necessary */
1752 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1753 else *lprcBox = Box;
1755 TRACE(" - box=%s\n", debugrect(&Box));
1761 * DESCRIPTION: [INTERNAL]
1764 * [I] infoPtr : valid pointer to the listview structure
1765 * [I] nItem : item number
1766 * [O] lprcBox : ptr to Box rectangle
1769 * TRUE if computations OK
1772 static BOOL LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
1774 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1775 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1776 POINT Position, Origin;
1779 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) return FALSE;
1780 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
1782 /* Be smart and try to figure out the minimum we have to do */
1784 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1785 lvItem.mask |= LVIF_TEXT;
1786 lvItem.iItem = nItem;
1787 lvItem.iSubItem = 0;
1788 lvItem.pszText = szDispText;
1789 lvItem.cchTextMax = DISP_TEXT_SIZE;
1790 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
1791 if (uView == LVS_ICON)
1793 lvItem.mask |= LVIF_STATE;
1794 lvItem.stateMask = LVIS_FOCUSED;
1795 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
1797 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0)) return FALSE;
1799 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
1806 * Aligns the items with the top edge of the window.
1809 * [I] infoPtr : valid pointer to the listview structure
1814 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1816 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1817 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1820 INT i, off_x=0, off_y=0;
1822 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1824 /* Since SetItemPosition uses upper-left of icon, and for
1825 style=LVS_ICON the icon is not left adjusted, get the offset */
1826 if (uView == LVS_ICON)
1828 off_y = ICON_TOP_PADDING;
1829 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1833 ZeroMemory(&rcView, sizeof(RECT));
1834 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1836 infoPtr->rcList.left, infoPtr->rcList.right);
1838 if (nListWidth > infoPtr->nItemWidth)
1840 for (i = 0; i < infoPtr->nItemCount; i++)
1842 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1845 ptItem.y += infoPtr->nItemHeight;
1848 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1849 ptItem.x += infoPtr->nItemWidth;
1850 rcView.right = max(rcView.right, ptItem.x);
1853 rcView.right -= off_x;
1854 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1858 for (i = 0; i < infoPtr->nItemCount; i++)
1860 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1861 ptItem.y += infoPtr->nItemHeight;
1864 rcView.right = infoPtr->nItemWidth;
1865 rcView.bottom = ptItem.y-off_y;
1868 infoPtr->rcView = rcView;
1874 * Aligns the items with the left edge of the window.
1877 * [I] infoPtr : valid pointer to the listview structure
1882 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1884 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1885 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1888 INT i, off_x=0, off_y=0;
1890 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1892 /* Since SetItemPosition uses upper-left of icon, and for
1893 style=LVS_ICON the icon is not left adjusted, get the offset */
1894 if (uView == LVS_ICON)
1896 off_y = ICON_TOP_PADDING;
1897 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1901 ZeroMemory(&rcView, sizeof(RECT));
1902 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1904 if (nListHeight > infoPtr->nItemHeight)
1906 for (i = 0; i < infoPtr->nItemCount; i++)
1908 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1911 ptItem.x += infoPtr->nItemWidth;
1914 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1915 ptItem.y += infoPtr->nItemHeight;
1916 rcView.bottom = max(rcView.bottom, ptItem.y);
1919 rcView.right = ptItem.x + infoPtr->nItemWidth;
1923 for (i = 0; i < infoPtr->nItemCount; i++)
1925 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1926 ptItem.x += infoPtr->nItemWidth;
1929 rcView.bottom = infoPtr->nItemHeight;
1930 rcView.right = ptItem.x;
1933 infoPtr->rcView = rcView;
1940 * Retrieves the bounding rectangle of all the items.
1943 * [I] infoPtr : valid pointer to the listview structure
1944 * [O] lprcView : bounding rectangle
1950 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1954 TRACE("(lprcView=%p)\n", lprcView);
1956 if (!lprcView) return FALSE;
1958 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return FALSE;
1960 *lprcView = infoPtr->rcView;
1961 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
1963 TRACE("lprcView=%s\n", debugrect(lprcView));
1970 * Retrieves the subitem pointer associated with the subitem index.
1973 * [I] HDPA : DPA handle for a specific item
1974 * [I] INT : index of subitem
1977 * SUCCESS : subitem pointer
1980 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
1982 LISTVIEW_SUBITEM *lpSubItem;
1985 /* we should binary search here if need be */
1986 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1988 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1989 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
1999 * Calculates the width of a specific item.
2002 * [I] infoPtr : valid pointer to the listview structure
2003 * [I] nItem : item to calculate width, or -1 for max of all
2006 * Returns the width of an item width an item.
2008 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr, INT nItem)
2010 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2011 INT nItemWidth = 0, i;
2013 if (uView == LVS_ICON)
2014 nItemWidth = infoPtr->iconSpacing.cx;
2015 else if (uView == LVS_REPORT)
2017 INT nHeaderItemCount;
2020 /* calculate width of header */
2021 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
2022 for (i = 0; i < nHeaderItemCount; i++)
2023 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem))
2024 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
2030 if (infoPtr->nItemCount == 0) return DEFAULT_COLUMN_WIDTH;
2032 /* get width of string */
2035 for (i = 0; i < infoPtr->nItemCount; i++)
2037 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
2038 nItemWidth = max(nItemWidth, nLabelWidth);
2042 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
2043 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
2044 nItemWidth += WIDTH_PADDING;
2045 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2046 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2047 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
2050 return max(nItemWidth, 1);
2055 * Calculates the max width of any item in the list.
2058 * [I] infoPtr : valid pointer to the listview structure
2059 * [I] LONG : window style
2062 * Returns item width.
2064 static inline INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *infoPtr)
2066 return LISTVIEW_CalculateItemWidth(infoPtr, -1);
2071 * Retrieves and saves important text metrics info for the current
2075 * [I] infoPtr : valid pointer to the listview structure
2078 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2080 HDC hdc = GetDC(infoPtr->hwndSelf);
2081 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2082 HFONT hOldFont = SelectObject(hdc, hFont);
2085 if (GetTextMetricsW(hdc, &tm))
2086 infoPtr->ntmHeight = tm.tmHeight;
2087 SelectObject(hdc, hOldFont);
2088 ReleaseDC(infoPtr->hwndSelf, hdc);
2090 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2096 * Calculates the height of an item.
2099 * [I] infoPtr : valid pointer to the listview structure
2102 * Returns item height.
2104 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *infoPtr)
2108 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
2109 nItemHeight = infoPtr->iconSpacing.cy;
2112 nItemHeight = infoPtr->ntmHeight;
2113 if (infoPtr->himlState)
2114 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2115 if (infoPtr->himlSmall)
2116 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2117 if (infoPtr->himlState || infoPtr->himlSmall)
2118 nItemHeight += HEIGHT_PADDING;
2124 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
2126 ERR("Selections are:\n");
2127 ranges_dump(infoPtr->hdpaSelectionRanges);
2133 * A compare function for ranges
2136 * [I] range1 : pointer to range 1;
2137 * [I] range2 : pointer to range 2;
2141 * >0 : if Item 1 > Item 2
2142 * <0 : if Item 2 > Item 1
2143 * 0 : if Item 1 == Item 2
2145 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2147 if (((RANGE*)range1)->upper < ((RANGE*)range2)->lower)
2149 if (((RANGE*)range2)->upper < ((RANGE*)range1)->lower)
2154 static void ranges_dump(HDPA ranges)
2158 for (i = 0; i < ranges->nItemCount; i++)
2160 RANGE *selection = DPA_GetPtr(ranges, i);
2161 TRACE(" [%d - %d]\n", selection->lower, selection->upper);
2165 static inline BOOL ranges_contain(HDPA ranges, INT nItem)
2167 RANGE srchrng = { nItem, nItem };
2169 TRACE("(nItem=%d)\n", nItem);
2170 if (TRACE_ON(listview)) ranges_dump(ranges);
2171 return DPA_Search(ranges, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2174 static BOOL ranges_shift(HDPA ranges, INT nItem, INT delta, INT nUpper)
2176 RANGE srchrng, *chkrng;
2179 srchrng.upper = nItem;
2180 srchrng.lower = nItem;
2182 index = DPA_Search(ranges, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2183 if (index == -1) return TRUE;
2185 for (;index < ranges->nItemCount; index++)
2187 chkrng = DPA_GetPtr(ranges, index);
2188 if (chkrng->lower >= nItem)
2189 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2190 if (chkrng->upper >= nItem)
2191 chkrng->upper = max(min(chkrng->upper + delta, nUpper - 1), 0);
2196 static BOOL ranges_add(HDPA ranges, RANGE range)
2201 TRACE("range=(%i - %i)\n", range.lower, range.upper);
2202 if (TRACE_ON(listview)) ranges_dump(ranges);
2204 /* try find overlapping regions first */
2205 srchrgn.lower = range.lower - 1;
2206 srchrgn.upper = range.upper + 1;
2207 index = DPA_Search(ranges, &srchrgn, 0, ranges_cmp, 0, 0);
2213 TRACE("Adding new range\n");
2215 /* create the brand new range to insert */
2216 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2217 if(!newrgn) return FALSE;
2220 /* figure out where to insert it */
2221 index = DPA_Search(ranges, newrgn, 0, ranges_cmp, 0, DPAS_INSERTAFTER);
2222 if (index == -1) index = 0;
2224 /* and get it over with */
2225 DPA_InsertPtr(ranges, index, newrgn);
2229 RANGE *chkrgn, *mrgrgn;
2230 INT fromindex, mergeindex;
2232 chkrgn = DPA_GetPtr(ranges, index);
2233 if (!chkrgn) return FALSE;
2234 TRACE("Merge with index %i (%d - %d)\n",
2235 index, chkrgn->lower, chkrgn->upper);
2237 chkrgn->lower = min(range.lower, chkrgn->lower);
2238 chkrgn->upper = max(range.upper, chkrgn->upper);
2240 TRACE("New range %i (%d - %d)\n",
2241 index, chkrgn->lower, chkrgn->upper);
2243 /* merge now common anges */
2245 srchrgn.lower = chkrgn->lower - 1;
2246 srchrgn.upper = chkrgn->upper + 1;
2250 mergeindex = DPA_Search(ranges, &srchrgn, fromindex, ranges_cmp, 0, 0);
2251 if (mergeindex == -1) break;
2252 if (mergeindex == index)
2254 fromindex = index + 1;
2258 TRACE("Merge with index %i\n", mergeindex);
2260 mrgrgn = DPA_GetPtr(ranges, mergeindex);
2261 if (!mrgrgn) return FALSE;
2263 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2264 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2265 COMCTL32_Free(mrgrgn);
2266 DPA_DeletePtr(ranges, mergeindex);
2267 if (mergeindex < index) index --;
2271 if (TRACE_ON(listview)) ranges_dump(ranges);
2275 static BOOL ranges_del(HDPA ranges, RANGE range)
2277 RANGE remrgn, tmprgn, *chkrgn;
2281 TRACE("range: (%d - %d)\n", range.lower, range.upper);
2286 index = DPA_Search(ranges, &remrgn, 0, ranges_cmp, 0, 0);
2287 if (index == -1) return TRUE;
2289 chkrgn = DPA_GetPtr(ranges, index);
2290 if (!chkrgn) return FALSE;
2292 TRACE("Matches range index %i (%d - %d)\n",
2293 index, chkrgn->lower, chkrgn->upper);
2295 /* case 1: Same range */
2296 if ( (chkrgn->upper == remrgn.upper) &&
2297 (chkrgn->lower == remrgn.lower) )
2299 DPA_DeletePtr(ranges, index);
2302 /* case 2: engulf */
2303 else if ( (chkrgn->upper <= remrgn.upper) &&
2304 (chkrgn->lower >= remrgn.lower) )
2306 DPA_DeletePtr(ranges, index);
2308 /* case 3: overlap upper */
2309 else if ( (chkrgn->upper <= remrgn.upper) &&
2310 (chkrgn->lower < remrgn.lower) )
2312 chkrgn->upper = remrgn.lower - 1;
2314 /* case 4: overlap lower */
2315 else if ( (chkrgn->upper > remrgn.upper) &&
2316 (chkrgn->lower >= remrgn.lower) )
2318 chkrgn->lower = remrgn.upper + 1;
2320 /* case 5: fully internal */
2323 RANGE *newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2324 if (!newrgn) return FALSE;
2326 newrgn->lower = chkrgn->lower;
2327 newrgn->upper = remrgn.lower - 1;
2328 chkrgn->lower = remrgn.upper + 1;
2329 DPA_InsertPtr(ranges, index, newrgn);
2340 * Removes all selection ranges
2343 * [I] infoPtr : valid pointer to the listview structure
2344 * [I] nSkipItem : item to skip removing the selection
2350 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr, INT nSkipItem)
2357 if (infoPtr->bRemovingAllSelections) return TRUE;
2359 infoPtr->bRemovingAllSelections = TRUE;
2364 lvItem.stateMask = LVIS_SELECTED;
2366 /* need to clone the DPA because callbacks can change it */
2367 clone = DPA_Clone(infoPtr->hdpaSelectionRanges, NULL);
2368 for ( pos = 0; (sel = DPA_GetPtr(clone, pos)); pos++ )
2370 for(i = sel->lower; i <= sel->upper; i++)
2373 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
2378 infoPtr->bRemovingAllSelections = FALSE;
2385 * Retrieves the number of items that are marked as selected.
2388 * [I] infoPtr : valid pointer to the listview structure
2391 * Number of items selected.
2393 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2395 INT i, nSelectedCount = 0;
2397 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2400 for (i = 0; i < infoPtr->nItemCount; i++)
2402 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2408 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
2410 RANGE *sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
2411 nSelectedCount += sel->upper - sel->lower + 1;
2415 TRACE("nSelectedCount=%d\n", nSelectedCount);
2416 return nSelectedCount;
2421 * Manages the item focus.
2424 * [I] infoPtr : valid pointer to the listview structure
2425 * [I] INT : item index
2428 * TRUE : focused item changed
2429 * FALSE : focused item has NOT changed
2431 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2433 INT oldFocus = infoPtr->nFocusedItem;
2436 lvItem.state = LVIS_FOCUSED;
2437 lvItem.stateMask = LVIS_FOCUSED;
2438 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2440 return oldFocus != infoPtr->nFocusedItem;
2443 /* Helper function for LISTVIEW_ShiftIndices *only* */
2444 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2446 if (nShiftItem < nItem) return nShiftItem;
2448 if (nShiftItem > nItem) return nShiftItem + direction;
2450 if (direction > 0) return nShiftItem + direction;
2452 return min(nShiftItem, infoPtr->nItemCount - 1);
2457 * Updates the various indices after an item has been inserted or deleted.
2460 * [I] infoPtr : valid pointer to the listview structure
2461 * [I] nItem : item index
2462 * [I] direction : Direction of shift, +1 or -1.
2467 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2471 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2473 ranges_shift(infoPtr->hdpaSelectionRanges, nItem, direction, infoPtr->nItemCount);
2475 assert(abs(direction) == 1);
2477 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2479 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2480 if (nNewFocus != infoPtr->nFocusedItem)
2481 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2483 /* But we are not supposed to modify nHotItem! */
2489 * Adds a block of selections.
2492 * [I] infoPtr : valid pointer to the listview structure
2493 * [I] INT : item index
2498 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2500 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2501 INT nLast = max(infoPtr->nSelectionMark, nItem);
2508 item.state = LVIS_SELECTED;
2509 item.stateMask = LVIS_SELECTED;
2511 /* FIXME: this is not correct LVS_OWNERDATA
2512 * See docu for LVN_ITEMCHANGED. Is there something similar for
2513 * RemoveGroupSelection (is there such a thing?)?
2515 for (i = nFirst; i <= nLast; i++)
2516 LISTVIEW_SetItemState(infoPtr,i,&item);
2518 LISTVIEW_SetItemFocus(infoPtr, nItem);
2519 infoPtr->nSelectionMark = nItem;
2525 * Sets a single group selection.
2528 * [I] infoPtr : valid pointer to the listview structure
2529 * [I] INT : item index
2534 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2536 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2542 LISTVIEW_RemoveAllSelections(infoPtr, -1);
2544 item.state = LVIS_SELECTED;
2545 item.stateMask = LVIS_SELECTED;
2547 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2551 if (infoPtr->nSelectionMark == -1)
2552 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2555 nFirst = min(infoPtr->nSelectionMark, nItem);
2556 nLast = max(infoPtr->nSelectionMark, nItem);
2558 for (i = nFirst; i <= nLast; i++)
2559 LISTVIEW_SetItemState(infoPtr, i, &item);
2563 RECT rcItem, rcSelMark;
2566 rcItem.left = LVIR_BOUNDS;
2567 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2568 rcSelMark.left = LVIR_BOUNDS;
2569 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2570 UnionRect(&rcSel, &rcItem, &rcSelMark);
2571 iterator_frameditems(&i, infoPtr, &rcSel);
2572 while(iterator_next(&i))
2574 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
2575 if (PtInRect(&rcSel, ptItem))
2576 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
2578 iterator_destroy(&i);
2581 LISTVIEW_SetItemFocus(infoPtr, nItem);
2586 * Sets a single selection.
2589 * [I] infoPtr : valid pointer to the listview structure
2590 * [I] INT : item index
2595 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2599 TRACE("nItem=%d\n", nItem);
2601 LISTVIEW_RemoveAllSelections(infoPtr, nItem);
2603 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2604 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2605 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2607 infoPtr->nSelectionMark = nItem;
2612 * Set selection(s) with keyboard.
2615 * [I] infoPtr : valid pointer to the listview structure
2616 * [I] INT : item index
2619 * SUCCESS : TRUE (needs to be repainted)
2620 * FAILURE : FALSE (nothing has changed)
2622 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2624 /* FIXME: pass in the state */
2625 LONG lStyle = infoPtr->dwStyle;
2626 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2627 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2628 BOOL bResult = FALSE;
2630 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2632 if (lStyle & LVS_SINGLESEL)
2635 LISTVIEW_SetSelection(infoPtr, nItem);
2642 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2646 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2651 LISTVIEW_SetSelection(infoPtr, nItem);
2654 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2657 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2664 * Called when the mouse is being actively tracked and has hovered for a specified
2668 * [I] infoPtr : valid pointer to the listview structure
2669 * [I] fwKeys : key indicator
2670 * [I] pts : mouse position
2673 * 0 if the message was processed, non-zero if there was an error
2676 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2677 * over the item for a certain period of time.
2680 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2682 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2683 /* FIXME: select the item!!! */
2684 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2691 * Called whenever WM_MOUSEMOVE is received.
2694 * [I] infoPtr : valid pointer to the listview structure
2695 * [I] fwKeys : key indicator
2696 * [I] pts : mouse position
2699 * 0 if the message is processed, non-zero if there was an error
2701 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2703 TRACKMOUSEEVENT trackinfo;
2705 /* see if we are supposed to be tracking mouse hovering */
2706 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2707 /* fill in the trackinfo struct */
2708 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2709 trackinfo.dwFlags = TME_QUERY;
2710 trackinfo.hwndTrack = infoPtr->hwndSelf;
2711 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2713 /* see if we are already tracking this hwnd */
2714 _TrackMouseEvent(&trackinfo);
2716 if(!(trackinfo.dwFlags & TME_HOVER)) {
2717 trackinfo.dwFlags = TME_HOVER;
2719 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2720 _TrackMouseEvent(&trackinfo);
2729 * Tests wheather the item is assignable to a list with style lStyle
2731 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2733 if ( (lpLVItem->mask & LVIF_TEXT) &&
2734 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2735 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2742 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2745 * [I] infoPtr : valid pointer to the listview structure
2746 * [I] lpLVItem : valid pointer to new item atttributes
2747 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2748 * [O] bChanged : will be set to TRUE if the item really changed
2754 static BOOL set_owner_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged)
2756 LONG lStyle = infoPtr->dwStyle;
2760 /* a virtual listview stores only the state for the main item */
2761 if (lpLVItem->iSubItem || !(lpLVItem->mask & LVIF_STATE)) return FALSE;
2763 oldState = (LVIS_FOCUSED | LVIS_SELECTED) & ~infoPtr->uCallbackMask;
2764 if (oldState) oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, oldState);
2765 TRACE("oldState=%x, newState=%x\n", oldState, lpLVItem->state);
2767 /* we're done if we don't need to change anything we handle */
2768 if ( !((oldState ^ lpLVItem->state) & lpLVItem->stateMask &
2769 ~infoPtr->uCallbackMask & (LVIS_FOCUSED | LVIS_SELECTED))) return TRUE;
2774 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2775 * by LVS_OWERNDATA list controls
2778 /* if we handle the focus, and we're asked to change it, do it now */
2779 if ( lpLVItem->stateMask & LVIS_FOCUSED )
2781 if (lpLVItem->state & LVIS_FOCUSED)
2782 infoPtr->nFocusedItem = lpLVItem->iItem;
2783 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2784 infoPtr->nFocusedItem = -1;
2787 /* and the selection is the only other state a virtual list may hold */
2788 if (lpLVItem->stateMask & LVIS_SELECTED)
2790 RANGE range = { lpLVItem->iItem, lpLVItem->iItem };
2792 if (lpLVItem->state & LVIS_SELECTED)
2794 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr, lpLVItem->iItem);
2795 ranges_add(infoPtr->hdpaSelectionRanges, range);
2798 ranges_del(infoPtr->hdpaSelectionRanges, range);
2801 /* notify the parent now that things have changed */
2802 ZeroMemory(&nmlv, sizeof(nmlv));
2803 nmlv.iItem = lpLVItem->iItem;
2804 nmlv.uNewState = lpLVItem->state;
2805 nmlv.uOldState = oldState;
2806 nmlv.uChanged = LVIF_STATE;
2807 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2814 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2817 * [I] infoPtr : valid pointer to the listview structure
2818 * [I] lpLVItem : valid pointer to new item atttributes
2819 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2820 * [O] bChanged : will be set to TRUE if the item really changed
2826 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged)
2828 LONG lStyle = infoPtr->dwStyle;
2830 LISTVIEW_ITEM *lpItem;
2832 UINT uChanged = 0, oldState;
2834 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2835 if (!hdpaSubItems && hdpaSubItems != (HDPA)-1) return FALSE;
2837 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2838 if (!lpItem) return FALSE;
2840 /* we need to handle the focus, and selection differently */
2841 oldState = (LVIS_FOCUSED | LVIS_SELECTED) & ~infoPtr->uCallbackMask;
2842 if (oldState) oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, oldState);
2844 TRACE("oldState=0x%x, state=0x%x\n", oldState, lpItem->state);
2845 /* determine what fields will change */
2846 if ((lpLVItem->mask & LVIF_STATE) && ((oldState ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
2847 uChanged |= LVIF_STATE;
2849 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2850 uChanged |= LVIF_IMAGE;
2852 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2853 uChanged |= LVIF_PARAM;
2855 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2856 uChanged |= LVIF_INDENT;
2858 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2859 uChanged |= LVIF_TEXT;
2861 TRACE("uChanged=0x%x\n", uChanged);
2862 if (!uChanged) return TRUE;
2865 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2866 nmlv.iItem = lpLVItem->iItem;
2867 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2868 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2869 nmlv.uChanged = uChanged;
2870 nmlv.lParam = lpItem->lParam;
2872 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2873 if(lpItem->valid && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
2876 /* copy information */
2877 if (lpLVItem->mask & LVIF_TEXT)
2878 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
2880 if (lpLVItem->mask & LVIF_IMAGE)
2881 lpItem->hdr.iImage = lpLVItem->iImage;
2883 if (lpLVItem->mask & LVIF_PARAM)
2884 lpItem->lParam = lpLVItem->lParam;
2886 if (lpLVItem->mask & LVIF_INDENT)
2887 lpItem->iIndent = lpLVItem->iIndent;
2889 if (uChanged & LVIF_STATE)
2891 RANGE range = { lpLVItem->iItem, lpLVItem->iItem };
2893 lpItem->state &= ~lpLVItem->stateMask;
2894 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2895 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
2897 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr, lpLVItem->iItem);
2898 ranges_add(infoPtr->hdpaSelectionRanges, range);
2900 else if (lpLVItem->stateMask & LVIS_SELECTED)
2901 ranges_del(infoPtr->hdpaSelectionRanges, range);
2903 /* if we are asked to change focus, and we manage it, do it */
2904 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
2906 if (lpLVItem->state & LVIS_FOCUSED)
2908 infoPtr->nFocusedItem = lpLVItem->iItem;
2909 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
2911 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2912 infoPtr->nFocusedItem = -1;
2916 /* if we're inserting the item, we're done */
2917 if (!lpItem->valid) return TRUE;
2919 /* send LVN_ITEMCHANGED notification */
2920 nmlv.lParam = lpItem->lParam;
2921 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2928 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2931 * [I] infoPtr : valid pointer to the listview structure
2932 * [I] lpLVItem : valid pointer to new subitem atttributes
2933 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2934 * [O] bChanged : will be set to TRUE if the item really changed
2940 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW, BOOL *bChanged)
2943 LISTVIEW_SUBITEM *lpSubItem;
2945 /* set subitem only if column is present */
2946 if (Header_GetItemCount(infoPtr->hwndHeader) <= lpLVItem->iSubItem)
2949 /* First do some sanity checks */
2950 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
2951 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
2953 /* get the subitem structure, and create it if not there */
2954 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2955 if (!hdpaSubItems) return FALSE;
2957 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
2960 LISTVIEW_SUBITEM *tmpSubItem;
2963 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2964 if (!lpSubItem) return FALSE;
2965 /* we could binary search here, if need be...*/
2966 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2968 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2969 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
2971 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
2973 COMCTL32_Free(lpSubItem);
2976 lpSubItem->iSubItem = lpLVItem->iSubItem;
2980 if (lpLVItem->mask & LVIF_IMAGE)
2981 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
2983 lpSubItem->hdr.iImage = lpLVItem->iImage;
2987 if (lpLVItem->mask & LVIF_TEXT)
2988 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
2990 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
2999 * Sets item attributes.
3002 * [I] infoPtr : valid pointer to the listview structure
3003 * [I] LPLVITEM : new item atttributes
3004 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3010 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
3012 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3013 INT nOldFocus = infoPtr->nFocusedItem;
3014 LPWSTR pszText = NULL;
3015 BOOL bResult, bChanged = FALSE;
3017 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3019 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3022 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3023 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3025 pszText = lpLVItem->pszText;
3026 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3029 /* actually set the fields */
3030 if (infoPtr->dwStyle & LVS_OWNERDATA)
3031 bResult = set_owner_item(infoPtr, lpLVItem, TRUE, &bChanged);
3034 /* sanity checks first */
3035 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3037 if (lpLVItem->iSubItem)
3038 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3040 bResult = set_main_item(infoPtr, lpLVItem, TRUE, &bChanged);
3043 /* redraw item, if necessary */
3044 if (bChanged && !infoPtr->bIsDrawing)
3046 if (nOldFocus != infoPtr->nFocusedItem && infoPtr->bFocus)
3047 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->rcFocus);
3049 /* this little optimization eliminates some nasty flicker */
3050 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3051 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3052 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3054 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3059 textfreeT(lpLVItem->pszText, isW);
3060 lpLVItem->pszText = pszText;
3068 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3071 * [I] infoPtr : valid pointer to the listview structure
3076 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3078 LONG lStyle = infoPtr->dwStyle;
3079 UINT uView = lStyle & LVS_TYPEMASK;
3081 SCROLLINFO scrollInfo;
3083 scrollInfo.cbSize = sizeof(SCROLLINFO);
3084 scrollInfo.fMask = SIF_POS;
3086 if (uView == LVS_LIST)
3088 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3089 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3091 else if (uView == LVS_REPORT)
3093 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3094 nItem = scrollInfo.nPos;
3098 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3099 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3102 TRACE("nItem=%d\n", nItem);
3110 * Erases the background of the given rectangle
3113 * [I] infoPtr : valid pointer to the listview structure
3114 * [I] hdc : device context handle
3115 * [I] lprcBox : clipping rectangle
3121 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
3123 if (!infoPtr->hBkBrush) return FALSE;
3125 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3127 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3135 * [I] infoPtr : valid pointer to the listview structure
3136 * [I] hdc : device context handle
3137 * [I] nItem : item index
3138 * [I] nSubItem : subitem index
3139 * [I] pos : item position in client coordinates
3140 * [I] cdmode : custom draw mode
3146 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3148 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3149 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3150 DWORD cditemmode = CDRF_DODEFAULT;
3151 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3152 NMLVCUSTOMDRAW nmlvcd;
3156 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, debugpoint(&pos));
3158 /* get information needed for drawing the item */
3159 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3160 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3161 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3162 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3163 lvItem.iItem = nItem;
3164 lvItem.iSubItem = nSubItem;
3165 lvItem.cchTextMax = DISP_TEXT_SIZE;
3166 lvItem.pszText = szDispText;
3167 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3168 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3170 /* now check if we need to update the focus rectangle */
3171 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3173 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3174 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel)) return FALSE;
3175 OffsetRect(&rcBox, pos.x, pos.y);
3176 OffsetRect(&rcState, pos.x, pos.y);
3177 OffsetRect(&rcIcon, pos.x, pos.y);
3178 OffsetRect(&rcLabel, pos.x, pos.y);
3179 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3180 debugrect(&rcBox), debugrect(&rcState), debugrect(&rcIcon), debugrect(&rcLabel));
3182 /* fill in the custom draw structure */
3183 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox);
3184 nmlvcd.nmcd.dwItemSpec = lvItem.iItem;
3185 nmlvcd.iSubItem = lvItem.iSubItem;
3186 if (lvItem.state & LVIS_SELECTED) nmlvcd.nmcd.uItemState |= CDIS_SELECTED;
3187 if (lvItem.state & LVIS_FOCUSED) nmlvcd.nmcd.uItemState |= CDIS_FOCUS;
3188 if (lvItem.iItem == infoPtr->nHotItem) nmlvcd.nmcd.uItemState |= CDIS_HOT;
3189 nmlvcd.nmcd.lItemlParam = lvItem.lParam;
3190 if ((lvItem.state & LVIS_SELECTED) &&
3191 (lvItem.iSubItem == 0 ||
3192 (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))))
3194 if (infoPtr->bFocus)
3196 nmlvcd.clrTextBk = comctl32_color.clrHighlight;
3197 nmlvcd.clrText = comctl32_color.clrHighlightText;
3199 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
3201 nmlvcd.clrTextBk = comctl32_color.clr3dFace;
3202 nmlvcd.clrText = comctl32_color.clrBtnText;
3206 if (cdmode & CDRF_NOTIFYITEMDRAW)
3207 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3208 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3211 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3213 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3215 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3219 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3220 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3221 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3222 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3224 /* Don't bother painting item being edited */
3225 if (infoPtr->hwndEdit && lprcFocus && nSubItem == 0) goto postpaint;
3227 /* Set the text attributes */
3228 SetBkMode(hdc, nmlvcd.clrTextBk == CLR_NONE ? TRANSPARENT : OPAQUE);
3229 if (nmlvcd.clrTextBk != CLR_NONE)
3230 SetBkColor(hdc, nmlvcd.clrTextBk == CLR_DEFAULT ? infoPtr->clrTextBkDefault : nmlvcd.clrTextBk);
3231 SetTextColor(hdc, nmlvcd.clrText);
3233 /* draw the selection background, if we're drawing the main item */
3237 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3238 rcSelect.right = rcBox.right;
3240 if (lvItem.state & LVIS_SELECTED)
3241 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3242 if(lprcFocus) *lprcFocus = rcSelect;
3245 /* figure out the text drawing flags */
3246 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3247 if (uView == LVS_ICON)
3248 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3251 INT align = DT_LEFT;
3256 lvColumn.mask = LVCF_FMT;
3257 LISTVIEW_GetColumnT(infoPtr, nSubItem, &lvColumn, TRUE);
3258 TRACE("lvColumn=%s\n", debuglvcolumn_t(&lvColumn, TRUE));
3259 if (lvColumn.fmt & LVCFMT_RIGHT) align = DT_RIGHT;
3260 else if (lvColumn.fmt & LVCFMT_CENTER) align = DT_CENTER;
3264 if (!(uFormat & (DT_RIGHT | DT_CENTER))) rcLabel.left += 2;
3265 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3268 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3269 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3275 * Draws listview items when in owner draw mode.
3278 * [I] infoPtr : valid pointer to the listview structure
3279 * [I] hdc : device context handle
3284 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
3286 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3287 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3288 POINT Origin, Position;
3295 ZeroMemory(&dis, sizeof(dis));
3297 /* Get scroll info once before loop */
3298 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3300 /* figure out what we need to draw */
3301 iterator_visibleitems(&i, infoPtr, hdc);
3303 /* send cache hint notification */
3304 if (infoPtr->dwStyle & LVS_OWNERDATA)
3306 RANGE range = iterator_range(&i);
3309 nmlv.iFrom = range.lower;
3310 nmlv.iTo = range.upper;
3311 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3314 /* iterate through the invalidated rows */
3315 while(iterator_prev(&i))
3317 item.iItem = i.nItem;
3319 item.mask = LVIF_PARAM | LVIF_STATE;
3320 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3321 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3323 dis.CtlType = ODT_LISTVIEW;
3325 dis.itemID = item.iItem;
3326 dis.itemAction = ODA_DRAWENTIRE;
3328 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3329 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3330 dis.hwndItem = infoPtr->hwndSelf;
3332 if (!LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position)) continue;
3333 dis.rcItem.left = Position.x + Origin.x;
3334 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3335 dis.rcItem.top = Position.y + Origin.y;
3336 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3337 dis.itemData = item.lParam;
3339 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3340 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3342 iterator_destroy(&i);
3347 * Draws listview items when in report display mode.
3350 * [I] infoPtr : valid pointer to the listview structure
3351 * [I] hdc : device context handle
3352 * [I] cdmode : custom draw mode
3357 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3359 INT rgntype, nColumnCount, nFirstCol, nLastCol, nCol;
3361 COLUMNCACHE *lpCols;
3362 POINT Origin, Position;
3367 /* figure out what to draw */
3368 rgntype = GetClipBox(hdc, &rcClip);
3369 if (rgntype == NULLREGION) return;
3371 /* cache column info */
3372 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3373 lpCols = COMCTL32_Alloc(nColumnCount * sizeof(COLUMNCACHE));
3374 if (!lpCols) return;
3375 for (nCol = 0; nCol < nColumnCount; nCol++)
3377 Header_GetItemRect(infoPtr->hwndHeader, nCol, &lpCols[nCol].rc);
3378 TRACE("lpCols[%d].rc=%s\n", nCol, debugrect(&lpCols[nCol].rc));
3381 /* Get scroll info once before loop */
3382 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3384 /* we now narrow the columns as well */
3385 nLastCol = nColumnCount - 1;
3386 for(nFirstCol = 0; nFirstCol < nColumnCount; nFirstCol++)
3387 if (lpCols[nFirstCol].rc.right + Origin.x >= rcClip.left) break;
3388 for(nLastCol = nColumnCount - 1; nLastCol >= 0; nLastCol--)
3389 if (lpCols[nLastCol].rc.left + Origin.x < rcClip.right) break;
3391 /* figure out what we need to draw */
3392 iterator_visibleitems(&i, infoPtr, hdc);
3394 /* a last few bits before we start drawing */
3395 TRACE("Colums=(%di - %d)\n", nFirstCol, nLastCol);
3397 /* iterate through the invalidated rows */
3398 while(iterator_prev(&i))
3400 /* iterate through the invalidated columns */
3401 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
3403 if (!LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position)) continue;
3404 Position.x += Origin.x;
3405 Position.y += Origin.y;
3407 if (rgntype == COMPLEXREGION)
3410 rcItem.left = Position.x + lpCols[nCol].rc.left;
3411 rcItem.right = rcItem.left + (lpCols[nCol].rc.right - lpCols[nCol].rc.left);
3412 rcItem.top = Position.y;
3413 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3414 if (!RectVisible(hdc, &rcItem)) continue;
3417 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, nCol, Position, cdmode);
3420 iterator_destroy(&i);
3422 /* cleanup the mess */
3423 COMCTL32_Free(lpCols);
3428 * Draws listview items when in list display mode.
3431 * [I] infoPtr : valid pointer to the listview structure
3432 * [I] hdc : device context handle
3433 * [I] cdmode : custom draw mode
3438 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3440 POINT Origin, Position;
3443 /* Get scroll info once before loop */
3444 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3446 /* figure out what we need to draw */
3447 iterator_visibleitems(&i, infoPtr, hdc);
3449 while(iterator_prev(&i))
3451 if (!LISTVIEW_GetItemOrigin(infoPtr, i.nItem, &Position)) continue;
3452 Position.x += Origin.x;
3453 Position.y += Origin.y;
3455 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, 0, Position, cdmode);
3457 iterator_destroy(&i);
3463 * Draws listview items.
3466 * [I] infoPtr : valid pointer to the listview structure
3467 * [I] HDC : device context handle
3472 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3474 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3475 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3476 NMLVCUSTOMDRAW nmlvcd;
3482 LISTVIEW_DUMP(infoPtr);
3484 infoPtr->bIsDrawing = TRUE;
3486 /* save dc values we're gonna trash while drawing */
3487 hOldFont = SelectObject(hdc, infoPtr->hFont);
3488 oldBkMode = GetBkMode(hdc);
3489 infoPtr->clrTextBkDefault = GetBkColor(hdc);
3490 oldTextColor = GetTextColor(hdc);
3492 oldClrTextBk = infoPtr->clrTextBk;
3493 oldClrText = infoPtr->clrText;
3495 GetClientRect(infoPtr->hwndSelf, &rcClient);
3496 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient);
3497 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3498 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3500 /* Use these colors to draw the items */
3501 infoPtr->clrTextBk = nmlvcd.clrTextBk;
3502 infoPtr->clrText = nmlvcd.clrText;
3504 /* nothing to draw */
3505 if(infoPtr->nItemCount == 0) goto enddraw;
3507 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3508 LISTVIEW_RefreshOwnerDraw(infoPtr, hdc);
3511 if (uView == LVS_REPORT)
3512 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3513 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
3514 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3516 /* if we have a focus rect, draw it */
3517 if (infoPtr->bFocus)
3518 DrawFocusRect(hdc, &infoPtr->rcFocus);
3522 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3523 notify_customdraw(infoPtr, CDDS_POSTPAINT, &nmlvcd);
3525 infoPtr->clrTextBk = oldClrTextBk;
3526 infoPtr->clrText = oldClrText;
3528 SelectObject(hdc, hOldFont);
3529 SetBkMode(hdc, oldBkMode);
3530 SetBkColor(hdc, infoPtr->clrTextBkDefault);
3531 SetTextColor(hdc, oldTextColor);
3532 infoPtr->bIsDrawing = FALSE;
3538 * Calculates the approximate width and height of a given number of items.
3541 * [I] infoPtr : valid pointer to the listview structure
3542 * [I] INT : number of items
3547 * Returns a DWORD. The width in the low word and the height in high word.
3549 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3550 WORD wWidth, WORD wHeight)
3552 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3553 INT nItemCountPerColumn = 1;
3554 INT nColumnCount = 0;
3555 DWORD dwViewRect = 0;
3557 if (nItemCount == -1)
3558 nItemCount = infoPtr->nItemCount;
3560 if (uView == LVS_LIST)
3562 if (wHeight == 0xFFFF)
3564 /* use current height */
3565 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3568 if (wHeight < infoPtr->nItemHeight)
3569 wHeight = infoPtr->nItemHeight;
3573 if (infoPtr->nItemHeight > 0)
3575 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3576 if (nItemCountPerColumn == 0)
3577 nItemCountPerColumn = 1;
3579 if (nItemCount % nItemCountPerColumn != 0)
3580 nColumnCount = nItemCount / nItemCountPerColumn;
3582 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3586 /* Microsoft padding magic */
3587 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3588 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3590 dwViewRect = MAKELONG(wWidth, wHeight);
3592 else if (uView == LVS_REPORT)
3593 FIXME("uView == LVS_REPORT: not implemented\n");
3594 else if (uView == LVS_SMALLICON)
3595 FIXME("uView == LVS_SMALLICON: not implemented\n");
3596 else if (uView == LVS_ICON)
3597 FIXME("uView == LVS_ICON: not implemented\n");
3604 * Arranges listview items in icon display mode.
3607 * [I] infoPtr : valid pointer to the listview structure
3608 * [I] INT : alignment code
3614 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3616 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3617 BOOL bResult = FALSE;
3619 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3624 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3627 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3630 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3632 case LVA_SNAPTOGRID:
3633 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3641 /* << LISTVIEW_CreateDragImage >> */
3646 * Removes all listview items and subitems.
3649 * [I] infoPtr : valid pointer to the listview structure
3655 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3657 LONG lStyle = infoPtr->dwStyle;
3658 UINT uView = lStyle & LVS_TYPEMASK;
3659 LISTVIEW_ITEM *lpItem;
3660 LISTVIEW_SUBITEM *lpSubItem;
3663 BOOL bResult = FALSE;
3668 LISTVIEW_RemoveAllSelections(infoPtr, -1);
3669 infoPtr->nSelectionMark=-1;
3670 infoPtr->nFocusedItem=-1;
3671 SetRectEmpty(&infoPtr->rcFocus);
3672 /* But we are supposed to leave nHotItem as is! */
3674 if (lStyle & LVS_OWNERDATA)
3676 infoPtr->nItemCount = 0;
3677 LISTVIEW_InvalidateList(infoPtr);
3681 if (infoPtr->nItemCount > 0)
3685 /* send LVN_DELETEALLITEMS notification */
3686 /* verify if subsequent LVN_DELETEITEM notifications should be
3688 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3690 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3692 for (i = 0; i < infoPtr->nItemCount; i++)
3694 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3695 if (hdpaSubItems != NULL)
3697 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3699 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3700 if (lpSubItem != NULL)
3702 /* free subitem string */
3703 if (is_textW(lpSubItem->hdr.pszText))
3704 COMCTL32_Free(lpSubItem->hdr.pszText);
3707 COMCTL32_Free(lpSubItem);
3711 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3716 /* send LVN_DELETEITEM notification */
3718 nmlv.lParam = lpItem->lParam;
3719 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3722 /* free item string */
3723 if (is_textW(lpItem->hdr.pszText))
3724 COMCTL32_Free(lpItem->hdr.pszText);
3727 COMCTL32_Free(lpItem);
3730 DPA_Destroy(hdpaSubItems);
3734 /* reinitialize listview memory */
3735 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3736 infoPtr->nItemCount = 0;
3737 DPA_DeleteAllPtrs(infoPtr->hdpaPosX);
3738 DPA_DeleteAllPtrs(infoPtr->hdpaPosY);
3740 /* align items (set position of each item) */
3741 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3743 if (lStyle & LVS_ALIGNLEFT)
3745 LISTVIEW_AlignLeft(infoPtr);
3749 LISTVIEW_AlignTop(infoPtr);
3753 LISTVIEW_UpdateScroll(infoPtr);
3755 LISTVIEW_InvalidateList(infoPtr);
3763 * Removes a column from the listview control.
3766 * [I] infoPtr : valid pointer to the listview structure
3767 * [I] INT : column index
3773 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3775 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3778 TRACE("nColumn=%d\n", nColumn);
3780 if (nColumn <= 0) return FALSE;
3782 if (uView == LVS_REPORT)
3784 if (!Header_GetItemRect(infoPtr->hwndHeader, nColumn, &rcCol))
3787 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3791 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3793 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3795 INT nItem, nSubItem, i;
3797 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3799 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3800 if (!hdpaSubItems) continue;
3803 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3805 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3806 if (!lpSubItem) break;
3807 if (lpSubItem->iSubItem == nColumn)
3810 lpDelItem = lpSubItem;
3812 else if (lpSubItem->iSubItem > nColumn)
3814 lpSubItem->iSubItem--;
3818 /* if we found our subitem, zapp it */
3822 if (is_textW(lpDelItem->hdr.pszText))
3823 COMCTL32_Free(lpDelItem->hdr.pszText);
3826 COMCTL32_Free(lpDelItem);
3828 /* free dpa memory */
3829 DPA_DeletePtr(hdpaSubItems, nSubItem);
3834 /* we need to worry about display issues in report mode only */
3835 if (uView != LVS_REPORT) return TRUE;
3837 /* if we have a focus, must first erase the focus rect */
3838 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
3840 /* Need to reset the item width when deleting a column */
3841 infoPtr->nItemWidth -= rcCol.right - rcCol.left;
3843 /* update scrollbar(s) */
3844 LISTVIEW_UpdateScroll(infoPtr);
3846 /* scroll to cover the deleted column, and invalidate for redraw */
3847 rcOld = infoPtr->rcList;
3848 rcOld.left = rcCol.left;
3849 ScrollWindowEx(infoPtr->hwndSelf, -(rcCol.right - rcCol.left), 0,
3850 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3852 /* we can restore focus now */
3853 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
3860 * Removes an item from the listview control.
3863 * [I] infoPtr : valid pointer to the listview structure
3864 * [I] INT : item index
3870 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
3872 LONG lStyle = infoPtr->dwStyle;
3873 UINT uView = lStyle & LVS_TYPEMASK;
3875 BOOL bResult = FALSE;
3877 LISTVIEW_ITEM *lpItem;
3878 LISTVIEW_SUBITEM *lpSubItem;
3882 TRACE("(nItem=%d)\n", nItem);
3885 /* First, send LVN_DELETEITEM notification. */
3886 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
3888 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3890 if (nItem == infoPtr->nFocusedItem)
3892 infoPtr->nFocusedItem = -1;
3893 SetRectEmpty(&infoPtr->rcFocus);
3896 /* remove it from the selection range */
3897 item.state = LVIS_SELECTED;
3898 item.stateMask = LVIS_SELECTED;
3899 LISTVIEW_SetItemState(infoPtr,nItem,&item);
3901 if (lStyle & LVS_OWNERDATA)
3903 infoPtr->nItemCount--;
3904 LISTVIEW_InvalidateList(infoPtr); /*FIXME: optimize */
3908 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3910 /* initialize memory */
3911 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3913 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
3914 if (hdpaSubItems != NULL)
3916 infoPtr->nItemCount--;
3917 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3919 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3920 if (lpSubItem != NULL)
3922 /* free item string */
3923 if (is_textW(lpSubItem->hdr.pszText))
3924 COMCTL32_Free(lpSubItem->hdr.pszText);
3927 COMCTL32_Free(lpSubItem);
3931 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3934 /* free item string */
3935 if (is_textW(lpItem->hdr.pszText))
3936 COMCTL32_Free(lpItem->hdr.pszText);
3939 COMCTL32_Free(lpItem);
3942 bResult = DPA_Destroy(hdpaSubItems);
3943 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
3944 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
3947 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
3949 /* align items (set position of each item) */
3950 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
3952 if (lStyle & LVS_ALIGNLEFT)
3953 LISTVIEW_AlignLeft(infoPtr);
3955 LISTVIEW_AlignTop(infoPtr);
3958 LISTVIEW_UpdateScroll(infoPtr);
3960 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
3969 * Callback implementation for editlabel control
3972 * [I] infoPtr : valid pointer to the listview structure
3973 * [I] pszText : modified text
3974 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
3980 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
3982 NMLVDISPINFOW dispInfo;
3984 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
3986 ZeroMemory(&dispInfo, sizeof(dispInfo));
3987 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
3988 dispInfo.item.iItem = infoPtr->nEditLabelItem;
3989 dispInfo.item.iSubItem = 0;
3990 dispInfo.item.stateMask = ~0;
3991 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
3992 /* add the text from the edit in */
3993 dispInfo.item.mask |= LVIF_TEXT;
3994 dispInfo.item.pszText = pszText;
3995 dispInfo.item.cchTextMax = textlenT(pszText, isW);
3997 /* Do we need to update the Item Text */
3998 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
3999 if (!pszText) return TRUE;
4001 ZeroMemory(&dispInfo, sizeof(dispInfo));
4002 dispInfo.item.mask = LVIF_TEXT;
4003 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4004 dispInfo.item.iSubItem = 0;
4005 dispInfo.item.pszText = pszText;
4006 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4007 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4012 * Begin in place editing of specified list view item
4015 * [I] infoPtr : valid pointer to the listview structure
4016 * [I] INT : item index
4017 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4023 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4025 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4026 NMLVDISPINFOW dispInfo;
4029 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4031 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4032 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4034 infoPtr->nEditLabelItem = nItem;
4036 /* Is the EditBox still there, if so remove it */
4037 if(infoPtr->hwndEdit != 0)
4039 SetFocus(infoPtr->hwndSelf);
4040 infoPtr->hwndEdit = 0;
4043 LISTVIEW_SetSelection(infoPtr, nItem);
4044 LISTVIEW_SetItemFocus(infoPtr, nItem);
4046 rect.left = LVIR_LABEL;
4047 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4049 ZeroMemory(&dispInfo, sizeof(dispInfo));
4050 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4051 dispInfo.item.iItem = nItem;
4052 dispInfo.item.iSubItem = 0;
4053 dispInfo.item.stateMask = ~0;
4054 dispInfo.item.pszText = szDispText;
4055 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4056 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4058 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4059 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4060 if (!infoPtr->hwndEdit) return 0;
4062 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4064 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4065 infoPtr->hwndEdit = 0;
4069 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4070 SetFocus(infoPtr->hwndEdit);
4071 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4072 return infoPtr->hwndEdit;
4078 * Ensures the specified item is visible, scrolling into view if necessary.
4081 * [I] infoPtr : valid pointer to the listview structure
4082 * [I] nItem : item index
4083 * [I] bPartial : partially or entirely visible
4089 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4091 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4092 INT nScrollPosHeight = 0;
4093 INT nScrollPosWidth = 0;
4094 INT nHorzAdjust = 0;
4095 INT nVertAdjust = 0;
4098 RECT rcItem, rcTemp;
4100 rcItem.left = LVIR_BOUNDS;
4101 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4103 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4105 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4107 /* scroll left/right, but in LVS_REPORT mode */
4108 if (uView == LVS_LIST)
4109 nScrollPosWidth = infoPtr->nItemWidth;
4110 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4111 nScrollPosWidth = 1;
4113 if (rcItem.left < infoPtr->rcList.left)
4116 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4121 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4125 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4127 /* scroll up/down, but not in LVS_LIST mode */
4128 if (uView == LVS_REPORT)
4129 nScrollPosHeight = infoPtr->nItemHeight;
4130 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4131 nScrollPosHeight = 1;
4133 if (rcItem.top < infoPtr->rcList.top)
4136 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4141 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4145 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4147 if (nScrollPosWidth)
4149 INT diff = nHorzDiff / nScrollPosWidth;
4150 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4151 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4154 if (nScrollPosHeight)
4156 INT diff = nVertDiff / nScrollPosHeight;
4157 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4158 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4166 * Searches for an item with specific characteristics.
4169 * [I] hwnd : window handle
4170 * [I] nStart : base item index
4171 * [I] lpFindInfo : item information to look for
4174 * SUCCESS : index of item
4177 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4178 LPLVFINDINFOW lpFindInfo)
4180 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4181 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4182 BOOL bWrap = FALSE, bNearest = FALSE;
4183 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4184 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4185 POINT Position, Destination;
4188 if (!lpFindInfo || nItem < 0) return -1;
4191 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4193 lvItem.mask |= LVIF_TEXT;
4194 lvItem.pszText = szDispText;
4195 lvItem.cchTextMax = DISP_TEXT_SIZE;
4198 if (lpFindInfo->flags & LVFI_WRAP)
4201 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4202 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4206 FIXME("LVFI_NEARESTXY is slow.\n");
4207 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
4208 Destination.x = lpFindInfo->pt.x - Origin.x;
4209 Destination.y = lpFindInfo->pt.y - Origin.y;
4210 switch(lpFindInfo->vkDirection)
4212 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4213 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4214 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4215 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4216 case VK_HOME: Destination.x = Destination.y = 0; break;
4217 case VK_END: Destination.x = infoPtr->rcView.right; Destination.y = infoPtr->rcView.bottom; break;
4218 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4219 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4220 default: FIXME("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4225 /* if LVFI_PARAM is specified, all other flags are ignored */
4226 if (lpFindInfo->flags & LVFI_PARAM)
4228 lvItem.mask |= LVIF_PARAM;
4230 lvItem.mask &= ~LVIF_TEXT;
4234 for (; nItem < nLast; nItem++)
4236 lvItem.iItem = nItem;
4237 lvItem.iSubItem = 0;
4238 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4240 if (lvItem.mask & LVIF_PARAM && lpFindInfo->lParam == lvItem.lParam)
4243 if (lvItem.mask & LVIF_TEXT)
4245 if (lpFindInfo->flags & LVFI_PARTIAL)
4247 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4251 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4255 if (!bNearest) return nItem;
4257 /* This is very inefficient. To do a good job here,
4258 * we need a sorted array of (x,y) item positions */
4259 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) continue;
4261 /* compute the distance^2 to the destination */
4262 xdist = Destination.x - Position.x;
4263 ydist = Destination.y - Position.y;
4264 dist = xdist * xdist + ydist * ydist;
4266 /* remember the distance, and item if it's closer */
4270 nNearestItem = nItem;
4277 nLast = min(nStart + 1, infoPtr->nItemCount);
4282 return nNearestItem;
4287 * Searches for an item with specific characteristics.
4290 * [I] hwnd : window handle
4291 * [I] nStart : base item index
4292 * [I] lpFindInfo : item information to look for
4295 * SUCCESS : index of item
4298 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4299 LPLVFINDINFOA lpFindInfo)
4301 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4305 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4306 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4307 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4308 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4314 * Retrieves the background image of the listview control.
4317 * [I] infoPtr : valid pointer to the listview structure
4318 * [O] LPLVMKBIMAGE : background image attributes
4324 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4326 /* FIXME (listview, "empty stub!\n"); */
4332 * Retrieves column attributes.
4335 * [I] infoPtr : valid pointer to the listview structure
4336 * [I] INT : column index
4337 * [IO] LPLVCOLUMNW : column information
4338 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4339 * otherwise it is in fact a LPLVCOLUMNA
4345 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4348 BOOL bResult = FALSE;
4350 if (lpColumn != NULL)
4353 /* initialize memory */
4354 ZeroMemory(&hdi, sizeof(hdi));
4356 if (lpColumn->mask & LVCF_FMT)
4357 hdi.mask |= HDI_FORMAT;
4359 if (lpColumn->mask & LVCF_WIDTH)
4360 hdi.mask |= HDI_WIDTH;
4362 if (lpColumn->mask & LVCF_TEXT)
4364 hdi.mask |= HDI_TEXT;
4365 hdi.cchTextMax = lpColumn->cchTextMax;
4366 hdi.pszText = lpColumn->pszText;
4369 if (lpColumn->mask & LVCF_IMAGE)
4370 hdi.mask |= HDI_IMAGE;
4372 if (lpColumn->mask & LVCF_ORDER)
4373 hdi.mask |= HDI_ORDER;
4376 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4378 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4382 if (lpColumn->mask & LVCF_FMT)
4386 if (hdi.fmt & HDF_LEFT)
4387 lpColumn->fmt |= LVCFMT_LEFT;
4388 else if (hdi.fmt & HDF_RIGHT)
4389 lpColumn->fmt |= LVCFMT_RIGHT;
4390 else if (hdi.fmt & HDF_CENTER)
4391 lpColumn->fmt |= LVCFMT_CENTER;
4393 if (hdi.fmt & HDF_IMAGE)
4394 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4396 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4397 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4400 if (lpColumn->mask & LVCF_WIDTH)
4401 lpColumn->cx = hdi.cxy;
4403 if (lpColumn->mask & LVCF_IMAGE)
4404 lpColumn->iImage = hdi.iImage;
4406 if (lpColumn->mask & LVCF_ORDER)
4407 lpColumn->iOrder = hdi.iOrder;
4409 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4410 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4419 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4426 /* FIXME: little hack */
4427 for (i = 0; i < iCount; i++)
4435 * Retrieves the column width.
4438 * [I] infoPtr : valid pointer to the listview structure
4439 * [I] int : column index
4442 * SUCCESS : column width
4445 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4447 INT nColumnWidth = 0;
4450 TRACE("nColumn=%d\n", nColumn);
4452 /* we have a 'column' in LIST and REPORT mode only */
4453 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4456 nColumnWidth = infoPtr->nItemWidth;
4459 hdi.mask = HDI_WIDTH;
4460 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4461 nColumnWidth = hdi.cxy;
4465 TRACE("nColumnWidth=%d\n", nColumnWidth);
4466 return nColumnWidth;
4471 * In list or report display mode, retrieves the number of items that can fit
4472 * vertically in the visible area. In icon or small icon display mode,
4473 * retrieves the total number of visible items.
4476 * [I] infoPtr : valid pointer to the listview structure
4479 * Number of fully visible items.
4481 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4483 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4486 if (uView == LVS_LIST)
4488 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4490 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4491 LISTVIEW_GetCountPerColumn(infoPtr);
4494 else if (uView == LVS_REPORT)
4496 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4500 nItemCount = infoPtr->nItemCount;
4509 * Retrieves an image list handle.
4512 * [I] infoPtr : valid pointer to the listview structure
4513 * [I] nImageList : image list identifier
4516 * SUCCESS : image list handle
4519 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4521 HIMAGELIST himl = NULL;
4526 himl = infoPtr->himlNormal;
4529 himl = infoPtr->himlSmall;
4532 himl = infoPtr->himlState;
4536 return (LRESULT)himl;
4539 /* LISTVIEW_GetISearchString */
4543 * Retrieves item attributes.
4546 * [I] hwnd : window handle
4547 * [IO] lpLVItem : item info
4548 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4549 * if FALSE, the lpLVItem is a LPLVITEMA.
4552 * This is the internal 'GetItem' interface -- it tries to
4553 * be smart, and avoids text copies, if possible, by modifing
4554 * lpLVItem->pszText to point to the text string. Please note
4555 * that this is not always possible (e.g. OWNERDATA), so on
4556 * entry you *must* supply valid values for pszText, and cchTextMax.
4557 * The only difference to the documented interface is that upon
4558 * return, you should use *only* the lpLVItem->pszText, rather than
4559 * the buffer pointer you provided on input. Most code already does
4560 * that, so it's not a problem.
4561 * For the two cases when the text must be copied (that is,
4562 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4568 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4570 NMLVDISPINFOW dispInfo;
4571 LISTVIEW_ITEM *lpItem;
4575 /* In the following:
4576 * lpLVItem describes the information requested by the user
4577 * lpItem is what we have
4578 * dispInfo is a structure we use to request the missing
4579 * information from the application
4582 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4584 if (!lpLVItem || (lpLVItem->iItem < 0) ||
4585 (lpLVItem->iItem >= infoPtr->nItemCount))
4588 /* a quick optimization if all we're asked is the focus state
4589 * these queries are worth optimising since they are common,
4590 * and can be answered in constant time, without the heavy accesses */
4591 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIF_STATE) &&
4592 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4594 lpLVItem->state = 0;
4595 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4596 lpLVItem->state |= LVIS_FOCUSED;
4600 ZeroMemory(&dispInfo, sizeof(dispInfo));
4602 /* if the app stores all the data, handle it separately */
4603 if (infoPtr->dwStyle & LVS_OWNERDATA)
4605 dispInfo.item.state = 0;
4607 /* if we need to callback, do it now */
4608 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4610 /* NOTE: copy only fields which we _know_ are initialized, some apps
4611 * depend on the uninitialized fields being 0 */
4612 dispInfo.item.mask = lpLVItem->mask;
4613 dispInfo.item.iItem = lpLVItem->iItem;
4614 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4615 if (lpLVItem->mask & LVIF_TEXT)
4617 dispInfo.item.pszText = lpLVItem->pszText;
4618 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4620 if (lpLVItem->mask & LVIF_STATE)
4621 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4622 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4623 dispInfo.item.stateMask = lpLVItem->stateMask;
4624 *lpLVItem = dispInfo.item;
4625 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4628 /* we store only a little state, so if we're not asked, we're done */
4629 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4631 /* if focus is handled by us, report it */
4632 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4634 lpLVItem->state &= ~LVIS_FOCUSED;
4635 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4636 lpLVItem->state |= LVIS_FOCUSED;
4639 /* and do the same for selection, if we handle it */
4640 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4642 lpLVItem->state &= ~LVIS_SELECTED;
4643 if (ranges_contain(infoPtr->hdpaSelectionRanges, lpLVItem->iItem))
4644 lpLVItem->state |= LVIS_SELECTED;
4650 /* find the item and subitem structures before we proceed */
4651 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4652 if (hdpaSubItems == NULL) return FALSE;
4654 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4657 if (lpLVItem->iSubItem)
4659 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4660 if(!lpSubItem) return FALSE;
4661 pItemHdr = &lpSubItem->hdr;
4664 pItemHdr = &lpItem->hdr;
4666 /* Do we need to query the state from the app? */
4667 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4669 dispInfo.item.mask |= LVIF_STATE;
4670 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4673 /* Do we need to enquire about the image? */
4674 if ((lpLVItem->mask & LVIF_IMAGE) && (pItemHdr->iImage==I_IMAGECALLBACK))
4675 dispInfo.item.mask |= LVIF_IMAGE;
4677 /* Do we need to enquire about the text? */
4678 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4680 dispInfo.item.mask |= LVIF_TEXT;
4681 dispInfo.item.pszText = lpLVItem->pszText;
4682 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4683 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4684 *dispInfo.item.pszText = '\0';
4687 /* If we don't have all the requested info, query the application */
4688 if (dispInfo.item.mask != 0)
4690 dispInfo.item.iItem = lpLVItem->iItem;
4691 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4692 dispInfo.item.lParam = lpItem->lParam;
4693 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4694 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4697 /* Now, handle the iImage field */
4698 if (dispInfo.item.mask & LVIF_IMAGE)
4700 lpLVItem->iImage = dispInfo.item.iImage;
4701 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (pItemHdr->iImage==I_IMAGECALLBACK))
4702 pItemHdr->iImage = dispInfo.item.iImage;
4704 else if (lpLVItem->mask & LVIF_IMAGE)
4705 lpLVItem->iImage = pItemHdr->iImage;
4707 /* The pszText field */
4708 if (dispInfo.item.mask & LVIF_TEXT)
4710 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4711 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4713 lpLVItem->pszText = dispInfo.item.pszText;
4715 else if (lpLVItem->mask & LVIF_TEXT)
4717 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4718 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4721 /* if this is a subitem, we're done*/
4722 if (lpLVItem->iSubItem) return TRUE;
4724 /* Next is the lParam field */
4725 if (dispInfo.item.mask & LVIF_PARAM)
4727 lpLVItem->lParam = dispInfo.item.lParam;
4728 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4729 lpItem->lParam = dispInfo.item.lParam;
4731 else if (lpLVItem->mask & LVIF_PARAM)
4732 lpLVItem->lParam = lpItem->lParam;
4734 /* ... the state field (this one is different due to uCallbackmask) */
4735 if (lpLVItem->mask & LVIF_STATE)
4737 lpLVItem->state = lpItem->state;
4738 if (dispInfo.item.mask & LVIF_STATE)
4740 lpLVItem->state &= ~dispInfo.item.stateMask;
4741 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4743 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4745 lpLVItem->state &= ~LVIS_FOCUSED;
4746 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4747 lpLVItem->state |= LVIS_FOCUSED;
4749 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4751 lpLVItem->state &= ~LVIS_SELECTED;
4752 if (ranges_contain(infoPtr->hdpaSelectionRanges, lpLVItem->iItem))
4753 lpLVItem->state |= LVIS_SELECTED;
4757 /* and last, but not least, the indent field */
4758 if (lpLVItem->mask & LVIF_INDENT)
4759 lpLVItem->iIndent = lpItem->iIndent;
4766 * Retrieves item attributes.
4769 * [I] hwnd : window handle
4770 * [IO] lpLVItem : item info
4771 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4772 * if FALSE, the lpLVItem is a LPLVITEMA.
4775 * This is the external 'GetItem' interface -- it properly copies
4776 * the text in the provided buffer.
4782 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4787 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4790 pszText = lpLVItem->pszText;
4791 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4792 if (bResult && lpLVItem->pszText != pszText)
4793 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4794 lpLVItem->pszText = pszText;
4802 * Retrieves the position (upper-left) of the listview control item.
4803 * Note that for LVS_ICON style, the upper-left is that of the icon
4804 * and not the bounding box.
4807 * [I] infoPtr : valid pointer to the listview structure
4808 * [I] nItem : item index
4809 * [O] lpptPosition : coordinate information
4815 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4817 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4820 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4822 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4823 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition)) return FALSE;
4824 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
4826 if (uView == LVS_ICON)
4828 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
4829 lpptPosition->y += ICON_TOP_PADDING;
4831 lpptPosition->x += Origin.x;
4832 lpptPosition->y += Origin.y;
4834 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
4841 * Retrieves the bounding rectangle for a listview control item.
4844 * [I] infoPtr : valid pointer to the listview structure
4845 * [I] nItem : item index
4846 * [IO] lprc : bounding rectangle coordinates
4847 * lprc->left specifies the portion of the item for which the bounding
4848 * rectangle will be retrieved.
4850 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
4851 * including the icon and label.
4854 * * Experiment shows that native control returns:
4855 * * width = min (48, length of text line)
4856 * * .left = position.x - (width - iconsize.cx)/2
4857 * * .right = .left + width
4858 * * height = #lines of text * ntmHeight + icon height + 8
4859 * * .top = position.y - 2
4860 * * .bottom = .top + height
4861 * * separation between items .y = itemSpacing.cy - height
4862 * * .x = itemSpacing.cx - width
4863 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
4866 * * Experiment shows that native control returns:
4867 * * width = iconSize.cx + 16
4868 * * .left = position.x - (width - iconsize.cx)/2
4869 * * .right = .left + width
4870 * * height = iconSize.cy + 4
4871 * * .top = position.y - 2
4872 * * .bottom = .top + height
4873 * * separation between items .y = itemSpacing.cy - height
4874 * * .x = itemSpacing.cx - width
4875 * LVIR_LABEL Returns the bounding rectangle of the item text.
4878 * * Experiment shows that native control returns:
4879 * * width = text length
4880 * * .left = position.x - width/2
4881 * * .right = .left + width
4882 * * height = ntmH * linecount + 2
4883 * * .top = position.y + iconSize.cy + 6
4884 * * .bottom = .top + height
4885 * * separation between items .y = itemSpacing.cy - height
4886 * * .x = itemSpacing.cx - width
4887 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
4888 * rectangles, but excludes columns in report view.
4895 * Note that the bounding rectangle of the label in the LVS_ICON view depends
4896 * upon whether the window has the focus currently and on whether the item
4897 * is the one with the focus. Ensure that the control's record of which
4898 * item has the focus agrees with the items' records.
4900 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
4902 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4903 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4904 BOOL doLabel = TRUE, oversizedBox = FALSE;
4905 POINT Position, Origin;
4909 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
4911 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4912 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
4913 if (!LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position)) return FALSE;
4915 /* Be smart and try to figure out the minimum we have to do */
4916 if (lprc->left == LVIR_ICON) doLabel = FALSE;
4917 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
4918 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
4919 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
4920 oversizedBox = TRUE;
4922 /* get what we need from the item before hand, so we make
4923 * only one request. This can speed up things, if data
4924 * is stored on the app side */
4926 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
4927 if (doLabel) lvItem.mask |= LVIF_TEXT;
4928 lvItem.iItem = nItem;
4929 lvItem.iSubItem = 0;
4930 lvItem.pszText = szDispText;
4931 lvItem.cchTextMax = DISP_TEXT_SIZE;
4932 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
4933 /* we got the state already up, simulate it here, to avoid a reget */
4934 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
4936 lvItem.mask |= LVIF_STATE;
4937 lvItem.stateMask = LVIS_FOCUSED;
4938 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
4941 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
4942 lprc->left = LVIR_BOUNDS;
4946 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL)) return FALSE;
4950 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc)) return FALSE;
4954 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL)) return FALSE;
4957 case LVIR_SELECTBOUNDS:
4958 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect)) return FALSE;
4959 UnionRect(lprc, lprc, &label_rect);
4963 WARN("Unknown value: %d\n", lprc->left);
4967 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
4969 TRACE(" rect=%s\n", debugrect(lprc));
4976 * Retrieves the spacing between listview control items.
4979 * [I] infoPtr : valid pointer to the listview structure
4980 * [IO] lprc : rectangle to receive the output
4981 * on input, lprc->top = nSubItem
4982 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
4984 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
4985 * not only those of the first column.
4986 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
4992 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
4994 POINT Position, Origin;
4997 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
4999 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5001 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
5002 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5004 lvItem.mask = lprc->top == 0 ? LVIF_INDENT : 0;
5005 lvItem.iItem = nItem;
5006 lvItem.iSubItem = lprc->top;
5008 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5012 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL)) return FALSE;
5017 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL)) return FALSE;
5021 ERR("Unknown bounds=%d\n", lprc->left);
5025 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5032 * Retrieves the width of a label.
5035 * [I] infoPtr : valid pointer to the listview structure
5038 * SUCCESS : string width (in pixels)
5041 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5043 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5046 TRACE("(nItem=%d)\n", nItem);
5048 lvItem.mask = LVIF_TEXT;
5049 lvItem.iItem = nItem;
5050 lvItem.iSubItem = 0;
5051 lvItem.pszText = szDispText;
5052 lvItem.cchTextMax = DISP_TEXT_SIZE;
5053 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5055 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5060 * Retrieves the spacing between listview control items.
5063 * [I] infoPtr : valid pointer to the listview structure
5064 * [I] BOOL : flag for small or large icon
5067 * Horizontal + vertical spacing
5069 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5075 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5079 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5080 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5082 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5089 * Retrieves the state of a listview control item.
5092 * [I] infoPtr : valid pointer to the listview structure
5093 * [I] nItem : item index
5094 * [I] uMask : state mask
5097 * State specified by the mask.
5099 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5103 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5105 lvItem.iItem = nItem;
5106 lvItem.iSubItem = 0;
5107 lvItem.mask = LVIF_STATE;
5108 lvItem.stateMask = uMask;
5109 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5111 return lvItem.state & uMask;
5116 * Retrieves the text of a listview control item or subitem.
5119 * [I] hwnd : window handle
5120 * [I] nItem : item index
5121 * [IO] lpLVItem : item information
5122 * [I] isW : TRUE if lpLVItem is Unicode
5125 * SUCCESS : string length
5128 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5130 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5132 lpLVItem->mask = LVIF_TEXT;
5133 lpLVItem->iItem = nItem;
5134 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5136 return textlenT(lpLVItem->pszText, isW);
5141 * Searches for an item based on properties + relationships.
5144 * [I] infoPtr : valid pointer to the listview structure
5145 * [I] nItem : item index
5146 * [I] uFlags : relationship flag
5149 * This function is very, very inefficient! Needs work.
5152 * SUCCESS : item index
5155 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5157 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5159 LVFINDINFOW lvFindInfo;
5160 INT nCountPerColumn;
5163 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5164 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5166 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5168 if (uFlags & LVNI_CUT)
5171 if (uFlags & LVNI_DROPHILITED)
5172 uMask |= LVIS_DROPHILITED;
5174 if (uFlags & LVNI_FOCUSED)
5175 uMask |= LVIS_FOCUSED;
5177 if (uFlags & LVNI_SELECTED)
5178 uMask |= LVIS_SELECTED;
5180 /* if we're asked for the focused item, that's only one,
5181 * so it's worth optimizing */
5182 if (uFlags & LVNI_FOCUSED)
5184 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5185 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5188 if (uFlags & LVNI_ABOVE)
5190 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5195 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5201 lvFindInfo.flags = LVFI_NEARESTXY;
5202 lvFindInfo.vkDirection = VK_UP;
5203 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5204 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5206 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5211 else if (uFlags & LVNI_BELOW)
5213 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5215 while (nItem < infoPtr->nItemCount)
5218 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5224 lvFindInfo.flags = LVFI_NEARESTXY;
5225 lvFindInfo.vkDirection = VK_DOWN;
5226 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5227 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5229 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5234 else if (uFlags & LVNI_TOLEFT)
5236 if (uView == LVS_LIST)
5238 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5239 while (nItem - nCountPerColumn >= 0)
5241 nItem -= nCountPerColumn;
5242 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5246 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5248 lvFindInfo.flags = LVFI_NEARESTXY;
5249 lvFindInfo.vkDirection = VK_LEFT;
5250 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5251 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5253 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5258 else if (uFlags & LVNI_TORIGHT)
5260 if (uView == LVS_LIST)
5262 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5263 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5265 nItem += nCountPerColumn;
5266 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5270 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5272 lvFindInfo.flags = LVFI_NEARESTXY;
5273 lvFindInfo.vkDirection = VK_RIGHT;
5274 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5275 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5277 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5286 /* search by index */
5287 for (i = nItem; i < infoPtr->nItemCount; i++)
5289 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5297 /* LISTVIEW_GetNumberOfWorkAreas */
5301 * Retrieves the origin coordinates when in icon or small icon display mode.
5304 * [I] infoPtr : valid pointer to the listview structure
5305 * [O] lpptOrigin : coordinate information
5311 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5313 DWORD lStyle = infoPtr->dwStyle;
5314 UINT uView = lStyle & LVS_TYPEMASK;
5315 INT nHorzPos = 0, nVertPos = 0;
5316 SCROLLINFO scrollInfo;
5318 if (!lpptOrigin) return FALSE;
5320 scrollInfo.cbSize = sizeof(SCROLLINFO);
5321 scrollInfo.fMask = SIF_POS;
5323 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5324 nHorzPos = scrollInfo.nPos;
5325 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5326 nVertPos = scrollInfo.nPos;
5328 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5330 lpptOrigin->x = infoPtr->rcList.left;
5331 lpptOrigin->y = infoPtr->rcList.top;
5332 if (uView == LVS_LIST)
5333 nHorzPos *= infoPtr->nItemWidth;
5334 else if (uView == LVS_REPORT)
5335 nVertPos *= infoPtr->nItemHeight;
5337 lpptOrigin->x -= nHorzPos;
5338 lpptOrigin->y -= nVertPos;
5340 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5347 * Retrieves the width of a string.
5350 * [I] hwnd : window handle
5351 * [I] lpszText : text string to process
5352 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5355 * SUCCESS : string width (in pixels)
5358 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5363 if (is_textT(lpszText, isW))
5365 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5366 HDC hdc = GetDC(infoPtr->hwndSelf);
5367 HFONT hOldFont = SelectObject(hdc, hFont);
5370 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5372 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5373 SelectObject(hdc, hOldFont);
5374 ReleaseDC(infoPtr->hwndSelf, hdc);
5376 return stringSize.cx;
5381 * Determines which listview item is located at the specified position.
5384 * [I] infoPtr : valid pointer to the listview structure
5385 * [IO] lpht : hit test information
5386 * [I] subitem : fill out iSubItem.
5387 * [I] select : return the index only if the hit selects the item
5390 * (mm 20001022): We must not allow iSubItem to be touched, for
5391 * an app might pass only a structure with space up to iItem!
5392 * (MS Office 97 does that for instance in the file open dialog)
5395 * SUCCESS : item index
5398 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5400 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5401 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5402 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
5403 POINT Origin, Position, opt;
5407 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5411 if (subitem) lpht->iSubItem = 0;
5413 if (infoPtr->rcList.left > lpht->pt.x)
5414 lpht->flags |= LVHT_TOLEFT;
5415 else if (infoPtr->rcList.right < lpht->pt.x)
5416 lpht->flags |= LVHT_TORIGHT;
5418 if (infoPtr->rcList.top > lpht->pt.y)
5419 lpht->flags |= LVHT_ABOVE;
5420 else if (infoPtr->rcList.bottom < lpht->pt.y)
5421 lpht->flags |= LVHT_BELOW;
5423 TRACE("lpht->flags=0x%x\n", lpht->flags);
5424 if (lpht->flags) return -1;
5426 lpht->flags |= LVHT_NOWHERE;
5428 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
5430 /* first deal with the large items */
5431 rcSearch.left = lpht->pt.x;
5432 rcSearch.top = lpht->pt.y;
5433 rcSearch.right = rcSearch.left + 1;
5434 rcSearch.bottom = rcSearch.top + 1;
5436 iterator_frameditems(&i, infoPtr, &rcSearch);
5437 iterator_next(&i); /* go to first item in the sequence */
5438 lpht->iItem = i.nItem;
5439 iterator_destroy(&i);
5441 TRACE("lpht->iItem=%d\n", lpht->iItem);
5442 if (lpht->iItem == -1) return -1;
5444 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5445 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5446 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5447 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
5448 lvItem.iItem = lpht->iItem;
5449 lvItem.iSubItem = 0;
5450 lvItem.pszText = szDispText;
5451 lvItem.cchTextMax = DISP_TEXT_SIZE;
5452 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5453 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
5455 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel)) return -1;
5456 if (!LISTVIEW_GetItemOrigin(infoPtr, lpht->iItem, &Position)) return -1;
5457 opt.x = lpht->pt.x - Position.x - Origin.x;
5458 opt.y = lpht->pt.y - Position.y - Origin.y;
5460 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
5463 UnionRect(&rcBounds, &rcIcon, &rcLabel);
5464 TRACE("rcBounds=%s\n", debugrect(&rcBounds));
5465 if (!PtInRect(&rcBounds, opt)) return -1;
5467 if (PtInRect(&rcIcon, opt))
5468 lpht->flags |= LVHT_ONITEMICON;
5469 else if (PtInRect(&rcLabel, opt))
5470 lpht->flags |= LVHT_ONITEMLABEL;
5471 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5472 lpht->flags |= LVHT_ONITEMSTATEICON;
5473 if (lpht->flags & LVHT_ONITEM)
5474 lpht->flags &= ~LVHT_NOWHERE;
5476 TRACE("lpht->flags=0x%x\n", lpht->flags);
5477 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5479 INT j, nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5480 rcBounds.right = rcBounds.left;
5481 for (j = 0; j < nColumnCount; j++)
5483 rcBounds.left = rcBounds.right;
5484 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5485 if (PtInRect(&rcBounds, opt))
5493 if (!select || lpht->iItem == -1) return lpht->iItem;
5495 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5497 if (uView == LVS_REPORT) UnionRect(&rcBounds, &rcIcon, &rcLabel);
5498 return PtInRect(&rcBounds, opt) ? lpht->iItem : -1;
5504 * Inserts a new column.
5507 * [I] infoPtr : valid pointer to the listview structure
5508 * [I] INT : column index
5509 * [I] LPLVCOLUMNW : column information
5512 * SUCCESS : new column index
5515 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5516 LPLVCOLUMNW lpColumn, BOOL isW)
5522 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5524 if (!lpColumn) return -1;
5526 hdi.mask = hdi.fmt = 0;
5527 if (lpColumn->mask & LVCF_FMT)
5529 /* format member is valid */
5530 hdi.mask |= HDI_FORMAT;
5532 /* set text alignment (leftmost column must be left-aligned) */
5533 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5534 hdi.fmt |= HDF_LEFT;
5535 else if (lpColumn->fmt & LVCFMT_RIGHT)
5536 hdi.fmt |= HDF_RIGHT;
5537 else if (lpColumn->fmt & LVCFMT_CENTER)
5538 hdi.fmt |= HDF_CENTER;
5540 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5541 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5543 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5545 hdi.fmt |= HDF_IMAGE;
5546 hdi.iImage = I_IMAGECALLBACK;
5549 if (lpColumn->fmt & LVCFMT_IMAGE)
5550 ; /* FIXME: enable images for *(sub)items* this column */
5553 if (lpColumn->mask & LVCF_WIDTH)
5555 hdi.mask |= HDI_WIDTH;
5556 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5558 /* make it fill the remainder of the controls width */
5563 /* get the width of every item except the current one */
5564 hdit.mask = HDI_WIDTH;
5567 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5568 if (Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit)))
5569 hdi.cxy += hdit.cxy;
5571 /* retrieve the layout of the header */
5572 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5573 TRACE("start cxy=%d rcHeader=%s\n", hdi.cxy, debugrect(&rcHeader));
5575 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
5578 hdi.cxy = lpColumn->cx;
5581 if (lpColumn->mask & LVCF_TEXT)
5583 hdi.mask |= HDI_TEXT | HDI_FORMAT;
5584 hdi.fmt |= HDF_STRING;
5585 hdi.pszText = lpColumn->pszText;
5586 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
5589 if (lpColumn->mask & LVCF_IMAGE)
5591 hdi.mask |= HDI_IMAGE;
5592 hdi.iImage = lpColumn->iImage;
5595 if (lpColumn->mask & LVCF_ORDER)
5597 hdi.mask |= HDI_ORDER;
5598 hdi.iOrder = lpColumn->iOrder;
5601 /* insert item in header control */
5602 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5603 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5604 (WPARAM)nColumn, (LPARAM)&hdi);
5605 if (nNewColumn == -1) return -1;
5606 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &rcCol)) return -1;
5608 /* now we have to actually adjust the data */
5609 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
5611 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
5615 /* preallocate memory, so we can fail gracefully */
5616 if (nNewColumn == 0)
5618 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->nItemCount);
5619 if (!lpNewItems) return -1;
5620 for (i = 0; i < infoPtr->nItemCount; i++)
5621 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
5622 if (i != infoPtr->nItemCount)
5624 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5625 COMCTL32_Free(lpNewItems);
5630 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5632 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5633 if (!hdpaSubItems) continue;
5634 for (i = 1; i < hdpaSubItems->nItemCount; i++)
5636 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
5637 if (!lpSubItem) break;
5638 if (lpSubItem->iSubItem >= nNewColumn)
5639 lpSubItem->iSubItem++;
5642 /* if we found our subitem, zapp it */
5643 if (nNewColumn == 0)
5645 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
5646 lpSubItem = lpNewItems[nItem];
5647 lpSubItem->hdr = lpMainItem->hdr;
5648 lpSubItem->iSubItem = 1;
5649 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
5650 lpMainItem->iSubItem = 0;
5651 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
5655 COMCTL32_Free(lpNewItems);
5658 /* we don't have to worry abiut display issues in non-report mode */
5659 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return nNewColumn;
5661 /* if we have a focus, must first erase the focus rect */
5662 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
5664 /* Need to reset the item width when inserting a new column */
5665 infoPtr->nItemWidth += rcCol.right - rcCol.left;
5667 LISTVIEW_UpdateScroll(infoPtr);
5669 /* scroll to cover the deleted column, and invalidate for redraw */
5670 rcOld = infoPtr->rcList;
5671 rcOld.left = rcCol.left;
5672 ScrollWindowEx(infoPtr->hwndSelf, rcCol.right - rcCol.left, 0,
5673 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5675 /* we can restore focus now */
5676 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
5681 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5682 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5683 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5684 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5685 their own sort proc. when sending LVM_SORTITEMS.
5688 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5690 LVS_SORTXXX must be specified,
5691 LVS_OWNERDRAW is not set,
5692 <item>.pszText is not LPSTR_TEXTCALLBACK.
5694 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5695 are sorted based on item text..."
5697 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5699 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5700 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5701 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5703 /* if we're sorting descending, negate the return value */
5704 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5709 * Inserts a new item in the listview control.
5712 * [I] infoPtr : valid pointer to the listview structure
5713 * [I] lpLVItem : item information
5714 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5717 * SUCCESS : new item index
5720 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5722 LONG lStyle = infoPtr->dwStyle;
5723 UINT uView = lStyle & LVS_TYPEMASK;
5727 LISTVIEW_ITEM *lpItem;
5730 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5732 if (lStyle & LVS_OWNERDATA)
5734 nItem = infoPtr->nItemCount;
5735 infoPtr->nItemCount++;
5739 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5740 if (!lpLVItem || lpLVItem->iSubItem) return -1;
5742 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5744 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5747 /* insert item in listview control data structure */
5748 if ( (hdpaSubItems = DPA_Create(8)) )
5749 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5750 if (nItem == -1) goto fail;
5752 /* FIXME: is the handling of this LVS_OWNERDRAWFIXED correct? */
5753 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5754 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5756 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
5757 is_sorted ? infoPtr->nItemCount + 1 : lpLVItem->iItem,
5759 if (nItem == -1) goto fail;
5760 infoPtr->nItemCount++;
5762 if (!LISTVIEW_SetItemT(infoPtr, lpLVItem, isW))
5765 /* if we're sorted, sort the list, and update the index */
5768 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5769 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5772 ERR("We can't find the item we just inserted, possible memory corruption.");
5773 /* we can't remove it from the list if we can't find it, so just fail */
5774 /* we don't deallocate memory here, as it will probably cause more problems */
5779 /* make room for the position, if we are in the right mode */
5780 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5782 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5784 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5786 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5791 /* Add the subitem list to the items array. Do this last in case we go to
5792 * fail during the above.
5794 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5796 lpItem->valid = TRUE;
5798 /* send LVN_INSERTITEM notification */
5799 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5801 nmlv.lParam = lpItem->lParam;
5802 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5804 /* align items (set position of each item) */
5805 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5807 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
5808 else LISTVIEW_AlignTop(infoPtr);
5811 LISTVIEW_UpdateScroll(infoPtr);
5813 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
5815 TRACE(" <- %d\n", nItem);
5819 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5820 infoPtr->nItemCount--;
5822 DPA_DeletePtr(hdpaSubItems, 0);
5823 DPA_Destroy (hdpaSubItems);
5824 COMCTL32_Free (lpItem);
5830 * Redraws a range of items.
5833 * [I] infoPtr : valid pointer to the listview structure
5834 * [I] INT : first item
5835 * [I] INT : last item
5841 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
5845 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
5846 max(nFirst, nLast) >= infoPtr->nItemCount)
5849 for (i = nFirst; i <= nLast; i++)
5850 LISTVIEW_InvalidateItem(infoPtr, i);
5857 * Scroll the content of a listview.
5860 * [I] infoPtr : valid pointer to the listview structure
5861 * [I] INT : horizontal scroll amount in pixels
5862 * [I] INT : vertical scroll amount in pixels
5869 * If the control is in report mode (LVS_REPORT) the control can
5870 * be scrolled only in line increments. "dy" will be rounded to the
5871 * nearest number of pixels that are a whole line. Ex: if line height
5872 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
5873 * is passed the the scroll will be 0. (per MSDN 7/2002)
5875 * For: (per experimentaion with native control and CSpy ListView)
5876 * LVS_ICON dy=1 = 1 pixel (vertical only)
5878 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
5880 * LVS_LIST dx=1 = 1 column (horizontal only)
5881 * but will only scroll 1 column per message
5882 * no matter what the value.
5883 * dy must be 0 or FALSE returned.
5884 * LVS_REPORT dx=1 = 1 pixel
5888 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
5890 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
5892 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
5893 dy /= infoPtr->nItemHeight;
5896 if (dy != 0) return FALSE;
5903 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
5904 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
5911 * Sets the background color.
5914 * [I] infoPtr : valid pointer to the listview structure
5915 * [I] COLORREF : background color
5921 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
5923 TRACE("(clrBk=%lx)\n", clrBk);
5925 if(infoPtr->clrBk != clrBk) {
5926 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
5927 infoPtr->clrBk = clrBk;
5928 if (clrBk == CLR_NONE)
5929 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
5931 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
5932 LISTVIEW_InvalidateList(infoPtr);
5938 /* LISTVIEW_SetBkImage */
5942 * Sets the attributes of a header item.
5945 * [I] infoPtr : valid pointer to the listview structure
5946 * [I] INT : column index
5947 * [I] LPLVCOLUMNW : column attributes
5948 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
5949 * otherwise it is in fact a LPLVCOLUMNA
5955 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5956 LPLVCOLUMNW lpColumn, BOOL isW)
5958 BOOL bResult = FALSE;
5959 HDITEMW hdi, hdiget;
5961 if ((lpColumn != NULL) && (nColumn >= 0) &&
5962 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
5964 /* initialize memory */
5965 ZeroMemory(&hdi, sizeof(hdi));
5967 if (lpColumn->mask & LVCF_FMT)
5969 /* format member is valid */
5970 hdi.mask |= HDI_FORMAT;
5972 /* get current format first */
5973 hdiget.mask = HDI_FORMAT;
5974 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
5975 /* preserve HDF_STRING if present */
5976 hdi.fmt = hdiget.fmt & HDF_STRING;
5978 /* set text alignment (leftmost column must be left-aligned) */
5981 hdi.fmt |= HDF_LEFT;
5985 if (lpColumn->fmt & LVCFMT_LEFT)
5986 hdi.fmt |= HDF_LEFT;
5987 else if (lpColumn->fmt & LVCFMT_RIGHT)
5988 hdi.fmt |= HDF_RIGHT;
5989 else if (lpColumn->fmt & LVCFMT_CENTER)
5990 hdi.fmt |= HDF_CENTER;
5993 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5994 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5996 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5997 hdi.fmt |= HDF_IMAGE;
5999 if (lpColumn->fmt & LVCFMT_IMAGE)
6001 hdi.fmt |= HDF_IMAGE;
6002 hdi.iImage = I_IMAGECALLBACK;
6006 if (lpColumn->mask & LVCF_WIDTH)
6008 hdi.mask |= HDI_WIDTH;
6009 hdi.cxy = lpColumn->cx;
6012 if (lpColumn->mask & LVCF_TEXT)
6014 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6015 hdi.pszText = lpColumn->pszText;
6016 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6017 hdi.fmt |= HDF_STRING;
6020 if (lpColumn->mask & LVCF_IMAGE)
6022 hdi.mask |= HDI_IMAGE;
6023 hdi.iImage = lpColumn->iImage;
6026 if (lpColumn->mask & LVCF_ORDER)
6028 hdi.mask |= HDI_ORDER;
6029 hdi.iOrder = lpColumn->iOrder;
6032 /* set header item attributes */
6034 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6036 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6044 * Sets the column order array
6047 * [I] infoPtr : valid pointer to the listview structure
6048 * [I] INT : number of elements in column order array
6049 * [I] INT : pointer to column order array
6055 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6057 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6068 * Sets the width of a column
6071 * [I] infoPtr : valid pointer to the listview structure
6072 * [I] INT : column index
6073 * [I] INT : column width
6079 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6083 LONG lStyle = infoPtr->dwStyle;
6084 UINT uView = lStyle & LVS_TYPEMASK;
6089 WCHAR text_buffer[DISP_TEXT_SIZE];
6090 INT header_item_count;
6095 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6097 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6099 /* set column width only if in report or list mode */
6100 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6102 /* take care of invalid cx values */
6103 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6104 else if (uView == LVS_LIST && cx < 1) return FALSE;
6106 /* resize all columns if in LVS_LIST mode */
6107 if(uView == LVS_LIST)
6109 infoPtr->nItemWidth = cx;
6110 LISTVIEW_InvalidateList(infoPtr);
6114 /* autosize based on listview items width */
6115 if(cx == LVSCW_AUTOSIZE)
6117 /* set the width of the column to the width of the widest item */
6118 if (iCol == 0 || uView == LVS_LIST)
6121 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6123 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6124 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6126 if (infoPtr->himlSmall)
6127 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6131 lvItem.iSubItem = iCol;
6132 lvItem.mask = LVIF_TEXT;
6133 lvItem.pszText = szDispText;
6134 lvItem.cchTextMax = DISP_TEXT_SIZE;
6136 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6138 lvItem.iItem = item_index;
6139 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6140 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6141 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6144 cx += TRAILING_PADDING;
6145 } /* autosize based on listview header width */
6146 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6148 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6150 /* if iCol is the last column make it fill the remainder of the controls width */
6151 if(iCol == (header_item_count - 1)) {
6152 /* get the width of every item except the current one */
6153 hdi.mask = HDI_WIDTH;
6156 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6157 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6161 /* retrieve the layout of the header */
6162 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6164 cx = (rcHeader.right - rcHeader.left) - cx;
6168 /* Despite what the MS docs say, if this is not the last
6169 column, then MS resizes the column to the width of the
6170 largest text string in the column, including headers
6171 and items. This is different from LVSCW_AUTOSIZE in that
6172 LVSCW_AUTOSIZE ignores the header string length.
6175 /* retrieve header font */
6176 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6178 /* retrieve header text */
6179 hdi.mask = HDI_TEXT;
6180 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6181 hdi.pszText = text_buffer;
6183 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6185 /* determine the width of the text in the header */
6186 hdc = GetDC(infoPtr->hwndSelf);
6187 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6189 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6191 SelectObject(hdc, old_font); /* restore the old font */
6192 ReleaseDC(infoPtr->hwndSelf, hdc);
6194 lvItem.iSubItem = iCol;
6195 lvItem.mask = LVIF_TEXT;
6196 lvItem.pszText = szDispText;
6197 lvItem.cchTextMax = DISP_TEXT_SIZE;
6199 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6201 lvItem.iItem = item_index;
6202 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6203 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6204 nLabelWidth += TRAILING_PADDING;
6205 /* While it is possible for subitems to have icons, even MS messes
6206 up the positioning, so I suspect no applications actually use
6208 if (item_index == 0 && infoPtr->himlSmall)
6209 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6210 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6215 /* call header to update the column change */
6216 hdi.mask = HDI_WIDTH;
6219 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6221 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6228 * Sets the extended listview style.
6231 * [I] infoPtr : valid pointer to the listview structure
6236 * SUCCESS : previous style
6239 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6241 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6245 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6247 infoPtr->dwLvExStyle = dwStyle;
6254 * Sets the new hot cursor used during hot tracking and hover selection.
6257 * [I] infoPtr : valid pointer to the listview structure
6258 * [I} hCurosr : the new hot cursor handle
6261 * Returns the previous hot cursor
6263 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6265 HCURSOR oldCursor = infoPtr->hHotCursor;
6266 infoPtr->hHotCursor = hCursor;
6273 * Sets the hot item index.
6276 * [I] infoPtr : valid pointer to the listview structure
6280 * SUCCESS : previous hot item index
6281 * FAILURE : -1 (no hot item)
6283 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6285 INT iOldIndex = infoPtr->nHotItem;
6286 infoPtr->nHotItem = iIndex;
6293 * Sets the amount of time the cursor must hover over an item before it is selected.
6296 * [I] infoPtr : valid pointer to the listview structure
6297 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6300 * Returns the previous hover time
6302 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6304 DWORD oldHoverTime = infoPtr->dwHoverTime;
6305 infoPtr->dwHoverTime = dwHoverTime;
6306 return oldHoverTime;
6311 * Sets spacing for icons of LVS_ICON style.
6314 * [I] infoPtr : valid pointer to the listview structure
6315 * [I] DWORD : MAKELONG(cx, cy)
6318 * MAKELONG(oldcx, oldcy)
6320 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6322 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6323 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6324 LONG lStyle = infoPtr->dwStyle;
6325 UINT uView = lStyle & LVS_TYPEMASK;
6327 TRACE("requested=(%d,%d)\n", cx, cy);
6329 /* this is supported only for LVS_ICON style */
6330 if (uView != LVS_ICON) return oldspacing;
6332 /* set to defaults, if instructed to */
6333 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6334 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6336 /* if 0 then compute width
6337 * FIXME: Should scan each item and determine max width of
6338 * icon or label, then make that the width */
6340 cx = infoPtr->iconSpacing.cx;
6342 /* if 0 then compute height */
6344 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6345 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6348 infoPtr->iconSpacing.cx = cx;
6349 infoPtr->iconSpacing.cy = cy;
6351 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6352 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6353 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6354 infoPtr->ntmHeight);
6356 /* these depend on the iconSpacing */
6357 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6358 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6363 inline void update_icon_size(HIMAGELIST himl, SIZE *size)
6367 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6373 size->cx = size->cy = 0;
6381 * [I] infoPtr : valid pointer to the listview structure
6382 * [I] INT : image list type
6383 * [I] HIMAGELIST : image list handle
6386 * SUCCESS : old image list
6389 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6391 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6392 INT oldHeight = infoPtr->nItemHeight;
6393 HIMAGELIST himlOld = 0;
6398 himlOld = infoPtr->himlNormal;
6399 infoPtr->himlNormal = himl;
6400 if (uView == LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6401 LISTVIEW_SetIconSpacing(infoPtr, 0);
6405 himlOld = infoPtr->himlSmall;
6406 infoPtr->himlSmall = himl;
6407 if (uView != LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6411 himlOld = infoPtr->himlState;
6412 infoPtr->himlState = himl;
6413 update_icon_size(himl, &infoPtr->iconStateSize);
6414 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6418 ERR("Unknown icon type=%d\n", nType);
6422 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6423 if (infoPtr->nItemHeight != oldHeight)
6424 LISTVIEW_UpdateScroll(infoPtr);
6431 * Preallocates memory (does *not* set the actual count of items !)
6434 * [I] infoPtr : valid pointer to the listview structure
6435 * [I] INT : item count (projected number of items to allocate)
6436 * [I] DWORD : update flags
6442 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6444 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6446 if (infoPtr->dwStyle & LVS_OWNERDATA)
6448 int precount,topvisible;
6450 TRACE("LVS_OWNERDATA is set!\n");
6451 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6452 FIXME("flags %s %s not implemented\n",
6453 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6455 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6457 LISTVIEW_RemoveAllSelections(infoPtr, -1);
6459 precount = infoPtr->nItemCount;
6460 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6461 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6463 infoPtr->nItemCount = nItems;
6464 infoPtr->nItemWidth = max(LISTVIEW_CalculateMaxWidth(infoPtr),
6465 DEFAULT_COLUMN_WIDTH);
6467 LISTVIEW_UpdateSize(infoPtr);
6468 LISTVIEW_UpdateScroll(infoPtr);
6470 if (min(precount,infoPtr->nItemCount) < topvisible)
6471 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6475 /* According to MSDN for non-LVS_OWNERDATA this is just
6476 * a performance issue. The control allocates its internal
6477 * data structures for the number of items specified. It
6478 * cuts down on the number of memory allocations. Therefore
6479 * we will just issue a WARN here
6481 WARN("for non-ownerdata performance option not implemented.\n");
6489 * Sets the position of an item.
6492 * [I] infoPtr : valid pointer to the listview structure
6493 * [I] nItem : item index
6494 * [I] pt : coordinate
6500 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6502 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6505 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6507 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6508 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6510 /* This point value seems to be an undocumented feature.
6511 * The best guess is that it means either at the origin,
6512 * or at true beginning of the list. I will assume the origin. */
6513 if ((pt.x == -1) && (pt.y == -1))
6514 LISTVIEW_GetOrigin(infoPtr, &pt);
6515 else if (uView == LVS_ICON)
6517 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6518 pt.y -= ICON_TOP_PADDING;
6521 /* save the old position */
6522 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
6523 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
6525 /* Is the position changing? */
6526 if (pt.x == old.x && pt.y == old.y) return TRUE;
6528 /* FIXME: shouldn't we invalidate, as the item moved? */
6530 /* Allocating a POINTER for every item is too resource intensive,
6531 * so we'll keep the (x,y) in different arrays */
6532 if (DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)pt.x) &&
6533 DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)pt.y) )
6536 ERR("We should never fail here (nItem=%d, pt=%s), please report.\n",
6537 nItem, debugpoint(&pt));
6543 * Sets the state of one or many items.
6546 * [I] infoPtr : valid pointer to the listview structure
6547 * [I]INT : item index
6548 * [I] LPLVITEM : item or subitem info
6554 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6556 BOOL bResult = TRUE;
6559 lvItem.iItem = nItem;
6560 lvItem.iSubItem = 0;
6561 lvItem.mask = LVIF_STATE;
6562 lvItem.state = lpLVItem->state;
6563 lvItem.stateMask = lpLVItem->stateMask;
6564 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6568 /* apply to all items */
6569 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6570 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6573 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6580 * Sets the text of an item or subitem.
6583 * [I] hwnd : window handle
6584 * [I] nItem : item index
6585 * [I] lpLVItem : item or subitem info
6586 * [I] isW : TRUE if input is Unicode
6592 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6596 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6598 lvItem.iItem = nItem;
6599 lvItem.iSubItem = lpLVItem->iSubItem;
6600 lvItem.mask = LVIF_TEXT;
6601 lvItem.pszText = lpLVItem->pszText;
6602 lvItem.cchTextMax = lpLVItem->cchTextMax;
6604 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6606 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6611 * Set item index that marks the start of a multiple selection.
6614 * [I] infoPtr : valid pointer to the listview structure
6618 * Index number or -1 if there is no selection mark.
6620 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6622 INT nOldIndex = infoPtr->nSelectionMark;
6624 TRACE("(nIndex=%d)\n", nIndex);
6626 infoPtr->nSelectionMark = nIndex;
6633 * Sets the text background color.
6636 * [I] infoPtr : valid pointer to the listview structure
6637 * [I] COLORREF : text background color
6643 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6645 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6647 if (infoPtr->clrTextBk != clrTextBk)
6649 infoPtr->clrTextBk = clrTextBk;
6650 LISTVIEW_InvalidateList(infoPtr);
6658 * Sets the text foreground color.
6661 * [I] infoPtr : valid pointer to the listview structure
6662 * [I] COLORREF : text color
6668 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6670 TRACE("(clrText=%lx)\n", clrText);
6672 if (infoPtr->clrText != clrText)
6674 infoPtr->clrText = clrText;
6675 LISTVIEW_InvalidateList(infoPtr);
6681 /* LISTVIEW_SetToolTips */
6682 /* LISTVIEW_SetUnicodeFormat */
6683 /* LISTVIEW_SetWorkAreas */
6687 * Callback internally used by LISTVIEW_SortItems()
6690 * [I] LPVOID : first LISTVIEW_ITEM to compare
6691 * [I] LPVOID : second LISTVIEW_ITEM to compare
6692 * [I] LPARAM : HWND of control
6695 * if first comes before second : negative
6696 * if first comes after second : positive
6697 * if first and second are equivalent : zero
6699 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6701 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6702 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6703 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6705 /* Forward the call to the client defined callback */
6706 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6711 * Sorts the listview items.
6714 * [I] infoPtr : valid pointer to the listview structure
6715 * [I] WPARAM : application-defined value
6716 * [I] LPARAM : pointer to comparision callback
6722 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6724 UINT lStyle = infoPtr->dwStyle;
6726 LISTVIEW_ITEM *lpItem;
6727 LPVOID selectionMarkItem;
6731 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6733 if (lStyle & LVS_OWNERDATA) return FALSE;
6735 if (!infoPtr->hdpaItems) return FALSE;
6737 /* if there are 0 or 1 items, there is no need to sort */
6738 if (infoPtr->nItemCount < 2) return TRUE;
6740 if (infoPtr->nFocusedItem >= 0)
6742 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6743 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6744 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6746 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
6747 /* clear the lpItem->state for non-selected ones */
6748 /* remove the selection ranges */
6750 infoPtr->pfnCompare = pfnCompare;
6751 infoPtr->lParamSort = lParamSort;
6752 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6754 /* Adjust selections and indices so that they are the way they should
6755 * be after the sort (otherwise, the list items move around, but
6756 * whatever is at the item's previous original position will be
6759 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6760 for (i=0; i < infoPtr->nItemCount; i++)
6762 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6763 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6765 if (lpItem->state & LVIS_SELECTED)
6767 item.state = LVIS_SELECTED;
6768 item.stateMask = LVIS_SELECTED;
6769 LISTVIEW_SetItemState(infoPtr, i, &item);
6771 if (lpItem->state & LVIS_FOCUSED)
6773 infoPtr->nFocusedItem = i;
6774 lpItem->state &= ~LVIS_FOCUSED;
6777 if (selectionMarkItem != NULL)
6778 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6779 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6781 /* align the items */
6782 LISTVIEW_AlignTop(infoPtr);
6784 /* refresh the display */
6785 LISTVIEW_InvalidateList(infoPtr); /* FIXME: display should not change for [SMALL]ICON view */
6792 * Updates an items or rearranges the listview control.
6795 * [I] infoPtr : valid pointer to the listview structure
6796 * [I] INT : item index
6802 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
6804 LONG lStyle = infoPtr->dwStyle;
6805 UINT uView = lStyle & LVS_TYPEMASK;
6807 TRACE("(nItem=%d)\n", nItem);
6809 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6811 /* rearrange with default alignment style */
6812 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
6813 LISTVIEW_Arrange(infoPtr, 0);
6815 LISTVIEW_InvalidateItem(infoPtr, nItem);
6823 * Creates the listview control.
6826 * [I] hwnd : window handle
6827 * [I] lpcs : the create parameters
6833 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
6835 LISTVIEW_INFO *infoPtr;
6836 UINT uView = lpcs->style & LVS_TYPEMASK;
6839 TRACE("(lpcs=%p)\n", lpcs);
6841 /* initialize info pointer */
6842 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
6843 if (!infoPtr) return -1;
6845 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
6847 infoPtr->hwndSelf = hwnd;
6848 infoPtr->dwStyle = lpcs->style;
6849 /* determine the type of structures to use */
6850 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
6851 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
6853 /* initialize color information */
6854 infoPtr->clrBk = CLR_NONE;
6855 infoPtr->clrText = comctl32_color.clrWindowText;
6856 infoPtr->clrTextBk = CLR_DEFAULT;
6857 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
6859 /* set default values */
6860 infoPtr->nFocusedItem = -1;
6861 infoPtr->nSelectionMark = -1;
6862 infoPtr->nHotItem = -1;
6863 infoPtr->bRedraw = TRUE;
6864 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
6865 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
6866 infoPtr->nEditLabelItem = -1;
6868 /* get default font (icon title) */
6869 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
6870 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
6871 infoPtr->hFont = infoPtr->hDefaultFont;
6872 LISTVIEW_SaveTextMetrics(infoPtr);
6875 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
6876 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
6877 0, 0, 0, 0, hwnd, (HMENU)0,
6878 lpcs->hInstance, NULL);
6880 /* set header unicode format */
6881 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
6883 /* set header font */
6884 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
6887 if (uView == LVS_ICON)
6889 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
6890 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
6892 else if (uView == LVS_REPORT)
6894 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
6896 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6900 /* set HDS_HIDDEN flag to hide the header bar */
6901 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
6902 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
6906 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6907 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6911 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6912 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6915 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
6916 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
6918 /* display unsupported listview window styles */
6919 LISTVIEW_UnsupportedStyles(lpcs->style);
6921 /* allocate memory for the data structure */
6922 infoPtr->hdpaItems = DPA_Create(10);
6923 infoPtr->hdpaPosX = DPA_Create(10);
6924 infoPtr->hdpaPosY = DPA_Create(10);
6926 /* allocate memory for the selection ranges */
6927 infoPtr->hdpaSelectionRanges = DPA_Create(10);
6929 /* initialize size of items */
6930 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6931 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6933 /* initialize the hover time to -1(indicating the default system hover time) */
6934 infoPtr->dwHoverTime = -1;
6941 * Erases the background of the listview control.
6944 * [I] infoPtr : valid pointer to the listview structure
6945 * [I] hdc : device context handle
6951 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
6955 TRACE("(hdc=%x)\n", hdc);
6957 if (!GetClipBox(hdc, &rc)) return FALSE;
6959 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
6965 * Helper function for LISTVIEW_[HV]Scroll *only*.
6966 * Performs vertical/horizontal scrolling by a give amount.
6969 * [I] infoPtr : valid pointer to the listview structure
6970 * [I] dx : amount of horizontal scroll
6971 * [I] dy : amount of vertical scroll
6973 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6975 /* now we can scroll the list */
6976 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
6977 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
6978 /* if we have focus, adjust rect */
6979 OffsetRect(&infoPtr->rcFocus, dx, dy);
6980 UpdateWindow(infoPtr->hwndSelf);
6985 * Performs vertical scrolling.
6988 * [I] infoPtr : valid pointer to the listview structure
6989 * [I] nScrollCode : scroll code
6990 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
6991 * [I] hScrollWnd : scrollbar control window handle
6997 * SB_LINEUP/SB_LINEDOWN:
6998 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
6999 * for LVS_REPORT is 1 line
7000 * for LVS_LIST cannot occur
7003 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7004 INT nScrollDiff, HWND hScrollWnd)
7006 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7007 INT nOldScrollPos, nNewScrollPos;
7008 SCROLLINFO scrollInfo;
7011 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7013 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7015 scrollInfo.cbSize = sizeof(SCROLLINFO);
7016 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7018 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7020 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7022 nOldScrollPos = scrollInfo.nPos;
7023 switch (nScrollCode)
7029 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7033 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7037 nScrollDiff = -scrollInfo.nPage;
7041 nScrollDiff = scrollInfo.nPage;
7044 case SB_THUMBPOSITION:
7046 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7053 /* quit right away if pos isn't changing */
7054 if (nScrollDiff == 0) return 0;
7056 /* calculate new position, and handle overflows */
7057 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7058 if (nScrollDiff > 0) {
7059 if (nNewScrollPos < nOldScrollPos ||
7060 nNewScrollPos > scrollInfo.nMax)
7061 nNewScrollPos = scrollInfo.nMax;
7063 if (nNewScrollPos > nOldScrollPos ||
7064 nNewScrollPos < scrollInfo.nMin)
7065 nNewScrollPos = scrollInfo.nMin;
7068 /* set the new position, and reread in case it changed */
7069 scrollInfo.fMask = SIF_POS;
7070 scrollInfo.nPos = nNewScrollPos;
7071 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7073 /* carry on only if it really changed */
7074 if (nNewScrollPos == nOldScrollPos) return 0;
7076 /* now adjust to client coordinates */
7077 nScrollDiff = nOldScrollPos - nNewScrollPos;
7078 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7080 /* and scroll the window */
7081 scroll_list(infoPtr, 0, nScrollDiff);
7088 * Performs horizontal scrolling.
7091 * [I] infoPtr : valid pointer to the listview structure
7092 * [I] nScrollCode : scroll code
7093 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7094 * [I] hScrollWnd : scrollbar control window handle
7100 * SB_LINELEFT/SB_LINERIGHT:
7101 * for LVS_ICON, LVS_SMALLICON 1 pixel
7102 * for LVS_REPORT is 1 pixel
7103 * for LVS_LIST is 1 column --> which is a 1 because the
7104 * scroll is based on columns not pixels
7107 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7108 INT nScrollDiff, HWND hScrollWnd)
7110 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7111 INT nOldScrollPos, nNewScrollPos;
7112 SCROLLINFO scrollInfo;
7114 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7116 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7118 scrollInfo.cbSize = sizeof(SCROLLINFO);
7119 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7121 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7123 nOldScrollPos = scrollInfo.nPos;
7125 switch (nScrollCode)
7139 nScrollDiff = -scrollInfo.nPage;
7143 nScrollDiff = scrollInfo.nPage;
7146 case SB_THUMBPOSITION:
7148 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7155 /* quit right away if pos isn't changing */
7156 if (nScrollDiff == 0) return 0;
7158 /* calculate new position, and handle overflows */
7159 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7160 if (nScrollDiff > 0) {
7161 if (nNewScrollPos < nOldScrollPos ||
7162 nNewScrollPos > scrollInfo.nMax)
7163 nNewScrollPos = scrollInfo.nMax;
7165 if (nNewScrollPos > nOldScrollPos ||
7166 nNewScrollPos < scrollInfo.nMin)
7167 nNewScrollPos = scrollInfo.nMin;
7170 /* set the new position, and reread in case it changed */
7171 scrollInfo.fMask = SIF_POS;
7172 scrollInfo.nPos = nNewScrollPos;
7173 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7175 /* carry on only if it really changed */
7176 if (nNewScrollPos == nOldScrollPos) return 0;
7178 if(uView == LVS_REPORT)
7179 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7181 /* now adjust to client coordinates */
7182 nScrollDiff = nOldScrollPos - nNewScrollPos;
7183 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7185 /* and scroll the window */
7186 scroll_list(infoPtr, nScrollDiff, 0);
7191 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7193 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7194 INT gcWheelDelta = 0;
7195 UINT pulScrollLines = 3;
7196 SCROLLINFO scrollInfo;
7198 TRACE("(wheelDelta=%d)\n", wheelDelta);
7200 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7201 gcWheelDelta -= wheelDelta;
7203 scrollInfo.cbSize = sizeof(SCROLLINFO);
7204 scrollInfo.fMask = SIF_POS;
7211 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7212 * should be fixed in the future.
7214 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7215 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7216 scrollInfo.nPos + (gcWheelDelta < 0) ?
7217 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7218 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7222 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7224 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7226 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7227 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7228 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7234 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7245 * [I] infoPtr : valid pointer to the listview structure
7246 * [I] INT : virtual key
7247 * [I] LONG : key data
7252 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7254 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7256 NMLVKEYDOWN nmKeyDown;
7258 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7260 /* send LVN_KEYDOWN notification */
7261 nmKeyDown.wVKey = nVirtualKey;
7262 nmKeyDown.flags = 0;
7263 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7265 switch (nVirtualKey)
7268 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7270 notify(infoPtr, NM_RETURN);
7271 notify(infoPtr, LVN_ITEMACTIVATE);
7276 if (infoPtr->nItemCount > 0)
7281 if (infoPtr->nItemCount > 0)
7282 nItem = infoPtr->nItemCount - 1;
7286 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7290 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7294 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7298 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7302 if (uView == LVS_REPORT)
7303 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7305 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7306 * LISTVIEW_GetCountPerRow(infoPtr);
7307 if(nItem < 0) nItem = 0;
7311 if (uView == LVS_REPORT)
7312 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7314 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7315 * LISTVIEW_GetCountPerRow(infoPtr);
7316 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7320 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7321 LISTVIEW_KeySelection(infoPtr, nItem);
7331 * [I] infoPtr : valid pointer to the listview structure
7336 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7340 /* if we did not have the focus, there's nothing to do */
7341 if (!infoPtr->bFocus) return 0;
7343 /* send NM_KILLFOCUS notification */
7344 notify(infoPtr, NM_KILLFOCUS);
7346 /* if we have a focus rectagle, get rid of it */
7347 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7349 /* set window focus flag */
7350 infoPtr->bFocus = FALSE;
7352 /* invalidate the selected items before reseting focus flag */
7353 LISTVIEW_InvalidateSelectedItems(infoPtr);
7360 * Processes double click messages (left mouse button).
7363 * [I] infoPtr : valid pointer to the listview structure
7364 * [I] wKey : key flag
7365 * [I] pts : mouse coordinate
7370 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7372 LVHITTESTINFO htInfo;
7374 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7376 /* send NM_RELEASEDCAPTURE notification */
7377 notify(infoPtr, NM_RELEASEDCAPTURE);
7379 htInfo.pt.x = pts.x;
7380 htInfo.pt.y = pts.y;
7382 /* send NM_DBLCLK notification */
7383 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7384 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7386 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7387 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7394 * Processes mouse down messages (left mouse button).
7397 * [I] infoPtr : valid pointer to the listview structure
7398 * [I] wKey : key flag
7399 * [I] pts : mouse coordinate
7404 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7406 LVHITTESTINFO lvHitTestInfo;
7407 LONG lStyle = infoPtr->dwStyle;
7408 static BOOL bGroupSelect = TRUE;
7409 POINT pt = { pts.x, pts.y };
7412 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7414 /* send NM_RELEASEDCAPTURE notification */
7415 notify(infoPtr, NM_RELEASEDCAPTURE);
7417 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7419 /* set left button down flag */
7420 infoPtr->bLButtonDown = TRUE;
7422 lvHitTestInfo.pt.x = pts.x;
7423 lvHitTestInfo.pt.y = pts.y;
7425 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7426 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7427 infoPtr->nEditLabelItem = -1;
7428 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7430 if (lStyle & LVS_SINGLESEL)
7432 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7433 infoPtr->nEditLabelItem = nItem;
7435 LISTVIEW_SetSelection(infoPtr, nItem);
7439 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7442 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7447 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7448 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7450 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7451 infoPtr->nSelectionMark = nItem;
7454 else if (wKey & MK_CONTROL)
7458 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7460 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7461 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7462 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7463 infoPtr->nSelectionMark = nItem;
7465 else if (wKey & MK_SHIFT)
7467 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7471 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7472 infoPtr->nEditLabelItem = nItem;
7474 /* set selection (clears other pre-existing selections) */
7475 LISTVIEW_SetSelection(infoPtr, nItem);
7481 /* remove all selections */
7482 LISTVIEW_RemoveAllSelections(infoPtr, -1);
7490 * Processes mouse up messages (left mouse button).
7493 * [I] infoPtr : valid pointer to the listview structure
7494 * [I] wKey : key flag
7495 * [I] pts : mouse coordinate
7500 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7502 LVHITTESTINFO lvHitTestInfo;
7504 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7506 if (!infoPtr->bLButtonDown) return 0;
7508 lvHitTestInfo.pt.x = pts.x;
7509 lvHitTestInfo.pt.y = pts.y;
7511 /* send NM_CLICK notification */
7512 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7513 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7515 /* set left button flag */
7516 infoPtr->bLButtonDown = FALSE;
7518 /* if we clicked on a selected item, edit the label */
7519 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7520 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7527 * Destroys the listview control (called after WM_DESTROY).
7530 * [I] infoPtr : valid pointer to the listview structure
7535 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7537 LONG lStyle = infoPtr->dwStyle;
7541 /* delete all items */
7542 LISTVIEW_DeleteAllItems(infoPtr);
7544 /* destroy data structure */
7545 DPA_Destroy(infoPtr->hdpaItems);
7546 DPA_Destroy(infoPtr->hdpaSelectionRanges);
7548 /* destroy image lists */
7549 if (!(lStyle & LVS_SHAREIMAGELISTS))
7551 /* FIXME: If the caller does a ImageList_Destroy and then we
7552 * do this code the area will be freed twice. Currently
7553 * this generates an "err:heap:HEAP_ValidateInUseArena
7554 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
7555 * has PREV_FREE flag" sometimes.
7557 * We will leak the memory till we figure out how to fix
7559 if (infoPtr->himlNormal)
7560 ImageList_Destroy(infoPtr->himlNormal);
7561 if (infoPtr->himlSmall)
7562 ImageList_Destroy(infoPtr->himlSmall);
7563 if (infoPtr->himlState)
7564 ImageList_Destroy(infoPtr->himlState);
7567 /* destroy font, bkgnd brush */
7569 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7570 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7572 /* free listview info pointer*/
7573 COMCTL32_Free(infoPtr);
7575 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7581 * Handles notifications from children.
7584 * [I] infoPtr : valid pointer to the listview structure
7585 * [I] INT : control identifier
7586 * [I] LPNMHDR : notification information
7591 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7593 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7595 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7597 /* handle notification from header control */
7598 if (lpnmh->code == HDN_ENDTRACKW)
7600 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7601 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7603 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7605 /* Handle sorting by Header Column */
7608 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7610 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
7611 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7613 else if(lpnmh->code == NM_RELEASEDCAPTURE)
7615 /* Idealy this should be done in HDN_ENDTRACKA
7616 * but since SetItemBounds in Header.c is called after
7617 * the notification is sent, it is neccessary to handle the
7618 * update of the scroll bar here (Header.c works fine as it is,
7619 * no need to disturb it)
7621 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7622 LISTVIEW_UpdateScroll(infoPtr);
7623 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7633 * Determines the type of structure to use.
7636 * [I] infoPtr : valid pointer to the listview structureof the sender
7637 * [I] HWND : listview window handle
7638 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
7643 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7645 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7647 if (nCommand == NF_REQUERY)
7648 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7649 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7655 * Paints/Repaints the listview control.
7658 * [I] infoPtr : valid pointer to the listview structure
7659 * [I] HDC : device context handle
7664 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7666 TRACE("(hdc=%x)\n", hdc);
7669 LISTVIEW_Refresh(infoPtr, hdc);
7674 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7676 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7677 LISTVIEW_Refresh(infoPtr, hdc);
7678 EndPaint(infoPtr->hwndSelf, &ps);
7686 * Processes double click messages (right mouse button).
7689 * [I] infoPtr : valid pointer to the listview structure
7690 * [I] wKey : key flag
7691 * [I] pts : mouse coordinate
7696 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7698 LVHITTESTINFO lvHitTestInfo;
7700 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7702 /* send NM_RELEASEDCAPTURE notification */
7703 notify(infoPtr, NM_RELEASEDCAPTURE);
7705 /* send NM_RDBLCLK notification */
7706 lvHitTestInfo.pt.x = pts.x;
7707 lvHitTestInfo.pt.y = pts.y;
7708 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7709 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
7716 * Processes mouse down messages (right mouse button).
7719 * [I] infoPtr : valid pointer to the listview structure
7720 * [I] wKey : key flag
7721 * [I] pts : mouse coordinate
7726 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7728 LVHITTESTINFO lvHitTestInfo;
7731 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7733 /* send NM_RELEASEDCAPTURE notification */
7734 notify(infoPtr, NM_RELEASEDCAPTURE);
7736 /* make sure the listview control window has the focus */
7737 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7739 /* set right button down flag */
7740 infoPtr->bRButtonDown = TRUE;
7742 /* determine the index of the selected item */
7743 lvHitTestInfo.pt.x = pts.x;
7744 lvHitTestInfo.pt.y = pts.y;
7745 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7747 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7749 LISTVIEW_SetItemFocus(infoPtr, nItem);
7750 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7751 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7752 LISTVIEW_SetSelection(infoPtr, nItem);
7756 LISTVIEW_RemoveAllSelections(infoPtr, -1);
7764 * Processes mouse up messages (right mouse button).
7767 * [I] infoPtr : valid pointer to the listview structure
7768 * [I] wKey : key flag
7769 * [I] pts : mouse coordinate
7774 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7776 LVHITTESTINFO lvHitTestInfo;
7779 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7781 if (!infoPtr->bRButtonDown) return 0;
7783 /* set button flag */
7784 infoPtr->bRButtonDown = FALSE;
7786 /* Send NM_RClICK notification */
7787 lvHitTestInfo.pt.x = pts.x;
7788 lvHitTestInfo.pt.y = pts.y;
7789 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7790 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
7792 /* Change to screen coordinate for WM_CONTEXTMENU */
7793 pt = lvHitTestInfo.pt;
7794 ClientToScreen(infoPtr->hwndSelf, &pt);
7796 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
7797 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
7798 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
7809 * [I] infoPtr : valid pointer to the listview structure
7810 * [I] hwnd : window handle of window containing the cursor
7811 * [I] nHittest : hit-test code
7812 * [I] wMouseMsg : ideintifier of the mouse message
7815 * TRUE if cursor is set
7818 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
7820 LVHITTESTINFO lvHitTestInfo;
7822 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
7824 if(!infoPtr->hHotCursor) return FALSE;
7826 GetCursorPos(&lvHitTestInfo.pt);
7827 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
7829 SetCursor(infoPtr->hHotCursor);
7839 * [I] infoPtr : valid pointer to the listview structure
7840 * [I] infoPtr : handle of previously focused window
7845 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
7847 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
7849 /* if we have the focus already, there's nothing to do */
7850 if (infoPtr->bFocus) return 0;
7852 /* send NM_SETFOCUS notification */
7853 notify(infoPtr, NM_SETFOCUS);
7855 /* set window focus flag */
7856 infoPtr->bFocus = TRUE;
7858 /* put the focus rect back on */
7859 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
7861 /* redraw all visible selected items */
7862 LISTVIEW_InvalidateSelectedItems(infoPtr);
7872 * [I] infoPtr : valid pointer to the listview structure
7873 * [I] HFONT : font handle
7874 * [I] WORD : redraw flag
7879 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
7881 HFONT oldFont = infoPtr->hFont;
7883 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
7885 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
7886 if (infoPtr->hFont == oldFont) return 0;
7888 LISTVIEW_SaveTextMetrics(infoPtr);
7890 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
7891 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
7893 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
7900 * Message handling for WM_SETREDRAW.
7901 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
7904 * [I] infoPtr : valid pointer to the listview structure
7905 * [I] bRedraw: state of redraw flag
7908 * DefWinProc return value
7910 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
7912 infoPtr->bRedraw = bRedraw;
7914 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
7915 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
7921 * Resizes the listview control. This function processes WM_SIZE
7922 * messages. At this time, the width and height are not used.
7925 * [I] infoPtr : valid pointer to the listview structure
7926 * [I] WORD : new width
7927 * [I] WORD : new height
7932 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
7934 LONG lStyle = infoPtr->dwStyle;
7935 UINT uView = lStyle & LVS_TYPEMASK;
7937 TRACE("(width=%d, height=%d)\n", Width, Height);
7939 if (LISTVIEW_UpdateSize(infoPtr))
7941 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
7943 if (lStyle & LVS_ALIGNLEFT)
7944 LISTVIEW_AlignLeft(infoPtr);
7946 LISTVIEW_AlignTop(infoPtr);
7949 LISTVIEW_UpdateScroll(infoPtr);
7951 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7959 * Sets the size information.
7962 * [I] infoPtr : valid pointer to the listview structure
7965 * Zero if no size change
7968 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
7970 LONG lStyle = infoPtr->dwStyle;
7971 UINT uView = lStyle & LVS_TYPEMASK;
7975 GetClientRect(infoPtr->hwndSelf, &rcList);
7976 CopyRect(&rcOld,&(infoPtr->rcList));
7977 infoPtr->rcList.left = 0;
7978 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
7979 infoPtr->rcList.top = 0;
7980 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
7982 if (uView == LVS_LIST)
7984 /* Apparently the "LIST" style is supposed to have the same
7985 * number of items in a column even if there is no scroll bar.
7986 * Since if a scroll bar already exists then the bottom is already
7987 * reduced, only reduce if the scroll bar does not currently exist.
7988 * The "2" is there to mimic the native control. I think it may be
7989 * related to either padding or edges. (GLA 7/2002)
7991 if (!(lStyle & WS_HSCROLL))
7993 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
7994 if (infoPtr->rcList.bottom > nHScrollHeight)
7995 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
7999 if (infoPtr->rcList.bottom > 2)
8000 infoPtr->rcList.bottom -= 2;
8003 else if (uView == LVS_REPORT)
8010 Header_Layout(infoPtr->hwndHeader, &hl);
8012 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8014 if (!(LVS_NOCOLUMNHEADER & lStyle))
8015 infoPtr->rcList.top = max(wp.cy, 0);
8017 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8022 * Processes WM_STYLECHANGED messages.
8025 * [I] infoPtr : valid pointer to the listview structure
8026 * [I] WPARAM : window style type (normal or extended)
8027 * [I] LPSTYLESTRUCT : window style information
8032 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8035 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8036 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8037 RECT rcList = infoPtr->rcList;
8039 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8040 wStyleType, lpss->styleOld, lpss->styleNew);
8042 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8044 if (wStyleType == GWL_STYLE)
8046 infoPtr->dwStyle = lpss->styleNew;
8048 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8049 ((lpss->styleNew & WS_HSCROLL) == 0))
8050 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8052 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8053 ((lpss->styleNew & WS_VSCROLL) == 0))
8054 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8056 /* If switching modes, then start with no scroll bars and then
8059 if (uNewView != uOldView)
8061 if (uOldView == LVS_REPORT)
8062 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8064 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8065 SetRectEmpty(&infoPtr->rcFocus);
8068 if (uNewView == LVS_ICON)
8072 /* First readjust the iconSize and if necessary the iconSpacing */
8073 oldcx = infoPtr->iconSize.cx;
8074 oldcy = infoPtr->iconSize.cy;
8075 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8076 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8077 if (infoPtr->himlNormal != NULL)
8080 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
8081 infoPtr->iconSize.cx = cx;
8082 infoPtr->iconSize.cy = cy;
8084 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
8086 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8087 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8088 LISTVIEW_SetIconSpacing(infoPtr,0);
8091 /* Now update the full item width and height */
8092 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8093 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8094 if (lpss->styleNew & LVS_ALIGNLEFT)
8095 LISTVIEW_AlignLeft(infoPtr);
8097 LISTVIEW_AlignTop(infoPtr);
8099 else if (uNewView == LVS_REPORT)
8106 Header_Layout(infoPtr->hwndHeader, &hl);
8107 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8109 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8110 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8112 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8113 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8114 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8115 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8117 else if (uNewView == LVS_LIST)
8119 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8120 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8121 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8122 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8126 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8127 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8128 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8129 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8130 if (lpss->styleNew & LVS_ALIGNLEFT)
8131 LISTVIEW_AlignLeft(infoPtr);
8133 LISTVIEW_AlignTop(infoPtr);
8136 /* update the size of the client area */
8137 LISTVIEW_UpdateSize(infoPtr);
8139 /* add scrollbars if needed */
8140 LISTVIEW_UpdateScroll(infoPtr);
8142 /* invalidate client area + erase background */
8143 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8145 /* print the list of unsupported window styles */
8146 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8149 /* If they change the view and we have an active edit control
8150 we will need to kill the control since the redraw will
8151 misplace the edit control.
8153 if (infoPtr->hwndEdit &&
8154 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8155 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8157 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8165 * Window procedure of the listview control.
8168 static LRESULT WINAPI
8169 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8171 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8173 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8175 if (!infoPtr && (uMsg != WM_CREATE))
8176 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8180 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8185 case LVM_APPROXIMATEVIEWRECT:
8186 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8187 LOWORD(lParam), HIWORD(lParam));
8189 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8191 /* case LVN_CANCELEDITLABEL */
8193 /* case LVM_CREATEDRAGIMAGE: */
8195 case LVM_DELETEALLITEMS:
8196 return LISTVIEW_DeleteAllItems(infoPtr);
8198 case LVM_DELETECOLUMN:
8199 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8201 case LVM_DELETEITEM:
8202 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8204 case LVM_EDITLABELW:
8205 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8207 case LVM_EDITLABELA:
8208 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8210 /* case LVN_ENABLEGROUPVIEW: */
8212 case LVM_ENSUREVISIBLE:
8213 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8216 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8219 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8221 case LVM_GETBKCOLOR:
8222 return infoPtr->clrBk;
8224 /* case LVM_GETBKIMAGE: */
8226 case LVM_GETCALLBACKMASK:
8227 return infoPtr->uCallbackMask;
8229 case LVM_GETCOLUMNA:
8230 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8232 case LVM_GETCOLUMNW:
8233 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8235 case LVM_GETCOLUMNORDERARRAY:
8236 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8238 case LVM_GETCOLUMNWIDTH:
8239 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8241 case LVM_GETCOUNTPERPAGE:
8242 return LISTVIEW_GetCountPerPage(infoPtr);
8244 case LVM_GETEDITCONTROL:
8245 return (LRESULT)infoPtr->hwndEdit;
8247 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8248 return infoPtr->dwLvExStyle;
8251 return (LRESULT)infoPtr->hwndHeader;
8253 case LVM_GETHOTCURSOR:
8254 return (LRESULT)infoPtr->hHotCursor;
8256 case LVM_GETHOTITEM:
8257 return infoPtr->nHotItem;
8259 case LVM_GETHOVERTIME:
8260 return infoPtr->dwHoverTime;
8262 case LVM_GETIMAGELIST:
8263 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8265 /* case LVN_GETINSERTMARK: */
8267 /* case LVN_GETINSERTMARKCOLOR: */
8269 /* case LVN_GETINSERTMARKRECT: */
8271 case LVM_GETISEARCHSTRINGA:
8272 case LVM_GETISEARCHSTRINGW:
8273 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8277 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8280 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8282 case LVM_GETITEMCOUNT:
8283 return infoPtr->nItemCount;
8285 case LVM_GETITEMPOSITION:
8286 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8288 case LVM_GETITEMRECT:
8289 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8291 case LVM_GETITEMSPACING:
8292 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8294 case LVM_GETITEMSTATE:
8295 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8297 case LVM_GETITEMTEXTA:
8298 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8300 case LVM_GETITEMTEXTW:
8301 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8303 case LVM_GETNEXTITEM:
8304 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8306 case LVM_GETNUMBEROFWORKAREAS:
8307 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8311 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8313 /* case LVN_GETOUTLINECOLOR: */
8315 /* case LVM_GETSELECTEDCOLUMN: */
8317 case LVM_GETSELECTEDCOUNT:
8318 return LISTVIEW_GetSelectedCount(infoPtr);
8320 case LVM_GETSELECTIONMARK:
8321 return infoPtr->nSelectionMark;
8323 case LVM_GETSTRINGWIDTHA:
8324 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8326 case LVM_GETSTRINGWIDTHW:
8327 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8329 case LVM_GETSUBITEMRECT:
8330 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8332 case LVM_GETTEXTBKCOLOR:
8333 return infoPtr->clrTextBk;
8335 case LVM_GETTEXTCOLOR:
8336 return infoPtr->clrText;
8338 /* case LVN_GETTILEINFO: */
8340 /* case LVN_GETTILEVIEWINFO: */
8342 case LVM_GETTOOLTIPS:
8343 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8346 case LVM_GETTOPINDEX:
8347 return LISTVIEW_GetTopIndex(infoPtr);
8349 /*case LVM_GETUNICODEFORMAT:
8350 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8353 case LVM_GETVIEWRECT:
8354 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8356 case LVM_GETWORKAREAS:
8357 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8360 /* case LVN_HASGROUP: */
8363 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8365 case LVM_INSERTCOLUMNA:
8366 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8368 case LVM_INSERTCOLUMNW:
8369 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8371 /* case LVN_INSERTGROUP: */
8373 /* case LVN_INSERTGROUPSORTED: */
8375 case LVM_INSERTITEMA:
8376 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8378 case LVM_INSERTITEMW:
8379 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8381 /* case LVN_INSERTMARKHITTEST: */
8383 /* case LVN_ISGROUPVIEWENABLED: */
8385 /* case LVN_MAPIDTOINDEX: */
8387 /* case LVN_INEDXTOID: */
8389 /* case LVN_MOVEGROUP: */
8391 /* case LVN_MOVEITEMTOGROUP: */
8393 case LVM_REDRAWITEMS:
8394 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8396 /* case LVN_REMOVEALLGROUPS: */
8398 /* case LVN_REMOVEGROUP: */
8401 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8403 case LVM_SETBKCOLOR:
8404 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8406 /* case LVM_SETBKIMAGE: */
8408 case LVM_SETCALLBACKMASK:
8409 infoPtr->uCallbackMask = (UINT)wParam;
8412 case LVM_SETCOLUMNA:
8413 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8415 case LVM_SETCOLUMNW:
8416 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8418 case LVM_SETCOLUMNORDERARRAY:
8419 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8421 case LVM_SETCOLUMNWIDTH:
8422 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8424 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8425 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8427 /* case LVN_SETGROUPINFO: */
8429 /* case LVN_SETGROUPMETRICS: */
8431 case LVM_SETHOTCURSOR:
8432 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8434 case LVM_SETHOTITEM:
8435 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8437 case LVM_SETHOVERTIME:
8438 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8440 case LVM_SETICONSPACING:
8441 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8443 case LVM_SETIMAGELIST:
8444 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8446 /* case LVN_SETINFOTIP: */
8448 /* case LVN_SETINSERTMARK: */
8450 /* case LVN_SETINSERTMARKCOLOR: */
8453 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8456 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8458 case LVM_SETITEMCOUNT:
8459 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8461 case LVM_SETITEMPOSITION:
8463 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8464 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8467 case LVM_SETITEMPOSITION32:
8468 if (lParam == 0) return FALSE;
8469 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8471 case LVM_SETITEMSTATE:
8472 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8474 case LVM_SETITEMTEXTA:
8475 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8477 case LVM_SETITEMTEXTW:
8478 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8480 /* case LVN_SETOUTLINECOLOR: */
8482 /* case LVN_SETSELECTEDCOLUMN: */
8484 case LVM_SETSELECTIONMARK:
8485 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8487 case LVM_SETTEXTBKCOLOR:
8488 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8490 case LVM_SETTEXTCOLOR:
8491 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8493 /* case LVN_SETTILEINFO: */
8495 /* case LVN_SETTILEVIEWINFO: */
8497 /* case LVN_SETTILEWIDTH: */
8499 /* case LVM_SETTOOLTIPS: */
8501 /* case LVM_SETUNICODEFORMAT: */
8503 /* case LVN_SETVIEW: */
8505 /* case LVM_SETWORKAREAS: */
8507 /* case LVN_SORTGROUPS: */
8510 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8512 case LVM_SUBITEMHITTEST:
8513 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8516 return LISTVIEW_Update(infoPtr, (INT)wParam);
8519 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8522 return LISTVIEW_Command(infoPtr, wParam, lParam);
8525 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8528 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8531 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8534 return infoPtr->hFont;
8537 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8540 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8543 return LISTVIEW_KillFocus(infoPtr);
8545 case WM_LBUTTONDBLCLK:
8546 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8548 case WM_LBUTTONDOWN:
8549 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8552 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8555 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8558 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8561 return LISTVIEW_NCDestroy(infoPtr);
8564 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8566 case WM_NOTIFYFORMAT:
8567 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8570 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8572 case WM_RBUTTONDBLCLK:
8573 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8575 case WM_RBUTTONDOWN:
8576 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8579 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8582 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8587 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8590 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8593 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8596 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8598 case WM_STYLECHANGED:
8599 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8601 case WM_SYSCOLORCHANGE:
8602 COMCTL32_RefreshSysColors();
8605 /* case WM_TIMER: */
8608 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8611 if (wParam & (MK_SHIFT | MK_CONTROL))
8612 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8613 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8615 case WM_WINDOWPOSCHANGED:
8616 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8617 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8618 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8619 LISTVIEW_UpdateSize(infoPtr);
8620 LISTVIEW_UpdateScroll(infoPtr);
8622 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8624 /* case WM_WININICHANGE: */
8627 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8628 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8631 /* call default window procedure */
8632 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8640 * Registers the window class.
8648 void LISTVIEW_Register(void)
8652 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8653 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8654 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8655 wndClass.cbClsExtra = 0;
8656 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8657 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8658 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8659 wndClass.lpszClassName = WC_LISTVIEWW;
8660 RegisterClassW(&wndClass);
8665 * Unregisters the window class.
8673 void LISTVIEW_Unregister(void)
8675 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8680 * Handle any WM_COMMAND messages
8686 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8688 switch (HIWORD(wParam))
8693 * Adjust the edit window size
8696 HDC hdc = GetDC(infoPtr->hwndEdit);
8697 HFONT hFont, hOldFont = 0;
8702 if (!infoPtr->hwndEdit || !hdc) return 0;
8703 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8704 GetWindowRect(infoPtr->hwndEdit, &rect);
8706 /* Select font to get the right dimension of the string */
8707 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8710 hOldFont = SelectObject(hdc, hFont);
8713 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8715 TEXTMETRICW textMetric;
8717 /* Add Extra spacing for the next character */
8718 GetTextMetricsW(hdc, &textMetric);
8719 sz.cx += (textMetric.tmMaxCharWidth * 2);
8727 rect.bottom - rect.top,
8728 SWP_DRAWFRAME|SWP_NOMOVE);
8731 SelectObject(hdc, hOldFont);
8733 ReleaseDC(infoPtr->hwndSelf, hdc);
8739 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8748 * Subclassed edit control windproc function
8754 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
8755 WPARAM wParam, LPARAM lParam, BOOL isW)
8757 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8758 BOOL cancel = FALSE;
8760 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8761 hwnd, uMsg, wParam, lParam, isW);
8766 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8773 WNDPROC editProc = infoPtr->EditWndProc;
8774 infoPtr->EditWndProc = 0;
8775 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
8776 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
8780 if (VK_ESCAPE == (INT)wParam)
8785 else if (VK_RETURN == (INT)wParam)
8789 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
8793 if (infoPtr->hwndEdit)
8795 LPWSTR buffer = NULL;
8797 infoPtr->hwndEdit = 0;
8800 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
8804 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
8806 if (isW) GetWindowTextW(hwnd, buffer, len+1);
8807 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
8811 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
8813 if (buffer) COMCTL32_Free(buffer);
8817 SendMessageW(hwnd, WM_CLOSE, 0, 0);
8823 * Subclassed edit control windproc function
8829 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8831 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
8836 * Subclassed edit control windproc function
8842 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8844 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
8849 * Creates a subclassed edit cotrol
8855 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
8856 INT x, INT y, INT width, INT height, BOOL isW)
8858 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
8863 TEXTMETRICW textMetric;
8864 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
8866 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
8868 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
8869 hdc = GetDC(infoPtr->hwndSelf);
8871 /* Select the font to get appropriate metric dimensions */
8872 if(infoPtr->hFont != 0)
8873 hOldFont = SelectObject(hdc, infoPtr->hFont);
8875 /*Get String Lenght in pixels */
8876 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
8878 /*Add Extra spacing for the next character */
8879 GetTextMetricsW(hdc, &textMetric);
8880 sz.cx += (textMetric.tmMaxCharWidth * 2);
8882 if(infoPtr->hFont != 0)
8883 SelectObject(hdc, hOldFont);
8885 ReleaseDC(infoPtr->hwndSelf, hdc);
8887 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8889 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8891 if (!hedit) return 0;
8893 infoPtr->EditWndProc = (WNDPROC)
8894 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
8895 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
8897 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);