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_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
55 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
56 * does *not* invoke DefWindowProc
57 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
58 * processing for "USEDOUBLECLICKTIME".
63 #include "wine/port.h"
75 #include "wine/debug.h"
77 WINE_DEFAULT_DEBUG_CHANNEL(listview);
79 typedef struct tagCOLUMNCACHE
83 } COLUMNCACHE, *LPCOLUMNCACHE;
85 typedef struct tagITEMHDR
89 } ITEMHDR, *LPITEMHDR;
91 typedef struct tagLISTVIEW_SUBITEM
97 typedef struct tagLISTVIEW_ITEM
106 typedef struct tagRANGE
112 typedef struct tagITERATOR
120 typedef struct tagLISTVIEW_INFO
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 */
157 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
162 DWORD dwStyle; /* the cached window GWL_STYLE */
163 DWORD dwLvExStyle; /* extended listview style */
166 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
167 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
168 PFNLVCOMPARE pfnCompare;
176 DWORD lastKeyPressTimestamp;
178 INT nSearchParamLength;
179 WCHAR szSearchParam[ MAX_PATH ];
186 /* How many we debug buffer to allocate */
187 #define DEBUG_BUFFERS 20
188 /* The size of a single debug bbuffer */
189 #define DEBUG_BUFFER_SIZE 256
191 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
192 #define SB_INTERNAL -1
194 /* maximum size of a label */
195 #define DISP_TEXT_SIZE 512
197 /* padding for items in list and small icon display modes */
198 #define WIDTH_PADDING 12
200 /* padding for items in list, report and small icon display modes */
201 #define HEIGHT_PADDING 1
203 /* offset of items in report display mode */
204 #define REPORT_MARGINX 2
206 /* padding for icon in large icon display mode
207 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
208 * that HITTEST will see.
209 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
210 * ICON_TOP_PADDING - sum of the two above.
211 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
212 * LABEL_VERT_PADDING - between bottom of text and end of box
214 * ICON_LR_PADDING - additional width above icon size.
215 * ICON_LR_HALF - half of the above value
217 #define ICON_TOP_PADDING_NOTHITABLE 2
218 #define ICON_TOP_PADDING_HITABLE 2
219 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
220 #define ICON_BOTTOM_PADDING 4
221 #define LABEL_VERT_PADDING 7
222 #define ICON_LR_PADDING 16
223 #define ICON_LR_HALF (ICON_LR_PADDING/2)
225 /* default label width for items in list and small icon display modes */
226 #define DEFAULT_LABEL_WIDTH 40
228 /* default column width for items in list display mode */
229 #define DEFAULT_COLUMN_WIDTH 128
231 /* Size of "line" scroll for V & H scrolls */
232 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
234 /* Padding betwen image and label */
235 #define IMAGE_PADDING 2
237 /* Padding behind the label */
238 #define TRAILING_PADDING 5
240 /* Border for the icon caption */
241 #define CAPTION_BORDER 2
243 /* Standard DrawText flags */
244 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
245 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
246 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
248 /* The time in milisecods to reset the search in the list */
249 #define KEY_DELAY 450
251 /* Dump the LISTVIEW_INFO structure to the debug channel */
252 #define LISTVIEW_DUMP(iP) do { \
253 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
254 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
255 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
256 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
257 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
258 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
259 (iP->bFocus) ? "true" : "false"); \
260 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
261 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
262 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
263 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
265 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
266 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
271 * forward declarations
273 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
274 static BOOL LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
275 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
276 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
277 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
278 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *);
279 static BOOL LISTVIEW_GetItemListOrigin(LISTVIEW_INFO *, INT, LPPOINT);
280 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
281 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
282 static INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *);
283 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *, INT, LPRECT);
284 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
285 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *, INT);
286 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
287 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
288 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
289 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
290 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, POINT);
291 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
292 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
293 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
294 static void LISTVIEW_UnsupportedStyles(LONG);
295 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
296 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
297 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
298 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
299 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
300 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
301 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
302 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
303 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
304 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
305 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
306 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
307 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
309 /******** Text handling functions *************************************/
311 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
312 * text string. The string may be ANSI or Unicode, in which case
313 * the boolean isW tells us the type of the string.
315 * The name of the function tell what type of strings it expects:
316 * W: Unicode, T: ANSI/Unicode - function of isW
319 static inline BOOL is_textW(LPCWSTR text)
321 return text != NULL && text != LPSTR_TEXTCALLBACKW;
324 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
326 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
327 return is_textW(text);
330 static inline int textlenT(LPCWSTR text, BOOL isW)
332 return !is_textT(text, isW) ? 0 :
333 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
336 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
339 if (isSrcW) lstrcpynW(dest, src, max);
340 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
342 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
343 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
346 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
348 LPWSTR wstr = (LPWSTR)text;
350 if (!isW && is_textT(text, isW))
352 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
353 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
354 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
356 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
360 static inline void textfreeT(LPWSTR wstr, BOOL isW)
362 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
366 * dest is a pointer to a Unicode string
367 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
369 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
373 if (src == LPSTR_TEXTCALLBACKW)
375 if (is_textW(*dest)) COMCTL32_Free(*dest);
376 *dest = LPSTR_TEXTCALLBACKW;
380 LPWSTR pszText = textdupTtoW(src, isW);
381 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
382 bResult = Str_SetPtrW(dest, pszText);
383 textfreeT(pszText, isW);
389 * compares a Unicode to a Unicode/ANSI text string
391 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
393 if (!aw) return bt ? -1 : 0;
394 if (!bt) return aw ? 1 : 0;
395 if (aw == LPSTR_TEXTCALLBACKW)
396 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
397 if (bt != LPSTR_TEXTCALLBACKW)
399 LPWSTR bw = textdupTtoW(bt, isW);
400 int r = bw ? lstrcmpW(aw, bw) : 1;
408 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
412 n = min(min(n, strlenW(s1)), strlenW(s2));
413 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
414 return res ? res - sizeof(WCHAR) : res;
417 /******** Debugging functions *****************************************/
419 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
421 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
422 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
425 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
427 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
428 n = min(textlenT(text, isW), n);
429 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
432 static char* debug_getbuf()
434 static int index = 0;
435 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
436 return buffers[index++ % DEBUG_BUFFERS];
439 static inline char* debugpoint(const POINT* lppt)
443 char* buf = debug_getbuf();
444 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
446 } else return "(null)";
449 static inline char* debugrect(const RECT* rect)
453 char* buf = debug_getbuf();
454 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
455 rect->left, rect->top, rect->right, rect->bottom);
457 } else return "(null)";
460 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
462 char* buf = debug_getbuf(), *text = buf;
463 int len, size = DEBUG_BUFFER_SIZE;
465 if (lpLVItem == NULL) return "(null)";
466 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
467 if (len == -1) goto end; buf += len; size -= len;
468 if (lpLVItem->mask & LVIF_STATE)
469 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
471 if (len == -1) goto end; buf += len; size -= len;
472 if (lpLVItem->mask & LVIF_TEXT)
473 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
475 if (len == -1) goto end; buf += len; size -= len;
476 if (lpLVItem->mask & LVIF_IMAGE)
477 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
479 if (len == -1) goto end; buf += len; size -= len;
480 if (lpLVItem->mask & LVIF_PARAM)
481 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
483 if (len == -1) goto end; buf += len; size -= len;
484 if (lpLVItem->mask & LVIF_INDENT)
485 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
487 if (len == -1) goto end; buf += len; size -= len;
490 buf = text + strlen(text);
492 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
496 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
498 char* buf = debug_getbuf(), *text = buf;
499 int len, size = DEBUG_BUFFER_SIZE;
501 if (lpColumn == NULL) return "(null)";
502 len = snprintf(buf, size, "{");
503 if (len == -1) goto end; buf += len; size -= len;
504 if (lpColumn->mask & LVCF_SUBITEM)
505 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
507 if (len == -1) goto end; buf += len; size -= len;
508 if (lpColumn->mask & LVCF_FMT)
509 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
511 if (len == -1) goto end; buf += len; size -= len;
512 if (lpColumn->mask & LVCF_WIDTH)
513 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
515 if (len == -1) goto end; buf += len; size -= len;
516 if (lpColumn->mask & LVCF_TEXT)
517 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
519 if (len == -1) goto end; buf += len; size -= len;
520 if (lpColumn->mask & LVCF_IMAGE)
521 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
523 if (len == -1) goto end; buf += len; size -= len;
524 if (lpColumn->mask & LVCF_ORDER)
525 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
527 if (len == -1) goto end; buf += len; size -= len;
530 buf = text + strlen(text);
532 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
537 /******** Notification functions i************************************/
539 static inline BOOL notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
541 pnmh->hwndFrom = infoPtr->hwndSelf;
542 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
544 return (BOOL)SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
545 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
548 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code)
551 return notify_hdr(infoPtr, code, &nmh);
554 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
556 notify(infoPtr, LVN_ITEMACTIVATE);
559 static inline BOOL notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
561 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
564 static BOOL notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
568 ZeroMemory(&nmlv, sizeof(nmlv));
569 nmlv.iItem = lvht->iItem;
570 nmlv.iSubItem = lvht->iSubItem;
571 nmlv.ptAction = lvht->pt;
572 return notify_listview(infoPtr, NM_RCLICK, &nmlv);
575 static int get_ansi_notification(INT unicodeNotificationCode)
577 switch (unicodeNotificationCode)
579 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
580 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
581 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
582 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
583 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
584 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
586 ERR("unknown notification %x\n", unicodeNotificationCode);
587 return unicodeNotificationCode;
591 Send notification. depends on dispinfoW having same
592 structure as dispinfoA.
593 infoPtr : listview struct
594 notificationCode : *Unicode* notification code
595 pdi : dispinfo structure (can be unicode or ansi)
596 isW : TRUE if dispinfo is Unicode
598 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
600 BOOL bResult = FALSE;
601 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
603 INT cchTempBufMax = 0, savCchTextMax = 0;
604 LPWSTR pszTempBuf = NULL, savPszText = NULL;
606 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
608 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
609 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
612 if (convertToAnsi || convertToUnicode)
614 if (notificationCode != LVN_GETDISPINFOW)
616 cchTempBufMax = convertToUnicode ?
617 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
618 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
622 cchTempBufMax = pdi->item.cchTextMax;
623 *pdi->item.pszText = 0; /* make sure we don't process garbage */
626 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
627 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
628 if (!pszTempBuf) return FALSE;
629 if (convertToUnicode)
630 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
631 pszTempBuf, cchTempBufMax);
633 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
634 cchTempBufMax, NULL, NULL);
635 savCchTextMax = pdi->item.cchTextMax;
636 savPszText = pdi->item.pszText;
637 pdi->item.pszText = pszTempBuf;
638 pdi->item.cchTextMax = cchTempBufMax;
641 if (infoPtr->notifyFormat == NFR_ANSI)
642 realNotifCode = get_ansi_notification(notificationCode);
644 realNotifCode = notificationCode;
645 bResult = notify_hdr(infoPtr, realNotifCode, (LPNMHDR)pdi);
647 if (convertToUnicode || convertToAnsi)
649 if (convertToUnicode) /* note : pointer can be changed by app ! */
650 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
651 savCchTextMax, NULL, NULL);
653 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
654 savPszText, savCchTextMax);
655 pdi->item.pszText = savPszText; /* restores our buffer */
656 pdi->item.cchTextMax = savCchTextMax;
657 HeapFree(GetProcessHeap(), 0, pszTempBuf);
662 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc, LPRECT rcBounds, LVITEMW *lpLVItem)
666 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
667 lpnmlvcd->nmcd.hdc = hdc;
668 lpnmlvcd->nmcd.rc = *rcBounds;
671 lpnmlvcd->nmcd.dwItemSpec = lpLVItem->iItem;
672 lpnmlvcd->iSubItem = lpLVItem->iSubItem;
673 if (lpLVItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
674 if (lpLVItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
675 if (lpLVItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
676 lpnmlvcd->nmcd.lItemlParam = lpLVItem->lParam;
679 isSelected = lpnmlvcd->nmcd.uItemState & CDIS_SELECTED;
680 /* subitems are selected only in full-row-select, report mode */
681 if ( lpnmlvcd->iSubItem != 0 &&
682 !((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
683 (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) )
686 if (isSelected && infoPtr->bFocus)
688 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
689 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
691 else if (isSelected && (infoPtr->dwStyle & LVS_SHOWSELALWAYS))
693 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
694 lpnmlvcd->clrText = comctl32_color.clrBtnText;
698 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
699 lpnmlvcd->clrText = infoPtr->clrText;
703 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
705 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
706 return notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
709 /******** Item iterator functions **********************************/
711 static BOOL ranges_add(HDPA ranges, RANGE range);
712 static BOOL ranges_del(HDPA ranges, RANGE range);
713 static void ranges_dump(HDPA ranges);
715 static inline BOOL iterator_next(ITERATOR* i)
719 if (i->ranges) goto pickarange;
720 return (i->nItem = i->range.lower) != -1;
723 if (i->nItem <= i->range.upper)
727 if (i->ranges && i->index < i->ranges->nItemCount)
729 i->range = *(RANGE*)DPA_GetPtr(i->ranges, i->index++);
730 return (i->nItem = i->range.lower) != -1;
732 i->nItem = i->range.lower = i->range.upper = -1;
736 static RANGE iterator_range(ITERATOR* i)
740 if (!i->ranges) return i->range;
742 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges, 0)).lower;
743 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges, i->ranges->nItemCount - 1)).upper;
747 static inline void iterator_destroy(ITERATOR* i)
749 if (i->ranges) DPA_Destroy(i->ranges);
752 static inline BOOL iterator_empty(ITERATOR* i)
754 ZeroMemory(i, sizeof(*i));
755 i->nItem = i->range.lower = i->range.upper = -1;
759 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT* lprc)
761 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
766 /* in case we fail, we want to return an empty iterator */
767 if (!iterator_empty(i)) return FALSE;
769 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
771 OffsetRect(&frame, -Origin.x, -Origin.y);
773 if (uView == LVS_ICON || uView == LVS_SMALLICON)
775 /* FIXME: we got to do better then this */
777 i->range.upper = infoPtr->nItemCount - 1;
780 else if (uView == LVS_REPORT)
782 if (frame.left >= infoPtr->nItemWidth) return TRUE;
783 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
785 lower = frame.top / infoPtr->nItemHeight;
786 upper = (frame.bottom - 1) / infoPtr->nItemHeight;
790 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
791 if (frame.top >= infoPtr->nItemHeight * nPerCol) return TRUE;
792 lower = (frame.left / infoPtr->nItemWidth) * nPerCol + frame.top / infoPtr->nItemHeight;
793 upper = ((frame.right - 1) / infoPtr->nItemWidth) * nPerCol + (frame.bottom - 1) / infoPtr->nItemHeight;
796 if (upper < 0 || lower >= infoPtr->nItemCount) return TRUE;
797 lower = max(lower, 0);
798 upper = min(upper, infoPtr->nItemCount - 1);
799 if (upper < lower) return TRUE;
800 i->range.lower = lower;
801 i->range.upper = upper;
806 static BOOL iterator_clippeditems(ITERATOR* i, LISTVIEW_INFO *infoPtr, HDC hdc)
808 POINT Origin, Position;
812 rgntype = GetClipBox(hdc, &rcClip);
813 if (rgntype == NULLREGION) return iterator_empty(i);
814 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
815 if (rgntype == SIMPLEREGION) return TRUE;
817 /* if we can't deal with the region, we'll just go with the simple range */
818 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return TRUE;
819 if (!(i->ranges = DPA_Create(10))) return TRUE;
820 if (!ranges_add(i->ranges, i->range))
822 DPA_Destroy(i->ranges);
827 /* no delete the invisible items from the list */
828 for (nItem = i->range.lower; nItem <= i->range.upper; nItem++)
830 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) continue;
831 rcItem.left = Position.x + Origin.x;
832 rcItem.top = Position.y + Origin.y;
833 rcItem.right = rcItem.left + infoPtr->nItemWidth;
834 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
835 if (!RectVisible(hdc, &rcItem))
837 RANGE item_range = { nItem, nItem };
838 ranges_del(i->ranges, item_range);
845 static inline BOOL iterator_visibleitems(ITERATOR* i, LISTVIEW_INFO *infoPtr)
847 return iterator_frameditems(i, infoPtr, &infoPtr->rcList);
850 /******** Misc helper functions ************************************/
852 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
853 WPARAM wParam, LPARAM lParam, BOOL isW)
855 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
856 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
859 /******** Internal API functions ************************************/
861 /* The Invalidate* are macros, so we preserve the caller location */
862 #define LISTVIEW_InvalidateRect(infoPtr, rect) while(infoPtr->bRedraw) { \
863 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
864 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
868 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
870 if(LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox)) \
871 LISTVIEW_InvalidateRect(infoPtr, &rcBox); \
874 #define LISTVIEW_InvalidateList(infoPtr)\
875 LISTVIEW_InvalidateRect(infoPtr, NULL)
877 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
879 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
884 * Retrieves the number of items that can fit vertically in the client area.
887 * [I] infoPtr : valid pointer to the listview structure
890 * Number of items per row.
892 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
894 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
896 return max(nListWidth/infoPtr->nItemWidth, 1);
901 * Retrieves the number of items that can fit horizontally in the client
905 * [I] infoPtr : valid pointer to the listview structure
908 * Number of items per column.
910 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
912 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
914 return max(nListHeight / infoPtr->nItemHeight, 1);
918 /*************************************************************************
919 * LISTVIEW_ProcessLetterKeys
921 * Processes keyboard messages generated by pressing the letter keys
923 * What this does is perform a case insensitive search from the
924 * current position with the following quirks:
925 * - If two chars or more are pressed in quick succession we search
926 * for the corresponding string (e.g. 'abc').
927 * - If there is a delay we wipe away the current search string and
928 * restart with just that char.
929 * - If the user keeps pressing the same character, whether slowly or
930 * fast, so that the search string is entirely composed of this
931 * character ('aaaaa' for instance), then we search for first item
932 * that starting with that character.
933 * - If the user types the above character in quick succession, then
934 * we must also search for the corresponding string ('aaaaa'), and
935 * go to that string if there is a match.
938 * [I] hwnd : handle to the window
939 * [I] charCode : the character code, the actual character
940 * [I] keyData : key data
948 * - The current implementation has a list of characters it will
949 * accept and it ignores averything else. In particular it will
950 * ignore accentuated characters which seems to match what
951 * Windows does. But I'm not sure it makes sense to follow
953 * - We don't sound a beep when the search fails.
957 * TREEVIEW_ProcessLetterKeys
959 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
964 WCHAR buffer[MAX_PATH];
965 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
967 /* simple parameter checking */
968 if (!charCode || !keyData) return 0;
970 /* only allow the valid WM_CHARs through */
971 if (!isalnum(charCode) &&
972 charCode != '.' && charCode != '`' && charCode != '!' &&
973 charCode != '@' && charCode != '#' && charCode != '$' &&
974 charCode != '%' && charCode != '^' && charCode != '&' &&
975 charCode != '*' && charCode != '(' && charCode != ')' &&
976 charCode != '-' && charCode != '_' && charCode != '+' &&
977 charCode != '=' && charCode != '\\'&& charCode != ']' &&
978 charCode != '}' && charCode != '[' && charCode != '{' &&
979 charCode != '/' && charCode != '?' && charCode != '>' &&
980 charCode != '<' && charCode != ',' && charCode != '~')
983 /* if there's one item or less, there is no where to go */
984 if (infoPtr->nItemCount <= 1) return 0;
986 /* update the search parameters */
987 infoPtr->lastKeyPressTimestamp = GetTickCount();
988 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
989 if (infoPtr->nSearchParamLength < MAX_PATH)
990 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
991 if (infoPtr->charCode != charCode)
992 infoPtr->charCode = charCode = 0;
994 infoPtr->charCode=charCode;
995 infoPtr->szSearchParam[0]=charCode;
996 infoPtr->nSearchParamLength=1;
997 /* Redundant with the 1 char string */
1001 /* and search from the current position */
1003 if (infoPtr->nFocusedItem >= 0) {
1004 endidx=infoPtr->nFocusedItem;
1006 /* if looking for single character match,
1007 * then we must always move forward
1009 if (infoPtr->nSearchParamLength == 1)
1012 endidx=infoPtr->nItemCount;
1016 if (idx == infoPtr->nItemCount) {
1017 if (endidx == infoPtr->nItemCount || endidx == 0)
1023 item.mask = LVIF_TEXT;
1026 item.pszText = buffer;
1027 item.cchTextMax = MAX_PATH;
1028 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1030 /* check for a match */
1031 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1034 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1035 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1036 /* This would work but we must keep looking for a longer match */
1040 } while (idx != endidx);
1043 LISTVIEW_KeySelection(infoPtr, nItem);
1048 /*************************************************************************
1049 * LISTVIEW_UpdateHeaderSize [Internal]
1051 * Function to resize the header control
1054 * hwnd [I] handle to a window
1055 * nNewScrollPos [I] Scroll Pos to Set
1062 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1067 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1069 GetWindowRect(infoPtr->hwndHeader, &winRect);
1070 point[0].x = winRect.left;
1071 point[0].y = winRect.top;
1072 point[1].x = winRect.right;
1073 point[1].y = winRect.bottom;
1075 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1076 point[0].x = -nNewScrollPos;
1077 point[1].x += nNewScrollPos;
1079 SetWindowPos(infoPtr->hwndHeader,0,
1080 point[0].x,point[0].y,point[1].x,point[1].y,
1081 SWP_NOZORDER | SWP_NOACTIVATE);
1086 * Update the scrollbars. This functions should be called whenever
1087 * the content, size or view changes.
1090 * [I] infoPtr : valid pointer to the listview structure
1095 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1097 LONG lStyle = infoPtr->dwStyle;
1098 UINT uView = lStyle & LVS_TYPEMASK;
1099 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1100 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1101 SCROLLINFO scrollInfo;
1103 if (lStyle & LVS_NOSCROLL) return;
1105 scrollInfo.cbSize = sizeof(SCROLLINFO);
1107 if (uView == LVS_LIST)
1109 /* update horizontal scrollbar */
1110 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1111 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1113 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1114 infoPtr->nItemCount, nCountPerColumn, nCountPerRow);
1116 scrollInfo.nMin = 0;
1117 scrollInfo.nMax = infoPtr->nItemCount / nCountPerColumn;
1118 if((infoPtr->nItemCount % nCountPerColumn) == 0)
1120 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1121 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1122 scrollInfo.nPage = nCountPerRow;
1123 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1124 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1125 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1127 else if (uView == LVS_REPORT)
1131 /* update vertical scrollbar */
1132 scrollInfo.nMin = 0;
1133 scrollInfo.nMax = infoPtr->nItemCount - 1;
1134 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1135 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1136 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1137 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1138 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1139 scrollInfo.nMax, scrollInfo.nPage, test);
1140 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1141 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1143 /* update horizontal scrollbar */
1144 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1145 scrollInfo.fMask = SIF_POS;
1146 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1147 || infoPtr->nItemCount == 0)
1149 scrollInfo.nPos = 0;
1151 scrollInfo.nMin = 0;
1152 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1153 scrollInfo.nPage = nListWidth;
1154 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1155 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1156 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1157 scrollInfo.nMax, scrollInfo.nPage, test);
1158 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1159 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1161 /* Update the Header Control */
1162 scrollInfo.fMask = SIF_POS;
1163 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1164 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1171 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1173 INT nViewWidth = rcView.right - rcView.left;
1174 INT nViewHeight = rcView.bottom - rcView.top;
1176 /* Update Horizontal Scrollbar */
1177 scrollInfo.fMask = SIF_POS;
1178 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1179 || infoPtr->nItemCount == 0)
1181 scrollInfo.nPos = 0;
1183 scrollInfo.nMin = 0;
1184 scrollInfo.nMax = max(nViewWidth, 0)-1;
1185 scrollInfo.nPage = nListWidth;
1186 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1187 TRACE("LVS_ICON/SMALLICON Horz.\n");
1188 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1190 /* Update Vertical Scrollbar */
1191 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1192 scrollInfo.fMask = SIF_POS;
1193 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1194 || infoPtr->nItemCount == 0)
1196 scrollInfo.nPos = 0;
1198 scrollInfo.nMin = 0;
1199 scrollInfo.nMax = max(nViewHeight,0)-1;
1200 scrollInfo.nPage = nListHeight;
1201 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1202 TRACE("LVS_ICON/SMALLICON Vert.\n");
1203 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1211 * Shows/hides the focus rectangle.
1214 * [I] infoPtr : valid pointer to the listview structure
1215 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1220 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1222 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1225 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1227 if (infoPtr->nFocusedItem < 0) return;
1229 /* we need some gymnastics in ICON mode to handle large items */
1230 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1234 if (!LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox))
1236 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1238 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1243 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1245 /* for some reason, owner draw should work only in report mode */
1246 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1251 item.iItem = infoPtr->nFocusedItem;
1253 item.mask = LVIF_PARAM;
1254 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1256 ZeroMemory(&dis, sizeof(dis));
1257 dis.CtlType = ODT_LISTVIEW;
1258 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1259 dis.itemID = item.iItem;
1260 dis.itemAction = ODA_FOCUS;
1261 if (fShow) dis.itemState |= ODS_FOCUS;
1262 dis.hwndItem = infoPtr->hwndSelf;
1264 if (!LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem)) goto done;
1265 dis.itemData = item.lParam;
1267 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1271 DrawFocusRect(hdc, &infoPtr->rcFocus);
1274 ReleaseDC(infoPtr->hwndSelf, hdc);
1278 * Invalidates all visible selected items.
1280 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1284 iterator_visibleitems(&i, infoPtr);
1285 while(iterator_next(&i))
1287 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1288 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1290 iterator_destroy(&i);
1296 * Prints a message for unsupported window styles.
1297 * A kind of TODO list for window styles.
1300 * [I] LONG : window style
1305 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1307 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1308 FIXME(" LVS_NOSCROLL\n");
1310 if (lStyle & LVS_NOLABELWRAP)
1311 FIXME(" LVS_NOLABELWRAP\n");
1313 if (lStyle & LVS_SORTASCENDING)
1314 FIXME(" LVS_SORTASCENDING\n");
1316 if (lStyle & LVS_SORTDESCENDING)
1317 FIXME(" LVS_SORTDESCENDING\n");
1322 * DESCRIPTION: [INTERNAL]
1323 * Computes an item's (left,top) corner, relative to rcView.
1324 * That is, the position has NOT been made relative to the Origin.
1325 * This is deliberate, to avoid computing the Origin over, and
1326 * over again, when this function is call in a loop. Instead,
1327 * one ca factor the computation of the Origin before the loop,
1328 * and offset the value retured by this function, on every iteration.
1331 * [I] infoPtr : valid pointer to the listview structure
1332 * [I] nItem : item number
1333 * [O] lpptOrig : item top, left corner
1336 * TRUE if computations OK
1339 static BOOL LISTVIEW_GetItemListOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1341 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1343 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1345 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1346 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1348 else if (uView == LVS_LIST)
1350 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1351 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1352 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1354 else /* LVS_REPORT */
1356 lpptPosition->x = REPORT_MARGINX;
1357 lpptPosition->y = nItem * infoPtr->nItemHeight;
1364 * DESCRIPTION: [INTERNAL]
1365 * Compute the rectangles of an item. This is to localize all
1366 * the computations in one place. If you are not interested in some
1367 * of these values, simply pass in a NULL -- the fucntion is smart
1368 * enough to compute only what's necessary. The function computes
1369 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1370 * one, the BOX rectangle. This rectangle is very cheap to compute,
1371 * and is guaranteed to contain all the other rectangles. Computing
1372 * the ICON rect is also cheap, but all the others are potentaily
1373 * expensive. This gives an easy and effective optimization when
1374 * searching (like point inclusion, or rectangle intersection):
1375 * first test against the BOX, and if TRUE, test agains the desired
1376 * rectangle. These optimizations are coded in:
1377 * LISTVIEW_PtInRect, and LISTVIEW_IntersectRect
1378 * use them wherever is appropriate.
1381 * [I] infoPtr : valid pointer to the listview structure
1382 * [I] lpLVItem : item to compute the measures for
1383 * [O] lprcBox : ptr to Box rectangle
1384 * The internal LVIR_BOX rectangle
1385 * [O] lprcBounds : ptr to Bounds rectangle
1386 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1387 * [0] lprcState : ptr to State icon rectangle
1388 * The internal LVIR_STATE rectangle
1389 * [O] lprcIcon : ptr to Icon rectangle
1390 * Same as LVM_GETITEMRECT with LVIR_ICON
1391 * [O] lprcLabel : ptr to Label rectangle
1392 * Same as LVM_GETITEMRECT with LVIR_LABEL
1395 * TRUE if computations OK
1398 static BOOL LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem,
1399 LPRECT lprcBox, LPRECT lprcBounds,
1400 LPRECT lprcState, LPRECT lprcIcon, LPRECT lprcLabel)
1402 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1403 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1404 RECT Box, State, Icon, Label;
1406 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1408 /* Be smart and try to figure out the minimum we have to do */
1411 if (uView == LVS_REPORT) doIcon = TRUE;
1412 else doLabel = TRUE;
1414 if (uView == LVS_ICON && (lprcBox || lprcBounds || lprcLabel))
1416 if (!(lpLVItem->mask & LVIF_STATE) ||
1417 !(lpLVItem->stateMask & LVIS_FOCUSED))
1419 if (lpLVItem->state & LVIS_FOCUSED)
1420 oversizedBox = doLabel = TRUE;
1422 if (lprcLabel) doLabel = TRUE;
1423 if (doLabel || lprcIcon) doIcon = TRUE;
1424 if (doIcon || lprcState) doState = TRUE;
1426 /************************************************************/
1427 /* compute the box rectangle (it should be cheap to do) */
1428 /************************************************************/
1431 Box.right = infoPtr->nItemWidth;
1432 Box.bottom = infoPtr->nItemHeight;
1434 /************************************************************/
1435 /* compute STATEICON bounding box */
1436 /************************************************************/
1439 if (uView == LVS_ICON)
1441 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1442 if (infoPtr->himlNormal)
1443 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1444 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1448 /* we need the ident in report mode, if we don't have it, we fail */
1449 if (uView == LVS_REPORT && !(lpLVItem->mask & LVIF_INDENT)) goto fail;
1450 State.left = Box.left;
1451 if (uView == LVS_REPORT) State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1452 State.top = Box.top;
1454 State.right = State.left;
1455 State.bottom = State.top;
1456 if (infoPtr->himlState)
1458 State.right += infoPtr->iconStateSize.cx;
1459 State.bottom += infoPtr->iconStateSize.cy;
1461 if (lprcState) *lprcState = State;
1462 TRACE(" - state=%s\n", debugrect(&State));
1465 /************************************************************/
1466 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1467 /************************************************************/
1470 if (uView == LVS_ICON)
1472 Icon.left = Box.left;
1473 if (infoPtr->himlNormal)
1474 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1475 Icon.top = Box.top + ICON_TOP_PADDING;
1476 Icon.right = Icon.left;
1477 Icon.bottom = Icon.top;
1478 if (infoPtr->himlNormal)
1480 Icon.right += infoPtr->iconSize.cx;
1481 Icon.bottom += infoPtr->iconSize.cy;
1484 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1486 Icon.left = State.right;
1487 if (infoPtr->himlState) Icon.left += IMAGE_PADDING;
1489 Icon.right = Icon.left;
1490 if (infoPtr->himlSmall) Icon.right += infoPtr->iconSize.cx;
1491 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1493 if(lprcIcon) *lprcIcon = Icon;
1494 TRACE(" - icon=%s\n", debugrect(&Icon));
1497 /************************************************************/
1498 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1499 /************************************************************/
1502 SIZE labelSize = { 0, 0 };
1504 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1506 labelSize.cx = infoPtr->nItemWidth;
1507 labelSize.cy = infoPtr->nItemHeight;
1510 /* we need the text in non owner draw mode */
1511 if (!(lpLVItem->mask & LVIF_TEXT)) goto fail;
1512 if (is_textT(lpLVItem->pszText, TRUE))
1514 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1515 HDC hdc = GetDC(infoPtr->hwndSelf);
1516 HFONT hOldFont = SelectObject(hdc, hFont);
1520 /* compute rough rectangle where the label will go */
1521 SetRectEmpty(&rcText);
1522 rcText.right = infoPtr->nItemWidth - TRAILING_PADDING;
1523 rcText.bottom = infoPtr->nItemHeight;
1524 if (uView == LVS_ICON)
1525 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1527 /* now figure out the flags */
1528 if (uView == LVS_ICON)
1529 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1531 uFormat = LV_SL_DT_FLAGS;
1533 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
1535 labelSize.cx = min(rcText.right - rcText.left + TRAILING_PADDING, infoPtr->nItemWidth);
1536 labelSize.cy = rcText.bottom - rcText.top;
1538 SelectObject(hdc, hOldFont);
1539 ReleaseDC(infoPtr->hwndSelf, hdc);
1543 if (uView == LVS_ICON)
1545 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
1546 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1547 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1548 Label.right = Label.left + labelSize.cx;
1549 Label.bottom = Label.top + infoPtr->nItemHeight;
1550 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1552 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
1553 labelSize.cy /= infoPtr->ntmHeight;
1554 labelSize.cy = max(labelSize.cy, 1);
1555 labelSize.cy *= infoPtr->ntmHeight;
1557 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1559 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1561 Label.left = Icon.right;
1562 if (infoPtr->himlSmall) Label.left += IMAGE_PADDING;
1563 Label.top = Box.top;
1564 Label.right = Label.left + labelSize.cx;
1565 if (Label.right > Box.right) Label.right = Box.right;
1566 Label.bottom = Label.top + infoPtr->nItemHeight;
1569 if (lprcLabel) *lprcLabel = Label;
1570 TRACE(" - label=%s\n", debugrect(&Label));
1573 /***********************************************************/
1574 /* compute bounds box for the item (ala LVM_GETITEMRECT) */
1575 /***********************************************************/
1578 if (uView == LVS_REPORT)
1580 lprcBounds->left = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT ? Box.left : Icon.left;
1581 lprcBounds->top = Box.top;
1582 lprcBounds->right = min(lprcBounds->left + infoPtr->nItemWidth, Box.right) - REPORT_MARGINX;
1583 if (lprcBounds->right < lprcBounds->left) lprcBounds->right = lprcBounds->left;
1584 lprcBounds->bottom = lprcBounds->top + infoPtr->nItemHeight;
1587 UnionRect(lprcBounds, &Icon, &Label);
1588 TRACE(" - bounds=%s\n", debugrect(lprcBounds));
1591 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1592 else if (lprcBox) *lprcBox = Box;
1593 TRACE(" - box=%s\n", debugrect(&Box));
1598 ERR("Incorrect item=%s; please report.\n", debuglvitem_t(lpLVItem, TRUE));
1599 *((char *)0) = 0; /* it's an internal function, should never happen */
1604 * DESCRIPTION: [INTERNAL]
1607 * [I] infoPtr : valid pointer to the listview structure
1608 * [I] nItem : item number
1609 * [O] lprcBox : ptr to Box rectangle
1610 * The internal LVIR_BOX rectangle
1613 * TRUE if computations OK
1616 static BOOL LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
1618 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1619 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1620 POINT Position, Origin;
1623 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) return FALSE;
1624 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
1626 /* Be smart and try to figure out the minimum we have to do */
1628 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1629 lvItem.mask |= LVIF_TEXT;
1630 lvItem.iItem = nItem;
1631 lvItem.iSubItem = 0;
1632 lvItem.pszText = szDispText;
1633 lvItem.cchTextMax = DISP_TEXT_SIZE;
1634 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
1635 if (uView == LVS_ICON)
1637 lvItem.mask |= LVIF_STATE;
1638 lvItem.stateMask = LVIS_FOCUSED;
1639 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
1641 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0)) return FALSE;
1643 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
1650 * Aligns the items with the top edge of the window.
1653 * [I] infoPtr : valid pointer to the listview structure
1658 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1660 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1661 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1664 INT i, off_x=0, off_y=0;
1666 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1668 /* Since SetItemPosition uses upper-left of icon, and for
1669 style=LVS_ICON the icon is not left adjusted, get the offset */
1670 if (uView == LVS_ICON)
1672 off_y = ICON_TOP_PADDING;
1673 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1677 ZeroMemory(&rcView, sizeof(RECT));
1678 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1680 infoPtr->rcList.left, infoPtr->rcList.right);
1682 if (nListWidth > infoPtr->nItemWidth)
1684 for (i = 0; i < infoPtr->nItemCount; i++)
1686 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1689 ptItem.y += infoPtr->nItemHeight;
1692 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1693 ptItem.x += infoPtr->nItemWidth;
1694 rcView.right = max(rcView.right, ptItem.x);
1697 rcView.right -= off_x;
1698 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1702 for (i = 0; i < infoPtr->nItemCount; i++)
1704 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1705 ptItem.y += infoPtr->nItemHeight;
1708 rcView.right = infoPtr->nItemWidth;
1709 rcView.bottom = ptItem.y-off_y;
1712 infoPtr->rcView = rcView;
1718 * Aligns the items with the left edge of the window.
1721 * [I] infoPtr : valid pointer to the listview structure
1726 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1728 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1729 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1732 INT i, off_x=0, off_y=0;
1734 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1736 /* Since SetItemPosition uses upper-left of icon, and for
1737 style=LVS_ICON the icon is not left adjusted, get the offset */
1738 if (uView == LVS_ICON)
1740 off_y = ICON_TOP_PADDING;
1741 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1745 ZeroMemory(&rcView, sizeof(RECT));
1746 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1748 if (nListHeight > infoPtr->nItemHeight)
1750 for (i = 0; i < infoPtr->nItemCount; i++)
1752 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1755 ptItem.x += infoPtr->nItemWidth;
1758 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1759 ptItem.y += infoPtr->nItemHeight;
1760 rcView.bottom = max(rcView.bottom, ptItem.y);
1763 rcView.right = ptItem.x + infoPtr->nItemWidth;
1767 for (i = 0; i < infoPtr->nItemCount; i++)
1769 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1770 ptItem.x += infoPtr->nItemWidth;
1773 rcView.bottom = infoPtr->nItemHeight;
1774 rcView.right = ptItem.x;
1777 infoPtr->rcView = rcView;
1784 * Retrieves the bounding rectangle of all the items.
1787 * [I] infoPtr : valid pointer to the listview structure
1788 * [O] lprcView : bounding rectangle
1794 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1798 TRACE("(lprcView=%p)\n", lprcView);
1800 if (!lprcView) return FALSE;
1802 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return FALSE;
1804 *lprcView = infoPtr->rcView;
1805 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
1807 TRACE("lprcView=%s\n", debugrect(lprcView));
1814 * Retrieves the subitem pointer associated with the subitem index.
1817 * [I] HDPA : DPA handle for a specific item
1818 * [I] INT : index of subitem
1821 * SUCCESS : subitem pointer
1824 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1827 LISTVIEW_SUBITEM *lpSubItem;
1830 /* we should binary search here if need be */
1831 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1833 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1834 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
1844 * Calculates the width of a specific item.
1847 * [I] infoPtr : valid pointer to the listview structure
1848 * [I] nItem : item to calculate width, or -1 for max of all
1851 * Returns the width of an item width an item.
1853 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr, INT nItem)
1855 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1856 INT nItemWidth = 0, i;
1858 if (uView == LVS_ICON)
1859 nItemWidth = infoPtr->iconSpacing.cx;
1860 else if (uView == LVS_REPORT)
1862 INT nHeaderItemCount;
1865 /* calculate width of header */
1866 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1867 for (i = 0; i < nHeaderItemCount; i++)
1868 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem))
1869 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1875 if (infoPtr->nItemCount == 0) return DEFAULT_COLUMN_WIDTH;
1877 /* get width of string */
1880 for (i = 0; i < infoPtr->nItemCount; i++)
1882 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
1883 nItemWidth = max(nItemWidth, nLabelWidth);
1887 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1888 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
1889 nItemWidth += WIDTH_PADDING;
1890 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
1891 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
1892 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1895 return max(nItemWidth, 1);
1900 * Calculates the max width of any item in the list.
1903 * [I] infoPtr : valid pointer to the listview structure
1904 * [I] LONG : window style
1907 * Returns item width.
1909 static inline INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *infoPtr)
1911 return LISTVIEW_CalculateItemWidth(infoPtr, -1);
1916 * Retrieves and saves important text metrics info for the current
1920 * [I] infoPtr : valid pointer to the listview structure
1923 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
1926 HDC hdc = GetDC(infoPtr->hwndSelf);
1927 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1928 INT oldHeight, oldACW;
1930 GetTextMetricsW(hdc, &tm);
1932 oldHeight = infoPtr->ntmHeight;
1933 oldACW = infoPtr->ntmAveCharWidth;
1934 infoPtr->ntmHeight = tm.tmHeight;
1935 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1937 SelectObject(hdc, hOldFont);
1938 ReleaseDC(infoPtr->hwndSelf, hdc);
1939 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1940 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1946 * Calculates the height of an item.
1949 * [I] infoPtr : valid pointer to the listview structure
1952 * Returns item height.
1954 static INT LISTVIEW_CalculateMaxHeight(LISTVIEW_INFO *infoPtr)
1958 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
1959 nItemHeight = infoPtr->iconSpacing.cy;
1962 nItemHeight = infoPtr->ntmHeight;
1963 if (infoPtr->himlState)
1964 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
1965 if (infoPtr->himlSmall)
1966 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
1967 if (infoPtr->himlState || infoPtr->himlSmall)
1968 nItemHeight += HEIGHT_PADDING;
1974 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
1976 ERR("Selections are:\n");
1977 ranges_dump(infoPtr->hdpaSelectionRanges);
1983 * A compare function for ranges
1986 * [I] range1 : pointer to range 1;
1987 * [I] range2 : pointer to range 2;
1991 * >0 : if Item 1 > Item 2
1992 * <0 : if Item 2 > Item 1
1993 * 0 : if Item 1 == Item 2
1995 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
1997 if (((RANGE*)range1)->upper < ((RANGE*)range2)->lower)
1999 if (((RANGE*)range2)->upper < ((RANGE*)range1)->lower)
2004 static void ranges_dump(HDPA ranges)
2008 for (i = 0; i < ranges->nItemCount; i++)
2010 RANGE *selection = DPA_GetPtr(ranges, i);
2011 ERR(" [%d - %d]\n", selection->lower, selection->upper);
2015 static inline BOOL ranges_contain(HDPA ranges, INT nItem)
2017 RANGE srchrng = { nItem, nItem };
2019 return DPA_Search(ranges, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2022 static BOOL ranges_shift(HDPA ranges, INT nItem, INT delta)
2024 RANGE srchrng, *chkrng;
2027 srchrng.upper = nItem;
2028 srchrng.lower = nItem;
2030 index = DPA_Search(ranges, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2031 if (index == -1) return TRUE;
2033 for (;index < ranges->nItemCount; index++)
2035 chkrng = DPA_GetPtr(ranges, index);
2036 if (chkrng->lower >= nItem && chkrng->lower + delta >= 0)
2037 chkrng->lower += delta;
2038 if (chkrng->upper >= nItem && chkrng->upper + delta >= 0)
2039 chkrng->upper += delta;
2044 static BOOL ranges_add(HDPA ranges, RANGE range)
2049 TRACE("range=(%i - %i)\n", range.lower, range.upper);
2050 if (TRACE_ON(listview)) ranges_dump(ranges);
2052 /* try find overlapping regions first */
2053 srchrgn.lower = range.lower - 1;
2054 srchrgn.upper = range.upper + 1;
2055 index = DPA_Search(ranges, &srchrgn, 0, ranges_cmp, 0, 0);
2061 /* create the brand new range to insert */
2062 newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2063 if(!newrgn) return FALSE;
2066 /* figure out where to insert it */
2067 index = DPA_Search(ranges, newrgn, 0, ranges_cmp, 0, DPAS_INSERTAFTER);
2068 if (index == -1) index = 0;
2070 /* and get it over with */
2071 DPA_InsertPtr(ranges, index, newrgn);
2075 RANGE *chkrgn, *mrgrgn;
2076 INT fromindex, mergeindex;
2078 chkrgn = DPA_GetPtr(ranges, index);
2079 if (!chkrgn) return FALSE;
2080 TRACE("Merge with index %i (%d - %d)\n",
2081 index, chkrgn->lower, chkrgn->upper);
2083 chkrgn->lower = min(range.lower, chkrgn->lower);
2084 chkrgn->upper = max(range.upper, chkrgn->upper);
2086 TRACE("New range %i (%d - %d)\n",
2087 index, chkrgn->lower, chkrgn->upper);
2089 /* merge now common anges */
2091 srchrgn.lower = chkrgn->lower - 1;
2092 srchrgn.upper = chkrgn->upper + 1;
2096 mergeindex = DPA_Search(ranges, &srchrgn, fromindex, ranges_cmp, 0, 0);
2097 if (mergeindex == -1) break;
2098 if (mergeindex == index)
2100 fromindex = index + 1;
2104 TRACE("Merge with index %i\n", mergeindex);
2106 mrgrgn = DPA_GetPtr(ranges, mergeindex);
2107 if (!mrgrgn) return FALSE;
2109 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2110 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2111 COMCTL32_Free(mrgrgn);
2112 DPA_DeletePtr(ranges, mergeindex);
2113 if (mergeindex < index) index --;
2120 static BOOL ranges_del(HDPA ranges, RANGE range)
2122 RANGE remrgn, tmprgn, *chkrgn;
2126 TRACE("range: (%d - %d)\n", range.lower, range.upper);
2131 index = DPA_Search(ranges, &remrgn, 0, ranges_cmp, 0, 0);
2132 if (index == -1) return TRUE;
2134 chkrgn = DPA_GetPtr(ranges, index);
2135 if (!chkrgn) return FALSE;
2137 TRACE("Matches range index %i (%d - %d)\n",
2138 index, chkrgn->lower, chkrgn->upper);
2140 /* case 1: Same range */
2141 if ( (chkrgn->upper == remrgn.upper) &&
2142 (chkrgn->lower == remrgn.lower) )
2144 DPA_DeletePtr(ranges, index);
2147 /* case 2: engulf */
2148 else if ( (chkrgn->upper <= remrgn.upper) &&
2149 (chkrgn->lower >= remrgn.lower) )
2151 DPA_DeletePtr(ranges, index);
2153 /* case 3: overlap upper */
2154 else if ( (chkrgn->upper <= remrgn.upper) &&
2155 (chkrgn->lower < remrgn.lower) )
2157 chkrgn->upper = remrgn.lower - 1;
2159 /* case 4: overlap lower */
2160 else if ( (chkrgn->upper > remrgn.upper) &&
2161 (chkrgn->lower >= remrgn.lower) )
2163 chkrgn->lower = remrgn.upper + 1;
2165 /* case 5: fully internal */
2168 RANGE *newrgn = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2169 if (!newrgn) return FALSE;
2171 newrgn->lower = chkrgn->lower;
2172 newrgn->upper = remrgn.lower - 1;
2173 chkrgn->lower = remrgn.upper + 1;
2174 DPA_InsertPtr(ranges, index, newrgn);
2184 * Helper function for LISTVIEW_RemoveSelectionRange, and LISTVIEW_SetItem.
2186 static BOOL remove_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
2188 RANGE range = { lower, upper };
2192 if (!ranges_del(infoPtr->hdpaSelectionRanges, range)) return FALSE;
2193 if (adj_sel_only) return TRUE;
2195 /* reset the selection on items */
2197 lvItem.stateMask = LVIS_SELECTED;
2198 for(i = lower; i <= upper; i++)
2199 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
2205 * Helper function for LISTVIEW_AddSelectionRange, and LISTVIEW_SetItem.
2207 static BOOL add_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
2209 RANGE range = { lower, upper };
2213 if (!ranges_add(infoPtr->hdpaSelectionRanges, range)) return FALSE;
2214 if (adj_sel_only) return TRUE;
2216 /* set the selection on items */
2217 lvItem.state = LVIS_SELECTED;
2218 lvItem.stateMask = LVIS_SELECTED;
2219 for(i = lower; i <= upper; i++)
2220 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
2227 * Adds a selection range.
2230 * [I] infoPtr : valid pointer to the listview structure
2231 * [I] lower : lower item index
2232 * [I] upper : upper item index
2238 static inline BOOL LISTVIEW_AddSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2240 return add_selection_range(infoPtr, lower, upper, FALSE);
2245 * Removes a range selections.
2248 * [I] infoPtr : valid pointer to the listview structure
2249 * [I] lower : lower item index
2250 * [I] upper : upper item index
2256 static inline BOOL LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2258 return remove_selection_range(infoPtr, lower, upper, FALSE);
2263 * Removes all selection ranges
2266 * [I] infoPtr : valid pointer to the listview structure
2272 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr)
2276 if (infoPtr->bRemovingAllSelections) return TRUE;
2278 infoPtr->bRemovingAllSelections = TRUE;
2284 sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, 0);
2285 if (sel) LISTVIEW_RemoveSelectionRange(infoPtr, sel->lower, sel->upper);
2287 while (infoPtr->hdpaSelectionRanges->nItemCount > 0);
2289 infoPtr->bRemovingAllSelections = FALSE;
2296 * Retrieves the number of items that are marked as selected.
2299 * [I] infoPtr : valid pointer to the listview structure
2302 * Number of items selected.
2304 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2306 INT i, nSelectedCount = 0;
2308 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2311 for (i = 0; i < infoPtr->nItemCount; i++)
2313 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2319 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
2321 RANGE *sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
2322 nSelectedCount += sel->upper - sel->lower + 1;
2326 TRACE("nSelectedCount=%d\n", nSelectedCount);
2327 return nSelectedCount;
2332 * Manages the item focus.
2335 * [I] infoPtr : valid pointer to the listview structure
2336 * [I] INT : item index
2339 * TRUE : focused item changed
2340 * FALSE : focused item has NOT changed
2342 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2344 INT oldFocus = infoPtr->nFocusedItem;
2347 lvItem.state = LVIS_FOCUSED;
2348 lvItem.stateMask = LVIS_FOCUSED;
2349 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2351 return oldFocus != infoPtr->nFocusedItem;
2356 * Updates the various indices after an item has been inserted or deleted.
2359 * [I] infoPtr : valid pointer to the listview structure
2360 * [I] nItem : item index
2361 * [I] direction : Direction of shift, +1 or -1.
2366 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2368 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2370 ranges_shift(infoPtr->hdpaSelectionRanges, nItem, direction);
2372 /* Note that the following will fail if direction != +1 and -1 */
2373 if (infoPtr->nSelectionMark > nItem)
2374 infoPtr->nSelectionMark += direction;
2375 else if (infoPtr->nSelectionMark == nItem)
2378 infoPtr->nSelectionMark += direction;
2379 else if (infoPtr->nSelectionMark >= infoPtr->nItemCount)
2380 infoPtr->nSelectionMark = infoPtr->nItemCount - 1;
2383 if (infoPtr->nFocusedItem > nItem)
2384 infoPtr->nFocusedItem += direction;
2385 else if (infoPtr->nFocusedItem == nItem)
2388 infoPtr->nFocusedItem += direction;
2391 if (infoPtr->nFocusedItem >= infoPtr->nItemCount)
2392 infoPtr->nFocusedItem = infoPtr->nItemCount - 1;
2393 if (infoPtr->nFocusedItem >= 0)
2394 LISTVIEW_SetItemFocus(infoPtr, infoPtr->nFocusedItem);
2397 /* But we are not supposed to modify nHotItem! */
2403 * Adds a block of selections.
2406 * [I] infoPtr : valid pointer to the listview structure
2407 * [I] INT : item index
2412 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2414 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2415 INT nLast = max(infoPtr->nSelectionMark, nItem);
2422 item.state = LVIS_SELECTED;
2423 item.stateMask = LVIS_SELECTED;
2425 /* FIXME: this is not correct LVS_OWNERDATA
2426 * See docu for LVN_ITEMCHANGED. Is there something similar for
2427 * RemoveGroupSelection (is there such a thing?)?
2429 for (i = nFirst; i <= nLast; i++)
2430 LISTVIEW_SetItemState(infoPtr,i,&item);
2432 LISTVIEW_SetItemFocus(infoPtr, nItem);
2433 infoPtr->nSelectionMark = nItem;
2439 * Sets a single group selection.
2442 * [I] infoPtr : valid pointer to the listview structure
2443 * [I] INT : item index
2448 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2450 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2456 LISTVIEW_RemoveAllSelections(infoPtr);
2458 item.state = LVIS_SELECTED;
2459 item.stateMask = LVIS_SELECTED;
2461 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2465 if (infoPtr->nSelectionMark == -1)
2466 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2469 nFirst = min(infoPtr->nSelectionMark, nItem);
2470 nLast = max(infoPtr->nSelectionMark, nItem);
2472 for (i = nFirst; i <= nLast; i++)
2473 LISTVIEW_SetItemState(infoPtr, i, &item);
2477 RECT rcItem, rcSelMark;
2480 rcItem.left = LVIR_BOUNDS;
2481 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2482 rcSelMark.left = LVIR_BOUNDS;
2483 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2484 UnionRect(&rcSel, &rcItem, &rcSelMark);
2485 iterator_frameditems(&i, infoPtr, &rcSel);
2486 while(iterator_next(&i))
2488 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
2489 if (PtInRect(&rcSel, ptItem))
2490 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
2492 iterator_destroy(&i);
2495 LISTVIEW_SetItemFocus(infoPtr, nItem);
2500 * Sets a single selection.
2503 * [I] infoPtr : valid pointer to the listview structure
2504 * [I] INT : item index
2509 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2513 TRACE("nItem=%d\n", nItem);
2515 LISTVIEW_RemoveAllSelections(infoPtr);
2517 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2518 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2519 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2521 infoPtr->nSelectionMark = nItem;
2526 * Set selection(s) with keyboard.
2529 * [I] infoPtr : valid pointer to the listview structure
2530 * [I] INT : item index
2533 * SUCCESS : TRUE (needs to be repainted)
2534 * FAILURE : FALSE (nothing has changed)
2536 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2538 /* FIXME: pass in the state */
2539 LONG lStyle = infoPtr->dwStyle;
2540 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2541 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2542 BOOL bResult = FALSE;
2544 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2546 if (lStyle & LVS_SINGLESEL)
2549 LISTVIEW_SetSelection(infoPtr, nItem);
2556 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2560 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2565 LISTVIEW_SetSelection(infoPtr, nItem);
2568 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2571 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2578 * Called when the mouse is being actively tracked and has hovered for a specified
2582 * [I] infoPtr : valid pointer to the listview structure
2583 * [I] fwKeys : key indicator
2584 * [I] pts : mouse position
2587 * 0 if the message was processed, non-zero if there was an error
2590 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2591 * over the item for a certain period of time.
2594 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2596 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2597 /* FIXME: select the item!!! */
2598 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2605 * Called whenever WM_MOUSEMOVE is received.
2608 * [I] infoPtr : valid pointer to the listview structure
2609 * [I] fwKeys : key indicator
2610 * [I] pts : mouse position
2613 * 0 if the message is processed, non-zero if there was an error
2615 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2617 TRACKMOUSEEVENT trackinfo;
2619 /* see if we are supposed to be tracking mouse hovering */
2620 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2621 /* fill in the trackinfo struct */
2622 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2623 trackinfo.dwFlags = TME_QUERY;
2624 trackinfo.hwndTrack = infoPtr->hwndSelf;
2625 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2627 /* see if we are already tracking this hwnd */
2628 _TrackMouseEvent(&trackinfo);
2630 if(!(trackinfo.dwFlags & TME_HOVER)) {
2631 trackinfo.dwFlags = TME_HOVER;
2633 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2634 _TrackMouseEvent(&trackinfo);
2643 * Tests wheather the item is assignable to a list with style lStyle
2645 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2647 if ( (lpLVItem->mask & LVIF_TEXT) &&
2648 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2649 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2656 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2659 * [I] infoPtr : valid pointer to the listview structure
2660 * [I] lpLVItem : valid pointer to new item atttributes
2661 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2667 static BOOL set_owner_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2669 LONG lStyle = infoPtr->dwStyle;
2673 /* a virtual listview stores only the state for the main item */
2674 if (lpLVItem->iSubItem || !(lpLVItem->mask & LVIF_STATE)) return FALSE;
2676 oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, LVIS_FOCUSED | LVIS_SELECTED);
2677 TRACE("oldState=%x, newState=%x, uCallbackMask=%x\n",
2678 oldState, lpLVItem->state, infoPtr->uCallbackMask);
2680 /* we're done if we don't need to change anything we handle */
2681 if ( !((oldState ^ lpLVItem->state) & lpLVItem->stateMask &
2682 ~infoPtr->uCallbackMask & (LVIS_FOCUSED | LVIS_SELECTED))) return TRUE;
2685 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2686 * by LVS_OWERNDATA list controls
2689 /* if we handle the focus, and we're asked to change it, do it now */
2690 if ( lpLVItem->stateMask & LVIS_FOCUSED )
2692 if (lpLVItem->state & LVIS_FOCUSED)
2693 infoPtr->nFocusedItem = lpLVItem->iItem;
2694 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2695 infoPtr->nFocusedItem = -1;
2698 /* and the selection is the only other state a virtual list may hold */
2699 if (lpLVItem->stateMask & LVIS_SELECTED)
2701 if (lpLVItem->state & LVIS_SELECTED)
2703 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2704 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2707 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2710 /* notify the parent now that things have changed */
2711 ZeroMemory(&nmlv, sizeof(nmlv));
2712 nmlv.iItem = lpLVItem->iItem;
2713 nmlv.uNewState = lpLVItem->state;
2714 nmlv.uOldState = oldState;
2715 nmlv.uChanged = LVIF_STATE;
2716 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2723 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2726 * [I] infoPtr : valid pointer to the listview structure
2727 * [I] lpLVItem : valid pointer to new item atttributes
2728 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2734 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2736 LONG lStyle = infoPtr->dwStyle;
2737 UINT uView = lStyle & LVS_TYPEMASK;
2739 LISTVIEW_ITEM *lpItem;
2743 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2744 if (!hdpaSubItems && hdpaSubItems != (HDPA)-1) return FALSE;
2746 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2747 if (!lpItem) return FALSE;
2749 /* determine what fields will change */
2750 if ((lpLVItem->mask & LVIF_STATE) &&
2751 ((lpItem->state ^ lpLVItem->state) & lpLVItem->stateMask))
2752 uChanged |= LVIF_STATE;
2754 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2755 uChanged |= LVIF_IMAGE;
2757 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2758 uChanged |= LVIF_PARAM;
2760 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2761 uChanged |= LVIF_INDENT;
2763 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2764 uChanged |= LVIF_TEXT;
2766 if (!uChanged) return TRUE;
2768 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2769 nmlv.iItem = lpLVItem->iItem;
2770 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2771 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2772 nmlv.uChanged = uChanged;
2773 nmlv.lParam = lpItem->lParam;
2775 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2776 if(lpItem->valid && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
2779 /* copy information */
2780 if (lpLVItem->mask & LVIF_TEXT)
2781 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
2783 if (lpLVItem->mask & LVIF_IMAGE)
2784 lpItem->hdr.iImage = lpLVItem->iImage;
2786 if (lpLVItem->mask & LVIF_PARAM)
2787 lpItem->lParam = lpLVItem->lParam;
2789 if (lpLVItem->mask & LVIF_INDENT)
2790 lpItem->iIndent = lpLVItem->iIndent;
2792 if (uChanged & LVIF_STATE)
2794 lpItem->state &= ~lpLVItem->stateMask;
2795 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2796 if (nmlv.uNewState & LVIS_SELECTED)
2798 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2799 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2801 else if (lpLVItem->stateMask & LVIS_SELECTED)
2802 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2804 /* if we are asked to change focus, and we manage it, do it */
2805 if (nmlv.uNewState & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
2807 if (lpLVItem->state & LVIS_FOCUSED)
2809 infoPtr->nFocusedItem = lpLVItem->iItem;
2810 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
2812 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2813 infoPtr->nFocusedItem = -1;
2817 /* if LVS_LIST or LVS_SMALLICON, update the width of the items */
2818 if((uChanged & LVIF_TEXT) && ((uView == LVS_LIST) || (uView == LVS_SMALLICON)))
2820 int item_width = LISTVIEW_CalculateItemWidth(infoPtr, lpLVItem->iItem);
2821 if(item_width > infoPtr->nItemWidth) infoPtr->nItemWidth = item_width;
2824 /* if we're inserting the item, we're done */
2825 if (!lpItem->valid) return TRUE;
2827 /* send LVN_ITEMCHANGED notification */
2828 nmlv.lParam = lpItem->lParam;
2829 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2836 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2839 * [I] infoPtr : valid pointer to the listview structure
2840 * [I] lpLVItem : valid pointer to new subitem atttributes
2841 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2847 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2850 LISTVIEW_SUBITEM *lpSubItem;
2851 BOOL bModified = FALSE;
2853 /* set subitem only if column is present */
2854 if (Header_GetItemCount(infoPtr->hwndHeader) <= lpLVItem->iSubItem)
2857 /* First do some sanity checks */
2858 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
2859 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
2861 /* get the subitem structure, and create it if not there */
2862 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2863 if (!hdpaSubItems) return FALSE;
2865 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
2868 LISTVIEW_SUBITEM *tmpSubItem;
2871 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2872 if (!lpSubItem) return FALSE;
2873 /* we could binary search here, if need be...*/
2874 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2876 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2877 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
2879 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
2881 COMCTL32_Free(lpSubItem);
2884 lpSubItem->iSubItem = lpLVItem->iSubItem;
2888 if (lpLVItem->mask & LVIF_IMAGE)
2889 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
2891 lpSubItem->hdr.iImage = lpLVItem->iImage;
2895 if (lpLVItem->mask & LVIF_TEXT)
2896 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
2898 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
2902 if (bModified && !infoPtr->bIsDrawing)
2906 rect.left = LVIR_BOUNDS;
2907 rect.top = lpLVItem->iSubItem;
2908 /* GetSubItemRect will fail in non-report mode, so there's
2909 * gonna be no invalidation then, yay! */
2910 if (LISTVIEW_GetSubItemRect(infoPtr, lpLVItem->iItem, &rect))
2911 LISTVIEW_InvalidateRect(infoPtr, &rect);
2919 * Sets item attributes.
2922 * [I] infoPtr : valid pointer to the listview structure
2923 * [I] LPLVITEM : new item atttributes
2924 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2930 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2932 INT nOldFocus = infoPtr->nFocusedItem;
2933 LPWSTR pszText = NULL;
2936 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2938 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
2941 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
2942 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
2944 pszText = lpLVItem->pszText;
2945 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
2948 /* actually set the fields */
2949 if (infoPtr->dwStyle & LVS_OWNERDATA)
2950 bResult = set_owner_item(infoPtr, lpLVItem, TRUE);
2953 /* sanity checks first */
2954 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
2956 if (lpLVItem->iSubItem)
2957 bResult = set_sub_item(infoPtr, lpLVItem, TRUE);
2959 bResult = set_main_item(infoPtr, lpLVItem, TRUE);
2962 /* redraw item, if necessary */
2963 if (bResult && !infoPtr->bIsDrawing && lpLVItem->iSubItem == 0)
2965 if (nOldFocus != infoPtr->nFocusedItem && infoPtr->bFocus)
2966 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->rcFocus);
2968 /* this little optimization eliminates some nasty flicker */
2969 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
2970 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
2971 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
2975 rect.left = LVIR_BOUNDS;
2977 if (LISTVIEW_GetSubItemRect(infoPtr, lpLVItem->iItem, &rect))
2978 LISTVIEW_InvalidateRect(infoPtr, &rect);
2981 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
2986 textfreeT(lpLVItem->pszText, isW);
2987 lpLVItem->pszText = pszText;
2995 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2998 * [I] infoPtr : valid pointer to the listview structure
3003 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3005 LONG lStyle = infoPtr->dwStyle;
3006 UINT uView = lStyle & LVS_TYPEMASK;
3008 SCROLLINFO scrollInfo;
3010 scrollInfo.cbSize = sizeof(SCROLLINFO);
3011 scrollInfo.fMask = SIF_POS;
3013 if (uView == LVS_LIST)
3015 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3016 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3018 else if (uView == LVS_REPORT)
3020 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3021 nItem = scrollInfo.nPos;
3025 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3026 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3029 TRACE("nItem=%d\n", nItem);
3034 /* helper function for the drawing code */
3035 static void select_text_attr(LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
3037 if ( (lpnmlvcd->clrTextBk != CLR_DEFAULT) && (lpnmlvcd->clrTextBk != CLR_NONE) )
3039 SetBkMode(hdc, OPAQUE);
3040 SetBkColor(hdc, lpnmlvcd->clrTextBk);
3043 SetBkMode(hdc, TRANSPARENT);
3045 SetTextColor(hdc, lpnmlvcd->clrText);
3050 * Erases the background of the given rectangle
3053 * [I] infoPtr : valid pointer to the listview structure
3054 * [I] hdc : device context handle
3055 * [I] lprcBox : clipping rectangle
3061 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
3063 if (!infoPtr->hBkBrush) return FALSE;
3065 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
3067 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3075 * [I] infoPtr : valid pointer to the listview structure
3076 * [I] HDC : device context handle
3077 * [I] INT : item index
3078 * [I] INT : subitem index
3079 * [I] RECT * : clipping rectangle
3080 * [I] cdmode : custom draw mode
3086 static BOOL LISTVIEW_DrawSubItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem,
3087 INT nSubItem, RECT rcItem, UINT align, DWORD cdmode)
3089 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3090 DWORD cditemmode = CDRF_DODEFAULT;
3091 NMLVCUSTOMDRAW nmlvcd;
3094 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, rcItem=%s)\n",
3095 hdc, nItem, nSubItem, debugrect(&rcItem));
3097 /* get information needed for drawing the item */
3098 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3099 lvItem.iItem = nItem;
3100 lvItem.iSubItem = nSubItem;
3101 lvItem.cchTextMax = DISP_TEXT_SIZE;
3102 lvItem.pszText = szDispText;
3103 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3104 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3106 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcItem, &lvItem);
3107 if (cdmode & CDRF_NOTIFYITEMDRAW)
3108 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3109 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3111 if (lvItem.iImage) FIXME("Draw the image for the subitem\n");
3113 select_text_attr(infoPtr, hdc, &nmlvcd);
3114 DrawTextW(hdc, lvItem.pszText, -1, &rcItem, LV_SL_DT_FLAGS | align);
3117 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3118 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3129 * [I] infoPtr : valid pointer to the listview structure
3130 * [I] hdc : device context handle
3131 * [I] nItem : item index
3132 * [I] pos : item position in client coordinates
3133 * [I] cdmode : custom draw mode
3139 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, POINT pos, DWORD cdmode)
3141 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3142 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3143 DWORD cditemmode = CDRF_DODEFAULT;
3144 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3145 NMLVCUSTOMDRAW nmlvcd;
3149 TRACE("(hdc=%x, nItem=%d)\n", hdc, nItem);
3151 /* get information needed for drawing the item */
3152 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3153 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3154 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3155 lvItem.iItem = nItem;
3156 lvItem.iSubItem = 0;
3157 lvItem.cchTextMax = DISP_TEXT_SIZE;
3158 lvItem.pszText = szDispText;
3159 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3160 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3162 /* now check if we need to update the focus rectangle */
3163 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3165 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3166 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcState, &rcIcon, &rcLabel)) return FALSE;
3167 OffsetRect(&rcBox, pos.x, pos.y);
3168 OffsetRect(&rcState, pos.x, pos.y);
3169 OffsetRect(&rcIcon, pos.x, pos.y);
3170 OffsetRect(&rcLabel, pos.x, pos.y);
3172 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3173 if (cdmode & CDRF_NOTIFYITEMDRAW)
3174 cditemmode = notify_customdraw (infoPtr, CDDS_ITEMPREPAINT, &nmlvcd);
3175 if (cditemmode & CDRF_SKIPDEFAULT) goto postpaint;
3178 if (infoPtr->himlState)
3180 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3182 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3186 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3187 if (himl && lvItem.iImage >= 0)
3188 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3189 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3191 /* Don't bother painting item being edited */
3192 if (infoPtr->bEditing && lprcFocus) goto postpaint;
3194 select_text_attr(infoPtr, hdc, &nmlvcd);
3197 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3198 rcSelect.right = rcBox.right;
3200 if (lvItem.state & LVIS_SELECTED)
3201 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3202 if(lprcFocus) *lprcFocus = rcSelect;
3204 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS | DT_CENTER);
3205 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3208 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3209 notify_customdraw(infoPtr, CDDS_ITEMPOSTPAINT, &nmlvcd);
3215 * Draws listview items when in owner draw mode.
3218 * [I] infoPtr : valid pointer to the listview structure
3219 * [I] hdc : device context handle
3224 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
3226 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3227 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3228 POINT Origin, Position;
3235 ZeroMemory(&dis, sizeof(dis));
3237 /* Get scroll info once before loop */
3238 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3240 /* figure out what we need to draw */
3241 iterator_clippeditems(&i, infoPtr, hdc);
3243 /* send cache hint notification */
3244 if (infoPtr->dwStyle & LVS_OWNERDATA)
3246 RANGE range = iterator_range(&i);
3249 nmlv.iFrom = range.lower;
3250 nmlv.iTo = range.upper;
3251 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
3254 /* iterate through the invalidated rows */
3255 while(iterator_next(&i))
3257 item.iItem = i.nItem;
3259 item.mask = LVIF_PARAM | LVIF_STATE;
3260 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3261 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3263 dis.CtlType = ODT_LISTVIEW;
3265 dis.itemID = item.iItem;
3266 dis.itemAction = ODA_DRAWENTIRE;
3268 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3269 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3270 dis.hwndItem = infoPtr->hwndSelf;
3272 if (!LISTVIEW_GetItemListOrigin(infoPtr, dis.itemID, &Position)) continue;
3273 dis.rcItem.left = Position.x + Origin.x;
3274 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3275 dis.rcItem.top = Position.y + Origin.y;
3276 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3277 dis.itemData = item.lParam;
3279 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3280 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3282 iterator_destroy(&i);
3287 * Draws listview items when in report display mode.
3290 * [I] infoPtr : valid pointer to the listview structure
3291 * [I] hdc : device context handle
3292 * [I] cdmode : custom draw mode
3297 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3299 INT rgntype, nDrawPosY, j;
3300 INT nColumnCount, nFirstCol, nLastCol;
3301 RECT rcItem, rcClip;
3302 COLUMNCACHE *lpCols;
3309 /* figure out what to draw */
3310 rgntype = GetClipBox(hdc, &rcClip);
3311 if (rgntype == NULLREGION) return;
3313 /* cache column info */
3314 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3315 lpCols = COMCTL32_Alloc(nColumnCount * sizeof(COLUMNCACHE));
3316 if (!lpCols) return;
3317 for (j = 0; j < nColumnCount; j++)
3319 Header_GetItemRect(infoPtr->hwndHeader, j, &lpCols[j].rc);
3320 TRACE("lpCols[%d].rc=%s\n", j, debugrect(&lpCols[j].rc));
3323 /* Get scroll info once before loop */
3324 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrig)) return;
3326 /* we now narrow the columns as well */
3327 nLastCol = nColumnCount - 1;
3328 for(nFirstCol = 0; nFirstCol < nColumnCount; nFirstCol++)
3329 if (lpCols[nFirstCol].rc.right + ptOrig.x >= rcClip.left) break;
3330 for(nLastCol = nColumnCount - 1; nLastCol >= 0; nLastCol--)
3331 if (lpCols[nLastCol].rc.left + ptOrig.x < rcClip.right) break;
3333 /* cache the per-column information before we start drawing */
3334 for (j = nFirstCol; j <= nLastCol; j++)
3336 lvColumn.mask = LVCF_FMT;
3337 LISTVIEW_GetColumnT(infoPtr, j, &lvColumn, TRUE);
3338 TRACE("lvColumn=%s\n", debuglvcolumn_t(&lvColumn, TRUE));
3339 lpCols[j].align = DT_LEFT;
3340 if (lvColumn.fmt & LVCFMT_RIGHT)
3341 lpCols[j].align = DT_RIGHT;
3342 else if (lvColumn.fmt & LVCFMT_CENTER)
3343 lpCols[j].align = DT_CENTER;
3346 /* figure out what we need to draw */
3347 iterator_clippeditems(&i, infoPtr, hdc);
3349 /* a last few bits before we start drawing */
3350 TRACE("Colums=(%di - %d)\n", nFirstCol, nLastCol);
3352 /* iterate through the invalidated rows */
3353 while(iterator_next(&i))
3355 nDrawPosY = i.nItem * infoPtr->nItemHeight;
3357 /* iterate through the invalidated columns */
3358 for (j = nFirstCol; j <= nLastCol; j++)
3360 rcItem = lpCols[j].rc;
3361 rcItem.left += REPORT_MARGINX;
3362 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3363 rcItem.top = nDrawPosY;
3364 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3366 /* Offset the Scroll Bar Pos */
3367 OffsetRect(&rcItem, ptOrig.x, ptOrig.y);
3369 if (rgntype == COMPLEXREGION && !RectVisible(hdc, &rcItem)) continue;
3373 POINT pos = { rcItem.left, rcItem.top };
3374 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, pos, cdmode);
3377 LISTVIEW_DrawSubItem(infoPtr, hdc, i.nItem, j, rcItem, lpCols[j].align, cdmode);
3380 iterator_destroy(&i);
3382 /* cleanup the mess */
3383 COMCTL32_Free(lpCols);
3388 * Draws listview items when in list display mode.
3391 * [I] infoPtr : valid pointer to the listview structure
3392 * [I] hdc : device context handle
3393 * [I] cdmode : custom draw mode
3398 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3400 POINT Origin, Position;
3403 /* Get scroll info once before loop */
3404 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3406 /* figure out what we need to draw */
3407 iterator_clippeditems(&i, infoPtr, hdc);
3409 while(iterator_next(&i))
3411 if (!LISTVIEW_GetItemListOrigin(infoPtr, i.nItem, &Position)) continue;
3412 Position.x += Origin.x;
3413 Position.y += Origin.y;
3415 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, Position, cdmode);
3417 iterator_destroy(&i);
3422 * Draws listview items when in icon or small icon display mode.
3425 * [I] infoPtr : valid pointer to the listview structure
3426 * [I] hdc : device context handle
3427 * [I] cdmode : custom draw mode
3432 static void LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3434 POINT Origin, Position;
3438 /* Get scroll info once before loop */
3439 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3441 /* figure out what we need to draw */
3442 iterator_clippeditems(&i, infoPtr, hdc);
3444 while(iterator_next(&i))
3446 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_FOCUSED))
3449 if (!LISTVIEW_GetItemListOrigin(infoPtr, i.nItem, &Position)) continue;
3450 Position.x += Origin.x;
3451 Position.y += Origin.y;
3452 LISTVIEW_DrawItem(infoPtr, hdc, i.nItem, Position, cdmode);
3454 iterator_destroy(&i);
3456 /* draw the focused item last, in case it's oversized */
3457 if (LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox) &&
3458 RectVisible(hdc, &rcBox))
3460 if (LISTVIEW_GetItemListOrigin(infoPtr, infoPtr->nFocusedItem, &Position))
3462 Position.x += Origin.x;
3463 Position.y += Origin.y;
3464 LISTVIEW_DrawItem(infoPtr, hdc, infoPtr->nFocusedItem, Position, cdmode);
3471 * Draws listview items.
3474 * [I] infoPtr : valid pointer to the listview structure
3475 * [I] HDC : device context handle
3480 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3482 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3483 COLORREF oldBkColor, oldTextColor;
3484 NMLVCUSTOMDRAW nmlvcd;
3490 LISTVIEW_DUMP(infoPtr);
3492 infoPtr->bIsDrawing = TRUE;
3494 /* save dc values we're gonna trash while drawing */
3495 hOldFont = SelectObject(hdc, infoPtr->hFont);
3496 oldBkMode = GetBkMode(hdc);
3497 oldBkColor = GetBkColor(hdc);
3498 oldTextColor = GetTextColor(hdc);
3500 GetClientRect(infoPtr->hwndSelf, &rcClient);
3501 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, NULL);
3502 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3503 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
3505 /* nothing to draw */
3506 if(infoPtr->nItemCount == 0) goto enddraw;
3508 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
3509 LISTVIEW_RefreshOwnerDraw(infoPtr, hdc);
3512 if (uView == LVS_ICON)
3513 LISTVIEW_RefreshIcon(infoPtr, hdc, cdmode);
3514 else if (uView == LVS_REPORT)
3515 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3516 else /* LVS_LIST or LVS_SMALLICON */
3517 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3519 /* if we have a focus rect, draw it */
3520 if (infoPtr->bFocus)
3521 DrawFocusRect(hdc, &infoPtr->rcFocus);
3525 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3526 notify_customdraw(infoPtr, CDDS_POSTPAINT, &nmlvcd);
3528 /* unselect objects */
3529 SelectObject(hdc, hOldFont);
3530 SetBkMode(hdc, oldBkMode);
3531 SetBkColor(hdc, oldBkColor);
3532 SetTextColor(hdc, oldTextColor);
3533 infoPtr->bIsDrawing = FALSE;
3539 * Calculates the approximate width and height of a given number of items.
3542 * [I] infoPtr : valid pointer to the listview structure
3543 * [I] INT : number of items
3548 * Returns a DWORD. The width in the low word and the height in high word.
3550 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3551 WORD wWidth, WORD wHeight)
3553 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3554 INT nItemCountPerColumn = 1;
3555 INT nColumnCount = 0;
3556 DWORD dwViewRect = 0;
3558 if (nItemCount == -1)
3559 nItemCount = infoPtr->nItemCount;
3561 if (uView == LVS_LIST)
3563 if (wHeight == 0xFFFF)
3565 /* use current height */
3566 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3569 if (wHeight < infoPtr->nItemHeight)
3570 wHeight = infoPtr->nItemHeight;
3574 if (infoPtr->nItemHeight > 0)
3576 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3577 if (nItemCountPerColumn == 0)
3578 nItemCountPerColumn = 1;
3580 if (nItemCount % nItemCountPerColumn != 0)
3581 nColumnCount = nItemCount / nItemCountPerColumn;
3583 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3587 /* Microsoft padding magic */
3588 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3589 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3591 dwViewRect = MAKELONG(wWidth, wHeight);
3593 else if (uView == LVS_REPORT)
3594 FIXME("uView == LVS_REPORT: not implemented\n");
3595 else if (uView == LVS_SMALLICON)
3596 FIXME("uView == LVS_SMALLICON: not implemented\n");
3597 else if (uView == LVS_ICON)
3598 FIXME("uView == LVS_ICON: not implemented\n");
3605 * Arranges listview items in icon display mode.
3608 * [I] infoPtr : valid pointer to the listview structure
3609 * [I] INT : alignment code
3615 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3617 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3618 BOOL bResult = FALSE;
3620 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3625 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3628 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3631 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3633 case LVA_SNAPTOGRID:
3634 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3642 /* << LISTVIEW_CreateDragImage >> */
3647 * Removes all listview items and subitems.
3650 * [I] infoPtr : valid pointer to the listview structure
3656 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3658 LONG lStyle = infoPtr->dwStyle;
3659 UINT uView = lStyle & LVS_TYPEMASK;
3660 LISTVIEW_ITEM *lpItem;
3661 LISTVIEW_SUBITEM *lpSubItem;
3664 BOOL bResult = FALSE;
3669 LISTVIEW_RemoveAllSelections(infoPtr);
3670 infoPtr->nSelectionMark=-1;
3671 infoPtr->nFocusedItem=-1;
3672 SetRectEmpty(&infoPtr->rcFocus);
3673 /* But we are supposed to leave nHotItem as is! */
3675 if (lStyle & LVS_OWNERDATA)
3677 infoPtr->nItemCount = 0;
3678 LISTVIEW_InvalidateList(infoPtr);
3682 if (infoPtr->nItemCount > 0)
3686 /* send LVN_DELETEALLITEMS notification */
3687 /* verify if subsequent LVN_DELETEITEM notifications should be
3689 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3691 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3693 for (i = 0; i < infoPtr->nItemCount; i++)
3695 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3696 if (hdpaSubItems != NULL)
3698 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3700 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3701 if (lpSubItem != NULL)
3703 /* free subitem string */
3704 if (is_textW(lpSubItem->hdr.pszText))
3705 COMCTL32_Free(lpSubItem->hdr.pszText);
3708 COMCTL32_Free(lpSubItem);
3712 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3717 /* send LVN_DELETEITEM notification */
3719 nmlv.lParam = lpItem->lParam;
3720 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3723 /* free item string */
3724 if (is_textW(lpItem->hdr.pszText))
3725 COMCTL32_Free(lpItem->hdr.pszText);
3728 COMCTL32_Free(lpItem);
3731 DPA_Destroy(hdpaSubItems);
3735 /* reinitialize listview memory */
3736 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3737 infoPtr->nItemCount = 0;
3738 DPA_DeleteAllPtrs(infoPtr->hdpaPosX);
3739 DPA_DeleteAllPtrs(infoPtr->hdpaPosY);
3741 /* align items (set position of each item) */
3742 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3744 if (lStyle & LVS_ALIGNLEFT)
3746 LISTVIEW_AlignLeft(infoPtr);
3750 LISTVIEW_AlignTop(infoPtr);
3754 LISTVIEW_UpdateScroll(infoPtr);
3756 LISTVIEW_InvalidateList(infoPtr);
3764 * Removes a column from the listview control.
3767 * [I] infoPtr : valid pointer to the listview structure
3768 * [I] INT : column index
3774 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3776 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3779 TRACE("nColumn=%d\n", nColumn);
3781 if (nColumn <= 0) return FALSE;
3783 if (uView == LVS_REPORT)
3785 if (!Header_GetItemRect(infoPtr->hwndHeader, nColumn, &rcCol))
3788 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3792 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3794 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3796 INT nItem, nSubItem, i;
3798 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3800 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3801 if (!hdpaSubItems) continue;
3804 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3806 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3807 if (!lpSubItem) break;
3808 if (lpSubItem->iSubItem == nColumn)
3811 lpDelItem = lpSubItem;
3813 else if (lpSubItem->iSubItem > nColumn)
3815 lpSubItem->iSubItem--;
3819 /* if we found our subitem, zapp it */
3823 if (is_textW(lpDelItem->hdr.pszText))
3824 COMCTL32_Free(lpDelItem->hdr.pszText);
3827 COMCTL32_Free(lpDelItem);
3829 /* free dpa memory */
3830 DPA_DeletePtr(hdpaSubItems, nSubItem);
3835 /* we need to worry about display issues in report mode only */
3836 if (uView != LVS_REPORT) return TRUE;
3838 /* if we have a focus, must first erase the focus rect */
3839 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
3841 /* Need to reset the item width when deleting a column */
3842 infoPtr->nItemWidth -= rcCol.right - rcCol.left;
3844 /* update scrollbar(s) */
3845 LISTVIEW_UpdateScroll(infoPtr);
3847 /* scroll to cover the deleted column, and invalidate for redraw */
3848 rcOld = infoPtr->rcList;
3849 rcOld.left = rcCol.left;
3850 ScrollWindowEx(infoPtr->hwndSelf, -(rcCol.right - rcCol.left), 0,
3851 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
3853 /* we can restore focus now */
3854 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
3861 * Removes an item from the listview control.
3864 * [I] infoPtr : valid pointer to the listview structure
3865 * [I] INT : item index
3871 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
3873 LONG lStyle = infoPtr->dwStyle;
3874 UINT uView = lStyle & LVS_TYPEMASK;
3875 LONG lCtrlId = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3877 BOOL bResult = FALSE;
3879 LISTVIEW_ITEM *lpItem;
3880 LISTVIEW_SUBITEM *lpSubItem;
3884 TRACE("(nItem=%d)\n", nItem);
3887 /* First, send LVN_DELETEITEM notification. */
3888 memset(&nmlv, 0, sizeof (NMLISTVIEW));
3889 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
3890 nmlv.hdr.idFrom = lCtrlId;
3891 nmlv.hdr.code = LVN_DELETEITEM;
3893 SendMessageW((infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)lCtrlId,
3896 if (nItem == infoPtr->nFocusedItem)
3898 infoPtr->nFocusedItem = -1;
3899 SetRectEmpty(&infoPtr->rcFocus);
3902 /* remove it from the selection range */
3903 item.state = LVIS_SELECTED;
3904 item.stateMask = LVIS_SELECTED;
3905 LISTVIEW_SetItemState(infoPtr,nItem,&item);
3907 if (lStyle & LVS_OWNERDATA)
3909 infoPtr->nItemCount--;
3910 LISTVIEW_InvalidateList(infoPtr); /*FIXME: optimize */
3914 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3916 /* initialize memory */
3917 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3919 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
3920 if (hdpaSubItems != NULL)
3922 infoPtr->nItemCount--;
3923 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3925 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3926 if (lpSubItem != NULL)
3928 /* free item string */
3929 if (is_textW(lpSubItem->hdr.pszText))
3930 COMCTL32_Free(lpSubItem->hdr.pszText);
3933 COMCTL32_Free(lpSubItem);
3937 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3940 /* free item string */
3941 if (is_textW(lpItem->hdr.pszText))
3942 COMCTL32_Free(lpItem->hdr.pszText);
3945 COMCTL32_Free(lpItem);
3948 bResult = DPA_Destroy(hdpaSubItems);
3949 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
3950 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
3953 LISTVIEW_ShiftIndices(infoPtr,nItem,-1);
3955 /* align items (set position of each item) */
3956 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
3958 if (lStyle & LVS_ALIGNLEFT)
3959 LISTVIEW_AlignLeft(infoPtr);
3961 LISTVIEW_AlignTop(infoPtr);
3964 LISTVIEW_UpdateScroll(infoPtr);
3966 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
3975 * Callback implementation for editlabel control
3978 * [I] infoPtr : valid pointer to the listview structure
3979 * [I] pszText : modified text
3980 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
3986 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
3988 NMLVDISPINFOW dispInfo;
3990 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
3992 infoPtr->bEditing = FALSE;
3994 ZeroMemory(&dispInfo, sizeof(dispInfo));
3995 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
3996 dispInfo.item.iItem = infoPtr->nEditLabelItem;
3997 dispInfo.item.iSubItem = 0;
3998 dispInfo.item.stateMask = ~0;
3999 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4000 dispInfo.item.pszText = pszText;
4001 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4003 /* Do we need to update the Item Text */
4004 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4005 if (!pszText) return TRUE;
4007 ZeroMemory(&dispInfo, sizeof(dispInfo));
4008 dispInfo.item.mask = LVIF_TEXT;
4009 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4010 dispInfo.item.iSubItem = 0;
4011 dispInfo.item.pszText = pszText;
4012 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4013 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4018 * Begin in place editing of specified list view item
4021 * [I] infoPtr : valid pointer to the listview structure
4022 * [I] INT : item index
4023 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4029 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4031 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4032 NMLVDISPINFOW dispInfo;
4035 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4037 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4039 infoPtr->nEditLabelItem = nItem;
4041 /* Is the EditBox still there, if so remove it */
4042 if(infoPtr->hwndEdit != 0)
4044 SetFocus(infoPtr->hwndSelf);
4045 infoPtr->hwndEdit = 0;
4048 LISTVIEW_SetSelection(infoPtr, nItem);
4049 LISTVIEW_SetItemFocus(infoPtr, nItem);
4051 rect.left = LVIR_LABEL;
4052 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4054 ZeroMemory(&dispInfo, sizeof(dispInfo));
4055 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4056 dispInfo.item.iItem = nItem;
4057 dispInfo.item.iSubItem = 0;
4058 dispInfo.item.stateMask = ~0;
4059 dispInfo.item.pszText = szDispText;
4060 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4061 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4063 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4064 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4065 if (!infoPtr->hwndEdit) return 0;
4067 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4069 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4070 infoPtr->hwndEdit = 0;
4074 infoPtr->bEditing = TRUE;
4075 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4076 SetFocus(infoPtr->hwndEdit);
4077 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4078 return infoPtr->hwndEdit;
4084 * Ensures the specified item is visible, scrolling into view if necessary.
4087 * [I] infoPtr : valid pointer to the listview structure
4088 * [I] nItem : item index
4089 * [I] bPartial : partially or entirely visible
4095 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4097 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4098 INT nScrollPosHeight = 0;
4099 INT nScrollPosWidth = 0;
4100 INT nHorzAdjust = 0;
4101 INT nVertAdjust = 0;
4104 RECT rcItem, rcTemp;
4106 rcItem.left = LVIR_BOUNDS;
4107 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4109 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4111 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4113 /* scroll left/right, but in LVS_REPORT mode */
4114 if (uView == LVS_LIST)
4115 nScrollPosWidth = infoPtr->nItemWidth;
4116 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4117 nScrollPosWidth = 1;
4119 if (rcItem.left < infoPtr->rcList.left)
4122 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4127 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4131 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4133 /* scroll up/down, but not in LVS_LIST mode */
4134 if (uView == LVS_REPORT)
4135 nScrollPosHeight = infoPtr->nItemHeight;
4136 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4137 nScrollPosHeight = 1;
4139 if (rcItem.top < infoPtr->rcList.top)
4142 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4147 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4151 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4153 if (nScrollPosWidth)
4155 INT diff = nHorzDiff / nScrollPosWidth;
4156 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4157 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4160 if (nScrollPosHeight)
4162 INT diff = nVertDiff / nScrollPosHeight;
4163 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4164 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4172 * Searches for an item with specific characteristics.
4175 * [I] hwnd : window handle
4176 * [I] nStart : base item index
4177 * [I] lpFindInfo : item information to look for
4180 * SUCCESS : index of item
4183 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4184 LPLVFINDINFOW lpFindInfo)
4186 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4187 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4188 BOOL bWrap = FALSE, bNearest = FALSE;
4189 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4190 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4191 POINT Position, Destination;
4194 if (!lpFindInfo || nItem < 0) return -1;
4197 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4199 lvItem.mask |= LVIF_TEXT;
4200 lvItem.pszText = szDispText;
4201 lvItem.cchTextMax = DISP_TEXT_SIZE;
4204 if (lpFindInfo->flags & LVFI_WRAP)
4207 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4208 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4212 FIXME("LVFI_NEARESTXY is slow.\n");
4213 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
4214 Destination.x = lpFindInfo->pt.x - Origin.x;
4215 Destination.y = lpFindInfo->pt.y - Origin.y;
4216 switch(lpFindInfo->vkDirection)
4218 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4219 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4220 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4221 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4222 case VK_HOME: Destination.x = Destination.y = 0; break;
4223 case VK_END: Destination.x = infoPtr->rcView.right; Destination.y = infoPtr->rcView.bottom; break;
4224 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4225 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4226 default: FIXME("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4231 /* if LVFI_PARAM is specified, all other flags are ignored */
4232 if (lpFindInfo->flags & LVFI_PARAM)
4234 lvItem.mask |= LVIF_PARAM;
4236 lvItem.mask &= ~LVIF_TEXT;
4240 for (; nItem < nLast; nItem++)
4242 lvItem.iItem = nItem;
4243 lvItem.iSubItem = 0;
4244 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4246 if (lvItem.mask & LVIF_PARAM && lpFindInfo->lParam == lvItem.lParam)
4249 if (lvItem.mask & LVIF_TEXT)
4251 if (lpFindInfo->flags & LVFI_PARTIAL)
4253 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4257 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4261 if (!bNearest) return nItem;
4263 /* This is very inefficient. To do a good job here,
4264 * we need a sorted array of (x,y) item positions */
4265 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) continue;
4267 /* compute the distance^2 to the destination */
4268 xdist = Destination.x - Position.x;
4269 ydist = Destination.y - Position.y;
4270 dist = xdist * xdist + ydist * ydist;
4272 /* remember the distance, and item if it's closer */
4276 nNearestItem = nItem;
4283 nLast = min(nStart + 1, infoPtr->nItemCount);
4288 return nNearestItem;
4293 * Searches for an item with specific characteristics.
4296 * [I] hwnd : window handle
4297 * [I] nStart : base item index
4298 * [I] lpFindInfo : item information to look for
4301 * SUCCESS : index of item
4304 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4305 LPLVFINDINFOA lpFindInfo)
4307 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4311 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4312 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4313 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4314 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4320 * Retrieves the background image of the listview control.
4323 * [I] infoPtr : valid pointer to the listview structure
4324 * [O] LPLVMKBIMAGE : background image attributes
4330 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4332 /* FIXME (listview, "empty stub!\n"); */
4338 * Retrieves column attributes.
4341 * [I] infoPtr : valid pointer to the listview structure
4342 * [I] INT : column index
4343 * [IO] LPLVCOLUMNW : column information
4344 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4345 * otherwise it is in fact a LPLVCOLUMNA
4351 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4354 BOOL bResult = FALSE;
4356 if (lpColumn != NULL)
4359 /* initialize memory */
4360 ZeroMemory(&hdi, sizeof(hdi));
4362 if (lpColumn->mask & LVCF_FMT)
4363 hdi.mask |= HDI_FORMAT;
4365 if (lpColumn->mask & LVCF_WIDTH)
4366 hdi.mask |= HDI_WIDTH;
4368 if (lpColumn->mask & LVCF_TEXT)
4370 hdi.mask |= HDI_TEXT;
4371 hdi.cchTextMax = lpColumn->cchTextMax;
4372 hdi.pszText = lpColumn->pszText;
4375 if (lpColumn->mask & LVCF_IMAGE)
4376 hdi.mask |= HDI_IMAGE;
4378 if (lpColumn->mask & LVCF_ORDER)
4379 hdi.mask |= HDI_ORDER;
4382 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4384 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4388 if (lpColumn->mask & LVCF_FMT)
4392 if (hdi.fmt & HDF_LEFT)
4393 lpColumn->fmt |= LVCFMT_LEFT;
4394 else if (hdi.fmt & HDF_RIGHT)
4395 lpColumn->fmt |= LVCFMT_RIGHT;
4396 else if (hdi.fmt & HDF_CENTER)
4397 lpColumn->fmt |= LVCFMT_CENTER;
4399 if (hdi.fmt & HDF_IMAGE)
4400 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4402 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4403 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4406 if (lpColumn->mask & LVCF_WIDTH)
4407 lpColumn->cx = hdi.cxy;
4409 if (lpColumn->mask & LVCF_IMAGE)
4410 lpColumn->iImage = hdi.iImage;
4412 if (lpColumn->mask & LVCF_ORDER)
4413 lpColumn->iOrder = hdi.iOrder;
4415 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4416 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4425 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4432 /* FIXME: little hack */
4433 for (i = 0; i < iCount; i++)
4441 * Retrieves the column width.
4444 * [I] infoPtr : valid pointer to the listview structure
4445 * [I] int : column index
4448 * SUCCESS : column width
4451 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4453 INT nColumnWidth = 0;
4456 TRACE("nColumn=%d\n", nColumn);
4458 switch(infoPtr->dwStyle & LVS_TYPEMASK)
4461 nColumnWidth = infoPtr->nItemWidth;
4464 hdi.mask = HDI_WIDTH;
4465 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4466 nColumnWidth = hdi.cxy;
4469 /* we don't have a 'column' in [SMALL]ICON mode */
4472 TRACE("nColumnWidth=%d\n", nColumnWidth);
4473 return nColumnWidth;
4478 * In list or report display mode, retrieves the number of items that can fit
4479 * vertically in the visible area. In icon or small icon display mode,
4480 * retrieves the total number of visible items.
4483 * [I] infoPtr : valid pointer to the listview structure
4486 * Number of fully visible items.
4488 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4490 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4493 if (uView == LVS_LIST)
4495 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4497 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4498 LISTVIEW_GetCountPerColumn(infoPtr);
4501 else if (uView == LVS_REPORT)
4503 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4507 nItemCount = infoPtr->nItemCount;
4516 * Retrieves an image list handle.
4519 * [I] infoPtr : valid pointer to the listview structure
4520 * [I] nImageList : image list identifier
4523 * SUCCESS : image list handle
4526 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4528 HIMAGELIST himl = NULL;
4533 himl = infoPtr->himlNormal;
4536 himl = infoPtr->himlSmall;
4539 himl = infoPtr->himlState;
4543 return (LRESULT)himl;
4546 /* LISTVIEW_GetISearchString */
4550 * Retrieves item attributes.
4553 * [I] hwnd : window handle
4554 * [IO] lpLVItem : item info
4555 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4556 * if FALSE, the lpLVItem is a LPLVITEMA.
4559 * This is the internal 'GetItem' interface -- it tries to
4560 * be smart, and avoids text copies, if possible, by modifing
4561 * lpLVItem->pszText to point to the text string. Please note
4562 * that this is not always possible (e.g. OWNERDATA), so on
4563 * entry you *must* supply valid values for pszText, and cchTextMax.
4564 * The only difference to the documented interface is that upon
4565 * return, you should use *only* the lpLVItem->pszText, rather than
4566 * the buffer pointer you provided on input. Most code already does
4567 * that, so it's not a problem.
4568 * For the two cases when the text must be copied (that is,
4569 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4575 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4577 NMLVDISPINFOW dispInfo;
4578 LISTVIEW_ITEM *lpItem;
4582 /* In the following:
4583 * lpLVItem describes the information requested by the user
4584 * lpItem is what we have
4585 * dispInfo is a structure we use to request the missing
4586 * information from the application
4589 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4591 if (!lpLVItem || (lpLVItem->iItem < 0) ||
4592 (lpLVItem->iItem >= infoPtr->nItemCount))
4595 /* a quick optimization if all we're asked is the focus state
4596 * these queries are worth optimising since they are common,
4597 * and can be answered in constant time, without the heavy accesses */
4598 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIF_STATE) &&
4599 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4601 lpLVItem->state = 0;
4602 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4603 lpLVItem->state |= LVIS_FOCUSED;
4607 ZeroMemory(&dispInfo, sizeof(dispInfo));
4609 /* if the app stores all the data, handle it separately */
4610 if (infoPtr->dwStyle & LVS_OWNERDATA)
4612 dispInfo.item.state = 0;
4614 /* if we need to callback, do it now */
4615 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4617 /* NOTE: copy only fields which we _know_ are initialized, some apps
4618 * depend on the uninitialized fields being 0 */
4619 dispInfo.item.mask = lpLVItem->mask;
4620 dispInfo.item.iItem = lpLVItem->iItem;
4621 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4622 if (lpLVItem->mask & LVIF_TEXT)
4624 dispInfo.item.pszText = lpLVItem->pszText;
4625 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4627 if (lpLVItem->mask & LVIF_STATE)
4628 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4629 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4630 dispInfo.item.stateMask = lpLVItem->stateMask;
4631 *lpLVItem = dispInfo.item;
4632 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4635 /* we store only a little state, so if we're not asked, we're done */
4636 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4638 /* if focus is handled by us, report it */
4639 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4641 lpLVItem->state &= ~LVIS_FOCUSED;
4642 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4643 lpLVItem->state |= LVIS_FOCUSED;
4646 /* and do the same for selection, if we handle it */
4647 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4649 lpLVItem->state &= ~LVIS_SELECTED;
4650 if (ranges_contain(infoPtr->hdpaSelectionRanges, lpLVItem->iItem))
4651 lpLVItem->state |= LVIS_SELECTED;
4657 /* find the item and subitem structures before we proceed */
4658 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4659 if (hdpaSubItems == NULL) return FALSE;
4661 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4664 if (lpLVItem->iSubItem)
4666 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4667 if(!lpSubItem) return FALSE;
4668 pItemHdr = &lpSubItem->hdr;
4671 pItemHdr = &lpItem->hdr;
4673 /* Do we need to query the state from the app? */
4674 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4676 dispInfo.item.mask |= LVIF_STATE;
4677 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4680 /* Do we need to enquire about the image? */
4681 if ((lpLVItem->mask & LVIF_IMAGE) && (pItemHdr->iImage==I_IMAGECALLBACK))
4682 dispInfo.item.mask |= LVIF_IMAGE;
4684 /* Do we need to enquire about the text? */
4685 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4687 dispInfo.item.mask |= LVIF_TEXT;
4688 dispInfo.item.pszText = lpLVItem->pszText;
4689 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4690 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4691 *dispInfo.item.pszText = '\0';
4694 /* If we don't have all the requested info, query the application */
4695 if (dispInfo.item.mask != 0)
4697 dispInfo.item.iItem = lpLVItem->iItem;
4698 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4699 dispInfo.item.lParam = lpItem->lParam;
4700 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4701 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4704 /* Now, handle the iImage field */
4705 if (dispInfo.item.mask & LVIF_IMAGE)
4707 lpLVItem->iImage = dispInfo.item.iImage;
4708 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (pItemHdr->iImage==I_IMAGECALLBACK))
4709 pItemHdr->iImage = dispInfo.item.iImage;
4711 else if (lpLVItem->mask & LVIF_IMAGE)
4712 lpLVItem->iImage = pItemHdr->iImage;
4714 /* The pszText field */
4715 if (dispInfo.item.mask & LVIF_TEXT)
4717 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4718 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4720 lpLVItem->pszText = dispInfo.item.pszText;
4722 else if (lpLVItem->mask & LVIF_TEXT)
4724 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4725 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4728 /* if this is a subitem, we're done*/
4729 if (lpLVItem->iSubItem) return TRUE;
4731 /* Next is the lParam field */
4732 if (dispInfo.item.mask & LVIF_PARAM)
4734 lpLVItem->lParam = dispInfo.item.lParam;
4735 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4736 lpItem->lParam = dispInfo.item.lParam;
4738 else if (lpLVItem->mask & LVIF_PARAM)
4739 lpLVItem->lParam = lpItem->lParam;
4741 /* ... the state field (this one is different due to uCallbackmask) */
4742 if (lpLVItem->mask & LVIF_STATE)
4744 lpLVItem->state = lpItem->state;
4745 if (dispInfo.item.mask & LVIF_STATE)
4747 lpLVItem->state &= ~dispInfo.item.stateMask;
4748 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4750 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4752 lpLVItem->state &= ~LVIS_FOCUSED;
4753 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4754 lpLVItem->state |= LVIS_FOCUSED;
4756 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4758 lpLVItem->state &= ~LVIS_SELECTED;
4759 if (ranges_contain(infoPtr->hdpaSelectionRanges, lpLVItem->iItem))
4760 lpLVItem->state |= LVIS_SELECTED;
4764 /* and last, but not least, the indent field */
4765 if (lpLVItem->mask & LVIF_INDENT)
4766 lpLVItem->iIndent = lpItem->iIndent;
4773 * Retrieves item attributes.
4776 * [I] hwnd : window handle
4777 * [IO] lpLVItem : item info
4778 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4779 * if FALSE, the lpLVItem is a LPLVITEMA.
4782 * This is the external 'GetItem' interface -- it properly copies
4783 * the text in the provided buffer.
4789 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4794 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4797 pszText = lpLVItem->pszText;
4798 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
4799 if (bResult && lpLVItem->pszText != pszText)
4800 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
4801 lpLVItem->pszText = pszText;
4809 * Retrieves the position (upper-left) of the listview control item.
4810 * Note that for LVS_ICON style, the upper-left is that of the icon
4811 * and not the bounding box.
4814 * [I] infoPtr : valid pointer to the listview structure
4815 * [I] nItem : item index
4816 * [O] lpptPosition : coordinate information
4822 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
4824 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4827 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
4829 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4830 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, lpptPosition)) return FALSE;
4831 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
4833 if (uView == LVS_ICON)
4835 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
4836 lpptPosition->y += ICON_TOP_PADDING;
4838 lpptPosition->x += Origin.x;
4839 lpptPosition->y += Origin.y;
4841 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
4848 * Retrieves the bounding rectangle for a listview control item.
4851 * [I] infoPtr : valid pointer to the listview structure
4852 * [I] nItem : item index
4853 * [IO] lprc : bounding rectangle coordinates
4854 * lprc->left specifies the portion of the item for which the bounding
4855 * rectangle will be retrieved.
4857 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
4858 * including the icon and label.
4861 * * Experiment shows that native control returns:
4862 * * width = min (48, length of text line)
4863 * * .left = position.x - (width - iconsize.cx)/2
4864 * * .right = .left + width
4865 * * height = #lines of text * ntmHeight + icon height + 8
4866 * * .top = position.y - 2
4867 * * .bottom = .top + height
4868 * * separation between items .y = itemSpacing.cy - height
4869 * * .x = itemSpacing.cx - width
4870 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
4873 * * Experiment shows that native control returns:
4874 * * width = iconSize.cx + 16
4875 * * .left = position.x - (width - iconsize.cx)/2
4876 * * .right = .left + width
4877 * * height = iconSize.cy + 4
4878 * * .top = position.y - 2
4879 * * .bottom = .top + height
4880 * * separation between items .y = itemSpacing.cy - height
4881 * * .x = itemSpacing.cx - width
4882 * LVIR_LABEL Returns the bounding rectangle of the item text.
4885 * * Experiment shows that native control returns:
4886 * * width = text length
4887 * * .left = position.x - width/2
4888 * * .right = .left + width
4889 * * height = ntmH * linecount + 2
4890 * * .top = position.y + iconSize.cy + 6
4891 * * .bottom = .top + height
4892 * * separation between items .y = itemSpacing.cy - height
4893 * * .x = itemSpacing.cx - width
4894 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
4895 * rectangles, but excludes columns in report view.
4902 * Note that the bounding rectangle of the label in the LVS_ICON view depends
4903 * upon whether the window has the focus currently and on whether the item
4904 * is the one with the focus. Ensure that the control's record of which
4905 * item has the focus agrees with the items' records.
4907 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
4909 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4910 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4911 BOOL doLabel = TRUE, oversizedBox = FALSE;
4912 POINT Position, Origin;
4916 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
4918 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4919 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
4920 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) return FALSE;
4922 /* Be smart and try to figure out the minimum we have to do */
4923 if (lprc->left == LVIR_ICON) doLabel = FALSE;
4924 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
4925 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
4926 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
4927 oversizedBox = TRUE;
4929 /* get what we need from the item before hand, so we make
4930 * only one request. This can speed up things, if data
4931 * is stored on the app side */
4933 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
4934 if (doLabel) lvItem.mask |= LVIF_TEXT;
4935 lvItem.iItem = nItem;
4936 lvItem.iSubItem = 0;
4937 lvItem.pszText = szDispText;
4938 lvItem.cchTextMax = DISP_TEXT_SIZE;
4939 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
4940 /* we got the state already up, simulate it here, to avoid a reget */
4941 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
4943 lvItem.mask |= LVIF_STATE;
4944 lvItem.stateMask = LVIS_FOCUSED;
4945 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
4951 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc, NULL)) return FALSE;
4955 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc)) return FALSE;
4959 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL)) return FALSE;
4962 case LVIR_SELECTBOUNDS:
4963 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, &label_rect)) return FALSE;
4964 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
4965 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) )
4966 lprc->right = label_rect.right;
4970 WARN("Unknown value: %d\n", lprc->left);
4974 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
4976 TRACE(" rect=%s\n", debugrect(lprc));
4983 * Retrieves the spacing between listview control items.
4986 * [I] infoPtr : valid pointer to the listview structure
4987 * [IO] lprc : rectangle to receive the output
4988 * on input, lprc->top = nSubItem
4989 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
4991 * NOTE: this call is succeeds only for REPORT style listviews.
4992 * Because we can calculate things much faster in report mode,
4993 * we're gonna do the calculations inline here, instead of
4994 * calling functions that do heavy lifting.
5000 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5003 INT nSubItem, flags;
5005 if (!lprc || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5007 nSubItem = lprc->top;
5010 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, nSubItem);
5012 if (!Header_GetItemRect(infoPtr->hwndHeader, nSubItem, lprc)) return FALSE;
5013 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptPosition)) return FALSE;
5014 lprc->top = ptPosition.y;
5015 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5020 FIXME("Unimplemented LVIR_ICON\n");
5024 /* nothing to do here, we're done */
5027 ERR("Unknown bounds=%d\n", lprc->left);
5036 * Retrieves the width of a label.
5039 * [I] infoPtr : valid pointer to the listview structure
5042 * SUCCESS : string width (in pixels)
5045 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5047 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5050 TRACE("(nItem=%d)\n", nItem);
5052 lvItem.mask = LVIF_TEXT;
5053 lvItem.iItem = nItem;
5054 lvItem.iSubItem = 0;
5055 lvItem.pszText = szDispText;
5056 lvItem.cchTextMax = DISP_TEXT_SIZE;
5057 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5059 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5064 * Retrieves the spacing between listview control items.
5067 * [I] infoPtr : valid pointer to the listview structure
5068 * [I] BOOL : flag for small or large icon
5071 * Horizontal + vertical spacing
5073 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5079 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5083 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5084 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5086 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5093 * Retrieves the state of a listview control item.
5096 * [I] infoPtr : valid pointer to the listview structure
5097 * [I] nItem : item index
5098 * [I] uMask : state mask
5101 * State specified by the mask.
5103 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5107 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5109 lvItem.iItem = nItem;
5110 lvItem.iSubItem = 0;
5111 lvItem.mask = LVIF_STATE;
5112 lvItem.stateMask = uMask;
5113 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5115 return lvItem.state & uMask;
5120 * Retrieves the text of a listview control item or subitem.
5123 * [I] hwnd : window handle
5124 * [I] nItem : item index
5125 * [IO] lpLVItem : item information
5126 * [I] isW : TRUE if lpLVItem is Unicode
5129 * SUCCESS : string length
5132 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5134 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5136 lpLVItem->mask = LVIF_TEXT;
5137 lpLVItem->iItem = nItem;
5138 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5140 return textlenT(lpLVItem->pszText, isW);
5145 * Searches for an item based on properties + relationships.
5148 * [I] infoPtr : valid pointer to the listview structure
5149 * [I] nItem : item index
5150 * [I] uFlags : relationship flag
5153 * This function is very, very inefficient! Needs work.
5156 * SUCCESS : item index
5159 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5161 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5163 LVFINDINFOW lvFindInfo;
5164 INT nCountPerColumn;
5167 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5168 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5170 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5172 if (uFlags & LVNI_CUT)
5175 if (uFlags & LVNI_DROPHILITED)
5176 uMask |= LVIS_DROPHILITED;
5178 if (uFlags & LVNI_FOCUSED)
5179 uMask |= LVIS_FOCUSED;
5181 if (uFlags & LVNI_SELECTED)
5182 uMask |= LVIS_SELECTED;
5184 /* if we're asked for the focused item, that's only one,
5185 * so it's worth optimizing */
5186 if (uFlags & LVNI_FOCUSED)
5188 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5189 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5192 if (uFlags & LVNI_ABOVE)
5194 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5199 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5205 lvFindInfo.flags = LVFI_NEARESTXY;
5206 lvFindInfo.vkDirection = VK_UP;
5207 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5208 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5210 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5215 else if (uFlags & LVNI_BELOW)
5217 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5219 while (nItem < infoPtr->nItemCount)
5222 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5228 lvFindInfo.flags = LVFI_NEARESTXY;
5229 lvFindInfo.vkDirection = VK_DOWN;
5230 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5231 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5233 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5238 else if (uFlags & LVNI_TOLEFT)
5240 if (uView == LVS_LIST)
5242 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5243 while (nItem - nCountPerColumn >= 0)
5245 nItem -= nCountPerColumn;
5246 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5250 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5252 lvFindInfo.flags = LVFI_NEARESTXY;
5253 lvFindInfo.vkDirection = VK_LEFT;
5254 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5255 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5257 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5262 else if (uFlags & LVNI_TORIGHT)
5264 if (uView == LVS_LIST)
5266 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5267 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5269 nItem += nCountPerColumn;
5270 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5274 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5276 lvFindInfo.flags = LVFI_NEARESTXY;
5277 lvFindInfo.vkDirection = VK_RIGHT;
5278 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5279 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5281 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5290 /* search by index */
5291 for (i = nItem; i < infoPtr->nItemCount; i++)
5293 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5301 /* LISTVIEW_GetNumberOfWorkAreas */
5305 * Retrieves the origin coordinates when in icon or small icon display mode.
5308 * [I] infoPtr : valid pointer to the listview structure
5309 * [O] lpptOrigin : coordinate information
5315 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5317 DWORD lStyle = infoPtr->dwStyle;
5318 UINT uView = lStyle & LVS_TYPEMASK;
5319 INT nHorzPos = 0, nVertPos = 0;
5320 SCROLLINFO scrollInfo;
5322 if (!lpptOrigin) return FALSE;
5324 scrollInfo.cbSize = sizeof(SCROLLINFO);
5325 scrollInfo.fMask = SIF_POS;
5327 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5328 nHorzPos = scrollInfo.nPos;
5329 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5330 nVertPos = scrollInfo.nPos;
5332 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5334 lpptOrigin->x = infoPtr->rcList.left;
5335 lpptOrigin->y = infoPtr->rcList.top;
5336 if (uView == LVS_LIST)
5337 nHorzPos *= infoPtr->nItemWidth;
5338 else if (uView == LVS_REPORT)
5339 nVertPos *= infoPtr->nItemHeight;
5341 lpptOrigin->x -= nHorzPos;
5342 lpptOrigin->y -= nVertPos;
5344 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5351 * Retrieves the width of a string.
5354 * [I] hwnd : window handle
5355 * [I] lpszText : text string to process
5356 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5359 * SUCCESS : string width (in pixels)
5362 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5367 if (is_textT(lpszText, isW))
5369 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5370 HDC hdc = GetDC(infoPtr->hwndSelf);
5371 HFONT hOldFont = SelectObject(hdc, hFont);
5374 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5376 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5377 SelectObject(hdc, hOldFont);
5378 ReleaseDC(infoPtr->hwndSelf, hdc);
5380 return stringSize.cx;
5385 * Determines which listview item is located at the specified position.
5388 * [I] infoPtr : valid pointer to the listview structure
5389 * [IO] lpht : hit test information
5390 * [I] subitem : fill out iSubItem.
5391 * [I] select : return the index only if the hit selects the item
5394 * (mm 20001022): We must not allow iSubItem to be touched, for
5395 * an app might pass only a structure with space up to iItem!
5396 * (MS Office 97 does that for instance in the file open dialog)
5399 * SUCCESS : item index
5402 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
5404 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5405 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5406 RECT rcBounds, rcState, rcIcon, rcLabel;
5407 POINT Origin, Position, opt;
5410 TRACE("(pt=%s, subitem=%d, select=%d)\n", debugpoint(&lpht->pt), subitem, select);
5414 if (subitem) lpht->iSubItem = 0;
5416 if (infoPtr->rcList.left > lpht->pt.x)
5417 lpht->flags |= LVHT_TOLEFT;
5418 else if (infoPtr->rcList.right < lpht->pt.x)
5419 lpht->flags |= LVHT_TORIGHT;
5421 if (infoPtr->rcList.top > lpht->pt.y)
5422 lpht->flags |= LVHT_ABOVE;
5423 else if (infoPtr->rcList.bottom < lpht->pt.y)
5424 lpht->flags |= LVHT_BELOW;
5426 if (lpht->flags) return -1;
5428 lpht->flags |= LVHT_NOWHERE;
5430 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return -1;
5432 /* first deal with the large items */
5433 if (uView == LVS_ICON && PtInRect (&infoPtr->rcFocus, lpht->pt))
5435 lpht->iItem = infoPtr->nFocusedItem;
5439 if (uView == LVS_ICON || uView == LVS_SMALLICON)
5444 rcSearch.left = lpht->pt.x - infoPtr->nItemWidth;
5445 rcSearch.top = lpht->pt.y - infoPtr->nItemHeight;
5446 rcSearch.right = lpht->pt.x + 1;
5447 rcSearch.bottom = lpht->pt.y + 1;
5449 iterator_frameditems(&i, infoPtr, &rcSearch);
5450 while(iterator_next(&i))
5452 if (!LISTVIEW_GetItemBox(infoPtr, i.nItem, &rcBounds)) continue;
5453 if (PtInRect(&rcBounds, lpht->pt)) break;
5455 lpht->iItem = i.nItem;
5456 iterator_destroy(&i);
5460 INT nPerCol = (uView == LVS_REPORT) ? infoPtr->nItemCount : LISTVIEW_GetCountPerColumn(infoPtr);
5462 Position.x = lpht->pt.x - Origin.x;
5463 Position.y = lpht->pt.y - Origin.y;
5464 TRACE("Position=%s, nPerCol=%d, nItemHeight=%d, nColHeight=%d\n",
5465 debugpoint(&Position), nPerCol, infoPtr->nItemHeight, nPerCol * infoPtr->nItemHeight);
5467 if (Position.y < nPerCol * infoPtr->nItemHeight)
5469 lpht->iItem = (Position.x / infoPtr->nItemWidth) * nPerCol + (Position.y / infoPtr->nItemHeight);
5470 TRACE("iItem=%d\n", lpht->iItem);
5471 if (lpht->iItem < 0 || lpht->iItem >= infoPtr->nItemCount) lpht->iItem = -1;
5476 if (lpht->iItem == -1) return -1;
5478 lvItem.mask = LVIF_STATE | LVIF_TEXT;
5479 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5480 lvItem.stateMask = LVIS_STATEIMAGEMASK;
5481 if (uView == LVS_ICON && infoPtr->bFocus) lvItem.stateMask |= LVIS_FOCUSED;
5482 lvItem.iItem = lpht->iItem;
5483 lvItem.iSubItem = 0;
5484 lvItem.pszText = szDispText;
5485 lvItem.cchTextMax = DISP_TEXT_SIZE;
5486 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
5488 if (!LISTVIEW_GetItemMetrics(infoPtr, &lvItem, 0, &rcBounds, &rcState, &rcIcon, &rcLabel)) return -1;
5489 if (!LISTVIEW_GetItemListOrigin(infoPtr, lpht->iItem, &Position)) return -1;
5490 opt.x = lpht->pt.x - Position.x - Origin.x;
5491 opt.y = lpht->pt.y - Position.y - Origin.y;
5493 if (!PtInRect(&rcBounds, opt)) return -1;
5495 if (PtInRect(&rcIcon, opt))
5496 lpht->flags |= LVHT_ONITEMICON;
5497 else if (PtInRect(&rcLabel, opt))
5498 lpht->flags |= LVHT_ONITEMLABEL;
5499 else if (infoPtr->himlState && ((lvItem.state & LVIS_STATEIMAGEMASK) >> 12) && PtInRect(&rcState, opt))
5500 lpht->flags |= LVHT_ONITEMSTATEICON;
5501 if (lpht->flags & LVHT_ONITEM)
5502 lpht->flags &= ~LVHT_NOWHERE;
5504 if (uView == LVS_REPORT && lpht->iItem != -1 && subitem)
5506 INT j, nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5507 rcBounds.right = rcBounds.left;
5508 for (j = 0; j < nColumnCount; j++)
5510 rcBounds.left = rcBounds.right;
5511 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5512 if (PtInRect(&rcBounds, opt))
5520 if (!select || lpht->iItem == -1) return lpht->iItem;
5522 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) return lpht->iItem;
5524 return lpht->flags & (LVHT_ONITEMICON | LVHT_ONITEMLABEL) ? lpht->iItem : -1;
5530 * Inserts a new column.
5533 * [I] infoPtr : valid pointer to the listview structure
5534 * [I] INT : column index
5535 * [I] LPLVCOLUMNW : column information
5538 * SUCCESS : new column index
5541 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5542 LPLVCOLUMNW lpColumn, BOOL isW)
5548 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5550 if (!lpColumn) return -1;
5552 hdi.mask = hdi.fmt = 0;
5553 if (lpColumn->mask & LVCF_FMT)
5555 /* format member is valid */
5556 hdi.mask |= HDI_FORMAT;
5558 /* set text alignment (leftmost column must be left-aligned) */
5559 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5560 hdi.fmt |= HDF_LEFT;
5561 else if (lpColumn->fmt & LVCFMT_RIGHT)
5562 hdi.fmt |= HDF_RIGHT;
5563 else if (lpColumn->fmt & LVCFMT_CENTER)
5564 hdi.fmt |= HDF_CENTER;
5566 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5567 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5569 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5571 hdi.fmt |= HDF_IMAGE;
5572 hdi.iImage = I_IMAGECALLBACK;
5575 if (lpColumn->fmt & LVCFMT_IMAGE)
5576 ; /* FIXME: enable images for *(sub)items* this column */
5579 if (lpColumn->mask & LVCF_WIDTH)
5581 hdi.mask |= HDI_WIDTH;
5582 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5584 /* make it fill the remainder of the controls width */
5589 /* get the width of every item except the current one */
5590 hdit.mask = HDI_WIDTH;
5593 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5594 if (Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit)))
5595 hdi.cxy += hdit.cxy;
5597 /* retrieve the layout of the header */
5598 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5599 TRACE("start cxy=%d rcHeader=%s\n", hdi.cxy, debugrect(&rcHeader));
5601 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
5604 hdi.cxy = lpColumn->cx;
5607 if (lpColumn->mask & LVCF_TEXT)
5609 hdi.mask |= HDI_TEXT | HDI_FORMAT;
5610 hdi.fmt |= HDF_STRING;
5611 hdi.pszText = lpColumn->pszText;
5612 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
5615 if (lpColumn->mask & LVCF_IMAGE)
5617 hdi.mask |= HDI_IMAGE;
5618 hdi.iImage = lpColumn->iImage;
5621 if (lpColumn->mask & LVCF_ORDER)
5623 hdi.mask |= HDI_ORDER;
5624 hdi.iOrder = lpColumn->iOrder;
5627 /* insert item in header control */
5628 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5629 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5630 (WPARAM)nColumn, (LPARAM)&hdi);
5631 if (nNewColumn == -1) return -1;
5632 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &rcCol)) return -1;
5634 /* now we have to actually adjust the data */
5635 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
5637 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
5641 /* preallocate memory, so we can fail gracefully */
5642 if (nNewColumn == 0)
5644 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->nItemCount);
5645 if (!lpNewItems) return -1;
5646 for (i = 0; i < infoPtr->nItemCount; i++)
5647 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
5648 if (i != infoPtr->nItemCount)
5650 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5651 COMCTL32_Free(lpNewItems);
5656 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5658 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5659 if (!hdpaSubItems) continue;
5660 for (i = 1; i < hdpaSubItems->nItemCount; i++)
5662 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
5663 if (!lpSubItem) break;
5664 if (lpSubItem->iSubItem >= nNewColumn)
5665 lpSubItem->iSubItem++;
5668 /* if we found our subitem, zapp it */
5669 if (nNewColumn == 0)
5671 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
5672 lpSubItem = lpNewItems[nItem];
5673 lpSubItem->hdr = lpMainItem->hdr;
5674 lpSubItem->iSubItem = 1;
5675 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
5676 lpMainItem->iSubItem = 0;
5677 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
5681 COMCTL32_Free(lpNewItems);
5684 /* we don't have to worry abiut display issues in non-report mode */
5685 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return nNewColumn;
5687 /* if we have a focus, must first erase the focus rect */
5688 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
5690 /* Need to reset the item width when inserting a new column */
5691 infoPtr->nItemWidth += rcCol.right - rcCol.left;
5693 LISTVIEW_UpdateScroll(infoPtr);
5695 /* scroll to cover the deleted column, and invalidate for redraw */
5696 rcOld = infoPtr->rcList;
5697 rcOld.left = rcCol.left;
5698 ScrollWindowEx(infoPtr->hwndSelf, rcCol.right - rcCol.left, 0,
5699 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5701 /* we can restore focus now */
5702 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
5707 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5708 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5709 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5710 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5711 their own sort proc. when sending LVM_SORTITEMS.
5714 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5716 LVS_SORTXXX must be specified,
5717 LVS_OWNERDRAW is not set,
5718 <item>.pszText is not LPSTR_TEXTCALLBACK.
5720 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5721 are sorted based on item text..."
5723 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5725 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5726 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5727 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5729 /* if we're sorting descending, negate the return value */
5730 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5735 * Inserts a new item in the listview control.
5738 * [I] infoPtr : valid pointer to the listview structure
5739 * [I] lpLVItem : item information
5740 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5743 * SUCCESS : new item index
5746 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5748 LONG lStyle = infoPtr->dwStyle;
5749 UINT uView = lStyle & LVS_TYPEMASK;
5753 LISTVIEW_ITEM *lpItem;
5756 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5758 if (lStyle & LVS_OWNERDATA)
5760 nItem = infoPtr->nItemCount;
5761 infoPtr->nItemCount++;
5765 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5766 if (!lpLVItem || lpLVItem->iSubItem) return -1;
5768 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5770 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5773 /* insert item in listview control data structure */
5774 if ( (hdpaSubItems = DPA_Create(8)) )
5775 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5776 if (nItem == -1) goto fail;
5778 /* FIXME: is the handling of this LVS_OWNERDRAWFIXED correct? */
5779 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5780 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5782 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
5783 is_sorted ? infoPtr->nItemCount + 1 : lpLVItem->iItem,
5785 if (nItem == -1) goto fail;
5786 infoPtr->nItemCount++;
5788 if (!LISTVIEW_SetItemT(infoPtr, lpLVItem, isW))
5791 /* if we're sorted, sort the list, and update the index */
5794 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5795 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5798 ERR("We can't find the item we just inserted, possible memory corruption.");
5799 /* we can't remove it from the list if we can't find it, so just fail */
5800 /* we don't deallocate memory here, as it will probably cause more problems */
5805 /* make room for the position, if we are in the right mode */
5806 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5808 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
5810 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
5812 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5817 /* Add the subitem list to the items array. Do this last in case we go to
5818 * fail during the above.
5820 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
5822 lpItem->valid = TRUE;
5824 /* send LVN_INSERTITEM notification */
5825 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5827 nmlv.lParam = lpItem->lParam;
5828 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
5830 /* align items (set position of each item) */
5831 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5833 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
5834 else LISTVIEW_AlignTop(infoPtr);
5837 LISTVIEW_UpdateScroll(infoPtr);
5839 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
5841 TRACE(" <- %d\n", nItem);
5845 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5846 infoPtr->nItemCount--;
5848 DPA_DeletePtr(hdpaSubItems, 0);
5849 DPA_Destroy (hdpaSubItems);
5850 COMCTL32_Free (lpItem);
5856 * Redraws a range of items.
5859 * [I] infoPtr : valid pointer to the listview structure
5860 * [I] INT : first item
5861 * [I] INT : last item
5867 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
5871 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
5872 max(nFirst, nLast) >= infoPtr->nItemCount)
5875 for (i = nFirst; i <= nLast; i++)
5876 LISTVIEW_InvalidateItem(infoPtr, i);
5883 * Scroll the content of a listview.
5886 * [I] infoPtr : valid pointer to the listview structure
5887 * [I] INT : horizontal scroll amount in pixels
5888 * [I] INT : vertical scroll amount in pixels
5895 * If the control is in report mode (LVS_REPORT) the control can
5896 * be scrolled only in line increments. "dy" will be rounded to the
5897 * nearest number of pixels that are a whole line. Ex: if line height
5898 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
5899 * is passed the the scroll will be 0. (per MSDN 7/2002)
5901 * For: (per experimentaion with native control and CSpy ListView)
5902 * LVS_ICON dy=1 = 1 pixel (vertical only)
5904 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
5906 * LVS_LIST dx=1 = 1 column (horizontal only)
5907 * but will only scroll 1 column per message
5908 * no matter what the value.
5909 * dy must be 0 or FALSE returned.
5910 * LVS_REPORT dx=1 = 1 pixel
5914 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
5916 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
5918 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
5919 dy /= infoPtr->nItemHeight;
5922 if (dy != 0) return FALSE;
5929 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
5930 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
5937 * Sets the background color.
5940 * [I] infoPtr : valid pointer to the listview structure
5941 * [I] COLORREF : background color
5947 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
5949 TRACE("(clrBk=%lx)\n", clrBk);
5951 if(infoPtr->clrBk != clrBk) {
5952 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
5953 infoPtr->clrBk = clrBk;
5954 if (clrBk == CLR_NONE)
5955 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
5957 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
5958 LISTVIEW_InvalidateList(infoPtr);
5964 /* LISTVIEW_SetBkImage */
5968 * Sets the attributes of a header item.
5971 * [I] infoPtr : valid pointer to the listview structure
5972 * [I] INT : column index
5973 * [I] LPLVCOLUMNW : column attributes
5974 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
5975 * otherwise it is in fact a LPLVCOLUMNA
5981 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5982 LPLVCOLUMNW lpColumn, BOOL isW)
5984 BOOL bResult = FALSE;
5985 HDITEMW hdi, hdiget;
5987 if ((lpColumn != NULL) && (nColumn >= 0) &&
5988 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
5990 /* initialize memory */
5991 ZeroMemory(&hdi, sizeof(hdi));
5993 if (lpColumn->mask & LVCF_FMT)
5995 /* format member is valid */
5996 hdi.mask |= HDI_FORMAT;
5998 /* get current format first */
5999 hdiget.mask = HDI_FORMAT;
6000 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6001 /* preserve HDF_STRING if present */
6002 hdi.fmt = hdiget.fmt & HDF_STRING;
6004 /* set text alignment (leftmost column must be left-aligned) */
6007 hdi.fmt |= HDF_LEFT;
6011 if (lpColumn->fmt & LVCFMT_LEFT)
6012 hdi.fmt |= HDF_LEFT;
6013 else if (lpColumn->fmt & LVCFMT_RIGHT)
6014 hdi.fmt |= HDF_RIGHT;
6015 else if (lpColumn->fmt & LVCFMT_CENTER)
6016 hdi.fmt |= HDF_CENTER;
6019 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6020 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6022 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6023 hdi.fmt |= HDF_IMAGE;
6025 if (lpColumn->fmt & LVCFMT_IMAGE)
6027 hdi.fmt |= HDF_IMAGE;
6028 hdi.iImage = I_IMAGECALLBACK;
6032 if (lpColumn->mask & LVCF_WIDTH)
6034 hdi.mask |= HDI_WIDTH;
6035 hdi.cxy = lpColumn->cx;
6038 if (lpColumn->mask & LVCF_TEXT)
6040 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6041 hdi.pszText = lpColumn->pszText;
6042 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6043 hdi.fmt |= HDF_STRING;
6046 if (lpColumn->mask & LVCF_IMAGE)
6048 hdi.mask |= HDI_IMAGE;
6049 hdi.iImage = lpColumn->iImage;
6052 if (lpColumn->mask & LVCF_ORDER)
6054 hdi.mask |= HDI_ORDER;
6055 hdi.iOrder = lpColumn->iOrder;
6058 /* set header item attributes */
6060 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6062 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6070 * Sets the column order array
6073 * [I] infoPtr : valid pointer to the listview structure
6074 * [I] INT : number of elements in column order array
6075 * [I] INT : pointer to column order array
6081 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6083 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6094 * Sets the width of a column
6097 * [I] infoPtr : valid pointer to the listview structure
6098 * [I] INT : column index
6099 * [I] INT : column width
6105 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6109 LONG lStyle = infoPtr->dwStyle;
6110 UINT uView = lStyle & LVS_TYPEMASK;
6115 WCHAR text_buffer[DISP_TEXT_SIZE];
6116 INT header_item_count;
6121 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6123 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6125 /* set column width only if in report or list mode */
6126 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6128 /* take care of invalid cx values */
6129 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6130 else if (uView == LVS_LIST && cx < 1) return FALSE;
6132 /* resize all columns if in LVS_LIST mode */
6133 if(uView == LVS_LIST)
6135 infoPtr->nItemWidth = cx;
6136 LISTVIEW_InvalidateList(infoPtr);
6140 /* autosize based on listview items width */
6141 if(cx == LVSCW_AUTOSIZE)
6143 /* set the width of the column to the width of the widest item */
6144 if (iCol == 0 || uView == LVS_LIST)
6147 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6149 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6150 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6152 if (infoPtr->himlSmall)
6153 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6157 lvItem.iSubItem = iCol;
6158 lvItem.mask = LVIF_TEXT;
6159 lvItem.pszText = szDispText;
6160 lvItem.cchTextMax = DISP_TEXT_SIZE;
6162 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6164 lvItem.iItem = item_index;
6165 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6166 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6167 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6170 cx += TRAILING_PADDING;
6171 } /* autosize based on listview header width */
6172 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6174 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6176 /* if iCol is the last column make it fill the remainder of the controls width */
6177 if(iCol == (header_item_count - 1)) {
6178 /* get the width of every item except the current one */
6179 hdi.mask = HDI_WIDTH;
6182 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6183 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6187 /* retrieve the layout of the header */
6188 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6190 cx = (rcHeader.right - rcHeader.left) - cx;
6194 /* Despite what the MS docs say, if this is not the last
6195 column, then MS resizes the column to the width of the
6196 largest text string in the column, including headers
6197 and items. This is different from LVSCW_AUTOSIZE in that
6198 LVSCW_AUTOSIZE ignores the header string length.
6201 /* retrieve header font */
6202 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6204 /* retrieve header text */
6205 hdi.mask = HDI_TEXT;
6206 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6207 hdi.pszText = text_buffer;
6209 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6211 /* determine the width of the text in the header */
6212 hdc = GetDC(infoPtr->hwndSelf);
6213 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6215 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6217 SelectObject(hdc, old_font); /* restore the old font */
6218 ReleaseDC(infoPtr->hwndSelf, hdc);
6220 lvItem.iSubItem = iCol;
6221 lvItem.mask = LVIF_TEXT;
6222 lvItem.pszText = szDispText;
6223 lvItem.cchTextMax = DISP_TEXT_SIZE;
6225 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6227 lvItem.iItem = item_index;
6228 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6229 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6230 nLabelWidth += TRAILING_PADDING;
6231 /* While it is possible for subitems to have icons, even MS messes
6232 up the positioning, so I suspect no applications actually use
6234 if (item_index == 0 && infoPtr->himlSmall)
6235 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6236 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6241 /* call header to update the column change */
6242 hdi.mask = HDI_WIDTH;
6245 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6247 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6254 * Sets the extended listview style.
6257 * [I] infoPtr : valid pointer to the listview structure
6262 * SUCCESS : previous style
6265 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6267 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6271 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6273 infoPtr->dwLvExStyle = dwStyle;
6280 * Sets the new hot cursor used during hot tracking and hover selection.
6283 * [I] infoPtr : valid pointer to the listview structure
6284 * [I} hCurosr : the new hot cursor handle
6287 * Returns the previous hot cursor
6289 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6291 HCURSOR oldCursor = infoPtr->hHotCursor;
6292 infoPtr->hHotCursor = hCursor;
6299 * Sets the hot item index.
6302 * [I] infoPtr : valid pointer to the listview structure
6306 * SUCCESS : previous hot item index
6307 * FAILURE : -1 (no hot item)
6309 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6311 INT iOldIndex = infoPtr->nHotItem;
6312 infoPtr->nHotItem = iIndex;
6319 * Sets the amount of time the cursor must hover over an item before it is selected.
6322 * [I] infoPtr : valid pointer to the listview structure
6323 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6326 * Returns the previous hover time
6328 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6330 DWORD oldHoverTime = infoPtr->dwHoverTime;
6331 infoPtr->dwHoverTime = dwHoverTime;
6332 return oldHoverTime;
6337 * Sets spacing for icons of LVS_ICON style.
6340 * [I] infoPtr : valid pointer to the listview structure
6341 * [I] DWORD : MAKELONG(cx, cy)
6344 * MAKELONG(oldcx, oldcy)
6346 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6348 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6349 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6350 LONG lStyle = infoPtr->dwStyle;
6351 UINT uView = lStyle & LVS_TYPEMASK;
6353 TRACE("requested=(%d,%d)\n", cx, cy);
6355 /* this is supported only for LVS_ICON style */
6356 if (uView != LVS_ICON) return oldspacing;
6358 /* set to defaults, if instructed to */
6359 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6360 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6362 /* if 0 then compute width
6363 * FIXME: Should scan each item and determine max width of
6364 * icon or label, then make that the width */
6366 cx = infoPtr->iconSpacing.cx;
6368 /* if 0 then compute height */
6370 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6371 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6374 infoPtr->iconSpacing.cx = cx;
6375 infoPtr->iconSpacing.cy = cy;
6377 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6378 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6379 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6380 infoPtr->ntmHeight);
6382 /* these depend on the iconSpacing */
6383 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6384 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6389 inline void update_icon_size(HIMAGELIST himl, SIZE *size)
6393 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6399 size->cx = size->cy = 0;
6407 * [I] infoPtr : valid pointer to the listview structure
6408 * [I] INT : image list type
6409 * [I] HIMAGELIST : image list handle
6412 * SUCCESS : old image list
6415 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6417 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6418 INT oldHeight = infoPtr->nItemHeight;
6419 HIMAGELIST himlOld = 0;
6424 himlOld = infoPtr->himlNormal;
6425 infoPtr->himlNormal = himl;
6426 if (uView == LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6427 LISTVIEW_SetIconSpacing(infoPtr, 0);
6431 himlOld = infoPtr->himlSmall;
6432 infoPtr->himlSmall = himl;
6433 if (uView != LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6437 himlOld = infoPtr->himlState;
6438 infoPtr->himlState = himl;
6439 update_icon_size(himl, &infoPtr->iconStateSize);
6440 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6444 ERR("Unknown icon type=%d\n", nType);
6448 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6449 if (infoPtr->nItemHeight != oldHeight)
6450 LISTVIEW_UpdateScroll(infoPtr);
6457 * Preallocates memory (does *not* set the actual count of items !)
6460 * [I] infoPtr : valid pointer to the listview structure
6461 * [I] INT : item count (projected number of items to allocate)
6462 * [I] DWORD : update flags
6468 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6470 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6472 if (infoPtr->dwStyle & LVS_OWNERDATA)
6474 int precount,topvisible;
6476 TRACE("LVS_OWNERDATA is set!\n");
6477 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6478 FIXME("flags %s %s not implemented\n",
6479 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6481 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6483 LISTVIEW_RemoveAllSelections(infoPtr);
6485 precount = infoPtr->nItemCount;
6486 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6487 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6489 infoPtr->nItemCount = nItems;
6490 infoPtr->nItemWidth = max(LISTVIEW_CalculateMaxWidth(infoPtr),
6491 DEFAULT_COLUMN_WIDTH);
6493 LISTVIEW_UpdateSize(infoPtr);
6494 LISTVIEW_UpdateScroll(infoPtr);
6496 if (min(precount,infoPtr->nItemCount) < topvisible)
6497 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6501 /* According to MSDN for non-LVS_OWNERDATA this is just
6502 * a performance issue. The control allocates its internal
6503 * data structures for the number of items specified. It
6504 * cuts down on the number of memory allocations. Therefore
6505 * we will just issue a WARN here
6507 WARN("for non-ownerdata performance option not implemented.\n");
6515 * Sets the position of an item.
6518 * [I] infoPtr : valid pointer to the listview structure
6519 * [I] nItem : item index
6520 * [I] pt : coordinate
6526 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6528 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6531 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6533 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6534 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6536 /* This point value seems to be an undocumented feature.
6537 * The best guess is that it means either at the origin,
6538 * or at true beginning of the list. I will assume the origin. */
6539 if ((pt.x == -1) && (pt.y == -1))
6540 LISTVIEW_GetOrigin(infoPtr, &pt);
6541 else if (uView == LVS_ICON)
6543 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6544 pt.y -= ICON_TOP_PADDING;
6547 /* save the old position */
6548 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
6549 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
6551 /* Is the position changing? */
6552 if (pt.x == old.x && pt.y == old.y) return TRUE;
6554 /* FIXME: shouldn't we invalidate, as the item moved? */
6556 /* Allocating a POINTER for every item is too resource intensive,
6557 * so we'll keep the (x,y) in different arrays */
6558 if (DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)pt.x) &&
6559 DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)pt.y) )
6562 ERR("We should never fail here (nItem=%d, pt=%s), please report.\n",
6563 nItem, debugpoint(&pt));
6569 * Sets the state of one or many items.
6572 * [I] infoPtr : valid pointer to the listview structure
6573 * [I]INT : item index
6574 * [I] LPLVITEM : item or subitem info
6580 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6582 BOOL bResult = TRUE;
6585 lvItem.iItem = nItem;
6586 lvItem.iSubItem = 0;
6587 lvItem.mask = LVIF_STATE;
6588 lvItem.state = lpLVItem->state;
6589 lvItem.stateMask = lpLVItem->stateMask;
6590 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6594 /* apply to all items */
6595 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6596 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6599 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6606 * Sets the text of an item or subitem.
6609 * [I] hwnd : window handle
6610 * [I] nItem : item index
6611 * [I] lpLVItem : item or subitem info
6612 * [I] isW : TRUE if input is Unicode
6618 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6622 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6624 lvItem.iItem = nItem;
6625 lvItem.iSubItem = lpLVItem->iSubItem;
6626 lvItem.mask = LVIF_TEXT;
6627 lvItem.pszText = lpLVItem->pszText;
6628 lvItem.cchTextMax = lpLVItem->cchTextMax;
6630 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6632 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6637 * Set item index that marks the start of a multiple selection.
6640 * [I] infoPtr : valid pointer to the listview structure
6644 * Index number or -1 if there is no selection mark.
6646 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6648 INT nOldIndex = infoPtr->nSelectionMark;
6650 TRACE("(nIndex=%d)\n", nIndex);
6652 infoPtr->nSelectionMark = nIndex;
6659 * Sets the text background color.
6662 * [I] infoPtr : valid pointer to the listview structure
6663 * [I] COLORREF : text background color
6669 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6671 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6673 if (infoPtr->clrTextBk != clrTextBk)
6675 infoPtr->clrTextBk = clrTextBk;
6676 LISTVIEW_InvalidateList(infoPtr);
6684 * Sets the text foreground color.
6687 * [I] infoPtr : valid pointer to the listview structure
6688 * [I] COLORREF : text color
6694 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6696 TRACE("(clrText=%lx)\n", clrText);
6698 if (infoPtr->clrText != clrText)
6700 infoPtr->clrText = clrText;
6701 LISTVIEW_InvalidateList(infoPtr);
6707 /* LISTVIEW_SetToolTips */
6708 /* LISTVIEW_SetUnicodeFormat */
6709 /* LISTVIEW_SetWorkAreas */
6713 * Callback internally used by LISTVIEW_SortItems()
6716 * [I] LPVOID : first LISTVIEW_ITEM to compare
6717 * [I] LPVOID : second LISTVIEW_ITEM to compare
6718 * [I] LPARAM : HWND of control
6721 * if first comes before second : negative
6722 * if first comes after second : positive
6723 * if first and second are equivalent : zero
6725 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6727 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6728 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6729 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6731 /* Forward the call to the client defined callback */
6732 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6737 * Sorts the listview items.
6740 * [I] infoPtr : valid pointer to the listview structure
6741 * [I] WPARAM : application-defined value
6742 * [I] LPARAM : pointer to comparision callback
6748 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6750 UINT lStyle = infoPtr->dwStyle;
6752 LISTVIEW_ITEM *lpItem;
6753 LPVOID selectionMarkItem;
6756 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6758 if (lStyle & LVS_OWNERDATA) return FALSE;
6760 if (!infoPtr->hdpaItems) return FALSE;
6762 /* if there are 0 or 1 items, there is no need to sort */
6763 if (infoPtr->nItemCount < 2) return TRUE;
6765 if (infoPtr->nFocusedItem >= 0)
6767 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6768 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6769 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6772 infoPtr->pfnCompare = pfnCompare;
6773 infoPtr->lParamSort = lParamSort;
6774 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6776 /* Adjust selections and indices so that they are the way they should
6777 * be after the sort (otherwise, the list items move around, but
6778 * whatever is at the item's previous original position will be
6780 * FIXME: can't this be made more efficient?
6782 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6783 for (i=0; i < infoPtr->nItemCount; i++)
6785 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6786 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6788 if (lpItem->state & LVIS_SELECTED)
6789 LISTVIEW_AddSelectionRange(infoPtr, i, i);
6791 LISTVIEW_RemoveSelectionRange(infoPtr, i, i);
6792 if (lpItem->state & LVIS_FOCUSED)
6794 infoPtr->nFocusedItem = i;
6795 lpItem->state &= ~LVIS_FOCUSED;
6798 if (selectionMarkItem != NULL)
6799 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6800 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
6802 /* align the items */
6803 LISTVIEW_AlignTop(infoPtr);
6805 /* refresh the display */
6806 LISTVIEW_InvalidateList(infoPtr); /* FIXME: display should not change for [SMALL]ICON view */
6813 * Updates an items or rearranges the listview control.
6816 * [I] infoPtr : valid pointer to the listview structure
6817 * [I] INT : item index
6823 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
6825 LONG lStyle = infoPtr->dwStyle;
6826 UINT uView = lStyle & LVS_TYPEMASK;
6828 TRACE("(nItem=%d)\n", nItem);
6830 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6832 /* rearrange with default alignment style */
6833 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
6834 LISTVIEW_Arrange(infoPtr, 0);
6836 LISTVIEW_InvalidateItem(infoPtr, nItem);
6844 * Creates the listview control.
6847 * [I] hwnd : window handle
6848 * [I] lpcs : the create parameters
6854 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
6856 LISTVIEW_INFO *infoPtr;
6857 UINT uView = lpcs->style & LVS_TYPEMASK;
6860 TRACE("(lpcs=%p)\n", lpcs);
6862 /* initialize info pointer */
6863 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
6864 if (!infoPtr) return -1;
6866 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
6868 infoPtr->hwndSelf = hwnd;
6869 infoPtr->dwStyle = lpcs->style;
6870 /* determine the type of structures to use */
6871 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
6872 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
6874 /* initialize color information */
6875 infoPtr->clrBk = CLR_NONE;
6876 infoPtr->clrText = comctl32_color.clrWindowText;
6877 infoPtr->clrTextBk = CLR_DEFAULT;
6878 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
6880 /* set default values */
6881 infoPtr->nFocusedItem = -1;
6882 infoPtr->nSelectionMark = -1;
6883 infoPtr->nHotItem = -1;
6884 infoPtr->bRedraw = TRUE;
6885 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
6886 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
6887 infoPtr->nEditLabelItem = -1;
6889 /* get default font (icon title) */
6890 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
6891 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
6892 infoPtr->hFont = infoPtr->hDefaultFont;
6893 LISTVIEW_SaveTextMetrics(infoPtr);
6896 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
6897 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
6898 0, 0, 0, 0, hwnd, (HMENU)0,
6899 lpcs->hInstance, NULL);
6901 /* set header unicode format */
6902 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
6904 /* set header font */
6905 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
6908 if (uView == LVS_ICON)
6910 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
6911 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
6913 else if (uView == LVS_REPORT)
6915 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
6917 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6921 /* set HDS_HIDDEN flag to hide the header bar */
6922 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
6923 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
6927 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6928 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6932 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6933 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6936 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
6937 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
6939 /* display unsupported listview window styles */
6940 LISTVIEW_UnsupportedStyles(lpcs->style);
6942 /* allocate memory for the data structure */
6943 infoPtr->hdpaItems = DPA_Create(10);
6944 infoPtr->hdpaPosX = DPA_Create(10);
6945 infoPtr->hdpaPosY = DPA_Create(10);
6947 /* allocate memory for the selection ranges */
6948 infoPtr->hdpaSelectionRanges = DPA_Create(10);
6950 /* initialize size of items */
6951 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6952 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
6954 /* initialize the hover time to -1(indicating the default system hover time) */
6955 infoPtr->dwHoverTime = -1;
6962 * Erases the background of the listview control.
6965 * [I] infoPtr : valid pointer to the listview structure
6966 * [I] hdc : device context handle
6972 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
6976 TRACE("(hdc=%x)\n", hdc);
6978 if (!GetClipBox(hdc, &rc)) return FALSE;
6980 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
6986 * Helper function for LISTVIEW_[HV]Scroll *only*.
6987 * Performs vertical/horizontal scrolling by a give amount.
6990 * [I] infoPtr : valid pointer to the listview structure
6991 * [I] dx : amount of horizontal scroll
6992 * [I] dy : amount of vertical scroll
6994 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6996 /* now we can scroll the list */
6997 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
6998 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
6999 /* if we have focus, adjust rect */
7000 OffsetRect(&infoPtr->rcFocus, dx, dy);
7001 UpdateWindow(infoPtr->hwndSelf);
7006 * Performs vertical scrolling.
7009 * [I] infoPtr : valid pointer to the listview structure
7010 * [I] nScrollCode : scroll code
7011 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7012 * [I] hScrollWnd : scrollbar control window handle
7018 * SB_LINEUP/SB_LINEDOWN:
7019 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7020 * for LVS_REPORT is 1 line
7021 * for LVS_LIST cannot occur
7024 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7025 INT nScrollDiff, HWND hScrollWnd)
7027 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7028 INT nOldScrollPos, nNewScrollPos;
7029 SCROLLINFO scrollInfo;
7032 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7034 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7036 scrollInfo.cbSize = sizeof(SCROLLINFO);
7037 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7039 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7041 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7043 nOldScrollPos = scrollInfo.nPos;
7044 switch (nScrollCode)
7050 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7054 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7058 nScrollDiff = -scrollInfo.nPage;
7062 nScrollDiff = scrollInfo.nPage;
7065 case SB_THUMBPOSITION:
7067 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7074 /* quit right away if pos isn't changing */
7075 if (nScrollDiff == 0) return 0;
7077 /* calculate new position, and handle overflows */
7078 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7079 if (nScrollDiff > 0) {
7080 if (nNewScrollPos < nOldScrollPos ||
7081 nNewScrollPos > scrollInfo.nMax)
7082 nNewScrollPos = scrollInfo.nMax;
7084 if (nNewScrollPos > nOldScrollPos ||
7085 nNewScrollPos < scrollInfo.nMin)
7086 nNewScrollPos = scrollInfo.nMin;
7089 /* set the new position, and reread in case it changed */
7090 scrollInfo.fMask = SIF_POS;
7091 scrollInfo.nPos = nNewScrollPos;
7092 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7094 /* carry on only if it really changed */
7095 if (nNewScrollPos == nOldScrollPos) return 0;
7097 /* now adjust to client coordinates */
7098 nScrollDiff = nOldScrollPos - nNewScrollPos;
7099 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7101 /* and scroll the window */
7102 scroll_list(infoPtr, 0, nScrollDiff);
7109 * Performs horizontal scrolling.
7112 * [I] infoPtr : valid pointer to the listview structure
7113 * [I] nScrollCode : scroll code
7114 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7115 * [I] hScrollWnd : scrollbar control window handle
7121 * SB_LINELEFT/SB_LINERIGHT:
7122 * for LVS_ICON, LVS_SMALLICON 1 pixel
7123 * for LVS_REPORT is 1 pixel
7124 * for LVS_LIST is 1 column --> which is a 1 because the
7125 * scroll is based on columns not pixels
7128 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7129 INT nScrollDiff, HWND hScrollWnd)
7131 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7132 INT nOldScrollPos, nNewScrollPos;
7133 SCROLLINFO scrollInfo;
7135 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7137 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7139 scrollInfo.cbSize = sizeof(SCROLLINFO);
7140 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7142 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7144 nOldScrollPos = scrollInfo.nPos;
7146 switch (nScrollCode)
7160 nScrollDiff = -scrollInfo.nPage;
7164 nScrollDiff = scrollInfo.nPage;
7167 case SB_THUMBPOSITION:
7169 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7176 /* quit right away if pos isn't changing */
7177 if (nScrollDiff == 0) return 0;
7179 /* calculate new position, and handle overflows */
7180 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7181 if (nScrollDiff > 0) {
7182 if (nNewScrollPos < nOldScrollPos ||
7183 nNewScrollPos > scrollInfo.nMax)
7184 nNewScrollPos = scrollInfo.nMax;
7186 if (nNewScrollPos > nOldScrollPos ||
7187 nNewScrollPos < scrollInfo.nMin)
7188 nNewScrollPos = scrollInfo.nMin;
7191 /* set the new position, and reread in case it changed */
7192 scrollInfo.fMask = SIF_POS;
7193 scrollInfo.nPos = nNewScrollPos;
7194 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7196 /* carry on only if it really changed */
7197 if (nNewScrollPos == nOldScrollPos) return 0;
7199 if(uView == LVS_REPORT)
7200 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7202 /* now adjust to client coordinates */
7203 nScrollDiff = nOldScrollPos - nNewScrollPos;
7204 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7206 /* and scroll the window */
7207 scroll_list(infoPtr, nScrollDiff, 0);
7212 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7214 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7215 INT gcWheelDelta = 0;
7216 UINT pulScrollLines = 3;
7217 SCROLLINFO scrollInfo;
7219 TRACE("(wheelDelta=%d)\n", wheelDelta);
7221 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7222 gcWheelDelta -= wheelDelta;
7224 scrollInfo.cbSize = sizeof(SCROLLINFO);
7225 scrollInfo.fMask = SIF_POS;
7232 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7233 * should be fixed in the future.
7235 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7236 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7237 scrollInfo.nPos + (gcWheelDelta < 0) ?
7238 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7239 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7243 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7245 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7247 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7248 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7249 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7255 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7266 * [I] infoPtr : valid pointer to the listview structure
7267 * [I] INT : virtual key
7268 * [I] LONG : key data
7273 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7275 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7277 NMLVKEYDOWN nmKeyDown;
7279 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7281 /* send LVN_KEYDOWN notification */
7282 nmKeyDown.wVKey = nVirtualKey;
7283 nmKeyDown.flags = 0;
7284 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7286 switch (nVirtualKey)
7289 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7291 notify(infoPtr, NM_RETURN);
7292 notify(infoPtr, LVN_ITEMACTIVATE);
7297 if (infoPtr->nItemCount > 0)
7302 if (infoPtr->nItemCount > 0)
7303 nItem = infoPtr->nItemCount - 1;
7307 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7311 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7315 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7319 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7323 if (uView == LVS_REPORT)
7324 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7326 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7327 * LISTVIEW_GetCountPerRow(infoPtr);
7328 if(nItem < 0) nItem = 0;
7332 if (uView == LVS_REPORT)
7333 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7335 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7336 * LISTVIEW_GetCountPerRow(infoPtr);
7337 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7341 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7342 LISTVIEW_KeySelection(infoPtr, nItem);
7352 * [I] infoPtr : valid pointer to the listview structure
7357 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7361 /* if we did not have the focus, there's nothing to do */
7362 if (!infoPtr->bFocus) return 0;
7364 /* send NM_KILLFOCUS notification */
7365 notify(infoPtr, NM_KILLFOCUS);
7367 /* if we have a focus rectagle, get rid of it */
7368 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7370 /* set window focus flag */
7371 infoPtr->bFocus = FALSE;
7373 /* invalidate the selected items before reseting focus flag */
7374 LISTVIEW_InvalidateSelectedItems(infoPtr);
7381 * Processes double click messages (left mouse button).
7384 * [I] infoPtr : valid pointer to the listview structure
7385 * [I] wKey : key flag
7386 * [I] pts : mouse coordinate
7391 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7393 LVHITTESTINFO htInfo;
7395 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7397 /* send NM_RELEASEDCAPTURE notification */
7398 notify(infoPtr, NM_RELEASEDCAPTURE);
7400 htInfo.pt.x = pts.x;
7401 htInfo.pt.y = pts.y;
7403 /* send NM_DBLCLK notification */
7404 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
7405 notify_click(infoPtr, NM_DBLCLK, &htInfo);
7407 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7408 if(htInfo.iItem != -1) notify(infoPtr, LVN_ITEMACTIVATE);
7415 * Processes mouse down messages (left mouse button).
7418 * [I] infoPtr : valid pointer to the listview structure
7419 * [I] wKey : key flag
7420 * [I] pts : mouse coordinate
7425 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7427 LVHITTESTINFO lvHitTestInfo;
7428 LONG lStyle = infoPtr->dwStyle;
7429 static BOOL bGroupSelect = TRUE;
7430 POINT pt = { pts.x, pts.y };
7433 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7435 /* send NM_RELEASEDCAPTURE notification */
7436 notify(infoPtr, NM_RELEASEDCAPTURE);
7438 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7440 /* set left button down flag */
7441 infoPtr->bLButtonDown = TRUE;
7443 lvHitTestInfo.pt.x = pts.x;
7444 lvHitTestInfo.pt.y = pts.y;
7446 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7447 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7448 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7450 if (lStyle & LVS_SINGLESEL)
7452 if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7453 && infoPtr->nEditLabelItem == -1)
7454 infoPtr->nEditLabelItem = nItem;
7456 LISTVIEW_SetSelection(infoPtr, nItem);
7460 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7463 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7468 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7469 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7471 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7472 infoPtr->nSelectionMark = nItem;
7475 else if (wKey & MK_CONTROL)
7479 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7481 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7482 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7483 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7484 infoPtr->nSelectionMark = nItem;
7486 else if (wKey & MK_SHIFT)
7488 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7492 BOOL was_selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
7494 /* set selection (clears other pre-existing selections) */
7495 LISTVIEW_SetSelection(infoPtr, nItem);
7497 if (was_selected && infoPtr->nEditLabelItem == -1)
7498 infoPtr->nEditLabelItem = nItem;
7504 /* remove all selections */
7505 LISTVIEW_RemoveAllSelections(infoPtr);
7513 * Processes mouse up messages (left mouse button).
7516 * [I] infoPtr : valid pointer to the listview structure
7517 * [I] wKey : key flag
7518 * [I] pts : mouse coordinate
7523 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7525 LVHITTESTINFO lvHitTestInfo;
7527 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7529 if (!infoPtr->bLButtonDown) return 0;
7531 lvHitTestInfo.pt.x = pts.x;
7532 lvHitTestInfo.pt.y = pts.y;
7534 /* send NM_CLICK notification */
7535 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7536 notify_click(infoPtr, NM_CLICK, &lvHitTestInfo);
7538 /* set left button flag */
7539 infoPtr->bLButtonDown = FALSE;
7541 if(infoPtr->nEditLabelItem != -1)
7543 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem &&
7544 (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7545 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7546 infoPtr->nEditLabelItem = -1;
7554 * Destroys the listview control (called after WM_DESTROY).
7557 * [I] infoPtr : valid pointer to the listview structure
7562 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7564 LONG lStyle = infoPtr->dwStyle;
7568 /* delete all items */
7569 LISTVIEW_DeleteAllItems(infoPtr);
7571 /* destroy data structure */
7572 DPA_Destroy(infoPtr->hdpaItems);
7573 DPA_Destroy(infoPtr->hdpaSelectionRanges);
7575 /* destroy image lists */
7576 if (!(lStyle & LVS_SHAREIMAGELISTS))
7578 /* FIXME: If the caller does a ImageList_Destroy and then we
7579 * do this code the area will be freed twice. Currently
7580 * this generates an "err:heap:HEAP_ValidateInUseArena
7581 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
7582 * has PREV_FREE flag" sometimes.
7584 * We will leak the memory till we figure out how to fix
7586 if (infoPtr->himlNormal)
7587 ImageList_Destroy(infoPtr->himlNormal);
7588 if (infoPtr->himlSmall)
7589 ImageList_Destroy(infoPtr->himlSmall);
7590 if (infoPtr->himlState)
7591 ImageList_Destroy(infoPtr->himlState);
7594 /* destroy font, bkgnd brush */
7596 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7597 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7599 /* free listview info pointer*/
7600 COMCTL32_Free(infoPtr);
7602 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7608 * Handles notifications from children.
7611 * [I] infoPtr : valid pointer to the listview structure
7612 * [I] INT : control identifier
7613 * [I] LPNMHDR : notification information
7618 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7620 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7622 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7624 /* handle notification from header control */
7625 if (lpnmh->code == HDN_ENDTRACKW)
7627 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7628 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7630 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7632 /* Handle sorting by Header Column */
7635 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7637 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
7638 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7640 else if(lpnmh->code == NM_RELEASEDCAPTURE)
7642 /* Idealy this should be done in HDN_ENDTRACKA
7643 * but since SetItemBounds in Header.c is called after
7644 * the notification is sent, it is neccessary to handle the
7645 * update of the scroll bar here (Header.c works fine as it is,
7646 * no need to disturb it)
7648 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7649 LISTVIEW_UpdateScroll(infoPtr);
7650 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7660 * Determines the type of structure to use.
7663 * [I] infoPtr : valid pointer to the listview structureof the sender
7664 * [I] HWND : listview window handle
7665 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
7670 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7672 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7674 if (nCommand == NF_REQUERY)
7675 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7676 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7682 * Paints/Repaints the listview control.
7685 * [I] infoPtr : valid pointer to the listview structure
7686 * [I] HDC : device context handle
7691 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7693 TRACE("(hdc=%x)\n", hdc);
7696 LISTVIEW_Refresh(infoPtr, hdc);
7701 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7703 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7704 LISTVIEW_Refresh(infoPtr, hdc);
7705 EndPaint(infoPtr->hwndSelf, &ps);
7713 * Processes double click messages (right mouse button).
7716 * [I] infoPtr : valid pointer to the listview structure
7717 * [I] wKey : key flag
7718 * [I] pts : mouse coordinate
7723 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7725 LVHITTESTINFO lvHitTestInfo;
7727 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7729 /* send NM_RELEASEDCAPTURE notification */
7730 notify(infoPtr, NM_RELEASEDCAPTURE);
7732 /* send NM_RDBLCLK notification */
7733 lvHitTestInfo.pt.x = pts.x;
7734 lvHitTestInfo.pt.y = pts.y;
7735 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7736 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
7743 * Processes mouse down messages (right mouse button).
7746 * [I] infoPtr : valid pointer to the listview structure
7747 * [I] wKey : key flag
7748 * [I] pts : mouse coordinate
7753 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7755 LVHITTESTINFO lvHitTestInfo;
7758 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7760 /* send NM_RELEASEDCAPTURE notification */
7761 notify(infoPtr, NM_RELEASEDCAPTURE);
7763 /* make sure the listview control window has the focus */
7764 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7766 /* set right button down flag */
7767 infoPtr->bRButtonDown = TRUE;
7769 /* determine the index of the selected item */
7770 lvHitTestInfo.pt.x = pts.x;
7771 lvHitTestInfo.pt.y = pts.y;
7772 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
7774 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7776 LISTVIEW_SetItemFocus(infoPtr, nItem);
7777 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7778 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7779 LISTVIEW_SetSelection(infoPtr, nItem);
7783 LISTVIEW_RemoveAllSelections(infoPtr);
7791 * Processes mouse up messages (right mouse button).
7794 * [I] infoPtr : valid pointer to the listview structure
7795 * [I] wKey : key flag
7796 * [I] pts : mouse coordinate
7801 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7803 LVHITTESTINFO lvHitTestInfo;
7806 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7808 if (!infoPtr->bRButtonDown) return 0;
7810 /* set button flag */
7811 infoPtr->bRButtonDown = FALSE;
7813 /* Send NM_RClICK notification */
7814 lvHitTestInfo.pt.x = pts.x;
7815 lvHitTestInfo.pt.y = pts.y;
7816 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
7817 notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo);
7819 /* Change to screen coordinate for WM_CONTEXTMENU */
7820 pt = lvHitTestInfo.pt;
7821 ClientToScreen(infoPtr->hwndSelf, &pt);
7823 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
7824 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
7825 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
7836 * [I] infoPtr : valid pointer to the listview structure
7837 * [I] hwnd : window handle of window containing the cursor
7838 * [I] nHittest : hit-test code
7839 * [I] wMouseMsg : ideintifier of the mouse message
7842 * TRUE if cursor is set
7845 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
7847 LVHITTESTINFO lvHitTestInfo;
7849 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
7851 if(!infoPtr->hHotCursor) return FALSE;
7853 GetCursorPos(&lvHitTestInfo.pt);
7854 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
7856 SetCursor(infoPtr->hHotCursor);
7866 * [I] infoPtr : valid pointer to the listview structure
7867 * [I] infoPtr : handle of previously focused window
7872 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
7874 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
7876 /* if we have the focus already, there's nothing to do */
7877 if (infoPtr->bFocus) return 0;
7879 /* send NM_SETFOCUS notification */
7880 notify(infoPtr, NM_SETFOCUS);
7882 /* set window focus flag */
7883 infoPtr->bFocus = TRUE;
7885 /* put the focus rect back on */
7886 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
7888 /* redraw all visible selected items */
7889 LISTVIEW_InvalidateSelectedItems(infoPtr);
7899 * [I] infoPtr : valid pointer to the listview structure
7900 * [I] HFONT : font handle
7901 * [I] WORD : redraw flag
7906 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
7908 HFONT oldFont = infoPtr->hFont;
7910 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
7912 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
7913 if (infoPtr->hFont == oldFont) return 0;
7915 LISTVIEW_SaveTextMetrics(infoPtr);
7917 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
7918 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
7920 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
7927 * Message handling for WM_SETREDRAW.
7928 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
7931 * [I] infoPtr : valid pointer to the listview structure
7932 * [I] bRedraw: state of redraw flag
7935 * DefWinProc return value
7937 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
7939 infoPtr->bRedraw = bRedraw;
7941 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
7942 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
7948 * Resizes the listview control. This function processes WM_SIZE
7949 * messages. At this time, the width and height are not used.
7952 * [I] infoPtr : valid pointer to the listview structure
7953 * [I] WORD : new width
7954 * [I] WORD : new height
7959 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
7961 LONG lStyle = infoPtr->dwStyle;
7962 UINT uView = lStyle & LVS_TYPEMASK;
7964 TRACE("(width=%d, height=%d)\n", Width, Height);
7966 if (LISTVIEW_UpdateSize(infoPtr))
7968 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
7970 if (lStyle & LVS_ALIGNLEFT)
7971 LISTVIEW_AlignLeft(infoPtr);
7973 LISTVIEW_AlignTop(infoPtr);
7976 LISTVIEW_UpdateScroll(infoPtr);
7978 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7986 * Sets the size information.
7989 * [I] infoPtr : valid pointer to the listview structure
7992 * Zero if no size change
7995 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
7997 LONG lStyle = infoPtr->dwStyle;
7998 UINT uView = lStyle & LVS_TYPEMASK;
8002 GetClientRect(infoPtr->hwndSelf, &rcList);
8003 CopyRect(&rcOld,&(infoPtr->rcList));
8004 infoPtr->rcList.left = 0;
8005 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8006 infoPtr->rcList.top = 0;
8007 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8009 if (uView == LVS_LIST)
8011 /* Apparently the "LIST" style is supposed to have the same
8012 * number of items in a column even if there is no scroll bar.
8013 * Since if a scroll bar already exists then the bottom is already
8014 * reduced, only reduce if the scroll bar does not currently exist.
8015 * The "2" is there to mimic the native control. I think it may be
8016 * related to either padding or edges. (GLA 7/2002)
8018 if (!(lStyle & WS_HSCROLL))
8020 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8021 if (infoPtr->rcList.bottom > nHScrollHeight)
8022 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8026 if (infoPtr->rcList.bottom > 2)
8027 infoPtr->rcList.bottom -= 2;
8030 else if (uView == LVS_REPORT)
8037 Header_Layout(infoPtr->hwndHeader, &hl);
8039 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8041 if (!(LVS_NOCOLUMNHEADER & lStyle))
8042 infoPtr->rcList.top = max(wp.cy, 0);
8044 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8049 * Processes WM_STYLECHANGED messages.
8052 * [I] infoPtr : valid pointer to the listview structure
8053 * [I] WPARAM : window style type (normal or extended)
8054 * [I] LPSTYLESTRUCT : window style information
8059 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8062 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8063 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8064 RECT rcList = infoPtr->rcList;
8066 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8067 wStyleType, lpss->styleOld, lpss->styleNew);
8069 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8071 if (wStyleType == GWL_STYLE)
8073 infoPtr->dwStyle = lpss->styleNew;
8075 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8076 ((lpss->styleNew & WS_HSCROLL) == 0))
8077 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8079 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8080 ((lpss->styleNew & WS_VSCROLL) == 0))
8081 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8083 /* If switching modes, then start with no scroll bars and then
8086 if (uNewView != uOldView)
8088 if (uOldView == LVS_REPORT)
8089 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8091 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8092 SetRectEmpty(&infoPtr->rcFocus);
8095 if (uNewView == LVS_ICON)
8099 /* First readjust the iconSize and if necessary the iconSpacing */
8100 oldcx = infoPtr->iconSize.cx;
8101 oldcy = infoPtr->iconSize.cy;
8102 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8103 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8104 if (infoPtr->himlNormal != NULL)
8107 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
8108 infoPtr->iconSize.cx = cx;
8109 infoPtr->iconSize.cy = cy;
8111 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
8113 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8114 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8115 LISTVIEW_SetIconSpacing(infoPtr,0);
8118 /* Now update the full item width and height */
8119 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8120 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8121 if (lpss->styleNew & LVS_ALIGNLEFT)
8122 LISTVIEW_AlignLeft(infoPtr);
8124 LISTVIEW_AlignTop(infoPtr);
8126 else if (uNewView == LVS_REPORT)
8133 Header_Layout(infoPtr->hwndHeader, &hl);
8134 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8136 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8137 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8139 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8140 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8141 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8142 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8144 else if (uNewView == LVS_LIST)
8146 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8147 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8148 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8149 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8153 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8154 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8155 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8156 infoPtr->nItemHeight = LISTVIEW_CalculateMaxHeight(infoPtr);
8157 if (lpss->styleNew & LVS_ALIGNLEFT)
8158 LISTVIEW_AlignLeft(infoPtr);
8160 LISTVIEW_AlignTop(infoPtr);
8163 /* update the size of the client area */
8164 LISTVIEW_UpdateSize(infoPtr);
8166 /* add scrollbars if needed */
8167 LISTVIEW_UpdateScroll(infoPtr);
8169 /* invalidate client area + erase background */
8170 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8172 /* print the list of unsupported window styles */
8173 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8176 /* If they change the view and we have an active edit control
8177 we will need to kill the control since the redraw will
8178 misplace the edit control.
8180 if (infoPtr->bEditing &&
8181 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8182 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8184 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8192 * Window procedure of the listview control.
8195 static LRESULT WINAPI
8196 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8198 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8200 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8202 if (!infoPtr && (uMsg != WM_CREATE))
8203 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8207 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8212 case LVM_APPROXIMATEVIEWRECT:
8213 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8214 LOWORD(lParam), HIWORD(lParam));
8216 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8218 /* case LVN_CANCELEDITLABEL */
8220 /* case LVM_CREATEDRAGIMAGE: */
8222 case LVM_DELETEALLITEMS:
8223 return LISTVIEW_DeleteAllItems(infoPtr);
8225 case LVM_DELETECOLUMN:
8226 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8228 case LVM_DELETEITEM:
8229 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8231 case LVM_EDITLABELW:
8232 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8234 case LVM_EDITLABELA:
8235 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8237 /* case LVN_ENABLEGROUPVIEW: */
8239 case LVM_ENSUREVISIBLE:
8240 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8243 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8246 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8248 case LVM_GETBKCOLOR:
8249 return infoPtr->clrBk;
8251 /* case LVM_GETBKIMAGE: */
8253 case LVM_GETCALLBACKMASK:
8254 return infoPtr->uCallbackMask;
8256 case LVM_GETCOLUMNA:
8257 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8259 case LVM_GETCOLUMNW:
8260 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8262 case LVM_GETCOLUMNORDERARRAY:
8263 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8265 case LVM_GETCOLUMNWIDTH:
8266 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8268 case LVM_GETCOUNTPERPAGE:
8269 return LISTVIEW_GetCountPerPage(infoPtr);
8271 case LVM_GETEDITCONTROL:
8272 return (LRESULT)infoPtr->hwndEdit;
8274 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8275 return infoPtr->dwLvExStyle;
8278 return (LRESULT)infoPtr->hwndHeader;
8280 case LVM_GETHOTCURSOR:
8281 return (LRESULT)infoPtr->hHotCursor;
8283 case LVM_GETHOTITEM:
8284 return infoPtr->nHotItem;
8286 case LVM_GETHOVERTIME:
8287 return infoPtr->dwHoverTime;
8289 case LVM_GETIMAGELIST:
8290 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8292 /* case LVN_GETINSERTMARK: */
8294 /* case LVN_GETINSERTMARKCOLOR: */
8296 /* case LVN_GETINSERTMARKRECT: */
8298 case LVM_GETISEARCHSTRINGA:
8299 case LVM_GETISEARCHSTRINGW:
8300 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8304 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8307 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8309 case LVM_GETITEMCOUNT:
8310 return infoPtr->nItemCount;
8312 case LVM_GETITEMPOSITION:
8313 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8315 case LVM_GETITEMRECT:
8316 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8318 case LVM_GETITEMSPACING:
8319 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8321 case LVM_GETITEMSTATE:
8322 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8324 case LVM_GETITEMTEXTA:
8325 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8327 case LVM_GETITEMTEXTW:
8328 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8330 case LVM_GETNEXTITEM:
8331 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8333 case LVM_GETNUMBEROFWORKAREAS:
8334 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8338 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8340 /* case LVN_GETOUTLINECOLOR: */
8342 /* case LVM_GETSELECTEDCOLUMN: */
8344 case LVM_GETSELECTEDCOUNT:
8345 return LISTVIEW_GetSelectedCount(infoPtr);
8347 case LVM_GETSELECTIONMARK:
8348 return infoPtr->nSelectionMark;
8350 case LVM_GETSTRINGWIDTHA:
8351 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8353 case LVM_GETSTRINGWIDTHW:
8354 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8356 case LVM_GETSUBITEMRECT:
8357 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8359 case LVM_GETTEXTBKCOLOR:
8360 return infoPtr->clrTextBk;
8362 case LVM_GETTEXTCOLOR:
8363 return infoPtr->clrText;
8365 /* case LVN_GETTILEINFO: */
8367 /* case LVN_GETTILEVIEWINFO: */
8369 case LVM_GETTOOLTIPS:
8370 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8373 case LVM_GETTOPINDEX:
8374 return LISTVIEW_GetTopIndex(infoPtr);
8376 /*case LVM_GETUNICODEFORMAT:
8377 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8380 case LVM_GETVIEWRECT:
8381 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8383 case LVM_GETWORKAREAS:
8384 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8387 /* case LVN_HASGROUP: */
8390 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
8392 case LVM_INSERTCOLUMNA:
8393 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8395 case LVM_INSERTCOLUMNW:
8396 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8398 /* case LVN_INSERTGROUP: */
8400 /* case LVN_INSERTGROUPSORTED: */
8402 case LVM_INSERTITEMA:
8403 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8405 case LVM_INSERTITEMW:
8406 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8408 /* case LVN_INSERTMARKHITTEST: */
8410 /* case LVN_ISGROUPVIEWENABLED: */
8412 /* case LVN_MAPIDTOINDEX: */
8414 /* case LVN_INEDXTOID: */
8416 /* case LVN_MOVEGROUP: */
8418 /* case LVN_MOVEITEMTOGROUP: */
8420 case LVM_REDRAWITEMS:
8421 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8423 /* case LVN_REMOVEALLGROUPS: */
8425 /* case LVN_REMOVEGROUP: */
8428 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8430 case LVM_SETBKCOLOR:
8431 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8433 /* case LVM_SETBKIMAGE: */
8435 case LVM_SETCALLBACKMASK:
8436 infoPtr->uCallbackMask = (UINT)wParam;
8439 case LVM_SETCOLUMNA:
8440 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8442 case LVM_SETCOLUMNW:
8443 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8445 case LVM_SETCOLUMNORDERARRAY:
8446 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8448 case LVM_SETCOLUMNWIDTH:
8449 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8451 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8452 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8454 /* case LVN_SETGROUPINFO: */
8456 /* case LVN_SETGROUPMETRICS: */
8458 case LVM_SETHOTCURSOR:
8459 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8461 case LVM_SETHOTITEM:
8462 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8464 case LVM_SETHOVERTIME:
8465 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8467 case LVM_SETICONSPACING:
8468 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8470 case LVM_SETIMAGELIST:
8471 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8473 /* case LVN_SETINFOTIP: */
8475 /* case LVN_SETINSERTMARK: */
8477 /* case LVN_SETINSERTMARKCOLOR: */
8480 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8483 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8485 case LVM_SETITEMCOUNT:
8486 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8488 case LVM_SETITEMPOSITION:
8490 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8491 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8494 case LVM_SETITEMPOSITION32:
8495 if (lParam == 0) return FALSE;
8496 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8498 case LVM_SETITEMSTATE:
8499 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8501 case LVM_SETITEMTEXTA:
8502 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8504 case LVM_SETITEMTEXTW:
8505 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8507 /* case LVN_SETOUTLINECOLOR: */
8509 /* case LVN_SETSELECTEDCOLUMN: */
8511 case LVM_SETSELECTIONMARK:
8512 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8514 case LVM_SETTEXTBKCOLOR:
8515 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8517 case LVM_SETTEXTCOLOR:
8518 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8520 /* case LVN_SETTILEINFO: */
8522 /* case LVN_SETTILEVIEWINFO: */
8524 /* case LVN_SETTILEWIDTH: */
8526 /* case LVM_SETTOOLTIPS: */
8528 /* case LVM_SETUNICODEFORMAT: */
8530 /* case LVN_SETVIEW: */
8532 /* case LVM_SETWORKAREAS: */
8534 /* case LVN_SORTGROUPS: */
8537 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8539 case LVM_SUBITEMHITTEST:
8540 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
8543 return LISTVIEW_Update(infoPtr, (INT)wParam);
8546 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8549 return LISTVIEW_Command(infoPtr, wParam, lParam);
8552 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8555 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8558 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8561 return infoPtr->hFont;
8564 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8567 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8570 return LISTVIEW_KillFocus(infoPtr);
8572 case WM_LBUTTONDBLCLK:
8573 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8575 case WM_LBUTTONDOWN:
8576 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8579 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8582 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8585 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8588 return LISTVIEW_NCDestroy(infoPtr);
8591 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8593 case WM_NOTIFYFORMAT:
8594 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8597 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8599 case WM_RBUTTONDBLCLK:
8600 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8602 case WM_RBUTTONDOWN:
8603 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8606 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8609 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8614 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8617 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8620 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8623 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8625 case WM_STYLECHANGED:
8626 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8628 case WM_SYSCOLORCHANGE:
8629 COMCTL32_RefreshSysColors();
8632 /* case WM_TIMER: */
8635 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8638 if (wParam & (MK_SHIFT | MK_CONTROL))
8639 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8640 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8642 case WM_WINDOWPOSCHANGED:
8643 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8644 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8645 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8646 LISTVIEW_UpdateSize(infoPtr);
8647 LISTVIEW_UpdateScroll(infoPtr);
8649 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8651 /* case WM_WININICHANGE: */
8654 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8655 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8658 /* call default window procedure */
8659 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8667 * Registers the window class.
8675 void LISTVIEW_Register(void)
8679 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8680 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8681 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8682 wndClass.cbClsExtra = 0;
8683 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8684 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8685 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8686 wndClass.lpszClassName = WC_LISTVIEWW;
8687 RegisterClassW(&wndClass);
8692 * Unregisters the window class.
8700 void LISTVIEW_Unregister(void)
8702 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8707 * Handle any WM_COMMAND messages
8713 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8715 switch (HIWORD(wParam))
8720 * Adjust the edit window size
8723 HDC hdc = GetDC(infoPtr->hwndEdit);
8724 HFONT hFont, hOldFont = 0;
8729 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8730 GetWindowRect(infoPtr->hwndEdit, &rect);
8732 /* Select font to get the right dimension of the string */
8733 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8736 hOldFont = SelectObject(hdc, hFont);
8739 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8741 TEXTMETRICW textMetric;
8743 /* Add Extra spacing for the next character */
8744 GetTextMetricsW(hdc, &textMetric);
8745 sz.cx += (textMetric.tmMaxCharWidth * 2);
8753 rect.bottom - rect.top,
8754 SWP_DRAWFRAME|SWP_NOMOVE);
8757 SelectObject(hdc, hOldFont);
8759 ReleaseDC(infoPtr->hwndSelf, hdc);
8765 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8774 * Subclassed edit control windproc function
8780 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
8781 WPARAM wParam, LPARAM lParam, BOOL isW)
8783 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8784 static BOOL bIgnoreKillFocus = FALSE;
8785 BOOL cancel = FALSE;
8787 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8788 hwnd, uMsg, wParam, lParam, isW);
8793 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
8796 if(bIgnoreKillFocus) return TRUE;
8801 WNDPROC editProc = infoPtr->EditWndProc;
8802 infoPtr->EditWndProc = 0;
8803 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
8804 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
8808 if (VK_ESCAPE == (INT)wParam)
8813 else if (VK_RETURN == (INT)wParam)
8817 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
8820 if (infoPtr->bEditing)
8822 LPWSTR buffer = NULL;
8826 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
8830 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
8832 if (isW) GetWindowTextW(hwnd, buffer, len+1);
8833 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
8837 /* Processing LVN_ENDLABELEDIT message could kill the focus */
8838 /* eg. Using a messagebox */
8839 bIgnoreKillFocus = TRUE;
8840 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
8842 if (buffer) COMCTL32_Free(buffer);
8844 bIgnoreKillFocus = FALSE;
8847 SendMessageW(hwnd, WM_CLOSE, 0, 0);
8853 * Subclassed edit control windproc function
8859 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8861 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
8866 * Subclassed edit control windproc function
8872 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8874 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
8879 * Creates a subclassed edit cotrol
8885 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
8886 INT x, INT y, INT width, INT height, BOOL isW)
8888 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
8893 TEXTMETRICW textMetric;
8894 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
8896 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
8898 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
8899 hdc = GetDC(infoPtr->hwndSelf);
8901 /* Select the font to get appropriate metric dimensions */
8902 if(infoPtr->hFont != 0)
8903 hOldFont = SelectObject(hdc, infoPtr->hFont);
8905 /*Get String Lenght in pixels */
8906 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
8908 /*Add Extra spacing for the next character */
8909 GetTextMetricsW(hdc, &textMetric);
8910 sz.cx += (textMetric.tmMaxCharWidth * 2);
8912 if(infoPtr->hFont != 0)
8913 SelectObject(hdc, hOldFont);
8915 ReleaseDC(infoPtr->hwndSelf, hdc);
8917 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8919 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
8921 if (!hedit) return 0;
8923 infoPtr->EditWndProc = (WNDPROC)
8924 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
8925 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
8927 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);