4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 Codeweavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 * Listview control implementation.
28 * -- Drawing optimizations.
29 * -- Hot item handling.
32 * LISTVIEW_Notify : most notifications from children (editbox and header)
35 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
37 * Advanced functionality:
38 * LISTVIEW_GetNumberOfWorkAreas : not implemented
39 * LISTVIEW_GetISearchString : not implemented
40 * LISTVIEW_GetBkImage : not implemented
41 * LISTVIEW_SetBkImage : not implemented
42 * LISTVIEW_GetColumnOrderArray : simple hack only
43 * LISTVIEW_SetColumnOrderArray : simple hack only
44 * LISTVIEW_Arrange : empty stub
45 * LISTVIEW_ApproximateViewRect : incomplete
46 * LISTVIEW_Update : not completed
48 * Known differences in message stream from native control (not known if
49 * these differences cause problems):
50 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
51 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
52 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
53 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
54 * does *not* invoke DefWindowProc
55 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
56 * processing for "USEDOUBLECLICKTIME".
61 #include "wine/port.h"
73 #include "wine/debug.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(listview);
77 /* Some definitions for inline edit control */
79 typedef struct tagCOLUMNCACHE
83 } COLUMNCACHE, *LPCOLUMNCACHE;
85 typedef struct tagITEMHDR
89 } ITEMHDR, *LPITEMHDR;
91 typedef struct tagLISTVIEW_SUBITEM
97 typedef struct tagLISTVIEW_ITEM
106 typedef struct tagRANGE
112 typedef struct tagLISTVIEW_INFO
119 HIMAGELIST himlNormal;
120 HIMAGELIST himlSmall;
121 HIMAGELIST himlState;
126 HDPA hdpaSelectionRanges;
128 BOOL bRemovingAllSelections;
131 RECT rcList; /* This rectangle is really the window
132 * client rectangle possibly reduced by the
133 * horizontal scroll bar and/or header - see
134 * LISTVIEW_UpdateSize. This rectangle offset
135 * by the LISTVIEW_GetOrigin value is in
136 * client coordinates */
137 RECT rcView; /* This rectangle contains all items -
138 * contructed in LISTVIEW_AlignTop and
139 * LISTVIEW_AlignLeft */
148 INT ntmHeight; /* from GetTextMetrics from above font */
149 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
153 DWORD dwStyle; /* the cached window GWL_STYLE */
154 DWORD dwLvExStyle; /* extended listview style */
157 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
158 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
159 PFNLVCOMPARE pfnCompare;
167 DWORD lastKeyPressTimestamp;
169 INT nSearchParamLength;
170 WCHAR szSearchParam[ MAX_PATH ];
174 DEFINE_COMMON_NOTIFICATIONS(LISTVIEW_INFO, hwndSelf);
179 /* How many we debug buffer to allocate */
180 #define DEBUG_BUFFERS 20
181 /* The size of a single debug bbuffer */
182 #define DEBUG_BUFFER_SIZE 256
184 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
185 #define SB_INTERNAL -1
187 /* maximum size of a label */
188 #define DISP_TEXT_SIZE 512
190 /* padding for items in list and small icon display modes */
191 #define WIDTH_PADDING 12
193 /* padding for items in list, report and small icon display modes */
194 #define HEIGHT_PADDING 1
196 /* offset of items in report display mode */
197 #define REPORT_MARGINX 2
199 /* padding for icon in large icon display mode
200 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
201 * that HITTEST will see.
202 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
203 * ICON_TOP_PADDING - sum of the two above.
204 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
205 * LABEL_VERT_PADDING - between bottom of text and end of box
207 * ICON_LR_PADDING - additional width above icon size.
208 * ICON_LR_HALF - half of the above value
210 #define ICON_TOP_PADDING_NOTHITABLE 2
211 #define ICON_TOP_PADDING_HITABLE 2
212 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
213 #define ICON_BOTTOM_PADDING 4
214 #define LABEL_VERT_PADDING 7
215 #define ICON_LR_PADDING 16
216 #define ICON_LR_HALF (ICON_LR_PADDING/2)
218 /* default label width for items in list and small icon display modes */
219 #define DEFAULT_LABEL_WIDTH 40
221 /* default column width for items in list display mode */
222 #define DEFAULT_COLUMN_WIDTH 128
224 /* Size of "line" scroll for V & H scrolls */
225 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
227 /* Padding betwen image and label */
228 #define IMAGE_PADDING 2
230 /* Padding behind the label */
231 #define TRAILING_PADDING 5
233 /* Border for the icon caption */
234 #define CAPTION_BORDER 2
236 /* Standard DrawText flags */
237 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
238 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
239 #define LV_SL_DT_FLAGS (DT_TOP | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
241 /* Dump the LISTVIEW_INFO structure to the debug channel */
242 #define LISTVIEW_DUMP(iP) do { \
243 TRACE("hwndSelf=%08x, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
244 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
245 iP->nItemHeight, iP->nItemWidth, infoPtr->dwStyle); \
246 TRACE("hwndSelf=%08x, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%s\n", \
247 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
248 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, \
249 (iP->bFocus) ? "true" : "false"); \
250 TRACE("hwndSelf=%08x, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
251 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
252 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
253 TRACE("hwndSelf=%08x, rcList=(%d,%d)-(%d,%d), rcView=(%d,%d)-(%d,%d)\n", \
255 iP->rcList.left, iP->rcList.top, iP->rcList.right, iP->rcList.bottom, \
256 iP->rcView.left, iP->rcView.top, iP->rcView.right, iP->rcView.bottom); \
261 * forward declarations
263 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
264 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
265 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *);
266 static void LISTVIEW_AlignTop(LISTVIEW_INFO *);
267 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *, INT);
268 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *);
269 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
270 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
271 static INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *);
272 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *, INT, LPRECT);
273 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
274 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *, INT);
275 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
276 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
277 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
278 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
279 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *, INT, POINT);
280 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
281 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
282 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *);
283 static void LISTVIEW_UnsupportedStyles(LONG);
284 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
285 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
286 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
287 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
288 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *, WPARAM, LPARAM);
289 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
290 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
291 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, LPLVITEMW);
292 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *, INT, LPLVCOLUMNW, BOOL);
293 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
294 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
295 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
296 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
297 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
299 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
300 #define KEY_DELAY 450
302 /******** Text handling functions *************************************/
304 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
305 * text string. The string may be ANSI or Unicode, in which case
306 * the boolean isW tells us the type of the string.
308 * The name of the function tell what type of strings it expects:
309 * W: Unicode, T: ANSI/Unicode - function of isW
312 static inline BOOL is_textW(LPCWSTR text)
314 return text != NULL && text != LPSTR_TEXTCALLBACKW;
317 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
319 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
320 return is_textW(text);
323 static inline int textlenT(LPCWSTR text, BOOL isW)
325 return !is_textT(text, isW) ? 0 :
326 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
329 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
332 if (isSrcW) lstrcpynW(dest, src, max);
333 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
335 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
336 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
339 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
341 LPWSTR wstr = (LPWSTR)text;
343 if (!isW && is_textT(text, isW))
345 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
346 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
347 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
349 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
353 static inline void textfreeT(LPWSTR wstr, BOOL isW)
355 if (!isW && is_textT(wstr, isW)) HeapFree(GetProcessHeap(), 0, wstr);
359 * dest is a pointer to a Unicode string
360 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
362 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
366 if (src == LPSTR_TEXTCALLBACKW)
368 if (is_textW(*dest)) COMCTL32_Free(*dest);
369 *dest = LPSTR_TEXTCALLBACKW;
373 LPWSTR pszText = textdupTtoW(src, isW);
374 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
375 bResult = Str_SetPtrW(dest, pszText);
376 textfreeT(pszText, isW);
382 * compares a Unicode to a Unicode/ANSI text string
384 static inline int textcmpWT(LPWSTR aw, LPWSTR bt, BOOL isW)
386 if (!aw) return bt ? -1 : 0;
387 if (!bt) return aw ? 1 : 0;
388 if (aw == LPSTR_TEXTCALLBACKW)
389 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
390 if (bt != LPSTR_TEXTCALLBACKW)
392 LPWSTR bw = textdupTtoW(bt, isW);
393 int r = bw ? lstrcmpW(aw, bw) : 1;
401 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
405 n = min(min(n, strlenW(s1)), strlenW(s2));
406 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
407 return res ? res - sizeof(WCHAR) : res;
410 /******** Debugging functions *****************************************/
412 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
414 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
415 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
418 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
420 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
421 n = min(textlenT(text, isW), n);
422 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
425 static char* debug_getbuf()
427 static int index = 0;
428 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
429 return buffers[index++ % DEBUG_BUFFERS];
432 static inline char* debugpoint(const POINT* lppt)
436 char* buf = debug_getbuf();
437 snprintf(buf, DEBUG_BUFFER_SIZE, "(%ld, %ld)", lppt->x, lppt->y);
439 } else return "(null)";
442 static inline char* debugrect(const RECT* rect)
446 char* buf = debug_getbuf();
447 snprintf(buf, DEBUG_BUFFER_SIZE, "[(%d, %d);(%d, %d)]",
448 rect->left, rect->top, rect->right, rect->bottom);
450 } else return "(null)";
453 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
455 char* buf = debug_getbuf(), *text = buf;
456 int len, size = DEBUG_BUFFER_SIZE;
458 if (lpLVItem == NULL) return "(null)";
459 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
460 if (len == -1) goto end; buf += len; size -= len;
461 if (lpLVItem->mask & LVIF_STATE)
462 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
464 if (len == -1) goto end; buf += len; size -= len;
465 if (lpLVItem->mask & LVIF_TEXT)
466 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
468 if (len == -1) goto end; buf += len; size -= len;
469 if (lpLVItem->mask & LVIF_IMAGE)
470 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
472 if (len == -1) goto end; buf += len; size -= len;
473 if (lpLVItem->mask & LVIF_PARAM)
474 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
476 if (len == -1) goto end; buf += len; size -= len;
477 if (lpLVItem->mask & LVIF_INDENT)
478 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
480 if (len == -1) goto end; buf += len; size -= len;
483 buf = text + strlen(text);
485 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
489 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
491 char* buf = debug_getbuf(), *text = buf;
492 int len, size = DEBUG_BUFFER_SIZE;
494 if (lpColumn == NULL) return "(null)";
495 len = snprintf(buf, size, "{");
496 if (len == -1) goto end; buf += len; size -= len;
497 if (lpColumn->mask & LVCF_SUBITEM)
498 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
500 if (len == -1) goto end; buf += len; size -= len;
501 if (lpColumn->mask & LVCF_FMT)
502 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
504 if (len == -1) goto end; buf += len; size -= len;
505 if (lpColumn->mask & LVCF_WIDTH)
506 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
508 if (len == -1) goto end; buf += len; size -= len;
509 if (lpColumn->mask & LVCF_TEXT)
510 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
512 if (len == -1) goto end; buf += len; size -= len;
513 if (lpColumn->mask & LVCF_IMAGE)
514 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
516 if (len == -1) goto end; buf += len; size -= len;
517 if (lpColumn->mask & LVCF_ORDER)
518 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
520 if (len == -1) goto end; buf += len; size -= len;
523 buf = text + strlen(text);
525 if (buf - text > 2) buf[-2] = '}'; buf[-1] = 0;
530 /******** Notification functions i************************************/
532 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
534 pnmh->hwndFrom = infoPtr->hwndSelf;
535 pnmh->idFrom = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
537 return (BOOL)SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFY,
538 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
541 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr)
544 notify(infoPtr, LVN_ITEMACTIVATE, &nmh);
547 static inline BOOL notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
549 return notify(infoPtr, code, (LPNMHDR)plvnm);
552 static int get_ansi_notification(INT unicodeNotificationCode)
554 switch (unicodeNotificationCode)
556 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
557 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
558 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
559 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
560 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
561 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
563 ERR("unknown notification %x\n", unicodeNotificationCode);
564 return unicodeNotificationCode;
568 Send notification. depends on dispinfoW having same
569 structure as dispinfoA.
570 infoPtr : listview struct
571 notificationCode : *Unicode* notification code
572 pdi : dispinfo structure (can be unicode or ansi)
573 isW : TRUE if dispinfo is Unicode
575 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
577 BOOL bResult = FALSE;
578 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
580 INT cchTempBufMax = 0, savCchTextMax = 0;
581 LPWSTR pszTempBuf = NULL, savPszText = NULL;
583 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
585 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
586 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
589 if (convertToAnsi || convertToUnicode)
591 if (notificationCode != LVN_GETDISPINFOW)
593 cchTempBufMax = convertToUnicode ?
594 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
595 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
599 cchTempBufMax = pdi->item.cchTextMax;
600 *pdi->item.pszText = 0; /* make sure we don't process garbage */
603 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
604 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
605 if (!pszTempBuf) return FALSE;
606 if (convertToUnicode)
607 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
608 pszTempBuf, cchTempBufMax);
610 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
611 cchTempBufMax, NULL, NULL);
612 savCchTextMax = pdi->item.cchTextMax;
613 savPszText = pdi->item.pszText;
614 pdi->item.pszText = pszTempBuf;
615 pdi->item.cchTextMax = cchTempBufMax;
618 if (infoPtr->notifyFormat == NFR_ANSI)
619 realNotifCode = get_ansi_notification(notificationCode);
621 realNotifCode = notificationCode;
622 bResult = notify(infoPtr, realNotifCode, (LPNMHDR)pdi);
624 if (convertToUnicode || convertToAnsi)
626 if (convertToUnicode) /* note : pointer can be changed by app ! */
627 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
628 savCchTextMax, NULL, NULL);
630 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
631 savPszText, savCchTextMax);
632 pdi->item.pszText = savPszText; /* restores our buffer */
633 pdi->item.cchTextMax = savCchTextMax;
634 HeapFree(GetProcessHeap(), 0, pszTempBuf);
639 static inline void notify_odcachehint(LISTVIEW_INFO *infoPtr, INT iFrom, INT iTo)
645 notify(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
648 static BOOL notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, HDC hdc, RECT rc)
650 NMLVCUSTOMDRAW nmlvcd;
652 TRACE("(dwDrawStage=%lx, hdc=%x, rc=?)\n", dwDrawStage, hdc);
654 nmlvcd.nmcd.dwDrawStage = dwDrawStage;
655 nmlvcd.nmcd.hdc = hdc;
657 nmlvcd.nmcd.dwItemSpec = 0;
658 nmlvcd.nmcd.uItemState = 0;
659 nmlvcd.nmcd.lItemlParam = 0;
660 nmlvcd.clrText = infoPtr->clrText;
661 nmlvcd.clrTextBk = infoPtr->clrBk;
663 return (BOOL)notify(infoPtr, NM_CUSTOMDRAW, &nmlvcd.nmcd.hdr);
666 /* FIXME: we should inline this where it's called somehow
667 * I think we need to pass in the structure
669 static BOOL notify_customdrawitem (LISTVIEW_INFO *infoPtr, HDC hdc, UINT iItem, UINT iSubItem, UINT uItemDrawState)
671 NMLVCUSTOMDRAW nmlvcd;
679 item.mask = LVIF_PARAM;
680 if (!LISTVIEW_GetItemT(infoPtr, &item, TRUE)) return FALSE;
684 if (LISTVIEW_GetItemState(infoPtr, iItem, LVIS_SELECTED)) uItemState |= CDIS_SELECTED;
685 if (LISTVIEW_GetItemState(infoPtr, iItem, LVIS_FOCUSED)) uItemState |= CDIS_FOCUS;
686 if (iItem == infoPtr->nHotItem) uItemState |= CDIS_HOT;
688 itemRect.left = LVIR_BOUNDS;
689 LISTVIEW_GetItemRect(infoPtr, iItem, &itemRect);
691 nmlvcd.nmcd.dwDrawStage = CDDS_ITEM | uItemDrawState;
692 nmlvcd.nmcd.hdc = hdc;
693 nmlvcd.nmcd.rc = itemRect;
694 nmlvcd.nmcd.dwItemSpec = iItem;
695 nmlvcd.nmcd.uItemState = uItemState;
696 nmlvcd.nmcd.lItemlParam = item.lParam;
697 nmlvcd.clrText = infoPtr->clrText;
698 nmlvcd.clrTextBk = infoPtr->clrBk;
699 nmlvcd.iSubItem = iSubItem;
701 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
702 nmlvcd.nmcd.dwDrawStage, nmlvcd.nmcd.hdc, nmlvcd.nmcd.dwItemSpec,
703 nmlvcd.nmcd.uItemState, nmlvcd.nmcd.lItemlParam);
705 bReturn = notify(infoPtr, NM_CUSTOMDRAW, &nmlvcd.nmcd.hdr);
707 infoPtr->clrText = nmlvcd.clrText;
708 infoPtr->clrBk = nmlvcd.clrTextBk;
713 /******** Misc helper functions ************************************/
715 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
716 WPARAM wParam, LPARAM lParam, BOOL isW)
718 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
719 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
722 /******** Internal API functions ************************************/
724 /* The Invalidate* are macros, so we preserve the caller location */
725 #define LISTVIEW_InvalidateRect(infoPtr, rect) do { \
726 TRACE(" invalidating rect=%s\n", debugrect(rect)); \
727 InvalidateRect(infoPtr->hwndSelf, rect, TRUE); \
730 #define LISTVIEW_InvalidateItem(infoPtr, nItem) do { \
732 rcItem.left = LVIR_BOUNDS; \
733 if(LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) \
734 LISTVIEW_InvalidateRect(infoPtr, &rcItem); \
737 #define LISTVIEW_InvalidateList(infoPtr)\
738 LISTVIEW_InvalidateRect(infoPtr, NULL)
740 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
742 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
745 static inline int LISTVIEW_GetType(LISTVIEW_INFO *infoPtr)
747 return infoPtr->dwStyle & LVS_TYPEMASK;
752 * Retrieves the number of items that can fit vertically in the client area.
755 * [I] infoPtr : valid pointer to the listview structure
758 * Number of items per row.
760 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
762 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
764 return max(nListWidth/infoPtr->nItemWidth, 1);
769 * Retrieves the number of items that can fit horizontally in the client
773 * [I] infoPtr : valid pointer to the listview structure
776 * Number of items per column.
778 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
780 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
782 return max(nListHeight / infoPtr->nItemHeight, 1);
787 * Retrieves the range of visible items. Note that the upper limit
788 * may be a bit larger than the actual last visible item.
791 * [I] infoPtr : valid pointer to the listview structure
794 * maximum range of visible items
796 static RANGE LISTVIEW_GetVisibleRange(LISTVIEW_INFO *infoPtr)
798 UINT uView = LISTVIEW_GetType(infoPtr);
799 INT nPerCol, nPerRow;
802 visrange.lower = LISTVIEW_GetTopIndex(infoPtr);
804 if (uView == LVS_REPORT)
806 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
809 else if (uView == LVS_LIST)
811 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
812 nPerRow = LISTVIEW_GetCountPerRow(infoPtr);
816 /* FIXME: this is correct only in autoarrange mode */
817 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr) + 1;
818 nPerRow = LISTVIEW_GetCountPerRow(infoPtr) + 1;
821 visrange.upper = visrange.lower + nPerCol * nPerRow;
822 if (visrange.upper > infoPtr->nItemCount)
823 visrange.upper = infoPtr->nItemCount;
825 TRACE("range=(%d, %d)\n", visrange.lower, visrange.upper);
831 /*************************************************************************
832 * LISTVIEW_ProcessLetterKeys
834 * Processes keyboard messages generated by pressing the letter keys
836 * What this does is perform a case insensitive search from the
837 * current position with the following quirks:
838 * - If two chars or more are pressed in quick succession we search
839 * for the corresponding string (e.g. 'abc').
840 * - If there is a delay we wipe away the current search string and
841 * restart with just that char.
842 * - If the user keeps pressing the same character, whether slowly or
843 * fast, so that the search string is entirely composed of this
844 * character ('aaaaa' for instance), then we search for first item
845 * that starting with that character.
846 * - If the user types the above character in quick succession, then
847 * we must also search for the corresponding string ('aaaaa'), and
848 * go to that string if there is a match.
851 * [I] hwnd : handle to the window
852 * [I] charCode : the character code, the actual character
853 * [I] keyData : key data
861 * - The current implementation has a list of characters it will
862 * accept and it ignores averything else. In particular it will
863 * ignore accentuated characters which seems to match what
864 * Windows does. But I'm not sure it makes sense to follow
866 * - We don't sound a beep when the search fails.
870 * TREEVIEW_ProcessLetterKeys
872 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
877 WCHAR buffer[MAX_PATH];
878 DWORD timestamp,elapsed;
880 /* simple parameter checking */
881 if (!charCode || !keyData) return 0;
883 /* only allow the valid WM_CHARs through */
884 if (!isalnum(charCode) &&
885 charCode != '.' && charCode != '`' && charCode != '!' &&
886 charCode != '@' && charCode != '#' && charCode != '$' &&
887 charCode != '%' && charCode != '^' && charCode != '&' &&
888 charCode != '*' && charCode != '(' && charCode != ')' &&
889 charCode != '-' && charCode != '_' && charCode != '+' &&
890 charCode != '=' && charCode != '\\'&& charCode != ']' &&
891 charCode != '}' && charCode != '[' && charCode != '{' &&
892 charCode != '/' && charCode != '?' && charCode != '>' &&
893 charCode != '<' && charCode != ',' && charCode != '~')
896 /* if there's one item or less, there is no where to go */
897 if (infoPtr->nItemCount <= 1) return 0;
899 /* compute how much time elapsed since last keypress */
900 timestamp=GetTickCount();
901 if (timestamp > infoPtr->lastKeyPressTimestamp) {
902 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
904 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
907 /* update the search parameters */
908 infoPtr->lastKeyPressTimestamp=timestamp;
909 if (elapsed < KEY_DELAY) {
910 if (infoPtr->nSearchParamLength < MAX_PATH) {
911 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
913 if (infoPtr->charCode != charCode) {
914 infoPtr->charCode=charCode=0;
917 infoPtr->charCode=charCode;
918 infoPtr->szSearchParam[0]=charCode;
919 infoPtr->nSearchParamLength=1;
920 /* Redundant with the 1 char string */
924 /* and search from the current position */
926 if (infoPtr->nFocusedItem >= 0) {
927 endidx=infoPtr->nFocusedItem;
929 /* if looking for single character match,
930 * then we must always move forward
932 if (infoPtr->nSearchParamLength == 1)
935 endidx=infoPtr->nItemCount;
939 if (idx == infoPtr->nItemCount) {
940 if (endidx == infoPtr->nItemCount || endidx == 0)
946 item.mask = LVIF_TEXT;
949 item.pszText = buffer;
950 item.cchTextMax = MAX_PATH;
951 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
953 /* check for a match */
954 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
957 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
958 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
959 /* This would work but we must keep looking for a longer match */
963 } while (idx != endidx);
966 LISTVIEW_KeySelection(infoPtr, nItem);
971 /*************************************************************************
972 * LISTVIEW_UpdateHeaderSize [Internal]
974 * Function to resize the header control
977 * hwnd [I] handle to a window
978 * nNewScrollPos [I] Scroll Pos to Set
985 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
990 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
992 GetWindowRect(infoPtr->hwndHeader, &winRect);
993 point[0].x = winRect.left;
994 point[0].y = winRect.top;
995 point[1].x = winRect.right;
996 point[1].y = winRect.bottom;
998 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
999 point[0].x = -nNewScrollPos;
1000 point[1].x += nNewScrollPos;
1002 SetWindowPos(infoPtr->hwndHeader,0,
1003 point[0].x,point[0].y,point[1].x,point[1].y,
1004 SWP_NOZORDER | SWP_NOACTIVATE);
1009 * Update the scrollbars. This functions should be called whenever
1010 * the content, size or view changes.
1013 * [I] infoPtr : valid pointer to the listview structure
1018 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1020 LONG lStyle = infoPtr->dwStyle;
1021 UINT uView = lStyle & LVS_TYPEMASK;
1022 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1023 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1024 SCROLLINFO scrollInfo;
1026 if (lStyle & LVS_NOSCROLL) return;
1028 scrollInfo.cbSize = sizeof(SCROLLINFO);
1030 if (uView == LVS_LIST)
1032 /* update horizontal scrollbar */
1033 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1034 INT nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
1036 TRACE("items=%d, perColumn=%d, perRow=%d\n",
1037 infoPtr->nItemCount, nCountPerColumn, nCountPerRow);
1039 scrollInfo.nMin = 0;
1040 scrollInfo.nMax = infoPtr->nItemCount / nCountPerColumn;
1041 if((infoPtr->nItemCount % nCountPerColumn) == 0)
1043 if (scrollInfo.nMax < 0) scrollInfo.nMax = 0;
1044 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf) / nCountPerColumn;
1045 scrollInfo.nPage = nCountPerRow;
1046 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1047 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1048 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
1050 else if (uView == LVS_REPORT)
1054 /* update vertical scrollbar */
1055 scrollInfo.nMin = 0;
1056 scrollInfo.nMax = infoPtr->nItemCount - 1;
1057 scrollInfo.nPos = ListView_GetTopIndex(infoPtr->hwndSelf);
1058 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(infoPtr);
1059 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1060 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1061 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
1062 scrollInfo.nMax, scrollInfo.nPage, test);
1063 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1064 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, (test) ? FALSE : TRUE);
1066 /* update horizontal scrollbar */
1067 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1068 scrollInfo.fMask = SIF_POS;
1069 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1070 || infoPtr->nItemCount == 0)
1072 scrollInfo.nPos = 0;
1074 scrollInfo.nMin = 0;
1075 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
1076 scrollInfo.nPage = nListWidth;
1077 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
1078 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
1079 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
1080 scrollInfo.nMax, scrollInfo.nPage, test);
1081 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1082 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, (test) ? FALSE : TRUE);
1084 /* Update the Header Control */
1085 scrollInfo.fMask = SIF_POS;
1086 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo);
1087 LISTVIEW_UpdateHeaderSize(infoPtr, scrollInfo.nPos);
1094 if (LISTVIEW_GetViewRect(infoPtr, &rcView))
1096 INT nViewWidth = rcView.right - rcView.left;
1097 INT nViewHeight = rcView.bottom - rcView.top;
1099 /* Update Horizontal Scrollbar */
1100 scrollInfo.fMask = SIF_POS;
1101 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)
1102 || infoPtr->nItemCount == 0)
1104 scrollInfo.nPos = 0;
1106 scrollInfo.nMin = 0;
1107 scrollInfo.nMax = max(nViewWidth, 0)-1;
1108 scrollInfo.nPage = nListWidth;
1109 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1110 TRACE("LVS_ICON/SMALLICON Horz.\n");
1111 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
1113 /* Update Vertical Scrollbar */
1114 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1115 scrollInfo.fMask = SIF_POS;
1116 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)
1117 || infoPtr->nItemCount == 0)
1119 scrollInfo.nPos = 0;
1121 scrollInfo.nMin = 0;
1122 scrollInfo.nMax = max(nViewHeight,0)-1;
1123 scrollInfo.nPage = nListHeight;
1124 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
1125 TRACE("LVS_ICON/SMALLICON Vert.\n");
1126 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
1134 * Shows/hides the focus rectangle.
1137 * [I] infoPtr : valid pointer to the listview structure
1138 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1143 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1146 INT nItem = infoPtr->nFocusedItem;
1148 TRACE("fShow=%d, nItem=%d\n", fShow, nItem);
1150 if (nItem < 0 || nItem >= infoPtr->nItemCount) return;
1152 rcItem.left = LVIR_BOUNDS;
1154 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
1155 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
1156 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
1158 /* this little optimization eliminates some nasty flicker */
1159 if (!LISTVIEW_GetSubItemRect(infoPtr, nItem, &rcItem)) return;
1163 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
1166 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
1174 item.mask = LVIF_PARAM;
1175 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto invalidate;
1177 if (!(hdc = GetDC(infoPtr->hwndSelf))) goto invalidate;
1178 ZeroMemory(&dis, sizeof(dis));
1179 dis.CtlType = ODT_LISTVIEW;
1180 dis.CtlID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
1182 dis.itemAction = ODA_FOCUS;
1183 if (fShow) dis.itemState |= ODS_FOCUS;
1184 dis.hwndItem = infoPtr->hwndSelf;
1186 dis.rcItem = rcItem;
1187 dis.itemData = item.lParam;
1189 SendMessageW(GetParent(infoPtr->hwndSelf), WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1190 ReleaseDC(infoPtr->hwndSelf, hdc);
1195 /* Here we are inneficient. We could, in theory, simply DrawFocusRect
1196 * to erase/show the focus, without all this heavy duty redraw.
1197 * Note that there are cases where we can not do that: when the list
1198 * is in ICON mode, and the item is large, we must to invalidate it.
1199 * Moreover, in the vast majority of cases, the selection status of
1200 * the item changes anyway, and so the item is invalidated already,
1201 * so not too much harm is done. If we do notice any flicker, we should
1202 * refine this method. */
1204 LISTVIEW_InvalidateRect(infoPtr, &rcItem);
1209 * Invalidates all visible selected items.
1211 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1216 visrange = LISTVIEW_GetVisibleRange(infoPtr);
1217 for (i = visrange.lower; i <= visrange.upper; i++)
1219 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
1220 LISTVIEW_InvalidateItem(infoPtr, i);
1227 * Prints a message for unsupported window styles.
1228 * A kind of TODO list for window styles.
1231 * [I] LONG : window style
1236 static void LISTVIEW_UnsupportedStyles(LONG lStyle)
1238 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
1239 FIXME(" LVS_NOSCROLL\n");
1241 if (lStyle & LVS_NOLABELWRAP)
1242 FIXME(" LVS_NOLABELWRAP\n");
1244 if (lStyle & LVS_SORTASCENDING)
1245 FIXME(" LVS_SORTASCENDING\n");
1247 if (lStyle & LVS_SORTDESCENDING)
1248 FIXME(" LVS_SORTDESCENDING\n");
1253 * DESCRIPTION: [INTERNAL]
1254 * Computes an item's (left,top) corner, relative to rcView.
1255 * That is, the position has NOT been made relative to the Origin.
1256 * This is deliberate, to avoid computing the Origin over, and
1257 * over again, when this function is call in a loop. Instead,
1258 * one ca factor the computation of the Origin before the loop,
1259 * and offset the value retured by this function, on every iteration.
1262 * [I] infoPtr : valid pointer to the listview structure
1263 * [I] nItem : item number
1264 * [O] lpptOrig : item top, left corner
1267 * TRUE if computations OK
1270 static BOOL LISTVIEW_GetItemListOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1272 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1274 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1276 lpptPosition->x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1277 lpptPosition->y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1279 else if (uView == LVS_LIST)
1281 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1282 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1283 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1285 else /* LVS_REPORT */
1287 lpptPosition->x = REPORT_MARGINX;
1288 lpptPosition->y = nItem * infoPtr->nItemHeight;
1295 * DESCRIPTION: [INTERNAL]
1296 * Compute the rectangles of an item. This is to localize all
1297 * the computations in one place. If you are not interested in some
1298 * of these values, simply pass in a NULL -- the fucntion is smart
1299 * enough to compute only what's necessary. The function computes
1300 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1301 * one, the BOX rectangle. This rectangle is very cheap to compute,
1302 * and is guaranteed to contain all the other rectangles. Computing
1303 * the ICON rect is also cheap, but all the others are potentaily
1304 * expensive. This gives an easy and effective optimization when
1305 * searching (like point inclusion, or rectangle intersection):
1306 * first test against the BOX, and if TRUE, test agains the desired
1307 * rectangle. These optimizations are coded in:
1308 * LISTVIEW_PtInRect, and LISTVIEW_IntersectRect
1309 * use them wherever is appropriate.
1312 * [I] infoPtr : valid pointer to the listview structure
1313 * [I] nItem : item number
1314 * [O] lprcBox : ptr to Box rectangle
1315 * The internal LVIR_BOX rectangle
1316 * [O] lprcBounds : ptr to Bounds rectangle
1317 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1318 * [O] lprcIcon : ptr to Icon rectangle
1319 * Same as LVM_GETITEMRECT with LVIR_ICON
1320 * [O] lprcLabel : ptr to Label rectangle
1321 * Same as LVM_GETITEMRECT with LVIR_LABEL
1324 * TRUE if computations OK
1327 static BOOL LISTVIEW_GetItemMeasures(LISTVIEW_INFO *infoPtr, INT nItem,
1328 LPRECT lprcBox, LPRECT lprcBounds,
1329 LPRECT lprcIcon, LPRECT lprcLabel)
1331 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1332 BOOL doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1333 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
1334 RECT Box, Icon, Label;
1335 POINT Position, Origin;
1338 /* Be smart and try to figure out the minimum we have to do */
1341 if (uView == LVS_REPORT) doIcon = TRUE;
1342 else doLabel = TRUE;
1344 if (uView == LVS_ICON && infoPtr->bFocus &&
1345 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
1346 LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
1347 oversizedBox = doLabel = TRUE;
1348 if (lprcLabel) doLabel = TRUE;
1349 if (doLabel || lprcIcon) doIcon = TRUE;
1351 /* get what we need from the item before hand, so we make
1352 * only one request. This can speed up things, if data
1353 * is stored on the app side */
1354 if (doLabel || (doIcon && uView == LVS_REPORT))
1357 if (doIcon) lvItem.mask |= LVIF_INDENT;
1358 if (doLabel) lvItem.mask |= LVIF_TEXT;
1359 lvItem.iItem = nItem;
1360 lvItem.iSubItem = 0;
1361 lvItem.pszText = szDispText;
1362 lvItem.cchTextMax = DISP_TEXT_SIZE;
1363 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
1366 /************************************************************/
1367 /* compute the box rectangle (it should be cheap to do) */
1368 /************************************************************/
1369 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) return FALSE;
1370 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
1371 Box.left = Position.x + Origin.x;
1372 Box.top = Position.y + Origin.y;
1373 Box.right = Box.left + infoPtr->nItemWidth;
1374 Box.bottom = Box.top + infoPtr->nItemHeight;
1376 /************************************************************/
1377 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1378 /************************************************************/
1381 if (uView == LVS_ICON)
1383 Icon.left = Box.left;
1384 if (infoPtr->himlNormal)
1385 Icon.left += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2 - ICON_LR_HALF;
1387 Icon.right = Icon.left;
1388 Icon.bottom = Icon.top;
1389 if (infoPtr->himlNormal)
1391 Icon.right += infoPtr->iconSize.cx + ICON_LR_PADDING;
1392 Icon.bottom += infoPtr->iconSize.cy + ICON_TOP_PADDING;
1395 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1398 Icon.left = Box.left;
1399 if (uView == LVS_REPORT) Icon.left += infoPtr->iconSize.cx * lvItem.iIndent;
1400 if (infoPtr->himlState) Icon.left += infoPtr->iconStateSize.cx;
1402 Icon.right = Icon.left;
1403 if (infoPtr->himlSmall) Icon.right += infoPtr->iconSize.cx;
1404 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1406 if(lprcIcon) *lprcIcon = Icon;
1407 TRACE("hwnd=%x, item=%d, icon=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Icon));
1410 /************************************************************/
1411 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1412 /************************************************************/
1415 SIZE labelSize = { 0, 0 };
1417 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
1419 labelSize.cx = infoPtr->nItemWidth;
1420 labelSize.cy = infoPtr->nItemHeight;
1422 else if (is_textT(lvItem.pszText, TRUE))
1424 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1425 HDC hdc = GetDC(infoPtr->hwndSelf);
1426 HFONT hOldFont = SelectObject(hdc, hFont);
1430 /* compute rough rectangle where the label will go */
1431 SetRectEmpty(&rcText);
1432 rcText.right = infoPtr->nItemWidth - TRAILING_PADDING;
1433 rcText.bottom = infoPtr->nItemHeight;
1434 if (uView == LVS_ICON)
1435 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1437 /* now figure out the flags */
1438 if (uView == LVS_ICON)
1439 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
1441 uFormat = LV_SL_DT_FLAGS;
1443 DrawTextW (hdc, lvItem.pszText, -1, &rcText, uFormat | DT_CALCRECT);
1445 labelSize.cx = min(rcText.right - rcText.left + TRAILING_PADDING, infoPtr->nItemWidth);
1446 labelSize.cy = rcText.bottom - rcText.top;
1448 SelectObject(hdc, hOldFont);
1449 ReleaseDC(infoPtr->hwndSelf, hdc);
1452 if (uView == LVS_ICON)
1454 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
1455 labelSize.cy = (labelSize.cy / infoPtr->ntmHeight) * infoPtr->ntmHeight;
1456 Label.left = Box.left + (infoPtr->iconSpacing.cx - labelSize.cx) / 2;
1457 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
1458 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
1459 Label.right = Label.left + labelSize.cx;
1460 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
1461 Label.bottom = Label.top + infoPtr->nItemHeight;
1463 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
1465 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1467 Label.left = Icon.right;
1468 Label.top = Box.top;
1469 Label.right = Label.left + labelSize.cx;
1470 if (infoPtr->himlSmall) Label.right += IMAGE_PADDING;
1471 if (Label.right > Box.right) Label.right = Box.right;
1472 Label.bottom = Label.top + infoPtr->nItemHeight;
1475 if (lprcLabel) *lprcLabel = Label;
1476 TRACE("hwnd=%x, item=%d, label=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Label));
1479 /***********************************************************/
1480 /* compute bounds box for the item (ala LVM_GETITEMRECT) */
1481 /***********************************************************/
1484 if (uView == LVS_REPORT)
1486 lprcBounds->left = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT ? Box.left : Icon.left;
1487 lprcBounds->top = Box.top;
1488 lprcBounds->right = min(lprcBounds->left + infoPtr->nItemWidth, Box.right) - REPORT_MARGINX;
1489 if (lprcBounds->right < lprcBounds->left) lprcBounds->right = lprcBounds->left;
1490 lprcBounds->bottom = lprcBounds->top + infoPtr->nItemHeight;
1493 UnionRect(lprcBounds, &Icon, &Label);
1494 TRACE("hwnd=%x, item=%d, bounds=%s\n", infoPtr->hwndSelf, nItem, debugrect(lprcBounds));
1497 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
1498 else if (lprcBox) *lprcBox = Box;
1499 TRACE("hwnd=%x, item=%d, box=%s\n", infoPtr->hwndSelf, nItem, debugrect(&Box));
1506 * Aligns the items with the top edge of the window.
1509 * [I] infoPtr : valid pointer to the listview structure
1514 static void LISTVIEW_AlignTop(LISTVIEW_INFO *infoPtr)
1516 UINT uView = LISTVIEW_GetType(infoPtr);
1517 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1520 INT i, off_x=0, off_y=0;
1522 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1524 /* Since SetItemPosition uses upper-left of icon, and for
1525 style=LVS_ICON the icon is not left adjusted, get the offset */
1526 if (uView == LVS_ICON)
1528 off_y = ICON_TOP_PADDING;
1529 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1533 ZeroMemory(&rcView, sizeof(RECT));
1534 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1536 infoPtr->rcList.left, infoPtr->rcList.right);
1538 if (nListWidth > infoPtr->nItemWidth)
1540 for (i = 0; i < infoPtr->nItemCount; i++)
1542 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1545 ptItem.y += infoPtr->nItemHeight;
1548 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1549 ptItem.x += infoPtr->nItemWidth;
1550 rcView.right = max(rcView.right, ptItem.x);
1553 rcView.right -= off_x;
1554 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1558 for (i = 0; i < infoPtr->nItemCount; i++)
1560 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1561 ptItem.y += infoPtr->nItemHeight;
1564 rcView.right = infoPtr->nItemWidth;
1565 rcView.bottom = ptItem.y-off_y;
1568 infoPtr->rcView = rcView;
1574 * Aligns the items with the left edge of the window.
1577 * [I] infoPtr : valid pointer to the listview structure
1582 static void LISTVIEW_AlignLeft(LISTVIEW_INFO *infoPtr)
1584 UINT uView = LISTVIEW_GetType(infoPtr);
1585 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1588 INT i, off_x=0, off_y=0;
1590 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1592 /* Since SetItemPosition uses upper-left of icon, and for
1593 style=LVS_ICON the icon is not left adjusted, get the offset */
1594 if (uView == LVS_ICON)
1596 off_y = ICON_TOP_PADDING;
1597 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1601 ZeroMemory(&rcView, sizeof(RECT));
1602 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1604 if (nListHeight > infoPtr->nItemHeight)
1606 for (i = 0; i < infoPtr->nItemCount; i++)
1608 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1611 ptItem.x += infoPtr->nItemWidth;
1614 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1615 ptItem.y += infoPtr->nItemHeight;
1616 rcView.bottom = max(rcView.bottom, ptItem.y);
1619 rcView.right = ptItem.x + infoPtr->nItemWidth;
1623 for (i = 0; i < infoPtr->nItemCount; i++)
1625 LISTVIEW_SetItemPosition(infoPtr, i, ptItem);
1626 ptItem.x += infoPtr->nItemWidth;
1629 rcView.bottom = infoPtr->nItemHeight;
1630 rcView.right = ptItem.x;
1633 infoPtr->rcView = rcView;
1640 * Retrieves the bounding rectangle of all the items.
1643 * [I] infoPtr : valid pointer to the listview structure
1644 * [O] lprcView : bounding rectangle
1650 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
1654 TRACE("(lprcView=%p)\n", lprcView);
1656 if (!lprcView) return FALSE;
1658 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrigin)) return FALSE;
1660 *lprcView = infoPtr->rcView;
1661 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
1663 TRACE("lprcView=%s\n", debugrect(lprcView));
1670 * Retrieves the subitem pointer associated with the subitem index.
1673 * [I] HDPA : DPA handle for a specific item
1674 * [I] INT : index of subitem
1677 * SUCCESS : subitem pointer
1680 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1683 LISTVIEW_SUBITEM *lpSubItem;
1686 /* we should binary search here if need be */
1687 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1689 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1690 if (lpSubItem && (lpSubItem->iSubItem == nSubItem))
1700 * Calculates the width of a specific item.
1703 * [I] infoPtr : valid pointer to the listview structure
1704 * [I] nItem : item to calculate width, or -1 for max of all
1707 * Returns the width of an item width an item.
1709 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr, INT nItem)
1711 UINT uView = LISTVIEW_GetType(infoPtr);
1712 INT nItemWidth = 0, i;
1714 if (uView == LVS_ICON)
1715 nItemWidth = infoPtr->iconSpacing.cx;
1716 else if (uView == LVS_REPORT)
1718 INT nHeaderItemCount;
1721 /* calculate width of header */
1722 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1723 for (i = 0; i < nHeaderItemCount; i++)
1724 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem))
1725 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1731 if (infoPtr->nItemCount == 0) return DEFAULT_COLUMN_WIDTH;
1733 /* get width of string */
1736 for (i = 0; i < infoPtr->nItemCount; i++)
1738 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, i);
1739 nItemWidth = max(nItemWidth, nLabelWidth);
1743 nItemWidth = LISTVIEW_GetLabelWidth(infoPtr, nItem);
1744 if (!nItemWidth) return DEFAULT_COLUMN_WIDTH;
1745 nItemWidth += WIDTH_PADDING;
1746 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
1747 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
1748 if (nItem == -1) nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1751 return max(nItemWidth, 1);
1756 * Calculates the max width of any item in the list.
1759 * [I] infoPtr : valid pointer to the listview structure
1760 * [I] LONG : window style
1763 * Returns item width.
1765 static inline INT LISTVIEW_CalculateMaxWidth(LISTVIEW_INFO *infoPtr)
1767 return LISTVIEW_CalculateItemWidth(infoPtr, -1);
1772 * Retrieves and saves important text metrics info for the current
1776 * [I] infoPtr : valid pointer to the listview structure
1779 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
1782 HDC hdc = GetDC(infoPtr->hwndSelf);
1783 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1784 INT oldHeight, oldACW;
1786 GetTextMetricsW(hdc, &tm);
1788 oldHeight = infoPtr->ntmHeight;
1789 oldACW = infoPtr->ntmAveCharWidth;
1790 infoPtr->ntmHeight = tm.tmHeight;
1791 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1793 SelectObject(hdc, hOldFont);
1794 ReleaseDC(infoPtr->hwndSelf, hdc);
1795 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1796 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1802 * Calculates the height of an item.
1805 * [I] infoPtr : valid pointer to the listview structure
1808 * Returns item height.
1810 static INT LISTVIEW_GetItemHeight(LISTVIEW_INFO *infoPtr)
1814 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
1815 nItemHeight = infoPtr->iconSpacing.cy;
1818 nItemHeight = infoPtr->ntmHeight;
1819 if (infoPtr->himlState)
1820 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
1821 if (infoPtr->himlSmall)
1822 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
1823 if (infoPtr->himlState || infoPtr->himlSmall)
1824 nItemHeight += HEIGHT_PADDING;
1830 static void LISTVIEW_PrintSelectionRanges(LISTVIEW_INFO *infoPtr)
1834 ERR("Selections are:\n");
1835 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
1837 RANGE *selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
1838 ERR(" [%d - %d]\n", selection->lower, selection->upper);
1845 * A compare function for selection ranges
1848 * [I] range1 : pointer to selection range 1;
1849 * [I] range2 : pointer to selection range 2;
1853 * >0 : if Item 1 > Item 2
1854 * <0 : if Item 2 > Item 1
1855 * 0 : if Item 1 == Item 2
1857 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2, LPARAM flags)
1859 if (((RANGE*)range1)->upper < ((RANGE*)range2)->lower)
1861 if (((RANGE*)range2)->upper < ((RANGE*)range1)->lower)
1867 * Helper function for LISTVIEW_AddSelectionRange, and LISTVIEW_SetItem.
1869 static BOOL add_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
1875 TRACE("range (%i - %i)\n", lower, upper);
1877 /* try find overlapping selections first */
1878 selection.lower = lower - 1;
1879 selection.upper = upper + 1;
1880 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1881 LISTVIEW_CompareSelectionRanges, 0, 0);
1887 /* create the brand new selection to insert */
1888 newsel = (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
1889 if(!newsel) return FALSE;
1890 newsel->lower = lower;
1891 newsel->upper = upper;
1893 /* figure out where to insert it */
1894 index = DPA_Search(infoPtr->hdpaSelectionRanges, newsel, 0,
1895 LISTVIEW_CompareSelectionRanges, 0, DPAS_INSERTAFTER);
1896 if (index == -1) index = 0;
1898 /* and get it over with */
1899 DPA_InsertPtr(infoPtr->hdpaSelectionRanges, index, newsel);
1903 RANGE *chksel, *mrgsel;
1904 INT fromindex, mergeindex;
1906 chksel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index);
1907 if (!chksel) return FALSE;
1908 TRACE("Merge with index %i (%d - %d)\n",
1909 index, chksel->lower, chksel->upper);
1911 chksel->lower = min(lower, chksel->lower);
1912 chksel->upper = max(upper, chksel->upper);
1914 TRACE("New range %i (%d - %d)\n",
1915 index, chksel->lower, chksel->upper);
1917 /* merge now common selection ranges */
1919 selection.lower = chksel->lower - 1;
1920 selection.upper = chksel->upper + 1;
1924 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, fromindex,
1925 LISTVIEW_CompareSelectionRanges, 0, 0);
1926 if (mergeindex == -1) break;
1927 if (mergeindex == index)
1929 fromindex = index + 1;
1933 TRACE("Merge with index %i\n", mergeindex);
1935 mrgsel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, mergeindex);
1936 if (!mrgsel) return FALSE;
1938 chksel->lower = min(chksel->lower, mrgsel->lower);
1939 chksel->upper = max(chksel->upper, mrgsel->upper);
1940 COMCTL32_Free(mrgsel);
1941 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, mergeindex);
1942 if (mergeindex < index) index --;
1946 /*DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);*/
1948 if (adj_sel_only) return TRUE;
1950 /* set the selection on items */
1951 lvItem.state = LVIS_SELECTED;
1952 lvItem.stateMask = LVIS_SELECTED;
1953 for(i = lower; i <= upper; i++)
1954 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
1960 * Helper function for LISTVIEW_RemoveSelectionRange, and LISTVIEW_SetItem.
1962 static BOOL remove_selection_range(LISTVIEW_INFO *infoPtr, INT lower, INT upper, BOOL adj_sel_only)
1964 RANGE remsel, tmpsel, *chksel;
1970 lvItem.stateMask = LVIS_SELECTED;
1972 remsel.lower = lower;
1973 remsel.upper = upper;
1975 TRACE("range: (%d - %d)\n", remsel.lower, remsel.upper);
1979 index = DPA_Search(infoPtr->hdpaSelectionRanges, &remsel, 0,
1980 LISTVIEW_CompareSelectionRanges, 0, 0);
1981 if (index == -1) return TRUE;
1983 chksel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index);
1984 if (!chksel) return FALSE;
1986 TRACE("Matches range index %i (%d - %d)\n",
1987 index, chksel->lower, chksel->upper);
1989 /* case 1: Same range */
1990 if ( (chksel->upper == remsel.upper) &&
1991 (chksel->lower == remsel.lower) )
1993 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, index);
1996 /* case 2: engulf */
1997 else if ( (chksel->upper <= remsel.upper) &&
1998 (chksel->lower >= remsel.lower) )
2000 DPA_DeletePtr(infoPtr->hdpaSelectionRanges, index);
2002 /* case 3: overlap upper */
2003 else if ( (chksel->upper < remsel.upper) &&
2004 (chksel->lower < remsel.lower) )
2006 chksel->upper = remsel.lower - 1;
2008 /* case 4: overlap lower */
2009 else if ( (chksel->upper > remsel.upper) &&
2010 (chksel->lower > remsel.lower) )
2012 chksel->lower = remsel.upper + 1;
2014 /* case 5: fully internal */
2018 (RANGE *)COMCTL32_Alloc(sizeof(RANGE));
2019 if (!newsel) return FALSE;
2021 newsel->lower = chksel->lower;
2022 newsel->upper = remsel.lower - 1;
2023 chksel->lower = remsel.upper + 1;
2024 DPA_InsertPtr(infoPtr->hdpaSelectionRanges, index, newsel);
2025 /*DPA_Sort(infoPtr->hdpaSelectionRanges, LISTVIEW_CompareSelectionRanges, 0);*/
2029 if (adj_sel_only) continue;
2031 /* here, chksel holds the selection to delete */
2032 for (i = chksel->lower; i <= chksel->upper; i++)
2033 LISTVIEW_SetItemState(infoPtr, i, &lvItem);
2042 * Adds a selection range.
2045 * [I] infoPtr : valid pointer to the listview structure
2046 * [I] lower : lower item index
2047 * [I] upper : upper item index
2053 static inline BOOL LISTVIEW_AddSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2055 return add_selection_range(infoPtr, lower, upper, FALSE);
2060 * Removes a range selections.
2063 * [I] infoPtr : valid pointer to the listview structure
2064 * [I] lower : lower item index
2065 * [I] upper : upper item index
2071 static inline BOOL LISTVIEW_RemoveSelectionRange(LISTVIEW_INFO *infoPtr, INT lower, INT upper)
2073 return remove_selection_range(infoPtr, lower, upper, FALSE);
2078 * Removes all selection ranges
2081 * [I] infoPtr : valid pointer to the listview structure
2087 static LRESULT LISTVIEW_RemoveAllSelections(LISTVIEW_INFO *infoPtr)
2091 if (infoPtr->bRemovingAllSelections) return TRUE;
2093 infoPtr->bRemovingAllSelections = TRUE;
2099 sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, 0);
2100 if (sel) LISTVIEW_RemoveSelectionRange(infoPtr, sel->lower, sel->upper);
2102 while (infoPtr->hdpaSelectionRanges->nItemCount > 0);
2104 infoPtr->bRemovingAllSelections = FALSE;
2111 * Retrieves the number of items that are marked as selected.
2114 * [I] infoPtr : valid pointer to the listview structure
2117 * Number of items selected.
2119 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2121 INT i, nSelectedCount = 0;
2123 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2126 for (i = 0; i < infoPtr->nItemCount; i++)
2128 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2134 for (i = 0; i < infoPtr->hdpaSelectionRanges->nItemCount; i++)
2136 RANGE *sel = DPA_GetPtr(infoPtr->hdpaSelectionRanges, i);
2137 nSelectedCount += sel->upper - sel->lower + 1;
2141 TRACE("nSelectedCount=%d\n", nSelectedCount);
2142 return nSelectedCount;
2147 * Manages the item focus.
2150 * [I] infoPtr : valid pointer to the listview structure
2151 * [I] INT : item index
2154 * TRUE : focused item changed
2155 * FALSE : focused item has NOT changed
2157 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2159 INT oldFocus = infoPtr->nFocusedItem;
2162 lvItem.state = LVIS_FOCUSED;
2163 lvItem.stateMask = LVIS_FOCUSED;
2164 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2166 return oldFocus != infoPtr->nFocusedItem;
2171 * Updates the various indices after an item has been inserted or deleted.
2174 * [I] infoPtr : valid pointer to the listview structure
2175 * [I] nItem : item index
2176 * [I] direction : Direction of shift, +1 or -1.
2181 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2183 RANGE selection,*checkselection;
2186 TRACE("Shifting %iu, %i steps\n",nItem,direction);
2188 selection.upper = nItem;
2189 selection.lower = nItem;
2191 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
2192 LISTVIEW_CompareSelectionRanges,
2193 0,DPAS_SORTED|DPAS_INSERTAFTER);
2195 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
2197 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
2198 if ((checkselection->lower >= nItem)&&
2199 ((int)(checkselection->lower + direction) >= 0))
2200 checkselection->lower += direction;
2201 if ((checkselection->upper >= nItem)&&
2202 ((int)(checkselection->upper + direction) >= 0))
2203 checkselection->upper += direction;
2207 /* Note that the following will fail if direction != +1 and -1 */
2208 if (infoPtr->nSelectionMark > nItem)
2209 infoPtr->nSelectionMark += direction;
2210 else if (infoPtr->nSelectionMark == nItem)
2213 infoPtr->nSelectionMark += direction;
2214 else if (infoPtr->nSelectionMark >= infoPtr->nItemCount)
2215 infoPtr->nSelectionMark = infoPtr->nItemCount - 1;
2218 if (infoPtr->nFocusedItem > nItem)
2219 infoPtr->nFocusedItem += direction;
2220 else if (infoPtr->nFocusedItem == nItem)
2223 infoPtr->nFocusedItem += direction;
2226 if (infoPtr->nFocusedItem >= infoPtr->nItemCount)
2227 infoPtr->nFocusedItem = infoPtr->nItemCount - 1;
2228 if (infoPtr->nFocusedItem >= 0)
2229 LISTVIEW_SetItemFocus(infoPtr, infoPtr->nFocusedItem);
2232 /* But we are not supposed to modify nHotItem! */
2238 * Adds a block of selections.
2241 * [I] infoPtr : valid pointer to the listview structure
2242 * [I] INT : item index
2247 static void LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2249 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2250 INT nLast = max(infoPtr->nSelectionMark, nItem);
2257 item.state = LVIS_SELECTED;
2258 item.stateMask = LVIS_SELECTED;
2260 /* FIXME: this is not correct LVS_OWNERDATA
2261 * See docu for LVN_ITEMCHANGED. Is there something similar for
2262 * RemoveGroupSelection (is there such a thing?)?
2264 for (i = nFirst; i <= nLast; i++)
2265 LISTVIEW_SetItemState(infoPtr,i,&item);
2267 LISTVIEW_SetItemFocus(infoPtr, nItem);
2268 infoPtr->nSelectionMark = nItem;
2274 * Sets a single group selection.
2277 * [I] infoPtr : valid pointer to the listview structure
2278 * [I] INT : item index
2283 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2285 UINT uView = LISTVIEW_GetType(infoPtr);
2291 LISTVIEW_RemoveAllSelections(infoPtr);
2293 item.state = LVIS_SELECTED;
2294 item.stateMask = LVIS_SELECTED;
2296 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2300 if (infoPtr->nSelectionMark == -1)
2301 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2304 nFirst = min(infoPtr->nSelectionMark, nItem);
2305 nLast = max(infoPtr->nSelectionMark, nItem);
2307 for (i = nFirst; i <= nLast; i++)
2308 LISTVIEW_SetItemState(infoPtr, i, &item);
2312 RECT rcItem, rcSelMark;
2314 rcItem.left = LVIR_BOUNDS;
2315 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
2316 rcSelMark.left = LVIR_BOUNDS;
2317 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
2318 UnionRect(&rcSel, &rcItem, &rcSelMark);
2319 for (i = 0; i <= infoPtr->nItemCount; i++)
2321 LISTVIEW_GetItemPosition(infoPtr, i, &ptItem);
2322 if (PtInRect(&rcSel, ptItem))
2323 LISTVIEW_SetItemState(infoPtr, i, &item);
2327 LISTVIEW_SetItemFocus(infoPtr, nItem);
2332 * Sets a single selection.
2335 * [I] infoPtr : valid pointer to the listview structure
2336 * [I] INT : item index
2341 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2345 TRACE("nItem=%d\n", nItem);
2347 LISTVIEW_RemoveAllSelections(infoPtr);
2349 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
2350 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2351 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
2353 infoPtr->nSelectionMark = nItem;
2358 * Set selection(s) with keyboard.
2361 * [I] infoPtr : valid pointer to the listview structure
2362 * [I] INT : item index
2365 * SUCCESS : TRUE (needs to be repainted)
2366 * FAILURE : FALSE (nothing has changed)
2368 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
2370 /* FIXME: pass in the state */
2371 LONG lStyle = infoPtr->dwStyle;
2372 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2373 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2374 BOOL bResult = FALSE;
2376 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
2378 if (lStyle & LVS_SINGLESEL)
2381 LISTVIEW_SetSelection(infoPtr, nItem);
2382 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2389 LISTVIEW_SetGroupSelection(infoPtr, nItem);
2393 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
2398 LISTVIEW_SetSelection(infoPtr, nItem);
2399 ListView_EnsureVisible(infoPtr->hwndSelf, nItem, FALSE);
2404 UpdateWindow(infoPtr->hwndSelf); /* update client area */
2410 * Selects an item based on coordinates.
2413 * [I] infoPtr : valid pointer to the listview structure
2414 * [I] pt : mouse click ccordinates
2417 * SUCCESS : item index
2420 static INT LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, POINT pt)
2426 visrange = LISTVIEW_GetVisibleRange(infoPtr);
2427 for (i = visrange.lower; i <= visrange.upper; i++)
2429 rcItem.left = LVIR_SELECTBOUNDS;
2430 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
2432 TRACE("i=%d, rcItem=%s\n", i, debugrect(&rcItem));
2433 if (PtInRect(&rcItem, pt)) return i;
2441 * Called when the mouse is being actively tracked and has hovered for a specified
2445 * [I] infoPtr : valid pointer to the listview structure
2446 * [I] fwKeys : key indicator
2447 * [I] pts : mouse position
2450 * 0 if the message was processed, non-zero if there was an error
2453 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2454 * over the item for a certain period of time.
2457 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, POINTS pts)
2459 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
2460 /* FIXME: select the item!!! */
2461 /*LISTVIEW_GetItemAtPt(infoPtr, pt)*/;
2468 * Called whenever WM_MOUSEMOVE is received.
2471 * [I] infoPtr : valid pointer to the listview structure
2472 * [I] fwKeys : key indicator
2473 * [I] pts : mouse position
2476 * 0 if the message is processed, non-zero if there was an error
2478 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, POINTS pts)
2480 TRACKMOUSEEVENT trackinfo;
2482 /* see if we are supposed to be tracking mouse hovering */
2483 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
2484 /* fill in the trackinfo struct */
2485 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2486 trackinfo.dwFlags = TME_QUERY;
2487 trackinfo.hwndTrack = infoPtr->hwndSelf;
2488 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2490 /* see if we are already tracking this hwnd */
2491 _TrackMouseEvent(&trackinfo);
2493 if(!(trackinfo.dwFlags & TME_HOVER)) {
2494 trackinfo.dwFlags = TME_HOVER;
2496 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2497 _TrackMouseEvent(&trackinfo);
2506 * Tests wheather the item is assignable to a list with style lStyle
2508 static inline BOOL is_assignable_item(LPLVITEMW lpLVItem, LONG lStyle)
2510 if ( (lpLVItem->mask & LVIF_TEXT) &&
2511 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
2512 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
2519 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2522 * [I] infoPtr : valid pointer to the listview structure
2523 * [I] lpLVItem : valid pointer to new item atttributes
2524 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2530 static BOOL set_owner_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2532 LONG lStyle = infoPtr->dwStyle;
2536 /* a virtual listview stores only the state for the main item */
2537 if (lpLVItem->iSubItem || !(lpLVItem->mask & LVIF_STATE)) return FALSE;
2539 oldState = LISTVIEW_GetItemState(infoPtr, lpLVItem->iItem, LVIS_FOCUSED | LVIS_SELECTED);
2540 TRACE("oldState=%x, newState=%x, uCallbackMask=%x\n",
2541 oldState, lpLVItem->state, infoPtr->uCallbackMask);
2543 /* we're done if we don't need to change anything we handle */
2544 if ( !((oldState ^ lpLVItem->state) & lpLVItem->stateMask &
2545 ~infoPtr->uCallbackMask & (LVIS_FOCUSED | LVIS_SELECTED))) return TRUE;
2548 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent for
2549 * by LVS_OWERNDATA list controls
2552 /* if we handle the focus, and we're asked to change it, do it now */
2553 if ( lpLVItem->stateMask & LVIS_FOCUSED )
2555 if (lpLVItem->state & LVIS_FOCUSED)
2556 infoPtr->nFocusedItem = lpLVItem->iItem;
2557 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2558 infoPtr->nFocusedItem = -1;
2561 /* and the selection is the only other state a virtual list may hold */
2562 if (lpLVItem->stateMask & LVIS_SELECTED)
2564 if (lpLVItem->state & LVIS_SELECTED)
2566 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2567 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2570 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2573 /* notify the parent now that things have changed */
2574 ZeroMemory(&nmlv, sizeof(nmlv));
2575 nmlv.iItem = lpLVItem->iItem;
2576 nmlv.uNewState = lpLVItem->state;
2577 nmlv.uOldState = oldState;
2578 nmlv.uChanged = LVIF_STATE;
2579 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2586 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
2589 * [I] infoPtr : valid pointer to the listview structure
2590 * [I] lpLVItem : valid pointer to new item atttributes
2591 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2597 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2599 LONG lStyle = infoPtr->dwStyle;
2600 UINT uView = lStyle & LVS_TYPEMASK;
2602 LISTVIEW_ITEM *lpItem;
2606 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2607 if (!hdpaSubItems && hdpaSubItems != (HDPA)-1) return FALSE;
2609 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2610 if (!lpItem) return FALSE;
2612 /* determine what fields will change */
2613 if ((lpLVItem->mask & LVIF_STATE) &&
2614 ((lpItem->state ^ lpLVItem->state) & lpLVItem->stateMask))
2615 uChanged |= LVIF_STATE;
2617 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
2618 uChanged |= LVIF_IMAGE;
2620 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
2621 uChanged |= LVIF_PARAM;
2623 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
2624 uChanged |= LVIF_INDENT;
2626 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
2627 uChanged |= LVIF_TEXT;
2629 if (!uChanged) return TRUE;
2631 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2632 nmlv.iItem = lpLVItem->iItem;
2633 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2634 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2635 nmlv.uChanged = uChanged;
2636 nmlv.lParam = lpItem->lParam;
2638 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
2639 if(lpItem->valid && notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
2642 /* copy information */
2643 if (lpLVItem->mask & LVIF_TEXT)
2644 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
2646 if (lpLVItem->mask & LVIF_IMAGE)
2647 lpItem->hdr.iImage = lpLVItem->iImage;
2649 if (lpLVItem->mask & LVIF_PARAM)
2650 lpItem->lParam = lpLVItem->lParam;
2652 if (lpLVItem->mask & LVIF_INDENT)
2653 lpItem->iIndent = lpLVItem->iIndent;
2655 if (uChanged & LVIF_STATE)
2657 lpItem->state &= ~lpLVItem->stateMask;
2658 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2659 if (nmlv.uNewState & LVIS_SELECTED)
2661 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(infoPtr);
2662 add_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2664 else if (lpLVItem->stateMask & LVIS_SELECTED)
2665 remove_selection_range(infoPtr, lpLVItem->iItem, lpLVItem->iItem, TRUE);
2667 /* if we are asked to change focus, and we manage it, do it */
2668 if (nmlv.uNewState & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
2670 if (lpLVItem->state & LVIS_FOCUSED)
2672 infoPtr->nFocusedItem = lpLVItem->iItem;
2673 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, FALSE);
2675 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2676 infoPtr->nFocusedItem = -1;
2680 /* if LVS_LIST or LVS_SMALLICON, update the width of the items */
2681 if((uChanged & LVIF_TEXT) && ((uView == LVS_LIST) || (uView == LVS_SMALLICON)))
2683 int item_width = LISTVIEW_CalculateItemWidth(infoPtr, lpLVItem->iItem);
2684 if(item_width > infoPtr->nItemWidth) infoPtr->nItemWidth = item_width;
2687 /* if we're inserting the item, we're done */
2688 if (!lpItem->valid) return TRUE;
2690 /* send LVN_ITEMCHANGED notification */
2691 nmlv.lParam = lpItem->lParam;
2692 notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
2699 * Helper for LISTVIEW_SetItemT *only*: sets subitem attributes.
2702 * [I] infoPtr : valid pointer to the listview structure
2703 * [I] lpLVItem : valid pointer to new subitem atttributes
2704 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2710 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2713 LISTVIEW_SUBITEM *lpSubItem;
2714 BOOL bModified = FALSE;
2716 /* set subitem only if column is present */
2717 if (Header_GetItemCount(infoPtr->hwndHeader) <= lpLVItem->iSubItem)
2720 /* First do some sanity checks */
2721 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
2722 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
2724 /* get the subitem structure, and create it if not there */
2725 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2726 if (!hdpaSubItems) return FALSE;
2728 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
2731 LISTVIEW_SUBITEM *tmpSubItem;
2734 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2735 if (!lpSubItem) return FALSE;
2736 /* we could binary search here, if need be...*/
2737 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2739 tmpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2740 if (tmpSubItem && tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
2742 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
2744 COMCTL32_Free(lpSubItem);
2747 lpSubItem->iSubItem = lpLVItem->iSubItem;
2751 if (lpLVItem->mask & LVIF_IMAGE)
2752 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
2754 lpSubItem->hdr.iImage = lpLVItem->iImage;
2758 if (lpLVItem->mask & LVIF_TEXT)
2759 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
2761 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
2765 if (bModified && !infoPtr->bIsDrawing)
2769 rect.left = LVIR_BOUNDS;
2770 rect.top = lpLVItem->iSubItem;
2771 /* GetSubItemRect will fail in non-report mode, so there's
2772 * gonna be no invalidation then, yay! */
2773 if (LISTVIEW_GetSubItemRect(infoPtr, lpLVItem->iItem, &rect))
2774 LISTVIEW_InvalidateRect(infoPtr, &rect);
2782 * Sets item attributes.
2785 * [I] infoPtr : valid pointer to the listview structure
2786 * [I] LPLVITEM : new item atttributes
2787 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2793 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
2795 INT nOldFocus = infoPtr->nFocusedItem;
2796 LPWSTR pszText = NULL;
2799 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
2801 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
2804 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
2805 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
2807 pszText = lpLVItem->pszText;
2808 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
2811 /* actually set the fields */
2812 if (infoPtr->dwStyle & LVS_OWNERDATA)
2813 bResult = set_owner_item(infoPtr, lpLVItem, TRUE);
2816 /* sanity checks first */
2817 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
2819 if (lpLVItem->iSubItem)
2820 bResult = set_sub_item(infoPtr, lpLVItem, TRUE);
2822 bResult = set_main_item(infoPtr, lpLVItem, TRUE);
2825 /* redraw item, if necessary */
2826 if (bResult && !infoPtr->bIsDrawing && lpLVItem->iSubItem == 0)
2828 if (nOldFocus != infoPtr->nFocusedItem && infoPtr->bFocus)
2829 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->rcFocus);
2831 /* this little optimization eliminates some nasty flicker */
2832 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
2833 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
2834 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
2838 rect.left = LVIR_BOUNDS;
2840 if (LISTVIEW_GetSubItemRect(infoPtr, lpLVItem->iItem, &rect))
2841 LISTVIEW_InvalidateRect(infoPtr, &rect);
2844 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
2849 textfreeT(lpLVItem->pszText, isW);
2850 lpLVItem->pszText = pszText;
2858 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2861 * [I] infoPtr : valid pointer to the listview structure
2866 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
2868 LONG lStyle = infoPtr->dwStyle;
2869 UINT uView = lStyle & LVS_TYPEMASK;
2871 SCROLLINFO scrollInfo;
2873 scrollInfo.cbSize = sizeof(SCROLLINFO);
2874 scrollInfo.fMask = SIF_POS;
2876 if (uView == LVS_LIST)
2878 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
2879 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
2881 else if (uView == LVS_REPORT)
2883 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2884 nItem = scrollInfo.nPos;
2888 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
2889 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
2892 TRACE("nItem=%d\n", nItem);
2897 /* used by the drawing code */
2898 typedef struct tagTEXTATTR
2905 /* helper function for the drawing code */
2906 static inline void set_text_attr(HDC hdc, TEXTATTR *ta)
2908 ta->bkMode = SetBkMode(hdc, ta->bkMode);
2909 ta->bkColor = SetBkColor(hdc, ta->bkColor);
2910 ta->fgColor = SetTextColor(hdc, ta->fgColor);
2913 /* helper function for the drawing code */
2914 static void select_text_attr(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL isSelected, TEXTATTR *ta)
2916 ta->bkMode = OPAQUE;
2918 if (isSelected && infoPtr->bFocus)
2920 ta->bkColor = comctl32_color.clrHighlight;
2921 ta->fgColor = comctl32_color.clrHighlightText;
2923 else if (isSelected && (infoPtr->dwStyle & LVS_SHOWSELALWAYS))
2925 ta->bkColor = comctl32_color.clr3dFace;
2926 ta->fgColor = comctl32_color.clrBtnText;
2928 else if ( (infoPtr->clrTextBk != CLR_DEFAULT) && (infoPtr->clrTextBk != CLR_NONE) )
2930 ta->bkColor = infoPtr->clrTextBk;
2931 ta->fgColor = infoPtr->clrText;
2935 ta->bkMode = TRANSPARENT;
2936 ta->bkColor = GetBkColor(hdc);
2937 ta->fgColor = infoPtr->clrText;
2940 set_text_attr(hdc, ta);
2945 * Erases the background of the given rectangle
2948 * [I] infoPtr : valid pointer to the listview structure
2949 * [I] hdc : device context handle
2950 * [I] lprcBox : clipping rectangle
2956 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT* lprcBox)
2958 if (!infoPtr->hBkBrush) return FALSE;
2960 TRACE("(hdc=%x, lprcBox=%s, hBkBrush=%x)\n", hdc, debugrect(lprcBox), infoPtr->hBkBrush);
2962 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
2970 * [I] infoPtr : valid pointer to the listview structure
2971 * [I] HDC : device context handle
2972 * [I] INT : item index
2973 * [I] INT : subitem index
2974 * [I] RECT * : clipping rectangle
2980 static BOOL LISTVIEW_DrawSubItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem,
2981 INT nSubItem, RECT rcItem, UINT align)
2983 WCHAR szDispText[DISP_TEXT_SIZE];
2986 TRACE("(hdc=%x, nItem=%d, nSubItem=%d, rcItem=%s)\n",
2987 hdc, nItem, nSubItem, debugrect(&rcItem));
2989 /* get information needed for drawing the item */
2990 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
2991 lvItem.iItem = nItem;
2992 lvItem.iSubItem = nSubItem;
2993 lvItem.cchTextMax = DISP_TEXT_SIZE;
2994 lvItem.pszText = szDispText;
2995 *lvItem.pszText = '\0';
2996 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
2998 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3000 if (lvItem.iImage) FIXME("Draw the image for the subitem\n");
3002 DrawTextW(hdc, lvItem.pszText, -1, &rcItem, LV_SL_DT_FLAGS | align);
3013 * [I] infoPtr : valid pointer to the listview structure
3014 * [I] hdc : device context handle
3015 * [I] nItem : item index
3016 * [I] rcItem : item rectangle
3019 * TRUE: if item is focused
3022 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem)
3024 WCHAR szDispText[DISP_TEXT_SIZE];
3025 INT nLabelWidth, imagePadding = 0;
3026 RECT* lprcFocus, rcOrig = rcItem;
3030 TRACE("(hdc=%x, nItem=%d)\n", hdc, nItem);
3032 /* get information needed for drawing the item */
3033 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3034 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3035 lvItem.iItem = nItem;
3036 lvItem.iSubItem = 0;
3037 lvItem.cchTextMax = DISP_TEXT_SIZE;
3038 lvItem.pszText = szDispText;
3039 *lvItem.pszText = '\0';
3040 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3041 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3043 /* now check if we need to update the focus rectangle */
3044 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3045 if (lprcFocus) SetRectEmpty(lprcFocus);
3048 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3051 if (infoPtr->himlState)
3053 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3056 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3057 rcItem.left, rcItem.top, ILD_NORMAL);
3059 rcItem.left += infoPtr->iconStateSize.cx;
3060 imagePadding = IMAGE_PADDING;
3064 if (infoPtr->himlSmall)
3066 if (lvItem.iImage >= 0)
3068 UINT mode = (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ?
3069 ILD_SELECTED : ILD_NORMAL;
3070 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3071 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc,
3072 rcItem.left, rcItem.top, mode);
3074 rcItem.left += infoPtr->iconSize.cx;
3075 imagePadding = IMAGE_PADDING;
3078 /* Don't bother painting item being edited */
3079 if (infoPtr->bEditing && lprcFocus)
3082 select_text_attr(infoPtr, hdc, lvItem.state & LVIS_SELECTED, &ta);
3084 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
3085 rcItem.left += imagePadding;
3086 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3087 if (rcItem.right > rcOrig.right) rcItem.right = rcOrig.right;
3091 TRACE("drawing text=%s, in rect=%s\n", debugstr_w(lvItem.pszText), debugrect(&rcItem));
3092 if(lprcFocus) *lprcFocus = rcItem;
3093 if (lvItem.state & LVIS_SELECTED)
3094 ExtTextOutW(hdc, rcItem.left, rcItem.top, ETO_OPAQUE, &rcItem, 0, 0, 0);
3095 DrawTextW(hdc, lvItem.pszText, -1, &rcItem, LV_SL_DT_FLAGS | DT_CENTER);
3098 set_text_attr(hdc, &ta);
3099 return lprcFocus != NULL;
3104 * Draws an item when in large icon display mode.
3107 * [I] infoPtr : valid pointer to the listview structure
3108 * [I] hdc : device context handle
3109 * [I] nItem : item index
3110 * [I] rcItem : clipping rectangle
3113 * TRUE: if item is focused
3116 static BOOL LISTVIEW_DrawLargeItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, RECT rcItem)
3118 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3121 RECT rcIcon, rcFocus, rcLabel, *lprcFocus;
3123 TRACE("(hdc=%x, nItem=%d, rcItem=%s)\n", hdc, nItem, debugrect(&rcItem));
3125 /* get information needed for drawing the item */
3126 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3127 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3128 lvItem.iItem = nItem;
3129 lvItem.iSubItem = 0;
3130 lvItem.cchTextMax = DISP_TEXT_SIZE;
3131 lvItem.pszText = szDispText;
3132 *lvItem.pszText = '\0';
3133 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3134 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3136 /* now check if we need to update the focus rectangle */
3137 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3139 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, &rcIcon, &rcLabel)) return FALSE;
3141 /* Set the item to the boundary box for now */
3142 TRACE("rcIcon=%s, rcLabel=%s\n", debugrect(&rcIcon), debugrect(&rcLabel));
3144 /* Figure out text colours etc. depending on state
3145 * At least the following states exist; there may be more.
3146 * Many items may be selected
3147 * At most one item may have the focus
3148 * The application may not actually be active currently
3149 * 1. The item is not selected in any way
3150 * 2. The cursor is flying over the icon or text and the text is being
3151 * expanded because it is not fully displayed currently.
3152 * 3. The item is selected and is focussed, i.e. the user has not clicked
3153 * in the blank area of the window, and the window (or application?)
3154 * still has the focus.
3155 * 4. As 3 except that a different window has the focus
3156 * 5. The item is the selected item of all the items, but the user has
3157 * clicked somewhere else on the window.
3158 * Only a few of these are handled currently. In particular 2 is not yet
3159 * handled since we do not support the functionality currently (or at least
3160 * we didn't when I wrote this)
3163 if (lvItem.state & LVIS_SELECTED)
3165 /* set item colors */
3166 SetBkColor(hdc, comctl32_color.clrHighlight);
3167 SetTextColor(hdc, comctl32_color.clrHighlightText);
3168 SetBkMode (hdc, OPAQUE);
3169 /* set raster mode */
3170 SetROP2(hdc, R2_XORPEN);
3171 /* When exactly is it in XOR? while being dragged? */
3175 /* set item colors */
3176 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3178 SetBkMode(hdc, TRANSPARENT);
3182 SetBkMode(hdc, OPAQUE);
3183 SetBkColor(hdc, infoPtr->clrTextBk);
3186 SetTextColor(hdc, infoPtr->clrText);
3187 /* set raster mode */
3188 SetROP2(hdc, R2_COPYPEN);
3191 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3192 * wrapping and long words split.
3193 * In cases 1 and 4 only a portion of the text is displayed with word
3194 * wrapping and both word and end ellipsis. (I don't yet know about path
3197 uFormat = lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
3200 if (infoPtr->himlState != NULL)
3202 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3205 x = rcIcon.left - infoPtr->iconStateSize.cx + 10;
3206 y = rcIcon.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
3207 if (uStateImage > 0)
3208 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, x, y, ILD_NORMAL);
3212 if (infoPtr->himlNormal != NULL)
3214 if (lvItem.iImage >= 0)
3215 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc,
3216 rcIcon.left + ICON_LR_HALF, rcIcon.top + ICON_TOP_PADDING,
3217 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3220 /* Draw the text below the icon */
3222 /* Don't bother painting item being edited */
3223 if ((infoPtr->bEditing && lprcFocus) || !lvItem.pszText || !lstrlenW(lvItem.pszText))
3225 if(lprcFocus) SetRectEmpty(lprcFocus);
3231 /* I am sure of most of the uFormat values. However I am not sure about
3232 * whether we need or do not need the following:
3233 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3234 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3235 * We certainly do not need
3236 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3237 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3240 /* If the text is being drawn without clipping (i.e. the full text) then we
3241 * need to jump through a few hoops to ensure that it all gets displayed and
3242 * that the background is complete
3244 rcFocus = rcLabel; /* save for focus */
3245 if (lvItem.state & LVIS_SELECTED)
3246 ExtTextOutW(hdc, rcLabel.left, rcLabel.top, ETO_OPAQUE, &rcLabel, 0, 0, 0);
3247 /* else ? What if we are losing the focus? will we not get a complete
3251 DrawTextW (hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3252 TRACE("text at rcLabel=%s is %s\n", debugrect(&rcLabel), debugstr_w(lvItem.pszText));
3254 if(lprcFocus) CopyRect(lprcFocus, &rcFocus);
3256 return lprcFocus != NULL;
3261 * Draws listview items when in owner draw mode.
3264 * [I] infoPtr : valid pointer to the listview structure
3265 * [I] hdc : device context handle
3270 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, HDC hdc)
3272 INT nTop, nItem, nLast, nUpdateHeight, nUpdateWidth, rgntype;
3273 UINT uID = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
3274 HWND hwndParent = GetParent(infoPtr->hwndSelf);
3275 POINT Origin, Position;
3282 /* figure out what to draw */
3283 /* FIXME: this works for REPORT only */
3284 rgntype = GetClipBox(hdc, &rcClip);
3285 if (rgntype == NULLREGION) return;
3286 nUpdateHeight = rcClip.bottom - rcClip.top + 1;
3287 nUpdateWidth = rcClip.right - rcClip.left;
3288 nTop = LISTVIEW_GetTopIndex(infoPtr);
3289 nItem = nTop + (rcClip.top - infoPtr->rcList.top) / infoPtr->nItemHeight;
3292 nLast = nItem + nUpdateHeight / infoPtr->nItemHeight;
3293 if (nUpdateHeight % infoPtr->nItemHeight) nLast++;
3294 if (nLast > infoPtr->nItemCount)
3295 nLast = infoPtr->nItemCount;
3296 ZeroMemory(&dis, sizeof(dis));
3298 /* Get scroll info once before loop */
3299 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return;
3302 /* send cache hint notification */
3303 if (infoPtr->dwStyle & LVS_OWNERDATA)
3304 notify_odcachehint(infoPtr, nItem, nLast);
3306 /* iterate through the invalidated rows */
3307 for (; nItem < nLast; nItem++)
3311 item.mask = LVIF_PARAM | LVIF_STATE;
3312 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3313 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3315 dis.CtlType = ODT_LISTVIEW;
3318 dis.itemAction = ODA_DRAWENTIRE;
3320 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3321 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3322 dis.hwndItem = infoPtr->hwndSelf;
3324 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, &Position)) continue;
3325 dis.rcItem.left = Position.x + Origin.x;
3326 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3327 dis.rcItem.top = Position.y + Origin.y;
3328 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3329 dis.itemData = item.lParam;
3331 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), debugrect(&dis.rcItem));
3332 SendMessageW(hwndParent, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3338 * Draws listview items when in report display mode.
3341 * [I] infoPtr : valid pointer to the listview structure
3342 * [I] hdc : device context handle
3343 * [I] cdmode : custom draw mode
3348 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3350 INT rgntype, nDrawPosY, j;
3351 INT nTop, nItem, nLast, nUpdateHeight, nUpdateWidth;
3352 INT nColumnCount, nFirstCol, nLastCol;
3353 RECT rcItem, rcClip, rcFullSelect;
3354 BOOL bFullSelected, isFocused;
3355 DWORD cditemmode = CDRF_DODEFAULT;
3356 TEXTATTR tmpTa, oldTa;
3357 COLUMNCACHE *lpCols;
3364 /* figure out what to draw */
3365 rgntype = GetClipBox(hdc, &rcClip);
3366 if (rgntype == NULLREGION) return;
3367 nUpdateHeight = rcClip.bottom - rcClip.top + 1;
3368 nUpdateWidth = rcClip.right - rcClip.left;
3369 nTop = LISTVIEW_GetTopIndex(infoPtr);
3370 nItem = nTop + (rcClip.top - infoPtr->rcList.top) / infoPtr->nItemHeight;
3373 nLast = nItem + nUpdateHeight / infoPtr->nItemHeight;
3374 if (nUpdateHeight % infoPtr->nItemHeight) nLast++;
3375 if (nLast > infoPtr->nItemCount)
3376 nLast = infoPtr->nItemCount;
3378 /* send cache hint notification */
3379 if (infoPtr->dwStyle & LVS_OWNERDATA)
3380 notify_odcachehint(infoPtr, nItem, nLast);
3382 /* cache column info */
3383 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3384 lpCols = COMCTL32_Alloc(nColumnCount * sizeof(COLUMNCACHE));
3385 if (!lpCols) return;
3386 for (j = 0; j < nColumnCount; j++)
3388 Header_GetItemRect(infoPtr->hwndHeader, j, &lpCols[j].rc);
3389 TRACE("lpCols[%d].rc=%s\n", j, debugrect(&lpCols[j].rc));
3392 /* Get scroll info once before loop */
3393 if (!LISTVIEW_GetOrigin(infoPtr, &ptOrig)) return;
3395 /* we now narrow the columns as well */
3396 nLastCol = nColumnCount - 1;
3397 for(nFirstCol = 0; nFirstCol < nColumnCount; nFirstCol++)
3398 if (lpCols[nFirstCol].rc.right + ptOrig.x >= rcClip.left) break;
3399 for(nLastCol = nColumnCount - 1; nLastCol >= 0; nLastCol--)
3400 if (lpCols[nLastCol].rc.left + ptOrig.x < rcClip.right) break;
3402 /* cache the per-column information before we start drawing */
3403 for (j = nFirstCol; j <= nLastCol; j++)
3405 lvColumn.mask = LVCF_FMT;
3406 LISTVIEW_GetColumnT(infoPtr, j, &lvColumn, TRUE);
3407 TRACE("lvColumn=%s\n", debuglvcolumn_t(&lvColumn, TRUE));
3408 lpCols[j].align = DT_LEFT;
3409 if (lvColumn.fmt & LVCFMT_RIGHT)
3410 lpCols[j].align = DT_RIGHT;
3411 else if (lvColumn.fmt & LVCFMT_CENTER)
3412 lpCols[j].align = DT_CENTER;
3415 /* a last few bits before we start drawing */
3416 TRACE("nTop=%d, nItem=%d, nLast=%d, nFirstCol=%d, nLastCol=%d\n",
3417 nTop, nItem, nLast, nFirstCol, nLastCol);
3418 bFullSelected = infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT;
3419 nDrawPosY = infoPtr->rcList.top + (nItem - nTop) * infoPtr->nItemHeight;
3421 /* save dc values we're gonna trash while drawing */
3422 oldTa.bkMode = GetBkMode(hdc);
3423 oldTa.bkColor = GetBkColor(hdc);
3424 oldTa.fgColor = GetTextColor(hdc);
3426 /* iterate through the invalidated rows */
3427 for (; nItem < nLast; nItem++, nDrawPosY += infoPtr->nItemHeight)
3429 /* compute the full select rectangle, if needed */
3432 item.mask = LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3433 item.stateMask = LVIS_SELECTED;
3436 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3438 rcFullSelect.left = lpCols[0].rc.left + REPORT_MARGINX +
3439 infoPtr->iconSize.cx * item.iIndent +
3440 (infoPtr->himlSmall ? infoPtr->iconSize.cx : 0);
3441 rcFullSelect.right = max(rcFullSelect.left, lpCols[nColumnCount - 1].rc.right - REPORT_MARGINX);
3442 rcFullSelect.top = nDrawPosY;
3443 rcFullSelect.bottom = rcFullSelect.top + infoPtr->nItemHeight;
3444 OffsetRect(&rcFullSelect, ptOrig.x, 0);
3447 /* draw the background of the selection rectangle, if need be */
3448 select_text_attr(infoPtr, hdc, bFullSelected && (item.state & LVIS_SELECTED), &tmpTa);
3449 if (bFullSelected && (item.state & LVIS_SELECTED))
3450 ExtTextOutW(hdc, rcFullSelect.left, rcFullSelect.top, ETO_OPAQUE, &rcFullSelect, 0, 0, 0);
3452 /* iterate through the invalidated columns */
3454 for (j = nFirstCol; j <= nLastCol; j++)
3456 if (cdmode & CDRF_NOTIFYITEMDRAW)
3457 cditemmode = notify_customdrawitem (infoPtr, hdc, nItem, j, CDDS_ITEMPREPAINT);
3458 if (cditemmode & CDRF_SKIPDEFAULT) continue;
3460 rcItem = lpCols[j].rc;
3461 rcItem.left += REPORT_MARGINX;
3462 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3463 rcItem.top = nDrawPosY;
3464 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3466 /* Offset the Scroll Bar Pos */
3467 OffsetRect(&rcItem, ptOrig.x, 0);
3470 isFocused = LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem);
3472 LISTVIEW_DrawSubItem(infoPtr, hdc, nItem, j, rcItem, lpCols[j].align);
3474 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3475 notify_customdrawitem(infoPtr, hdc, nItem, 0, CDDS_ITEMPOSTPAINT);
3478 /* Adjust focus if we have it, and we are in full select */
3479 if (bFullSelected && isFocused) infoPtr->rcFocus = rcFullSelect;
3482 /* cleanup the mess */
3483 set_text_attr(hdc, &oldTa);
3484 COMCTL32_Free(lpCols);
3489 * Draws listview items when in list display mode.
3492 * [I] infoPtr : valid pointer to the listview structure
3493 * [I] hdc : device context handle
3494 * [I] cdmode : custom draw mode
3499 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD cdmode)
3505 INT nCountPerColumn;
3506 INT nItemWidth = infoPtr->nItemWidth;
3507 INT nItemHeight = infoPtr->nItemHeight;
3508 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3509 DWORD cditemmode = CDRF_DODEFAULT;
3511 /* get number of fully visible columns */
3512 nColumnCount = nListWidth / nItemWidth;
3513 if (nListWidth % nItemWidth) nColumnCount++;
3514 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
3515 nItem = ListView_GetTopIndex(infoPtr->hwndSelf);
3516 TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3517 nColumnCount, nCountPerColumn, nItem);
3519 for (i = 0; i < nColumnCount; i++)
3521 for (j = 0; j < nCountPerColumn; j++, nItem++)
3523 if (nItem >= infoPtr->nItemCount)
3526 if (cdmode & CDRF_NOTIFYITEMDRAW)
3527 cditemmode = notify_customdrawitem (infoPtr, hdc, nItem, 0, CDDS_ITEMPREPAINT);
3528 if (cditemmode & CDRF_SKIPDEFAULT)
3531 rcItem.top = j * nItemHeight;
3532 rcItem.left = i * nItemWidth;
3533 rcItem.bottom = rcItem.top + nItemHeight;
3534 rcItem.right = rcItem.left + nItemWidth;
3536 LISTVIEW_DrawItem(infoPtr, hdc, nItem, rcItem);
3538 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3539 notify_customdrawitem(infoPtr, hdc, nItem, 0, CDDS_ITEMPOSTPAINT);
3547 * Draws listview items when in icon or small icon display mode.
3550 * [I] infoPtr : valid pointer to the listview structure
3551 * [I] hdc : device context handle
3552 * [I] cdmode : custom draw mode
3557 static void LISTVIEW_RefreshIcon(LISTVIEW_INFO *infoPtr, HDC hdc, BOOL bSmall, DWORD cdmode)
3560 RECT rcItem, rcClip, rcTemp;
3562 DWORD cditemmode = CDRF_DODEFAULT;
3566 GetClipBox(hdc, &rcClip);
3568 /* Draw the visible non-selected items */
3569 for (i = 0; i < infoPtr->nItemCount; i++)
3571 if (LISTVIEW_GetItemState(infoPtr,i,LVIS_SELECTED))
3574 rcItem.left = LVIR_BOUNDS;
3575 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
3576 if (!IntersectRect(&rcTemp, &rcItem, &rcClip))
3579 if (cdmode & CDRF_NOTIFYITEMDRAW)
3580 cditemmode = notify_customdrawitem (infoPtr, hdc, i, 0, CDDS_ITEMPREPAINT);
3581 if (cditemmode & CDRF_SKIPDEFAULT)
3584 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3586 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3588 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3590 if (ptPosition.y < infoPtr->rcList.bottom)
3592 if (ptPosition.x < infoPtr->rcList.right)
3594 rcItem.top = ptPosition.y;
3595 rcItem.left = ptPosition.x;
3596 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3597 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3600 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem);
3602 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem);
3607 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3608 notify_customdrawitem(infoPtr, hdc, i, 0, CDDS_ITEMPOSTPAINT);
3611 /* Draw the visible selected items */
3612 for (i = 0; i < infoPtr->nItemCount; i++)
3614 if (!LISTVIEW_GetItemState(infoPtr,i,LVIS_SELECTED))
3617 rcItem.left = LVIR_BOUNDS;
3618 LISTVIEW_GetItemRect(infoPtr, i, &rcItem);
3619 if (!IntersectRect(&rcTemp, &rcItem, &rcClip))
3622 if (cdmode & CDRF_NOTIFYITEMDRAW)
3623 cditemmode = notify_customdrawitem (infoPtr, hdc, i, 0, CDDS_ITEMPREPAINT);
3624 if (cditemmode & CDRF_SKIPDEFAULT)
3627 LISTVIEW_GetItemPosition(infoPtr, i, &ptPosition);
3629 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3631 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3633 if (ptPosition.y < infoPtr->rcList.bottom)
3635 if (ptPosition.x < infoPtr->rcList.right)
3637 rcItem.top = ptPosition.y;
3638 rcItem.left = ptPosition.x;
3639 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3640 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3643 LISTVIEW_DrawItem(infoPtr, hdc, i, rcItem);
3645 LISTVIEW_DrawLargeItem(infoPtr, hdc, i, rcItem);
3650 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3651 notify_customdrawitem(infoPtr, hdc, i, 0, CDDS_ITEMPOSTPAINT);
3657 * Draws listview items.
3660 * [I] infoPtr : valid pointer to the listview structure
3661 * [I] HDC : device context handle
3666 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3668 UINT uView = LISTVIEW_GetType(infoPtr);
3673 LISTVIEW_DUMP(infoPtr);
3675 GetClientRect(infoPtr->hwndSelf, &rcClient);
3677 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, hdc, rcClient);
3678 if (cdmode == CDRF_SKIPDEFAULT) return;
3680 infoPtr->bIsDrawing = TRUE;
3682 /* nothing to draw */
3683 if(infoPtr->nItemCount == 0) goto enddraw;
3686 hOldFont = SelectObject(hdc, infoPtr->hFont);
3688 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
3689 LISTVIEW_RefreshOwnerDraw(infoPtr, hdc);
3690 else if (uView == LVS_LIST)
3691 LISTVIEW_RefreshList(infoPtr, hdc, cdmode);
3692 else if (uView == LVS_REPORT)
3693 LISTVIEW_RefreshReport(infoPtr, hdc, cdmode);
3695 LISTVIEW_RefreshIcon(infoPtr, hdc, uView == LVS_SMALLICON, cdmode);
3697 /* if we have a focus rect, draw it */
3698 if (infoPtr->bFocus && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED))
3699 DrawFocusRect(hdc, &infoPtr->rcFocus);
3701 /* unselect objects */
3702 SelectObject(hdc, hOldFont);
3705 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3706 notify_customdraw(infoPtr, CDDS_POSTPAINT, hdc, rcClient);
3708 infoPtr->bIsDrawing = FALSE;
3714 * Calculates the approximate width and height of a given number of items.
3717 * [I] infoPtr : valid pointer to the listview structure
3718 * [I] INT : number of items
3723 * Returns a DWORD. The width in the low word and the height in high word.
3725 static LRESULT LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
3726 WORD wWidth, WORD wHeight)
3728 UINT uView = LISTVIEW_GetType(infoPtr);
3729 INT nItemCountPerColumn = 1;
3730 INT nColumnCount = 0;
3731 DWORD dwViewRect = 0;
3733 if (nItemCount == -1)
3734 nItemCount = infoPtr->nItemCount;
3736 if (uView == LVS_LIST)
3738 if (wHeight == 0xFFFF)
3740 /* use current height */
3741 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3744 if (wHeight < infoPtr->nItemHeight)
3745 wHeight = infoPtr->nItemHeight;
3749 if (infoPtr->nItemHeight > 0)
3751 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3752 if (nItemCountPerColumn == 0)
3753 nItemCountPerColumn = 1;
3755 if (nItemCount % nItemCountPerColumn != 0)
3756 nColumnCount = nItemCount / nItemCountPerColumn;
3758 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3762 /* Microsoft padding magic */
3763 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3764 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3766 dwViewRect = MAKELONG(wWidth, wHeight);
3768 else if (uView == LVS_REPORT)
3769 FIXME("uView == LVS_REPORT: not implemented\n");
3770 else if (uView == LVS_SMALLICON)
3771 FIXME("uView == LVS_SMALLICON: not implemented\n");
3772 else if (uView == LVS_ICON)
3773 FIXME("uView == LVS_ICON: not implemented\n");
3780 * Arranges listview items in icon display mode.
3783 * [I] infoPtr : valid pointer to the listview structure
3784 * [I] INT : alignment code
3790 static LRESULT LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
3792 UINT uView = LISTVIEW_GetType(infoPtr);
3793 BOOL bResult = FALSE;
3795 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3800 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3803 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3806 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3808 case LVA_SNAPTOGRID:
3809 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3817 /* << LISTVIEW_CreateDragImage >> */
3822 * Removes all listview items and subitems.
3825 * [I] infoPtr : valid pointer to the listview structure
3831 static LRESULT LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
3833 LONG lStyle = infoPtr->dwStyle;
3834 UINT uView = lStyle & LVS_TYPEMASK;
3835 LISTVIEW_ITEM *lpItem;
3836 LISTVIEW_SUBITEM *lpSubItem;
3839 BOOL bResult = FALSE;
3844 LISTVIEW_RemoveAllSelections(infoPtr);
3845 infoPtr->nSelectionMark=-1;
3846 infoPtr->nFocusedItem=-1;
3847 SetRectEmpty(&infoPtr->rcFocus);
3848 /* But we are supposed to leave nHotItem as is! */
3850 if (lStyle & LVS_OWNERDATA)
3852 infoPtr->nItemCount = 0;
3853 LISTVIEW_InvalidateList(infoPtr);
3857 if (infoPtr->nItemCount > 0)
3861 /* send LVN_DELETEALLITEMS notification */
3862 /* verify if subsequent LVN_DELETEITEM notifications should be
3864 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3866 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
3868 for (i = 0; i < infoPtr->nItemCount; i++)
3870 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3871 if (hdpaSubItems != NULL)
3873 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3875 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3876 if (lpSubItem != NULL)
3878 /* free subitem string */
3879 if (is_textW(lpSubItem->hdr.pszText))
3880 COMCTL32_Free(lpSubItem->hdr.pszText);
3883 COMCTL32_Free(lpSubItem);
3887 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3892 /* send LVN_DELETEITEM notification */
3894 nmlv.lParam = lpItem->lParam;
3895 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
3898 /* free item string */
3899 if (is_textW(lpItem->hdr.pszText))
3900 COMCTL32_Free(lpItem->hdr.pszText);
3903 COMCTL32_Free(lpItem);
3906 DPA_Destroy(hdpaSubItems);
3910 /* reinitialize listview memory */
3911 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3912 infoPtr->nItemCount = 0;
3913 DPA_DeleteAllPtrs(infoPtr->hdpaPosX);
3914 DPA_DeleteAllPtrs(infoPtr->hdpaPosY);
3916 /* align items (set position of each item) */
3917 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3919 if (lStyle & LVS_ALIGNLEFT)
3921 LISTVIEW_AlignLeft(infoPtr);
3925 LISTVIEW_AlignTop(infoPtr);
3929 LISTVIEW_UpdateScroll(infoPtr);
3931 LISTVIEW_InvalidateList(infoPtr);
3939 * Removes a column from the listview control.
3942 * [I] infoPtr : valid pointer to the listview structure
3943 * [I] INT : column index
3949 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
3951 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3954 TRACE("nColumn=%d\n", nColumn);
3956 if (nColumn <= 0) return FALSE;
3958 if (uView == LVS_REPORT)
3960 if (!Header_GetItemRect(infoPtr->hwndHeader, nColumn, &rcCol))
3963 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
3967 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
3969 LISTVIEW_SUBITEM *lpSubItem, *lpDelItem;
3971 INT nItem, nSubItem, i;
3973 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
3975 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3976 if (!hdpaSubItems) continue;
3979 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3981 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3982 if (!lpSubItem) break;
3983 if (lpSubItem->iSubItem == nColumn)
3986 lpDelItem = lpSubItem;
3988 else if (lpSubItem->iSubItem > nColumn)
3990 lpSubItem->iSubItem--;
3994 /* if we found our subitem, zapp it */
3998 if (is_textW(lpDelItem->hdr.pszText))
3999 COMCTL32_Free(lpDelItem->hdr.pszText);
4002 COMCTL32_Free(lpDelItem);
4004 /* free dpa memory */
4005 DPA_DeletePtr(hdpaSubItems, nSubItem);
4010 /* we need to worry about display issues in report mode only */
4011 if (uView != LVS_REPORT) return TRUE;
4013 /* if we have a focus, must first erase the focus rect */
4014 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4016 /* Need to reset the item width when deleting a column */
4017 infoPtr->nItemWidth -= rcCol.right - rcCol.left;
4019 /* update scrollbar(s) */
4020 LISTVIEW_UpdateScroll(infoPtr);
4022 /* scroll to cover the deleted column, and invalidate for redraw */
4023 rcOld = infoPtr->rcList;
4024 rcOld.left = rcCol.left;
4025 ScrollWindowEx(infoPtr->hwndSelf, -(rcCol.right - rcCol.left), 0,
4026 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4028 /* we can restore focus now */
4029 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4036 * Removes an item from the listview control.
4039 * [I] infoPtr : valid pointer to the listview structure
4040 * [I] INT : item index
4046 static LRESULT LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4048 LONG lStyle = infoPtr->dwStyle;
4049 UINT uView = lStyle & LVS_TYPEMASK;
4050 LONG lCtrlId = GetWindowLongW(infoPtr->hwndSelf, GWL_ID);
4052 BOOL bResult = FALSE;
4054 LISTVIEW_ITEM *lpItem;
4055 LISTVIEW_SUBITEM *lpSubItem;
4059 TRACE("(nItem=%d)\n", nItem);
4062 /* First, send LVN_DELETEITEM notification. */
4063 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4064 nmlv.hdr.hwndFrom = infoPtr->hwndSelf;
4065 nmlv.hdr.idFrom = lCtrlId;
4066 nmlv.hdr.code = LVN_DELETEITEM;
4068 SendMessageW((infoPtr->hwndSelf), WM_NOTIFY, (WPARAM)lCtrlId,
4071 if (nItem == infoPtr->nFocusedItem)
4073 infoPtr->nFocusedItem = -1;
4074 SetRectEmpty(&infoPtr->rcFocus);
4077 /* remove it from the selection range */
4078 item.state = LVIS_SELECTED;
4079 item.stateMask = LVIS_SELECTED;
4080 LISTVIEW_SetItemState(infoPtr,nItem,&item);
4082 if (lStyle & LVS_OWNERDATA)
4084 infoPtr->nItemCount--;
4085 LISTVIEW_InvalidateList(infoPtr); /*FIXME: optimize */
4089 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
4091 /* initialize memory */
4092 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4094 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4095 if (hdpaSubItems != NULL)
4097 infoPtr->nItemCount--;
4098 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4100 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4101 if (lpSubItem != NULL)
4103 /* free item string */
4104 if (is_textW(lpSubItem->hdr.pszText))
4105 COMCTL32_Free(lpSubItem->hdr.pszText);
4108 COMCTL32_Free(lpSubItem);
4112 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4115 /* free item string */
4116 if (is_textW(lpItem->hdr.pszText))
4117 COMCTL32_Free(lpItem->hdr.pszText);
4120 COMCTL32_Free(lpItem);
4123 bResult = DPA_Destroy(hdpaSubItems);
4124 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4125 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4128 LISTVIEW_ShiftIndices(infoPtr,nItem,-1);
4130 /* align items (set position of each item) */
4131 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4133 if (lStyle & LVS_ALIGNLEFT)
4134 LISTVIEW_AlignLeft(infoPtr);
4136 LISTVIEW_AlignTop(infoPtr);
4139 LISTVIEW_UpdateScroll(infoPtr);
4141 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
4150 * Callback implementation for editlabel control
4153 * [I] infoPtr : valid pointer to the listview structure
4154 * [I] pszText : modified text
4155 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4161 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4163 NMLVDISPINFOW dispInfo;
4165 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4167 infoPtr->bEditing = FALSE;
4169 ZeroMemory(&dispInfo, sizeof(dispInfo));
4170 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4171 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4172 dispInfo.item.iSubItem = 0;
4173 dispInfo.item.stateMask = ~0;
4174 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4175 dispInfo.item.pszText = pszText;
4176 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4178 /* Do we need to update the Item Text */
4179 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4180 if (!pszText) return TRUE;
4182 ZeroMemory(&dispInfo, sizeof(dispInfo));
4183 dispInfo.item.mask = LVIF_TEXT;
4184 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4185 dispInfo.item.iSubItem = 0;
4186 dispInfo.item.pszText = pszText;
4187 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4188 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4193 * Begin in place editing of specified list view item
4196 * [I] infoPtr : valid pointer to the listview structure
4197 * [I] INT : item index
4198 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4204 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4206 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4207 NMLVDISPINFOW dispInfo;
4210 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4212 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4214 infoPtr->nEditLabelItem = nItem;
4216 /* Is the EditBox still there, if so remove it */
4217 if(infoPtr->hwndEdit != 0)
4219 SetFocus(infoPtr->hwndSelf);
4220 infoPtr->hwndEdit = 0;
4223 LISTVIEW_SetSelection(infoPtr, nItem);
4224 LISTVIEW_SetItemFocus(infoPtr, nItem);
4226 rect.left = LVIR_LABEL;
4227 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4229 ZeroMemory(&dispInfo, sizeof(dispInfo));
4230 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4231 dispInfo.item.iItem = nItem;
4232 dispInfo.item.iSubItem = 0;
4233 dispInfo.item.stateMask = ~0;
4234 dispInfo.item.pszText = szDispText;
4235 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4236 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4238 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4239 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4240 if (!infoPtr->hwndEdit) return 0;
4242 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4244 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4245 infoPtr->hwndEdit = 0;
4249 infoPtr->bEditing = TRUE;
4250 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4251 SetFocus(infoPtr->hwndEdit);
4252 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4253 return infoPtr->hwndEdit;
4259 * Ensures the specified item is visible, scrolling into view if necessary.
4262 * [I] infoPtr : valid pointer to the listview structure
4263 * [I] nItem : item index
4264 * [I] bPartial : partially or entirely visible
4270 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4272 UINT uView = LISTVIEW_GetType(infoPtr);
4273 INT nScrollPosHeight = 0;
4274 INT nScrollPosWidth = 0;
4275 INT nHorzAdjust = 0;
4276 INT nVertAdjust = 0;
4281 /* FIXME: ALWAYS bPartial == FALSE, FOR NOW! */
4283 rcItem.left = LVIR_BOUNDS;
4284 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4286 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4288 /* scroll left/right, but in LVS_REPORT mode */
4289 if (uView == LVS_LIST)
4290 nScrollPosWidth = infoPtr->nItemWidth;
4291 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4292 nScrollPosWidth = 1;
4294 if (rcItem.left < infoPtr->rcList.left)
4297 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4302 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4306 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4308 /* scroll up/down, but not in LVS_LIST mode */
4309 if (uView == LVS_REPORT)
4310 nScrollPosHeight = infoPtr->nItemHeight;
4311 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4312 nScrollPosHeight = 1;
4314 if (rcItem.top < infoPtr->rcList.top)
4317 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4322 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4326 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4328 if (nScrollPosWidth)
4330 INT diff = nHorzDiff / nScrollPosWidth;
4331 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4332 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4335 if (nScrollPosHeight)
4337 INT diff = nVertDiff / nScrollPosHeight;
4338 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4339 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4347 * Retrieves the nearest item, given a position and a direction.
4350 * [I] infoPtr : valid pointer to the listview structure
4351 * [I] POINT : start position
4352 * [I] UINT : direction
4355 * Item index if successdful, -1 otherwise.
4357 static INT LISTVIEW_GetNearestItem(LISTVIEW_INFO *infoPtr, POINT pt, UINT vkDirection)
4362 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4363 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4364 ((vkDirection == VK_UP) ? "VK_UP" :
4365 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4367 if (!LISTVIEW_GetViewRect(infoPtr, &rcView)) return -1;
4369 if (!LISTVIEW_GetOrigin(infoPtr, &ht.pt)) return -1;
4374 if (vkDirection == VK_DOWN) ht.pt.y += infoPtr->nItemHeight;
4375 else if (vkDirection == VK_UP) ht.pt.y -= infoPtr->nItemHeight;
4376 else if (vkDirection == VK_LEFT) ht.pt.x -= infoPtr->nItemWidth;
4377 else if (vkDirection == VK_RIGHT) ht.pt.x += infoPtr->nItemWidth;
4379 if (!PtInRect(&rcView, ht.pt)) return -1;
4381 return LISTVIEW_SuperHitTestItem(infoPtr, &ht, TRUE, TRUE);
4386 * Searches for an item with specific characteristics.
4389 * [I] hwnd : window handle
4390 * [I] nStart : base item index
4391 * [I] lpFindInfo : item information to look for
4394 * SUCCESS : index of item
4397 static LRESULT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4398 LPLVFINDINFOW lpFindInfo)
4401 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4405 INT nLast = infoPtr->nItemCount;
4407 if ((nItem >= -1) && (lpFindInfo != NULL))
4410 if (lpFindInfo->flags & LVFI_PARAM)
4412 lvItem.mask |= LVIF_PARAM;
4415 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4417 lvItem.mask |= LVIF_TEXT;
4418 lvItem.pszText = szDispText;
4419 lvItem.cchTextMax = DISP_TEXT_SIZE;
4422 if (lpFindInfo->flags & LVFI_WRAP)
4425 if (lpFindInfo->flags & LVFI_NEARESTXY)
4427 ptItem.x = lpFindInfo->pt.x;
4428 ptItem.y = lpFindInfo->pt.y;
4433 while (nItem < nLast)
4435 if (lpFindInfo->flags & LVFI_NEARESTXY)
4437 nItem = LISTVIEW_GetNearestItem(infoPtr, ptItem,
4438 lpFindInfo->vkDirection);
4441 /* get position of the new item index */
4442 if (!ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &ptItem))
4453 lvItem.iItem = nItem;
4454 lvItem.iSubItem = 0;
4455 if (LISTVIEW_GetItemW(infoPtr, &lvItem))
4457 if (lvItem.mask & LVIF_TEXT)
4459 if (lpFindInfo->flags & LVFI_PARTIAL)
4461 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4466 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4471 if (lvItem.mask & LVIF_PARAM)
4473 if (lpFindInfo->lParam != lvItem.lParam)
4499 * Searches for an item with specific characteristics.
4502 * [I] hwnd : window handle
4503 * [I] nStart : base item index
4504 * [I] lpFindInfo : item information to look for
4507 * SUCCESS : index of item
4510 static LRESULT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4511 LPLVFINDINFOA lpFindInfo)
4513 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4517 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4518 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4519 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4520 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4526 * Retrieves the background image of the listview control.
4529 * [I] infoPtr : valid pointer to the listview structure
4530 * [O] LPLVMKBIMAGE : background image attributes
4536 /* static LRESULT LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4538 /* FIXME (listview, "empty stub!\n"); */
4544 * Retrieves column attributes.
4547 * [I] infoPtr : valid pointer to the listview structure
4548 * [I] INT : column index
4549 * [IO] LPLVCOLUMNW : column information
4550 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4551 * otherwise it is in fact a LPLVCOLUMNA
4557 static LRESULT LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4560 BOOL bResult = FALSE;
4562 if (lpColumn != NULL)
4565 /* initialize memory */
4566 ZeroMemory(&hdi, sizeof(hdi));
4568 if (lpColumn->mask & LVCF_FMT)
4569 hdi.mask |= HDI_FORMAT;
4571 if (lpColumn->mask & LVCF_WIDTH)
4572 hdi.mask |= HDI_WIDTH;
4574 if (lpColumn->mask & LVCF_TEXT)
4576 hdi.mask |= HDI_TEXT;
4577 hdi.cchTextMax = lpColumn->cchTextMax;
4578 hdi.pszText = lpColumn->pszText;
4581 if (lpColumn->mask & LVCF_IMAGE)
4582 hdi.mask |= HDI_IMAGE;
4584 if (lpColumn->mask & LVCF_ORDER)
4585 hdi.mask |= HDI_ORDER;
4588 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4590 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4594 if (lpColumn->mask & LVCF_FMT)
4598 if (hdi.fmt & HDF_LEFT)
4599 lpColumn->fmt |= LVCFMT_LEFT;
4600 else if (hdi.fmt & HDF_RIGHT)
4601 lpColumn->fmt |= LVCFMT_RIGHT;
4602 else if (hdi.fmt & HDF_CENTER)
4603 lpColumn->fmt |= LVCFMT_CENTER;
4605 if (hdi.fmt & HDF_IMAGE)
4606 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4608 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4609 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4612 if (lpColumn->mask & LVCF_WIDTH)
4613 lpColumn->cx = hdi.cxy;
4615 if (lpColumn->mask & LVCF_IMAGE)
4616 lpColumn->iImage = hdi.iImage;
4618 if (lpColumn->mask & LVCF_ORDER)
4619 lpColumn->iOrder = hdi.iOrder;
4621 TRACE("(col=%d, lpColumn=%s, isW=%d)\n",
4622 nItem, debuglvcolumn_t(lpColumn, isW), isW);
4631 static LRESULT LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
4638 /* FIXME: little hack */
4639 for (i = 0; i < iCount; i++)
4647 * Retrieves the column width.
4650 * [I] infoPtr : valid pointer to the listview structure
4651 * [I] int : column index
4654 * SUCCESS : column width
4657 static LRESULT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
4659 INT nColumnWidth = 0;
4662 TRACE("nColumn=%d\n", nColumn);
4664 switch(LISTVIEW_GetType(infoPtr))
4667 nColumnWidth = infoPtr->nItemWidth;
4670 hdi.mask = HDI_WIDTH;
4671 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
4672 nColumnWidth = hdi.cxy;
4675 /* we don't have a 'column' in [SMALL]ICON mode */
4678 TRACE("nColumnWidth=%d\n", nColumnWidth);
4679 return nColumnWidth;
4684 * In list or report display mode, retrieves the number of items that can fit
4685 * vertically in the visible area. In icon or small icon display mode,
4686 * retrieves the total number of visible items.
4689 * [I] infoPtr : valid pointer to the listview structure
4692 * Number of fully visible items.
4694 static LRESULT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
4696 UINT uView = LISTVIEW_GetType(infoPtr);
4699 if (uView == LVS_LIST)
4701 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4703 nItemCount = LISTVIEW_GetCountPerRow(infoPtr) *
4704 LISTVIEW_GetCountPerColumn(infoPtr);
4707 else if (uView == LVS_REPORT)
4709 nItemCount = LISTVIEW_GetCountPerColumn(infoPtr);
4713 nItemCount = infoPtr->nItemCount;
4722 * Retrieves an image list handle.
4725 * [I] infoPtr : valid pointer to the listview structure
4726 * [I] nImageList : image list identifier
4729 * SUCCESS : image list handle
4732 static LRESULT LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
4734 HIMAGELIST himl = NULL;
4739 himl = infoPtr->himlNormal;
4742 himl = infoPtr->himlSmall;
4745 himl = infoPtr->himlState;
4749 return (LRESULT)himl;
4752 /* LISTVIEW_GetISearchString */
4755 * Helper function for LISTVIEW_GetItemT *only*. Tests if an item is selected.
4756 * It is important that no other functions call this because of callbacks.
4758 static inline BOOL is_item_selected(LISTVIEW_INFO *infoPtr, INT nItem)
4760 RANGE selection = { nItem, nItem };
4762 return DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
4763 LISTVIEW_CompareSelectionRanges, 0, DPAS_SORTED) != -1;
4768 * Retrieves item attributes.
4771 * [I] hwnd : window handle
4772 * [IO] lpLVItem : item info
4773 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4774 * if FALSE, the lpLVItem is a LPLVITEMA.
4777 * This is the internal 'GetItem' interface -- it tries to
4778 * be smart, and avoids text copies, if possible, by modifing
4779 * lpLVItem->pszText to point to the text string. Please note
4780 * that this is not always possible (e.g. OWNERDATA), so on
4781 * entry you *must* supply valid values for pszText, and cchTextMax.
4782 * The only difference to the documented interface is that upon
4783 * return, you should use *only* the lpLVItem->pszText, rather than
4784 * the buffer pointer you provided on input. Most code already does
4785 * that, so it's not a problem.
4786 * For the two cases when the text must be copied (that is,
4787 * for LVM_GETITEM, and LVMGETITEMTEXT), use LISTVIEW_GetItemExtT.
4793 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
4795 NMLVDISPINFOW dispInfo;
4796 LISTVIEW_ITEM *lpItem;
4800 /* In the following:
4801 * lpLVItem describes the information requested by the user
4802 * lpItem is what we have
4803 * dispInfo is a structure we use to request the missing
4804 * information from the application
4807 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4809 if (!lpLVItem || (lpLVItem->iItem < 0) ||
4810 (lpLVItem->iItem >= infoPtr->nItemCount))
4813 /* a quick optimization if all we're asked is the focus state
4814 * these queries are worth optimising since they are common,
4815 * and can be answered in constant time, without the heavy accesses */
4816 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIF_STATE) &&
4817 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
4819 lpLVItem->state = 0;
4820 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4821 lpLVItem->state |= LVIS_FOCUSED;
4825 ZeroMemory(&dispInfo, sizeof(dispInfo));
4827 /* if the app stores all the data, handle it separately */
4828 if (infoPtr->dwStyle & LVS_OWNERDATA)
4830 dispInfo.item.state = 0;
4832 /* if we need to callback, do it now */
4833 if ((lpLVItem->mask & ~LVIF_STATE) || infoPtr->uCallbackMask)
4835 /* NOTE: copy only fields which we _know_ are initialized, some apps
4836 * depend on the uninitialized fields being 0 */
4837 dispInfo.item.mask = lpLVItem->mask;
4838 dispInfo.item.iItem = lpLVItem->iItem;
4839 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4840 if (lpLVItem->mask & LVIF_TEXT)
4842 dispInfo.item.pszText = lpLVItem->pszText;
4843 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4845 if (lpLVItem->mask & LVIF_STATE)
4846 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
4847 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4848 dispInfo.item.stateMask = lpLVItem->stateMask;
4849 *lpLVItem = dispInfo.item;
4850 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
4853 /* we store only a little state, so if we're not asked, we're done */
4854 if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
4856 /* if focus is handled by us, report it */
4857 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4859 lpLVItem->state &= ~LVIS_FOCUSED;
4860 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4861 lpLVItem->state |= LVIS_FOCUSED;
4864 /* and do the same for selection, if we handle it */
4865 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4867 lpLVItem->state &= ~LVIS_SELECTED;
4868 if (is_item_selected(infoPtr, lpLVItem->iItem))
4869 lpLVItem->state |= LVIS_SELECTED;
4875 /* find the item and subitem structures before we proceed */
4876 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4877 if (hdpaSubItems == NULL) return FALSE;
4879 if ( !(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
4882 if (lpLVItem->iSubItem)
4884 LISTVIEW_SUBITEM *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4885 if(!lpSubItem) return FALSE;
4886 pItemHdr = &lpSubItem->hdr;
4889 pItemHdr = &lpItem->hdr;
4891 /* Do we need to query the state from the app? */
4892 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && lpLVItem->iSubItem == 0)
4894 dispInfo.item.mask |= LVIF_STATE;
4895 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4898 /* Do we need to enquire about the image? */
4899 if ((lpLVItem->mask & LVIF_IMAGE) && (pItemHdr->iImage==I_IMAGECALLBACK))
4900 dispInfo.item.mask |= LVIF_IMAGE;
4902 /* Do we need to enquire about the text? */
4903 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
4905 dispInfo.item.mask |= LVIF_TEXT;
4906 dispInfo.item.pszText = lpLVItem->pszText;
4907 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4908 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
4909 *dispInfo.item.pszText = '\0';
4912 /* If we don't have all the requested info, query the application */
4913 if (dispInfo.item.mask != 0)
4915 dispInfo.item.iItem = lpLVItem->iItem;
4916 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4917 dispInfo.item.lParam = lpItem->lParam;
4918 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
4919 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
4922 /* Now, handle the iImage field */
4923 if (dispInfo.item.mask & LVIF_IMAGE)
4925 lpLVItem->iImage = dispInfo.item.iImage;
4926 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (pItemHdr->iImage==I_IMAGECALLBACK))
4927 pItemHdr->iImage = dispInfo.item.iImage;
4929 else if (lpLVItem->mask & LVIF_IMAGE)
4930 lpLVItem->iImage = pItemHdr->iImage;
4932 /* The pszText field */
4933 if (dispInfo.item.mask & LVIF_TEXT)
4935 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
4936 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
4938 lpLVItem->pszText = dispInfo.item.pszText;
4940 else if (lpLVItem->mask & LVIF_TEXT)
4942 if (isW) lpLVItem->pszText = pItemHdr->pszText;
4943 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
4946 /* if this is a subitem, we're done*/
4947 if (lpLVItem->iSubItem) return TRUE;
4949 /* Next is the lParam field */
4950 if (dispInfo.item.mask & LVIF_PARAM)
4952 lpLVItem->lParam = dispInfo.item.lParam;
4953 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
4954 lpItem->lParam = dispInfo.item.lParam;
4956 else if (lpLVItem->mask & LVIF_PARAM)
4957 lpLVItem->lParam = lpItem->lParam;
4959 /* ... the state field (this one is different due to uCallbackmask) */
4960 if (lpLVItem->mask & LVIF_STATE)
4962 lpLVItem->state = lpItem->state;
4963 if (dispInfo.item.mask & LVIF_STATE)
4965 lpLVItem->state &= ~dispInfo.item.stateMask;
4966 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4968 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
4970 lpLVItem->state &= ~LVIS_FOCUSED;
4971 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4972 lpLVItem->state |= LVIS_FOCUSED;
4974 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
4976 lpLVItem->state &= ~LVIS_SELECTED;
4977 if (is_item_selected(infoPtr, lpLVItem->iItem))
4978 lpLVItem->state |= LVIS_SELECTED;
4982 /* and last, but not least, the indent field */
4983 if (lpLVItem->mask & LVIF_INDENT)
4984 lpLVItem->iIndent = lpItem->iIndent;
4991 * Retrieves item attributes.
4994 * [I] hwnd : window handle
4995 * [IO] lpLVItem : item info
4996 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4997 * if FALSE, the lpLVItem is a LPLVITEMA.
5000 * This is the external 'GetItem' interface -- it properly copies
5001 * the text in the provided buffer.
5007 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5012 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5015 pszText = lpLVItem->pszText;
5016 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5017 if (bResult && lpLVItem->pszText != pszText)
5018 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5019 lpLVItem->pszText = pszText;
5027 * Retrieves the position (upper-left) of the listview control item.
5028 * Note that for LVS_ICON style, the upper-left is that of the icon
5029 * and not the bounding box.
5032 * [I] infoPtr : valid pointer to the listview structure
5033 * [I] nItem : item index
5034 * [O] lpptPosition : coordinate information
5040 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5042 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5045 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5047 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5048 if (!LISTVIEW_GetItemListOrigin(infoPtr, nItem, lpptPosition)) return FALSE;
5049 if (!LISTVIEW_GetOrigin(infoPtr, &Origin)) return FALSE;
5051 if (uView == LVS_ICON)
5053 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5054 lpptPosition->y += ICON_TOP_PADDING;
5056 lpptPosition->x += Origin.x;
5057 lpptPosition->y += Origin.y;
5059 TRACE (" lpptPosition=%s\n", debugpoint(lpptPosition));
5066 * Retrieves the bounding rectangle for a listview control item.
5069 * [I] infoPtr : valid pointer to the listview structure
5070 * [I] nItem : item index
5071 * [IO] lprc : bounding rectangle coordinates
5072 * lprc->left specifies the portion of the item for which the bounding
5073 * rectangle will be retrieved.
5075 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5076 * including the icon and label.
5079 * * Experiment shows that native control returns:
5080 * * width = min (48, length of text line)
5081 * * .left = position.x - (width - iconsize.cx)/2
5082 * * .right = .left + width
5083 * * height = #lines of text * ntmHeight + icon height + 8
5084 * * .top = position.y - 2
5085 * * .bottom = .top + height
5086 * * separation between items .y = itemSpacing.cy - height
5087 * * .x = itemSpacing.cx - width
5088 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5091 * * Experiment shows that native control returns:
5092 * * width = iconSize.cx + 16
5093 * * .left = position.x - (width - iconsize.cx)/2
5094 * * .right = .left + width
5095 * * height = iconSize.cy + 4
5096 * * .top = position.y - 2
5097 * * .bottom = .top + height
5098 * * separation between items .y = itemSpacing.cy - height
5099 * * .x = itemSpacing.cx - width
5100 * LVIR_LABEL Returns the bounding rectangle of the item text.
5103 * * Experiment shows that native control returns:
5104 * * width = text length
5105 * * .left = position.x - width/2
5106 * * .right = .left + width
5107 * * height = ntmH * linecount + 2
5108 * * .top = position.y + iconSize.cy + 6
5109 * * .bottom = .top + height
5110 * * separation between items .y = itemSpacing.cy - height
5111 * * .x = itemSpacing.cx - width
5112 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5113 * rectangles, but excludes columns in report view.
5120 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5121 * upon whether the window has the focus currently and on whether the item
5122 * is the one with the focus. Ensure that the control's record of which
5123 * item has the focus agrees with the items' records.
5125 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5129 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5131 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5136 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, lprc, NULL)) return FALSE;
5140 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, NULL, NULL, lprc)) return FALSE;
5144 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, lprc, NULL, NULL)) return FALSE;
5147 case LVIR_SELECTBOUNDS:
5148 if (!LISTVIEW_GetItemMeasures(infoPtr, nItem, NULL, lprc, NULL, &label_rect)) return FALSE;
5149 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
5150 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) )
5151 lprc->right = label_rect.right;
5155 WARN("Unknown value: %d\n", lprc->left);
5159 TRACE(" rect=%s\n", debugrect(lprc));
5166 * Retrieves the spacing between listview control items.
5169 * [I] infoPtr : valid pointer to the listview structure
5170 * [IO] lprc : rectangle to receive the output
5171 * on input, lprc->top = nSubItem
5172 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5174 * NOTE: this call is succeeds only for REPORT style listviews.
5175 * Because we can calculate things much faster in report mode,
5176 * we're gonna do the calculations inline here, instead of
5177 * calling functions that do heavy lifting.
5183 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5186 INT nSubItem, flags;
5188 if (!lprc || LISTVIEW_GetType(infoPtr) != LVS_REPORT) return FALSE;
5190 nSubItem = lprc->top;
5193 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, nSubItem);
5195 if (!Header_GetItemRect(infoPtr->hwndHeader, nSubItem, lprc)) return FALSE;
5196 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &ptPosition)) return FALSE;
5197 lprc->top = ptPosition.y;
5198 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5203 FIXME("Unimplemented LVIR_ICON\n");
5207 /* nothing to do here, we're done */
5210 ERR("Unknown bounds=%d\n", lprc->left);
5219 * Retrieves the width of a label.
5222 * [I] infoPtr : valid pointer to the listview structure
5225 * SUCCESS : string width (in pixels)
5228 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5230 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5233 TRACE("(nItem=%d)\n", nItem);
5235 lvItem.mask = LVIF_TEXT;
5236 lvItem.iItem = nItem;
5237 lvItem.iSubItem = 0;
5238 lvItem.pszText = szDispText;
5239 lvItem.cchTextMax = DISP_TEXT_SIZE;
5240 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5242 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5247 * Retrieves the spacing between listview control items.
5250 * [I] infoPtr : valid pointer to the listview structure
5251 * [I] BOOL : flag for small or large icon
5254 * Horizontal + vertical spacing
5256 static LRESULT LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5262 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5266 if (LISTVIEW_GetType(infoPtr) == LVS_ICON)
5267 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5269 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5276 * Retrieves the state of a listview control item.
5279 * [I] infoPtr : valid pointer to the listview structure
5280 * [I] nItem : item index
5281 * [I] uMask : state mask
5284 * State specified by the mask.
5286 static LRESULT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5290 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5292 lvItem.iItem = nItem;
5293 lvItem.iSubItem = 0;
5294 lvItem.mask = LVIF_STATE;
5295 lvItem.stateMask = uMask;
5296 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5298 return lvItem.state & uMask;
5303 * Retrieves the text of a listview control item or subitem.
5306 * [I] hwnd : window handle
5307 * [I] nItem : item index
5308 * [IO] lpLVItem : item information
5309 * [I] isW : TRUE if lpLVItem is Unicode
5312 * SUCCESS : string length
5315 static LRESULT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5317 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5319 lpLVItem->mask = LVIF_TEXT;
5320 lpLVItem->iItem = nItem;
5321 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5323 return textlenT(lpLVItem->pszText, isW);
5328 * Searches for an item based on properties + relationships.
5331 * [I] infoPtr : valid pointer to the listview structure
5332 * [I] nItem : item index
5333 * [I] uFlags : relationship flag
5336 * This function is ver, very inefficient! Needs work.
5339 * SUCCESS : item index
5342 static LRESULT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5344 UINT uView = LISTVIEW_GetType(infoPtr);
5346 LVFINDINFOW lvFindInfo;
5347 INT nCountPerColumn;
5350 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5351 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5353 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5355 if (uFlags & LVNI_CUT)
5358 if (uFlags & LVNI_DROPHILITED)
5359 uMask |= LVIS_DROPHILITED;
5361 if (uFlags & LVNI_FOCUSED)
5362 uMask |= LVIS_FOCUSED;
5364 if (uFlags & LVNI_SELECTED)
5365 uMask |= LVIS_SELECTED;
5367 /* if we're asked for the focused item, that's only one,
5368 * so it's worth optimizing */
5369 if (uFlags & LVNI_FOCUSED)
5371 if (!(LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) == uMask) return -1;
5372 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5375 if (uFlags & LVNI_ABOVE)
5377 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5382 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5388 lvFindInfo.flags = LVFI_NEARESTXY;
5389 lvFindInfo.vkDirection = VK_UP;
5390 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5391 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5393 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5398 else if (uFlags & LVNI_BELOW)
5400 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5402 while (nItem < infoPtr->nItemCount)
5405 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5411 lvFindInfo.flags = LVFI_NEARESTXY;
5412 lvFindInfo.vkDirection = VK_DOWN;
5413 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5414 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5416 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5421 else if (uFlags & LVNI_TOLEFT)
5423 if (uView == LVS_LIST)
5425 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5426 while (nItem - nCountPerColumn >= 0)
5428 nItem -= nCountPerColumn;
5429 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5433 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5435 lvFindInfo.flags = LVFI_NEARESTXY;
5436 lvFindInfo.vkDirection = VK_LEFT;
5437 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5438 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5440 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5445 else if (uFlags & LVNI_TORIGHT)
5447 if (uView == LVS_LIST)
5449 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5450 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5452 nItem += nCountPerColumn;
5453 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5457 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5459 lvFindInfo.flags = LVFI_NEARESTXY;
5460 lvFindInfo.vkDirection = VK_RIGHT;
5461 ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt);
5462 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5464 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5473 /* search by index */
5474 for (i = nItem; i < infoPtr->nItemCount; i++)
5476 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5484 /* LISTVIEW_GetNumberOfWorkAreas */
5488 * Retrieves the origin coordinates when in icon or small icon display mode.
5491 * [I] infoPtr : valid pointer to the listview structure
5492 * [O] lpptOrigin : coordinate information
5498 static BOOL LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5500 DWORD lStyle = infoPtr->dwStyle;
5501 UINT uView = lStyle & LVS_TYPEMASK;
5502 INT nHorzPos = 0, nVertPos = 0;
5503 SCROLLINFO scrollInfo;
5505 if (!lpptOrigin) return FALSE;
5507 scrollInfo.cbSize = sizeof(SCROLLINFO);
5508 scrollInfo.fMask = SIF_POS;
5510 if ((lStyle & WS_HSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5511 nHorzPos = scrollInfo.nPos;
5512 if ((lStyle & WS_VSCROLL) && GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5513 nVertPos = scrollInfo.nPos;
5515 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5517 lpptOrigin->x = infoPtr->rcList.left;
5518 lpptOrigin->y = infoPtr->rcList.top;
5519 if (uView == LVS_LIST)
5520 nHorzPos *= infoPtr->nItemWidth;
5521 else if (uView == LVS_REPORT)
5522 nVertPos *= infoPtr->nItemHeight;
5524 lpptOrigin->x -= nHorzPos;
5525 lpptOrigin->y -= nVertPos;
5527 TRACE(" origin=%s\n", debugpoint(lpptOrigin));
5534 * Retrieves the width of a string.
5537 * [I] hwnd : window handle
5538 * [I] lpszText : text string to process
5539 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
5542 * SUCCESS : string width (in pixels)
5545 static LRESULT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
5550 if (is_textT(lpszText, isW))
5552 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5553 HDC hdc = GetDC(infoPtr->hwndSelf);
5554 HFONT hOldFont = SelectObject(hdc, hFont);
5557 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
5559 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
5560 SelectObject(hdc, hOldFont);
5561 ReleaseDC(infoPtr->hwndSelf, hdc);
5563 return stringSize.cx;
5569 * Determines item if a hit or closest if not
5572 * [I] infoPtr : valid pointer to the listview structure
5573 * [IO] lpht : hit test information
5574 * [I] subitem : fill out iSubItem.
5575 * [I] bNearItem : return the nearest item
5578 * SUCCESS : item index of hit
5581 static INT LISTVIEW_SuperHitTestItem(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL bNearItem)
5583 LONG lStyle = infoPtr->dwStyle;
5584 UINT uView = lStyle & LVS_TYPEMASK;
5585 INT i,j,topindex,bottomindex,nearestItem;
5586 RECT rcItem,rcSubItem;
5587 DWORD xterm, yterm, dist, mindist;
5589 TRACE("(lpht->pt=%s, subitem=%d\n", debugpoint(&lpht->pt), subitem);
5594 /* FIXME: get the visible range */
5595 topindex = LISTVIEW_GetTopIndex(infoPtr);
5596 if (uView == LVS_REPORT)
5598 bottomindex = topindex + LISTVIEW_GetCountPerColumn(infoPtr) + 1;
5599 bottomindex = min(bottomindex,infoPtr->nItemCount);
5603 bottomindex = infoPtr->nItemCount;
5606 for (i = topindex; i < bottomindex; i++)
5608 rcItem.left = LVIR_BOUNDS;
5609 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5611 if (PtInRect(&rcItem, lpht->pt))
5614 rcItem.left = LVIR_ICON;
5615 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5617 if (PtInRect(&rcItem, lpht->pt))
5619 lpht->flags = LVHT_ONITEMICON;
5625 rcItem.left = LVIR_LABEL;
5626 if (LISTVIEW_GetItemRect(infoPtr, i, &rcItem))
5628 if (PtInRect(&rcItem, lpht->pt))
5630 lpht->flags = LVHT_ONITEMLABEL;
5636 lpht->flags = LVHT_ONITEMSTATEICON;
5641 INT nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5643 rcSubItem.right = rcSubItem.left;
5644 for (j = 0; j < nColumnCount; j++)
5646 rcSubItem.left = rcSubItem.right;
5647 rcSubItem.right += LISTVIEW_GetColumnWidth(infoPtr, j);
5648 if (PtInRect(&rcSubItem, lpht->pt))
5655 TRACE("hit on item %d\n", i);
5661 * Now compute distance from point to center of boundary
5662 * box. Since we are only interested in the relative
5663 * distance, we can skip the nasty square root operation
5665 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpht->pt.x;
5666 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpht->pt.y;
5667 dist = xterm * xterm + yterm * yterm;
5668 if (mindist < 0 || dist < mindist)
5677 lpht->flags = LVHT_NOWHERE;
5679 return bNearItem ? nearestItem : -1;
5685 * Determines which listview item is located at the specified position.
5688 * [I] infoPtr : valid pointer to the listview structure
5689 * [IO] lpht : hit test information
5690 * [I] subitem : fill out iSubItem.
5693 * SUCCESS : item index
5696 static LRESULT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem)
5698 TRACE("(x=%ld, y=%ld)\n", lpht->pt.x, lpht->pt.y);
5702 if (subitem) lpht->iSubItem = 0;
5704 if (infoPtr->rcList.left > lpht->pt.x)
5705 lpht->flags |= LVHT_TOLEFT;
5706 else if (infoPtr->rcList.right < lpht->pt.x)
5707 lpht->flags |= LVHT_TORIGHT;
5709 if (infoPtr->rcList.top > lpht->pt.y)
5710 lpht->flags |= LVHT_ABOVE;
5711 else if (infoPtr->rcList.bottom < lpht->pt.y)
5712 lpht->flags |= LVHT_BELOW;
5714 if (lpht->flags) return -1;
5716 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
5717 * an app might pass only a structure with space up to iItem!
5718 * (MS Office 97 does that for instance in the file open dialog)
5720 return LISTVIEW_SuperHitTestItem(infoPtr, lpht, subitem, FALSE);
5726 * Inserts a new column.
5729 * [I] infoPtr : valid pointer to the listview structure
5730 * [I] INT : column index
5731 * [I] LPLVCOLUMNW : column information
5734 * SUCCESS : new column index
5737 static LRESULT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
5738 LPLVCOLUMNW lpColumn, BOOL isW)
5744 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
5746 if (!lpColumn) return -1;
5748 hdi.mask = hdi.fmt = 0;
5749 if (lpColumn->mask & LVCF_FMT)
5751 /* format member is valid */
5752 hdi.mask |= HDI_FORMAT;
5754 /* set text alignment (leftmost column must be left-aligned) */
5755 if (nColumn == 0 || lpColumn->fmt & LVCFMT_LEFT)
5756 hdi.fmt |= HDF_LEFT;
5757 else if (lpColumn->fmt & LVCFMT_RIGHT)
5758 hdi.fmt |= HDF_RIGHT;
5759 else if (lpColumn->fmt & LVCFMT_CENTER)
5760 hdi.fmt |= HDF_CENTER;
5762 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5763 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5765 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5767 hdi.fmt |= HDF_IMAGE;
5768 hdi.iImage = I_IMAGECALLBACK;
5771 if (lpColumn->fmt & LVCFMT_IMAGE)
5772 ; /* FIXME: enable images for *(sub)items* this column */
5775 if (lpColumn->mask & LVCF_WIDTH)
5777 hdi.mask |= HDI_WIDTH;
5778 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
5780 /* make it fill the remainder of the controls width */
5785 /* get the width of every item except the current one */
5786 hdit.mask = HDI_WIDTH;
5789 for(item_index = 0; item_index < (nColumn - 1); item_index++)
5790 if (Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit)))
5791 hdi.cxy += hdit.cxy;
5793 /* retrieve the layout of the header */
5794 GetClientRect(infoPtr->hwndSelf, &rcHeader);
5795 TRACE("start cxy=%d rcHeader=%s\n", hdi.cxy, debugrect(&rcHeader));
5797 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
5800 hdi.cxy = lpColumn->cx;
5803 if (lpColumn->mask & LVCF_TEXT)
5805 hdi.mask |= HDI_TEXT | HDI_FORMAT;
5806 hdi.fmt |= HDF_STRING;
5807 hdi.pszText = lpColumn->pszText;
5808 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
5811 if (lpColumn->mask & LVCF_IMAGE)
5813 hdi.mask |= HDI_IMAGE;
5814 hdi.iImage = lpColumn->iImage;
5817 if (lpColumn->mask & LVCF_ORDER)
5819 hdi.mask |= HDI_ORDER;
5820 hdi.iOrder = lpColumn->iOrder;
5823 /* insert item in header control */
5824 nNewColumn = SendMessageW(infoPtr->hwndHeader,
5825 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
5826 (WPARAM)nColumn, (LPARAM)&hdi);
5827 if (nNewColumn == -1) return -1;
5828 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &rcCol)) return -1;
5830 /* now we have to actually adjust the data */
5831 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
5833 LISTVIEW_SUBITEM *lpSubItem, *lpMainItem, **lpNewItems = 0;
5837 /* preallocate memory, so we can fail gracefully */
5838 if (nNewColumn == 0)
5840 lpNewItems = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM *) * infoPtr->nItemCount);
5841 if (!lpNewItems) return -1;
5842 for (i = 0; i < infoPtr->nItemCount; i++)
5843 if (!(lpNewItems[i] = COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)))) break;
5844 if (i != infoPtr->nItemCount)
5846 for(; i >=0; i--) COMCTL32_Free(lpNewItems[i]);
5847 COMCTL32_Free(lpNewItems);
5852 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5854 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5855 if (!hdpaSubItems) continue;
5856 for (i = 1; i < hdpaSubItems->nItemCount; i++)
5858 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
5859 if (!lpSubItem) break;
5860 if (lpSubItem->iSubItem >= nNewColumn)
5861 lpSubItem->iSubItem++;
5864 /* if we found our subitem, zapp it */
5865 if (nNewColumn == 0)
5867 lpMainItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, 0);
5868 lpSubItem = lpNewItems[nItem];
5869 lpSubItem->hdr = lpMainItem->hdr;
5870 lpSubItem->iSubItem = 1;
5871 ZeroMemory(&lpMainItem->hdr, sizeof(lpMainItem->hdr));
5872 lpMainItem->iSubItem = 0;
5873 DPA_InsertPtr(hdpaSubItems, 1, lpSubItem);
5877 COMCTL32_Free(lpNewItems);
5880 /* we don't have to worry abiut display issues in non-report mode */
5881 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return nNewColumn;
5883 /* if we have a focus, must first erase the focus rect */
5884 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
5886 /* Need to reset the item width when inserting a new column */
5887 infoPtr->nItemWidth += rcCol.right - rcCol.left;
5889 LISTVIEW_UpdateScroll(infoPtr);
5891 /* scroll to cover the deleted column, and invalidate for redraw */
5892 rcOld = infoPtr->rcList;
5893 rcOld.left = rcCol.left;
5894 ScrollWindowEx(infoPtr->hwndSelf, rcCol.right - rcCol.left, 0,
5895 &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5897 /* we can restore focus now */
5898 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
5903 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
5904 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
5905 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
5906 and not during the processing of a LVM_SORTITEMS message. Applications should provide
5907 their own sort proc. when sending LVM_SORTITEMS.
5910 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
5912 LVS_SORTXXX must be specified,
5913 LVS_OWNERDRAW is not set,
5914 <item>.pszText is not LPSTR_TEXTCALLBACK.
5916 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
5917 are sorted based on item text..."
5919 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
5921 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
5922 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
5923 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
5925 /* if we're sorting descending, negate the return value */
5926 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
5931 * Inserts a new item in the listview control.
5934 * [I] infoPtr : valid pointer to the listview structure
5935 * [I] lpLVItem : item information
5936 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
5939 * SUCCESS : new item index
5942 static LRESULT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5944 LONG lStyle = infoPtr->dwStyle;
5945 UINT uView = lStyle & LVS_TYPEMASK;
5949 LISTVIEW_ITEM *lpItem;
5952 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5954 if (lStyle & LVS_OWNERDATA)
5956 nItem = infoPtr->nItemCount;
5957 infoPtr->nItemCount++;
5961 /* make sure it's an item, and not a subitem; cannot insert a subitem */
5962 if (!lpLVItem || lpLVItem->iSubItem) return -1;
5964 if (!is_assignable_item(lpLVItem, lStyle)) return -1;
5966 if ( !(lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
5969 /* insert item in listview control data structure */
5970 if ( (hdpaSubItems = DPA_Create(8)) )
5971 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
5972 if (nItem == -1) goto fail;
5974 is_sorted = (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
5975 !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
5977 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
5978 is_sorted ? infoPtr->nItemCount + 1 : lpLVItem->iItem,
5980 if (nItem == -1) goto fail;
5981 infoPtr->nItemCount++;
5983 if (!LISTVIEW_SetItemT(infoPtr, lpLVItem, isW))
5986 /* if we're sorted, sort the list, and update the index */
5989 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
5990 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
5993 ERR("We can't find the item we just inserted, possible memory corruption.");
5994 /* we can't remove it from the list if we can't find it, so just fail */
5995 /* we don't deallocate memory here, as it will probably cause more problems */
6000 /* make room for the position, if we are in the right mode */
6001 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6003 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6005 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6007 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6012 /* Add the subitem list to the items array. Do this last in case we go to
6013 * fail during the above.
6015 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6017 lpItem->valid = TRUE;
6019 /* send LVN_INSERTITEM notification */
6020 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6022 nmlv.lParam = lpItem->lParam;
6023 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6025 /* align items (set position of each item) */
6026 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6028 if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(infoPtr);
6029 else LISTVIEW_AlignTop(infoPtr);
6032 LISTVIEW_UpdateScroll(infoPtr);
6034 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6036 TRACE(" <- %d\n", nItem);
6040 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6041 infoPtr->nItemCount--;
6043 DPA_DeletePtr(hdpaSubItems, 0);
6044 DPA_Destroy (hdpaSubItems);
6045 COMCTL32_Free (lpItem);
6051 * Redraws a range of items.
6054 * [I] infoPtr : valid pointer to the listview structure
6055 * [I] INT : first item
6056 * [I] INT : last item
6062 static LRESULT LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6066 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6067 max(nFirst, nLast) >= infoPtr->nItemCount)
6070 for (i = nFirst; i <= nLast; i++)
6071 LISTVIEW_InvalidateItem(infoPtr, i);
6078 * Scroll the content of a listview.
6081 * [I] infoPtr : valid pointer to the listview structure
6082 * [I] INT : horizontal scroll amount in pixels
6083 * [I] INT : vertical scroll amount in pixels
6090 * If the control is in report mode (LVS_REPORT) the control can
6091 * be scrolled only in line increments. "dy" will be rounded to the
6092 * nearest number of pixels that are a whole line. Ex: if line height
6093 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6094 * is passed the the scroll will be 0. (per MSDN 7/2002)
6096 * For: (per experimentaion with native control and CSpy ListView)
6097 * LVS_ICON dy=1 = 1 pixel (vertical only)
6099 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6101 * LVS_LIST dx=1 = 1 column (horizontal only)
6102 * but will only scroll 1 column per message
6103 * no matter what the value.
6104 * dy must be 0 or FALSE returned.
6105 * LVS_REPORT dx=1 = 1 pixel
6109 static LRESULT LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6111 switch(LISTVIEW_GetType(infoPtr)) {
6113 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6114 dy /= infoPtr->nItemHeight;
6117 if (dy != 0) return FALSE;
6124 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6125 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6132 * Sets the background color.
6135 * [I] infoPtr : valid pointer to the listview structure
6136 * [I] COLORREF : background color
6142 static LRESULT LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6144 TRACE("(clrBk=%lx)\n", clrBk);
6146 if(infoPtr->clrBk != clrBk) {
6147 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6148 infoPtr->clrBk = clrBk;
6149 if (clrBk == CLR_NONE)
6150 infoPtr->hBkBrush = GetClassLongW(infoPtr->hwndSelf, GCL_HBRBACKGROUND);
6152 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6153 LISTVIEW_InvalidateList(infoPtr);
6159 /* LISTVIEW_SetBkImage */
6163 * Sets the attributes of a header item.
6166 * [I] infoPtr : valid pointer to the listview structure
6167 * [I] INT : column index
6168 * [I] LPLVCOLUMNW : column attributes
6169 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6170 * otherwise it is in fact a LPLVCOLUMNA
6176 static LRESULT LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6177 LPLVCOLUMNW lpColumn, BOOL isW)
6179 BOOL bResult = FALSE;
6180 HDITEMW hdi, hdiget;
6182 if ((lpColumn != NULL) && (nColumn >= 0) &&
6183 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6185 /* initialize memory */
6186 ZeroMemory(&hdi, sizeof(hdi));
6188 if (lpColumn->mask & LVCF_FMT)
6190 /* format member is valid */
6191 hdi.mask |= HDI_FORMAT;
6193 /* get current format first */
6194 hdiget.mask = HDI_FORMAT;
6195 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6196 /* preserve HDF_STRING if present */
6197 hdi.fmt = hdiget.fmt & HDF_STRING;
6199 /* set text alignment (leftmost column must be left-aligned) */
6202 hdi.fmt |= HDF_LEFT;
6206 if (lpColumn->fmt & LVCFMT_LEFT)
6207 hdi.fmt |= HDF_LEFT;
6208 else if (lpColumn->fmt & LVCFMT_RIGHT)
6209 hdi.fmt |= HDF_RIGHT;
6210 else if (lpColumn->fmt & LVCFMT_CENTER)
6211 hdi.fmt |= HDF_CENTER;
6214 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6215 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6217 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6218 hdi.fmt |= HDF_IMAGE;
6220 if (lpColumn->fmt & LVCFMT_IMAGE)
6222 hdi.fmt |= HDF_IMAGE;
6223 hdi.iImage = I_IMAGECALLBACK;
6227 if (lpColumn->mask & LVCF_WIDTH)
6229 hdi.mask |= HDI_WIDTH;
6230 hdi.cxy = lpColumn->cx;
6233 if (lpColumn->mask & LVCF_TEXT)
6235 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6236 hdi.pszText = lpColumn->pszText;
6237 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6238 hdi.fmt |= HDF_STRING;
6241 if (lpColumn->mask & LVCF_IMAGE)
6243 hdi.mask |= HDI_IMAGE;
6244 hdi.iImage = lpColumn->iImage;
6247 if (lpColumn->mask & LVCF_ORDER)
6249 hdi.mask |= HDI_ORDER;
6250 hdi.iOrder = lpColumn->iOrder;
6253 /* set header item attributes */
6255 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6257 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6265 * Sets the column order array
6268 * [I] infoPtr : valid pointer to the listview structure
6269 * [I] INT : number of elements in column order array
6270 * [I] INT : pointer to column order array
6276 static LRESULT LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6278 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6289 * Sets the width of a column
6292 * [I] infoPtr : valid pointer to the listview structure
6293 * [I] INT : column index
6294 * [I] INT : column width
6300 static LRESULT LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT iCol, INT cx)
6304 LONG lStyle = infoPtr->dwStyle;
6305 UINT uView = lStyle & LVS_TYPEMASK;
6310 WCHAR text_buffer[DISP_TEXT_SIZE];
6311 INT header_item_count;
6316 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6318 if (!infoPtr->hwndHeader) /* make sure we have a header */
6321 /* set column width only if in report or list mode */
6322 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6325 TRACE("(iCol=%d, cx=%d\n", iCol, cx);
6327 /* take care of invalid cx values */
6328 if((uView == LVS_REPORT) && (cx < -2))
6329 cx = LVSCW_AUTOSIZE;
6330 else if (uView == LVS_LIST && (cx < 1))
6333 /* resize all columns if in LVS_LIST mode */
6334 if(uView == LVS_LIST) {
6335 infoPtr->nItemWidth = cx;
6336 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6340 /* autosize based on listview items width */
6341 if(cx == LVSCW_AUTOSIZE)
6343 /* set the width of the column to the width of the widest item */
6344 if (iCol == 0 || uView == LVS_LIST)
6347 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6349 nLabelWidth = LISTVIEW_GetLabelWidth(infoPtr, item_index);
6350 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6352 if (infoPtr->himlSmall)
6353 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
6357 lvItem.iSubItem = iCol;
6358 lvItem.mask = LVIF_TEXT;
6359 lvItem.pszText = szDispText;
6360 lvItem.cchTextMax = DISP_TEXT_SIZE;
6362 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6364 lvItem.iItem = item_index;
6365 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6366 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6367 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6370 cx += TRAILING_PADDING;
6371 } /* autosize based on listview header width */
6372 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6374 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6376 /* if iCol is the last column make it fill the remainder of the controls width */
6377 if(iCol == (header_item_count - 1)) {
6378 /* get the width of every item except the current one */
6379 hdi.mask = HDI_WIDTH;
6382 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6383 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6387 /* retrieve the layout of the header */
6388 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6390 cx = (rcHeader.right - rcHeader.left) - cx;
6394 /* Despite what the MS docs say, if this is not the last
6395 column, then MS resizes the column to the width of the
6396 largest text string in the column, including headers
6397 and items. This is different from LVSCW_AUTOSIZE in that
6398 LVSCW_AUTOSIZE ignores the header string length.
6401 /* retrieve header font */
6402 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6404 /* retrieve header text */
6405 hdi.mask = HDI_TEXT;
6406 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6407 hdi.pszText = text_buffer;
6409 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6411 /* determine the width of the text in the header */
6412 hdc = GetDC(infoPtr->hwndSelf);
6413 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6415 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6417 SelectObject(hdc, old_font); /* restore the old font */
6418 ReleaseDC(infoPtr->hwndSelf, hdc);
6420 lvItem.iSubItem = iCol;
6421 lvItem.mask = LVIF_TEXT;
6422 lvItem.pszText = szDispText;
6423 lvItem.cchTextMax = DISP_TEXT_SIZE;
6425 for(item_index = 0; item_index < infoPtr->nItemCount; item_index++)
6427 lvItem.iItem = item_index;
6428 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6429 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6430 nLabelWidth += TRAILING_PADDING;
6431 /* While it is possible for subitems to have icons, even MS messes
6432 up the positioning, so I suspect no applications actually use
6434 if (item_index == 0 && infoPtr->himlSmall)
6435 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
6436 cx = (nLabelWidth>cx)?nLabelWidth:cx;
6441 /* call header to update the column change */
6442 hdi.mask = HDI_WIDTH;
6445 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6447 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6454 * Sets the extended listview style.
6457 * [I] infoPtr : valid pointer to the listview structure
6462 * SUCCESS : previous style
6465 static LRESULT LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6467 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6471 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6473 infoPtr->dwLvExStyle = dwStyle;
6480 * Sets the new hot cursor used during hot tracking and hover selection.
6483 * [I] infoPtr : valid pointer to the listview structure
6484 * [I} hCurosr : the new hot cursor handle
6487 * Returns the previous hot cursor
6489 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6491 HCURSOR oldCursor = infoPtr->hHotCursor;
6492 infoPtr->hHotCursor = hCursor;
6499 * Sets the hot item index.
6502 * [I] infoPtr : valid pointer to the listview structure
6506 * SUCCESS : previous hot item index
6507 * FAILURE : -1 (no hot item)
6509 static LRESULT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6511 INT iOldIndex = infoPtr->nHotItem;
6512 infoPtr->nHotItem = iIndex;
6519 * Sets the amount of time the cursor must hover over an item before it is selected.
6522 * [I] infoPtr : valid pointer to the listview structure
6523 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6526 * Returns the previous hover time
6528 static LRESULT LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6530 DWORD oldHoverTime = infoPtr->dwHoverTime;
6531 infoPtr->dwHoverTime = dwHoverTime;
6532 return oldHoverTime;
6537 * Sets spacing for icons of LVS_ICON style.
6540 * [I] infoPtr : valid pointer to the listview structure
6541 * [I] DWORD : MAKELONG(cx, cy)
6544 * MAKELONG(oldcx, oldcy)
6546 static LRESULT LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, DWORD spacing)
6548 INT cy = HIWORD(spacing), cx = LOWORD(spacing);
6549 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6550 LONG lStyle = infoPtr->dwStyle;
6551 UINT uView = lStyle & LVS_TYPEMASK;
6553 TRACE("requested=(%d,%d)\n", cx, cy);
6555 /* this is supported only for LVS_ICON style */
6556 if (uView != LVS_ICON) return oldspacing;
6558 /* set to defaults, if instructed to */
6559 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6560 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6562 /* if 0 then compute width
6563 * FIXME: Should scan each item and determine max width of
6564 * icon or label, then make that the width */
6566 cx = infoPtr->iconSpacing.cx;
6568 /* if 0 then compute height */
6570 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6571 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6574 infoPtr->iconSpacing.cx = cx;
6575 infoPtr->iconSpacing.cy = cy;
6577 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6578 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6579 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6580 infoPtr->ntmHeight);
6582 /* these depend on the iconSpacing */
6583 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
6584 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6589 inline void update_icon_size(HIMAGELIST himl, SIZE *size)
6593 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6599 size->cx = size->cy = 0;
6607 * [I] infoPtr : valid pointer to the listview structure
6608 * [I] INT : image list type
6609 * [I] HIMAGELIST : image list handle
6612 * SUCCESS : old image list
6615 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6617 UINT uView = LISTVIEW_GetType(infoPtr);
6618 INT oldHeight = infoPtr->nItemHeight;
6619 HIMAGELIST himlOld = 0;
6624 himlOld = infoPtr->himlNormal;
6625 infoPtr->himlNormal = himl;
6626 if (uView == LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6627 LISTVIEW_SetIconSpacing(infoPtr, 0);
6631 himlOld = infoPtr->himlSmall;
6632 infoPtr->himlSmall = himl;
6633 if (uView != LVS_ICON) update_icon_size(himl, &infoPtr->iconSize);
6637 himlOld = infoPtr->himlState;
6638 infoPtr->himlState = himl;
6639 update_icon_size(himl, &infoPtr->iconStateSize);
6640 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6644 ERR("Unknown icon type=%d\n", nType);
6648 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
6649 if (infoPtr->nItemHeight != oldHeight)
6650 LISTVIEW_UpdateScroll(infoPtr);
6657 * Preallocates memory (does *not* set the actual count of items !)
6660 * [I] infoPtr : valid pointer to the listview structure
6661 * [I] INT : item count (projected number of items to allocate)
6662 * [I] DWORD : update flags
6668 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
6670 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
6672 if (infoPtr->dwStyle & LVS_OWNERDATA)
6674 int precount,topvisible;
6676 TRACE("LVS_OWNERDATA is set!\n");
6677 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
6678 FIXME("flags %s %s not implemented\n",
6679 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
6681 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
6683 LISTVIEW_RemoveAllSelections(infoPtr);
6685 precount = infoPtr->nItemCount;
6686 topvisible = LISTVIEW_GetTopIndex(infoPtr) +
6687 LISTVIEW_GetCountPerColumn(infoPtr) + 1;
6689 infoPtr->nItemCount = nItems;
6690 infoPtr->nItemWidth = max(LISTVIEW_CalculateMaxWidth(infoPtr),
6691 DEFAULT_COLUMN_WIDTH);
6693 LISTVIEW_UpdateSize(infoPtr);
6694 LISTVIEW_UpdateScroll(infoPtr);
6696 if (min(precount,infoPtr->nItemCount) < topvisible)
6697 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
6701 /* According to MSDN for non-LVS_OWNERDATA this is just
6702 * a performance issue. The control allocates its internal
6703 * data structures for the number of items specified. It
6704 * cuts down on the number of memory allocations. Therefore
6705 * we will just issue a WARN here
6707 WARN("for non-ownerdata performance option not implemented.\n");
6715 * Sets the position of an item.
6718 * [I] infoPtr : valid pointer to the listview structure
6719 * [I] nItem : item index
6720 * [I] pt : coordinate
6726 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
6728 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6731 TRACE("(nItem=%d, &pt=%s\n", nItem, debugpoint(&pt));
6733 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
6734 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
6736 /* This point value seems to be an undocumented feature.
6737 * The best guess is that it means either at the origin,
6738 * or at true beginning of the list. I will assume the origin. */
6739 if ((pt.x == -1) && (pt.y == -1))
6740 LISTVIEW_GetOrigin(infoPtr, &pt);
6741 else if (uView == LVS_ICON)
6743 pt.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
6744 pt.y -= ICON_TOP_PADDING;
6747 /* save the old position */
6748 old.x = (LONG)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
6749 old.y = (LONG)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
6751 /* Is the position changing? */
6752 if (pt.x == old.x && pt.y == old.y) return TRUE;
6754 /* FIXME: shouldn't we invalidate, as the item moved? */
6756 /* Allocating a POINTER for every item is too resource intensive,
6757 * so we'll keep the (x,y) in different arrays */
6758 if (DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)pt.x) &&
6759 DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)pt.y) )
6762 ERR("We should never fail here (nItem=%d, pt=%s), please report.\n",
6763 nItem, debugpoint(&pt));
6769 * Sets the state of one or many items.
6772 * [I] infoPtr : valid pointer to the listview structure
6773 * [I]INT : item index
6774 * [I] LPLVITEM : item or subitem info
6780 static LRESULT LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem)
6782 BOOL bResult = TRUE;
6785 lvItem.iItem = nItem;
6786 lvItem.iSubItem = 0;
6787 lvItem.mask = LVIF_STATE;
6788 lvItem.state = lpLVItem->state;
6789 lvItem.stateMask = lpLVItem->stateMask;
6790 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
6794 /* apply to all items */
6795 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6796 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
6799 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
6806 * Sets the text of an item or subitem.
6809 * [I] hwnd : window handle
6810 * [I] nItem : item index
6811 * [I] lpLVItem : item or subitem info
6812 * [I] isW : TRUE if input is Unicode
6818 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6822 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
6824 lvItem.iItem = nItem;
6825 lvItem.iSubItem = lpLVItem->iSubItem;
6826 lvItem.mask = LVIF_TEXT;
6827 lvItem.pszText = lpLVItem->pszText;
6828 lvItem.cchTextMax = lpLVItem->cchTextMax;
6830 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
6832 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
6837 * Set item index that marks the start of a multiple selection.
6840 * [I] infoPtr : valid pointer to the listview structure
6844 * Index number or -1 if there is no selection mark.
6846 static LRESULT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
6848 INT nOldIndex = infoPtr->nSelectionMark;
6850 TRACE("(nIndex=%d)\n", nIndex);
6852 infoPtr->nSelectionMark = nIndex;
6859 * Sets the text background color.
6862 * [I] infoPtr : valid pointer to the listview structure
6863 * [I] COLORREF : text background color
6869 static LRESULT LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
6871 TRACE("(clrTextBk=%lx)\n", clrTextBk);
6873 if (infoPtr->clrTextBk != clrTextBk)
6875 infoPtr->clrTextBk = clrTextBk;
6876 LISTVIEW_InvalidateList(infoPtr);
6884 * Sets the text foreground color.
6887 * [I] infoPtr : valid pointer to the listview structure
6888 * [I] COLORREF : text color
6894 static LRESULT LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
6896 TRACE("(clrText=%lx)\n", clrText);
6898 if (infoPtr->clrText != clrText)
6900 infoPtr->clrText = clrText;
6901 LISTVIEW_InvalidateList(infoPtr);
6907 /* LISTVIEW_SetToolTips */
6908 /* LISTVIEW_SetUnicodeFormat */
6909 /* LISTVIEW_SetWorkAreas */
6913 * Callback internally used by LISTVIEW_SortItems()
6916 * [I] LPVOID : first LISTVIEW_ITEM to compare
6917 * [I] LPVOID : second LISTVIEW_ITEM to compare
6918 * [I] LPARAM : HWND of control
6921 * if first comes before second : negative
6922 * if first comes after second : positive
6923 * if first and second are equivalent : zero
6925 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
6927 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
6928 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6929 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6931 /* Forward the call to the client defined callback */
6932 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
6937 * Sorts the listview items.
6940 * [I] infoPtr : valid pointer to the listview structure
6941 * [I] WPARAM : application-defined value
6942 * [I] LPARAM : pointer to comparision callback
6948 static LRESULT LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
6950 UINT lStyle = infoPtr->dwStyle;
6952 LISTVIEW_ITEM *lpItem;
6953 LPVOID selectionMarkItem;
6956 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
6958 if (lStyle & LVS_OWNERDATA) return FALSE;
6960 if (!infoPtr->hdpaItems) return FALSE;
6962 /* if there are 0 or 1 items, there is no need to sort */
6963 if (infoPtr->nItemCount < 2) return TRUE;
6965 if (infoPtr->nFocusedItem >= 0)
6967 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
6968 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6969 if (lpItem) lpItem->state |= LVIS_FOCUSED;
6972 infoPtr->pfnCompare = pfnCompare;
6973 infoPtr->lParamSort = lParamSort;
6974 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr->hwndSelf);
6976 /* Adjust selections and indices so that they are the way they should
6977 * be after the sort (otherwise, the list items move around, but
6978 * whatever is at the item's previous original position will be
6981 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
6982 for (i=0; i < infoPtr->nItemCount; i++)
6984 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
6985 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6987 if (lpItem->state & LVIS_SELECTED)
6988 LISTVIEW_AddSelectionRange(infoPtr, i, i);
6990 LISTVIEW_RemoveSelectionRange(infoPtr, i, i);
6991 if (lpItem->state & LVIS_FOCUSED)
6993 infoPtr->nFocusedItem = i;
6994 lpItem->state &= ~LVIS_FOCUSED;
6997 if (selectionMarkItem != NULL)
6998 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
6999 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7001 /* align the items */
7002 LISTVIEW_AlignTop(infoPtr);
7004 /* refresh the display */
7005 LISTVIEW_InvalidateList(infoPtr); /* FIXME: display should not change for [SMALL]ICON view */
7012 * Updates an items or rearranges the listview control.
7015 * [I] infoPtr : valid pointer to the listview structure
7016 * [I] INT : item index
7022 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7024 LONG lStyle = infoPtr->dwStyle;
7025 UINT uView = lStyle & LVS_TYPEMASK;
7027 TRACE("(nItem=%d)\n", nItem);
7029 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7031 /* rearrange with default alignment style */
7032 if ((lStyle & LVS_AUTOARRANGE) && ((uView == LVS_ICON) ||(uView == LVS_SMALLICON)))
7033 LISTVIEW_Arrange(infoPtr, 0);
7035 LISTVIEW_InvalidateItem(infoPtr, nItem);
7043 * Creates the listview control.
7046 * [I] hwnd : window handle
7047 * [I] lpcs : the create parameters
7053 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7055 LISTVIEW_INFO *infoPtr;
7056 UINT uView = lpcs->style & LVS_TYPEMASK;
7059 TRACE("(lpcs=%p)\n", lpcs);
7061 /* initialize info pointer */
7062 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7063 if (!infoPtr) return -1;
7065 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
7067 infoPtr->hwndSelf = hwnd;
7068 infoPtr->dwStyle = lpcs->style;
7069 /* determine the type of structures to use */
7070 infoPtr->notifyFormat = SendMessageW(GetParent(infoPtr->hwndSelf), WM_NOTIFYFORMAT,
7071 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7073 /* initialize color information */
7074 infoPtr->clrBk = CLR_NONE;
7075 infoPtr->clrText = comctl32_color.clrWindowText;
7076 infoPtr->clrTextBk = CLR_DEFAULT;
7077 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7079 /* set default values */
7080 infoPtr->nFocusedItem = -1;
7081 infoPtr->nSelectionMark = -1;
7082 infoPtr->nHotItem = -1;
7083 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7084 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7085 infoPtr->nEditLabelItem = -1;
7087 /* get default font (icon title) */
7088 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7089 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7090 infoPtr->hFont = infoPtr->hDefaultFont;
7091 LISTVIEW_SaveTextMetrics(infoPtr);
7094 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7095 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7096 0, 0, 0, 0, hwnd, (HMENU)0,
7097 lpcs->hInstance, NULL);
7099 /* set header unicode format */
7100 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
7102 /* set header font */
7103 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7106 if (uView == LVS_ICON)
7108 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7109 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7111 else if (uView == LVS_REPORT)
7113 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7115 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7119 /* set HDS_HIDDEN flag to hide the header bar */
7120 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7121 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7125 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7126 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7130 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7131 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7134 infoPtr->iconStateSize.cx = GetSystemMetrics(SM_CXSMICON);
7135 infoPtr->iconStateSize.cy = GetSystemMetrics(SM_CYSMICON);
7137 /* display unsupported listview window styles */
7138 LISTVIEW_UnsupportedStyles(lpcs->style);
7140 /* allocate memory for the data structure */
7141 infoPtr->hdpaItems = DPA_Create(10);
7142 infoPtr->hdpaPosX = DPA_Create(10);
7143 infoPtr->hdpaPosY = DPA_Create(10);
7145 /* allocate memory for the selection ranges */
7146 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7148 /* initialize size of items */
7149 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7150 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
7152 /* initialize the hover time to -1(indicating the default system hover time) */
7153 infoPtr->dwHoverTime = -1;
7160 * Erases the background of the listview control.
7163 * [I] infoPtr : valid pointer to the listview structure
7164 * [I] hdc : device context handle
7170 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7174 TRACE("(hdc=%x)\n", hdc);
7176 if (!GetClipBox(hdc, &rc)) return FALSE;
7178 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7184 * Helper function for LISTVIEW_[HV]Scroll *only*.
7185 * Performs vertical/horizontal scrolling by a give amount.
7188 * [I] infoPtr : valid pointer to the listview structure
7189 * [I] dx : amount of horizontal scroll
7190 * [I] dy : amount of vertical scroll
7192 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7194 /* now we can scroll the list */
7195 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7196 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7197 /* if we have focus, adjust rect */
7198 OffsetRect(&infoPtr->rcFocus, dx, dy);
7199 UpdateWindow(infoPtr->hwndSelf);
7204 * Performs vertical scrolling.
7207 * [I] infoPtr : valid pointer to the listview structure
7208 * [I] nScrollCode : scroll code
7209 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7210 * [I] hScrollWnd : scrollbar control window handle
7216 * SB_LINEUP/SB_LINEDOWN:
7217 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7218 * for LVS_REPORT is 1 line
7219 * for LVS_LIST cannot occur
7222 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7223 INT nScrollDiff, HWND hScrollWnd)
7225 UINT uView = LISTVIEW_GetType(infoPtr);
7226 INT nOldScrollPos, nNewScrollPos;
7227 SCROLLINFO scrollInfo;
7230 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7232 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7234 scrollInfo.cbSize = sizeof(SCROLLINFO);
7235 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7237 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7239 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7241 nOldScrollPos = scrollInfo.nPos;
7242 switch (nScrollCode)
7248 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7252 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7256 nScrollDiff = -scrollInfo.nPage;
7260 nScrollDiff = scrollInfo.nPage;
7263 case SB_THUMBPOSITION:
7265 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7272 /* quit right away if pos isn't changing */
7273 if (nScrollDiff == 0) return 0;
7275 /* calculate new position, and handle overflows */
7276 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7277 if (nScrollDiff > 0) {
7278 if (nNewScrollPos < nOldScrollPos ||
7279 nNewScrollPos > scrollInfo.nMax)
7280 nNewScrollPos = scrollInfo.nMax;
7282 if (nNewScrollPos > nOldScrollPos ||
7283 nNewScrollPos < scrollInfo.nMin)
7284 nNewScrollPos = scrollInfo.nMin;
7287 /* set the new position, and reread in case it changed */
7288 scrollInfo.fMask = SIF_POS;
7289 scrollInfo.nPos = nNewScrollPos;
7290 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7292 /* carry on only if it really changed */
7293 if (nNewScrollPos == nOldScrollPos) return 0;
7295 /* now adjust to client coordinates */
7296 nScrollDiff = nOldScrollPos - nNewScrollPos;
7297 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7299 /* and scroll the window */
7300 scroll_list(infoPtr, 0, nScrollDiff);
7307 * Performs horizontal scrolling.
7310 * [I] infoPtr : valid pointer to the listview structure
7311 * [I] nScrollCode : scroll code
7312 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7313 * [I] hScrollWnd : scrollbar control window handle
7319 * SB_LINELEFT/SB_LINERIGHT:
7320 * for LVS_ICON, LVS_SMALLICON 1 pixel
7321 * for LVS_REPORT is 1 pixel
7322 * for LVS_LIST is 1 column --> which is a 1 because the
7323 * scroll is based on columns not pixels
7326 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7327 INT nScrollDiff, HWND hScrollWnd)
7329 UINT uView = LISTVIEW_GetType(infoPtr);
7330 INT nOldScrollPos, nNewScrollPos;
7331 SCROLLINFO scrollInfo;
7333 TRACE("(nScrollCode=%d, nScrollDiff=%d)\n", nScrollCode, nScrollDiff);
7335 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7337 scrollInfo.cbSize = sizeof(SCROLLINFO);
7338 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7340 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7342 nOldScrollPos = scrollInfo.nPos;
7344 switch (nScrollCode)
7358 nScrollDiff = -scrollInfo.nPage;
7362 nScrollDiff = scrollInfo.nPage;
7365 case SB_THUMBPOSITION:
7367 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7374 /* quit right away if pos isn't changing */
7375 if (nScrollDiff == 0) return 0;
7377 /* calculate new position, and handle overflows */
7378 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7379 if (nScrollDiff > 0) {
7380 if (nNewScrollPos < nOldScrollPos ||
7381 nNewScrollPos > scrollInfo.nMax)
7382 nNewScrollPos = scrollInfo.nMax;
7384 if (nNewScrollPos > nOldScrollPos ||
7385 nNewScrollPos < scrollInfo.nMin)
7386 nNewScrollPos = scrollInfo.nMin;
7389 /* set the new position, and reread in case it changed */
7390 scrollInfo.fMask = SIF_POS;
7391 scrollInfo.nPos = nNewScrollPos;
7392 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7394 /* carry on only if it really changed */
7395 if (nNewScrollPos == nOldScrollPos) return 0;
7397 if(uView == LVS_REPORT)
7398 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7400 /* now adjust to client coordinates */
7401 nScrollDiff = nOldScrollPos - nNewScrollPos;
7402 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7404 /* and scroll the window */
7405 scroll_list(infoPtr, nScrollDiff, 0);
7410 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7412 UINT uView = LISTVIEW_GetType(infoPtr);
7413 INT gcWheelDelta = 0;
7414 UINT pulScrollLines = 3;
7415 SCROLLINFO scrollInfo;
7417 TRACE("(wheelDelta=%d)\n", wheelDelta);
7419 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7420 gcWheelDelta -= wheelDelta;
7422 scrollInfo.cbSize = sizeof(SCROLLINFO);
7423 scrollInfo.fMask = SIF_POS;
7430 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7431 * should be fixed in the future.
7433 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7434 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION,
7435 scrollInfo.nPos + (gcWheelDelta < 0) ?
7436 LISTVIEW_SCROLL_ICON_LINE_SIZE :
7437 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7441 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7443 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7445 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7446 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7447 LISTVIEW_VScroll(infoPtr, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7453 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7464 * [I] infoPtr : valid pointer to the listview structure
7465 * [I] INT : virtual key
7466 * [I] LONG : key data
7471 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
7473 UINT uView = LISTVIEW_GetType(infoPtr);
7475 NMLVKEYDOWN nmKeyDown;
7477 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
7479 /* send LVN_KEYDOWN notification */
7480 nmKeyDown.wVKey = nVirtualKey;
7481 nmKeyDown.flags = 0;
7482 notify(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
7484 switch (nVirtualKey)
7487 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
7489 notify_return(infoPtr);
7490 notify_itemactivate(infoPtr);
7495 if (infoPtr->nItemCount > 0)
7500 if (infoPtr->nItemCount > 0)
7501 nItem = infoPtr->nItemCount - 1;
7505 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
7509 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
7513 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
7517 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
7521 if (uView == LVS_REPORT)
7522 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr);
7524 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
7525 * LISTVIEW_GetCountPerRow(infoPtr);
7526 if(nItem < 0) nItem = 0;
7530 if (uView == LVS_REPORT)
7531 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr);
7533 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
7534 * LISTVIEW_GetCountPerRow(infoPtr);
7535 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
7539 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7540 LISTVIEW_KeySelection(infoPtr, nItem);
7550 * [I] infoPtr : valid pointer to the listview structure
7555 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
7559 /* if we did not have the focus, there's nothing to do */
7560 if (!infoPtr->bFocus) return 0;
7562 /* send NM_KILLFOCUS notification */
7563 notify_killfocus(infoPtr);
7565 /* if we have a focus rectagle, get rid of it */
7566 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
7568 /* set window focus flag */
7569 infoPtr->bFocus = FALSE;
7571 /* invalidate the selected items before reseting focus flag */
7572 LISTVIEW_InvalidateSelectedItems(infoPtr);
7579 * Processes double click messages (left mouse button).
7582 * [I] infoPtr : valid pointer to the listview structure
7583 * [I] wKey : key flag
7584 * [I] pts : mouse coordinate
7589 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7591 LVHITTESTINFO htInfo;
7594 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7596 htInfo.pt.x = pts.x;
7597 htInfo.pt.y = pts.y;
7599 /* send NM_DBLCLK notification */
7600 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7601 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE);
7602 nmlv.iItem = htInfo.iItem;
7603 nmlv.iSubItem = htInfo.iSubItem;
7604 nmlv.ptAction = htInfo.pt;
7605 notify_listview(infoPtr, NM_DBLCLK, &nmlv);
7607 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7608 if(nmlv.iItem != -1)
7609 notify_itemactivate(infoPtr);
7616 * Processes mouse down messages (left mouse button).
7619 * [I] infoPtr : valid pointer to the listview structure
7620 * [I] wKey : key flag
7621 * [I] pts : mouse coordinate
7626 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7628 LONG lStyle = infoPtr->dwStyle;
7629 static BOOL bGroupSelect = TRUE;
7630 POINT pt = { pts.x, pts.y };
7633 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7635 /* FIXME: NM_CLICK */
7637 /* send NM_RELEASEDCAPTURE notification */
7638 notify_releasedcapture(infoPtr);
7640 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7642 /* set left button down flag */
7643 infoPtr->bLButtonDown = TRUE;
7645 nItem = LISTVIEW_GetItemAtPt(infoPtr, pt);
7646 TRACE("at %s, nItem=%d\n", debugpoint(&pt), nItem);
7647 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7649 if (lStyle & LVS_SINGLESEL)
7651 if ((LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7652 && infoPtr->nEditLabelItem == -1)
7653 infoPtr->nEditLabelItem = nItem;
7655 LISTVIEW_SetSelection(infoPtr, nItem);
7659 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7662 LISTVIEW_AddGroupSelection(infoPtr, nItem);
7667 item.state = LVIS_SELECTED | LVIS_FOCUSED;
7668 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7670 LISTVIEW_SetItemState(infoPtr,nItem,&item);
7671 infoPtr->nSelectionMark = nItem;
7674 else if (wKey & MK_CONTROL)
7678 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
7680 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
7681 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
7682 LISTVIEW_SetItemState(infoPtr, nItem, &item);
7683 infoPtr->nSelectionMark = nItem;
7685 else if (wKey & MK_SHIFT)
7687 LISTVIEW_SetGroupSelection(infoPtr, nItem);
7691 BOOL was_selected = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
7693 /* set selection (clears other pre-existing selections) */
7694 LISTVIEW_SetSelection(infoPtr, nItem);
7696 if (was_selected && infoPtr->nEditLabelItem == -1)
7697 infoPtr->nEditLabelItem = nItem;
7703 /* remove all selections */
7704 LISTVIEW_RemoveAllSelections(infoPtr);
7712 * Processes mouse up messages (left mouse button).
7715 * [I] infoPtr : valid pointer to the listview structure
7716 * [I] wKey : key flag
7717 * [I] pts : mouse coordinate
7722 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7724 LVHITTESTINFO lvHitTestInfo;
7727 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, pts.x, pts.y);
7729 if (!infoPtr->bLButtonDown) return 0;
7731 lvHitTestInfo.pt.x = pts.x;
7732 lvHitTestInfo.pt.y = pts.y;
7734 /* send NM_CLICK notification */
7735 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7736 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE);
7737 nmlv.iItem = lvHitTestInfo.iItem;
7738 nmlv.iSubItem = lvHitTestInfo.iSubItem;
7739 nmlv.ptAction = lvHitTestInfo.pt;
7740 notify_listview(infoPtr, NM_CLICK, &nmlv);
7742 /* set left button flag */
7743 infoPtr->bLButtonDown = FALSE;
7745 if(infoPtr->nEditLabelItem != -1)
7747 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem &&
7748 (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
7749 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
7750 infoPtr->nEditLabelItem = -1;
7758 * Destroys the listview control (called after WM_DESTROY).
7761 * [I] infoPtr : valid pointer to the listview structure
7766 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
7768 LONG lStyle = infoPtr->dwStyle;
7772 /* delete all items */
7773 LISTVIEW_DeleteAllItems(infoPtr);
7775 /* destroy data structure */
7776 DPA_Destroy(infoPtr->hdpaItems);
7777 DPA_Destroy(infoPtr->hdpaSelectionRanges);
7779 /* destroy image lists */
7780 if (!(lStyle & LVS_SHAREIMAGELISTS))
7782 /* FIXME: If the caller does a ImageList_Destroy and then we
7783 * do this code the area will be freed twice. Currently
7784 * this generates an "err:heap:HEAP_ValidateInUseArena
7785 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
7786 * has PREV_FREE flag" sometimes.
7788 * We will leak the memory till we figure out how to fix
7790 if (infoPtr->himlNormal)
7791 ImageList_Destroy(infoPtr->himlNormal);
7792 if (infoPtr->himlSmall)
7793 ImageList_Destroy(infoPtr->himlSmall);
7794 if (infoPtr->himlState)
7795 ImageList_Destroy(infoPtr->himlState);
7798 /* destroy font, bkgnd brush */
7800 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
7801 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7803 /* free listview info pointer*/
7804 COMCTL32_Free(infoPtr);
7806 SetWindowLongW(infoPtr->hwndSelf, 0, 0);
7812 * Handles notifications from children.
7815 * [I] infoPtr : valid pointer to the listview structure
7816 * [I] INT : control identifier
7817 * [I] LPNMHDR : notification information
7822 static LRESULT LISTVIEW_Notify(LISTVIEW_INFO *infoPtr, INT nCtrlId, LPNMHDR lpnmh)
7824 TRACE("(nCtrlId=%d, lpnmh=%p)\n", nCtrlId, lpnmh);
7826 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
7828 /* handle notification from header control */
7829 if (lpnmh->code == HDN_ENDTRACKW)
7831 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7832 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7834 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
7836 /* Handle sorting by Header Column */
7839 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7841 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
7842 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
7844 else if(lpnmh->code == NM_RELEASEDCAPTURE)
7846 /* Idealy this should be done in HDN_ENDTRACKA
7847 * but since SetItemBounds in Header.c is called after
7848 * the notification is sent, it is neccessary to handle the
7849 * update of the scroll bar here (Header.c works fine as it is,
7850 * no need to disturb it)
7852 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
7853 LISTVIEW_UpdateScroll(infoPtr);
7854 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
7864 * Determines the type of structure to use.
7867 * [I] infoPtr : valid pointer to the listview structureof the sender
7868 * [I] HWND : listview window handle
7869 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
7874 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
7876 TRACE("(hwndFrom=%x, nCommand=%d)\n", hwndFrom, nCommand);
7878 if (nCommand == NF_REQUERY)
7879 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
7880 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7886 * Paints/Repaints the listview control.
7889 * [I] infoPtr : valid pointer to the listview structure
7890 * [I] HDC : device context handle
7895 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
7897 TRACE("(hdc=%x)\n", hdc);
7900 LISTVIEW_Refresh(infoPtr, hdc);
7905 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
7907 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
7908 LISTVIEW_Refresh(infoPtr, hdc);
7909 EndPaint(infoPtr->hwndSelf, &ps);
7917 * Processes double click messages (right mouse button).
7920 * [I] infoPtr : valid pointer to the listview structure
7921 * [I] wKey : key flag
7922 * [I] pts : mouse coordinate
7927 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7929 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7931 /* send NM_RELEASEDCAPTURE notification */
7932 notify_releasedcapture(infoPtr);
7934 /* send NM_RDBLCLK notification */
7935 notify_rdblclk(infoPtr);
7942 * Processes mouse down messages (right mouse button).
7945 * [I] infoPtr : valid pointer to the listview structure
7946 * [I] wKey : key flag
7947 * [I] pts : mouse coordinate
7952 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
7954 LVHITTESTINFO lvHitTestInfo;
7958 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
7960 /* FIXME: NM_CLICK */
7962 /* send NM_RELEASEDCAPTURE notification */
7963 notify_releasedcapture(infoPtr);
7965 /* make sure the listview control window has the focus */
7966 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
7968 /* set right button down flag */
7969 infoPtr->bRButtonDown = TRUE;
7971 /* determine the index of the selected item */
7972 lvHitTestInfo.pt.x = pts.x;
7973 lvHitTestInfo.pt.y = pts.y;
7974 nItem = LISTVIEW_GetItemAtPt(infoPtr, lvHitTestInfo.pt);
7976 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
7978 LISTVIEW_SetItemFocus(infoPtr,nItem);
7979 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
7980 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
7981 LISTVIEW_SetSelection(infoPtr, nItem);
7985 LISTVIEW_RemoveAllSelections(infoPtr);
7989 /* Send NM_RClICK notification */
7990 ZeroMemory(&nmlv, sizeof(nmlv));
7991 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE);
7992 nmlv.iItem = lvHitTestInfo.iItem;
7993 nmlv.iSubItem = lvHitTestInfo.iSubItem;
7994 nmlv.ptAction = lvHitTestInfo.pt;
7995 notify_listview(infoPtr, NM_RCLICK, &nmlv);
8002 * Processes mouse up messages (right mouse button).
8005 * [I] infoPtr : valid pointer to the listview structure
8006 * [I] wKey : key flag
8007 * [I] pts : mouse coordinate
8012 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, POINTS pts)
8014 POINT pt = { pts.x, pts.y };
8016 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, pts.x, pts.y);
8018 if (!infoPtr->bRButtonDown) return 0;
8020 /* set button flag */
8021 infoPtr->bRButtonDown = FALSE;
8023 /* Change to screen coordinate for WM_CONTEXTMENU */
8024 ClientToScreen(infoPtr->hwndSelf, &pt);
8026 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8027 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8028 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8039 * [I] infoPtr : valid pointer to the listview structure
8040 * [I] hwnd : window handle of window containing the cursor
8041 * [I] nHittest : hit-test code
8042 * [I] wMouseMsg : ideintifier of the mouse message
8045 * TRUE if cursor is set
8048 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8052 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8054 if(!infoPtr->hHotCursor) return FALSE;
8057 if (LISTVIEW_GetItemAtPt(infoPtr, pt) < 0) return FALSE;
8059 SetCursor(infoPtr->hHotCursor);
8069 * [I] infoPtr : valid pointer to the listview structure
8070 * [I] infoPtr : handle of previously focused window
8075 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8077 TRACE("(hwndLoseFocus=%x)\n", hwndLoseFocus);
8079 /* if we have the focus already, there's nothing to do */
8080 if (infoPtr->bFocus) return 0;
8082 /* send NM_SETFOCUS notification */
8083 notify_setfocus(infoPtr);
8085 /* set window focus flag */
8086 infoPtr->bFocus = TRUE;
8088 /* put the focus rect back on */
8089 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8091 /* redraw all visible selected items */
8092 LISTVIEW_InvalidateSelectedItems(infoPtr);
8102 * [I] infoPtr : valid pointer to the listview structure
8103 * [I] HFONT : font handle
8104 * [I] WORD : redraw flag
8109 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8111 HFONT oldFont = infoPtr->hFont;
8113 TRACE("(hfont=%x,redraw=%hu)\n", hFont, fRedraw);
8115 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8116 if (infoPtr->hFont == oldFont) return 0;
8118 LISTVIEW_SaveTextMetrics(infoPtr);
8120 if (LISTVIEW_GetType(infoPtr) == LVS_REPORT)
8121 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8123 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8130 * Message handling for WM_SETREDRAW.
8131 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8134 * [I] infoPtr : valid pointer to the listview structure
8135 * [I] bRedraw: state of redraw flag
8138 * DefWinProc return value
8140 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8142 /* FIXME: this is bogus */
8143 LRESULT lResult = DefWindowProcW(infoPtr->hwndSelf, WM_SETREDRAW, bRedraw, 0);
8145 RedrawWindow(infoPtr->hwndSelf, NULL, 0,
8146 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8152 * Resizes the listview control. This function processes WM_SIZE
8153 * messages. At this time, the width and height are not used.
8156 * [I] infoPtr : valid pointer to the listview structure
8157 * [I] WORD : new width
8158 * [I] WORD : new height
8163 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8165 LONG lStyle = infoPtr->dwStyle;
8166 UINT uView = lStyle & LVS_TYPEMASK;
8168 TRACE("(width=%d, height=%d)\n", Width, Height);
8170 if (LISTVIEW_UpdateSize(infoPtr))
8172 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8174 if (lStyle & LVS_ALIGNLEFT)
8175 LISTVIEW_AlignLeft(infoPtr);
8177 LISTVIEW_AlignTop(infoPtr);
8180 LISTVIEW_UpdateScroll(infoPtr);
8182 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8190 * Sets the size information.
8193 * [I] infoPtr : valid pointer to the listview structure
8196 * Zero if no size change
8199 static BOOL LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8201 LONG lStyle = infoPtr->dwStyle;
8202 UINT uView = lStyle & LVS_TYPEMASK;
8206 GetClientRect(infoPtr->hwndSelf, &rcList);
8207 CopyRect(&rcOld,&(infoPtr->rcList));
8208 infoPtr->rcList.left = 0;
8209 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8210 infoPtr->rcList.top = 0;
8211 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8213 if (uView == LVS_LIST)
8215 /* Apparently the "LIST" style is supposed to have the same
8216 * number of items in a column even if there is no scroll bar.
8217 * Since if a scroll bar already exists then the bottom is already
8218 * reduced, only reduce if the scroll bar does not currently exist.
8219 * The "2" is there to mimic the native control. I think it may be
8220 * related to either padding or edges. (GLA 7/2002)
8222 if (!(lStyle & WS_HSCROLL))
8224 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8225 if (infoPtr->rcList.bottom > nHScrollHeight)
8226 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
8230 if (infoPtr->rcList.bottom > 2)
8231 infoPtr->rcList.bottom -= 2;
8234 else if (uView == LVS_REPORT)
8241 Header_Layout(infoPtr->hwndHeader, &hl);
8243 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8245 if (!(LVS_NOCOLUMNHEADER & lStyle))
8246 infoPtr->rcList.top = max(wp.cy, 0);
8248 return (EqualRect(&rcOld,&(infoPtr->rcList)));
8253 * Processes WM_STYLECHANGED messages.
8256 * [I] infoPtr : valid pointer to the listview structure
8257 * [I] WPARAM : window style type (normal or extended)
8258 * [I] LPSTYLESTRUCT : window style information
8263 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8266 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8267 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8268 RECT rcList = infoPtr->rcList;
8270 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
8271 wStyleType, lpss->styleOld, lpss->styleNew);
8273 /* FIXME: if LVS_NOSORTHEADER changed, update header */
8275 if (wStyleType == GWL_STYLE)
8277 infoPtr->dwStyle = lpss->styleNew;
8279 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
8280 ((lpss->styleNew & WS_HSCROLL) == 0))
8281 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
8283 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
8284 ((lpss->styleNew & WS_VSCROLL) == 0))
8285 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
8287 /* If switching modes, then start with no scroll bars and then
8290 if (uNewView != uOldView)
8292 if (uOldView == LVS_REPORT)
8293 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8295 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8296 SetRectEmpty(&infoPtr->rcFocus);
8299 if (uNewView == LVS_ICON)
8303 /* First readjust the iconSize and if necessary the iconSpacing */
8304 oldcx = infoPtr->iconSize.cx;
8305 oldcy = infoPtr->iconSize.cy;
8306 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8307 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8308 if (infoPtr->himlNormal != NULL)
8311 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
8312 infoPtr->iconSize.cx = cx;
8313 infoPtr->iconSize.cy = cy;
8315 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
8317 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
8318 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8319 LISTVIEW_SetIconSpacing(infoPtr,0);
8322 /* Now update the full item width and height */
8323 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8324 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8325 if (lpss->styleNew & LVS_ALIGNLEFT)
8326 LISTVIEW_AlignLeft(infoPtr);
8328 LISTVIEW_AlignTop(infoPtr);
8330 else if (uNewView == LVS_REPORT)
8337 Header_Layout(infoPtr->hwndHeader, &hl);
8338 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8340 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8341 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8343 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8344 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8345 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8346 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8348 else if (uNewView == LVS_LIST)
8350 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8351 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8352 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8353 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8357 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8358 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8359 infoPtr->nItemWidth = LISTVIEW_CalculateMaxWidth(infoPtr);
8360 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(infoPtr);
8361 if (lpss->styleNew & LVS_ALIGNLEFT)
8362 LISTVIEW_AlignLeft(infoPtr);
8364 LISTVIEW_AlignTop(infoPtr);
8367 /* update the size of the client area */
8368 LISTVIEW_UpdateSize(infoPtr);
8370 /* add scrollbars if needed */
8371 LISTVIEW_UpdateScroll(infoPtr);
8373 /* invalidate client area + erase background */
8374 LISTVIEW_InvalidateList(infoPtr); /* FIXME: optimize */
8376 /* print the list of unsupported window styles */
8377 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8380 /* If they change the view and we have an active edit control
8381 we will need to kill the control since the redraw will
8382 misplace the edit control.
8384 if (infoPtr->bEditing &&
8385 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8386 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8388 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8396 * Window procedure of the listview control.
8399 static LRESULT WINAPI
8400 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
8402 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8404 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
8406 if (!infoPtr && (uMsg != WM_CREATE))
8407 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8411 infoPtr->dwStyle = GetWindowLongW(hwnd, GWL_STYLE);
8416 case LVM_APPROXIMATEVIEWRECT:
8417 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
8418 LOWORD(lParam), HIWORD(lParam));
8420 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
8422 /* case LVN_CANCELEDITLABEL */
8424 /* case LVM_CREATEDRAGIMAGE: */
8426 case LVM_DELETEALLITEMS:
8427 return LISTVIEW_DeleteAllItems(infoPtr);
8429 case LVM_DELETECOLUMN:
8430 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
8432 case LVM_DELETEITEM:
8433 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
8435 case LVM_EDITLABELW:
8436 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
8438 case LVM_EDITLABELA:
8439 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
8441 /* case LVN_ENABLEGROUPVIEW: */
8443 case LVM_ENSUREVISIBLE:
8444 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
8447 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
8450 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
8452 case LVM_GETBKCOLOR:
8453 return infoPtr->clrBk;
8455 /* case LVM_GETBKIMAGE: */
8457 case LVM_GETCALLBACKMASK:
8458 return infoPtr->uCallbackMask;
8460 case LVM_GETCOLUMNA:
8461 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8463 case LVM_GETCOLUMNW:
8464 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8466 case LVM_GETCOLUMNORDERARRAY:
8467 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8469 case LVM_GETCOLUMNWIDTH:
8470 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
8472 case LVM_GETCOUNTPERPAGE:
8473 return LISTVIEW_GetCountPerPage(infoPtr);
8475 case LVM_GETEDITCONTROL:
8476 return (LRESULT)infoPtr->hwndEdit;
8478 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8479 return infoPtr->dwLvExStyle;
8482 return (LRESULT)infoPtr->hwndHeader;
8484 case LVM_GETHOTCURSOR:
8485 return infoPtr->hHotCursor;
8487 case LVM_GETHOTITEM:
8488 return infoPtr->nHotItem;
8490 case LVM_GETHOVERTIME:
8491 return infoPtr->dwHoverTime;
8493 case LVM_GETIMAGELIST:
8494 return LISTVIEW_GetImageList(infoPtr, (INT)wParam);
8496 /* case LVN_GETINSERTMARK: */
8498 /* case LVN_GETINSERTMARKCOLOR: */
8500 /* case LVN_GETINSERTMARKRECT: */
8502 case LVM_GETISEARCHSTRINGA:
8503 case LVM_GETISEARCHSTRINGW:
8504 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
8508 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
8511 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
8513 case LVM_GETITEMCOUNT:
8514 return infoPtr->nItemCount;
8516 case LVM_GETITEMPOSITION:
8517 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
8519 case LVM_GETITEMRECT:
8520 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
8522 case LVM_GETITEMSPACING:
8523 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
8525 case LVM_GETITEMSTATE:
8526 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
8528 case LVM_GETITEMTEXTA:
8529 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8531 case LVM_GETITEMTEXTW:
8532 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8534 case LVM_GETNEXTITEM:
8535 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
8537 case LVM_GETNUMBEROFWORKAREAS:
8538 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
8542 return LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
8544 /* case LVN_GETOUTLINECOLOR: */
8546 /* case LVM_GETSELECTEDCOLUMN: */
8548 case LVM_GETSELECTEDCOUNT:
8549 return LISTVIEW_GetSelectedCount(infoPtr);
8551 case LVM_GETSELECTIONMARK:
8552 return infoPtr->nSelectionMark;
8554 case LVM_GETSTRINGWIDTHA:
8555 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
8557 case LVM_GETSTRINGWIDTHW:
8558 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
8560 case LVM_GETSUBITEMRECT:
8561 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
8563 case LVM_GETTEXTBKCOLOR:
8564 return infoPtr->clrTextBk;
8566 case LVM_GETTEXTCOLOR:
8567 return infoPtr->clrText;
8569 /* case LVN_GETTILEINFO: */
8571 /* case LVN_GETTILEVIEWINFO: */
8573 case LVM_GETTOOLTIPS:
8574 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
8577 case LVM_GETTOPINDEX:
8578 return LISTVIEW_GetTopIndex(infoPtr);
8580 /*case LVM_GETUNICODEFORMAT:
8581 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
8584 case LVM_GETVIEWRECT:
8585 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
8587 case LVM_GETWORKAREAS:
8588 FIXME("LVM_GETWORKAREAS: unimplemented\n");
8591 /* case LVN_HASGROUP: */
8594 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE);
8596 case LVM_INSERTCOLUMNA:
8597 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8599 case LVM_INSERTCOLUMNW:
8600 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8602 /* case LVN_INSERTGROUP: */
8604 /* case LVN_INSERTGROUPSORTED: */
8606 case LVM_INSERTITEMA:
8607 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8609 case LVM_INSERTITEMW:
8610 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8612 /* case LVN_INSERTMARKHITTEST: */
8614 /* case LVN_ISGROUPVIEWENABLED: */
8616 /* case LVN_MAPIDTOINDEX: */
8618 /* case LVN_INEDXTOID: */
8620 /* case LVN_MOVEGROUP: */
8622 /* case LVN_MOVEITEMTOGROUP: */
8624 case LVM_REDRAWITEMS:
8625 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
8627 /* case LVN_REMOVEALLGROUPS: */
8629 /* case LVN_REMOVEGROUP: */
8632 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
8634 case LVM_SETBKCOLOR:
8635 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
8637 /* case LVM_SETBKIMAGE: */
8639 case LVM_SETCALLBACKMASK:
8640 infoPtr->uCallbackMask = (UINT)wParam;
8643 case LVM_SETCOLUMNA:
8644 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8646 case LVM_SETCOLUMNW:
8647 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8649 case LVM_SETCOLUMNORDERARRAY:
8650 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
8652 case LVM_SETCOLUMNWIDTH:
8653 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, SLOWORD(lParam));
8655 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8656 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
8658 /* case LVN_SETGROUPINFO: */
8660 /* case LVN_SETGROUPMETRICS: */
8662 case LVM_SETHOTCURSOR:
8663 return LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
8665 case LVM_SETHOTITEM:
8666 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
8668 case LVM_SETHOVERTIME:
8669 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
8671 case LVM_SETICONSPACING:
8672 return LISTVIEW_SetIconSpacing(infoPtr, (DWORD)lParam);
8674 case LVM_SETIMAGELIST:
8675 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
8677 /* case LVN_SETINFOTIP: */
8679 /* case LVN_SETINSERTMARK: */
8681 /* case LVN_SETINSERTMARKCOLOR: */
8684 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
8687 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
8689 case LVM_SETITEMCOUNT:
8690 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
8692 case LVM_SETITEMPOSITION:
8694 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
8695 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
8698 case LVM_SETITEMPOSITION32:
8699 if (lParam == 0) return FALSE;
8700 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
8702 case LVM_SETITEMSTATE:
8703 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
8705 case LVM_SETITEMTEXTA:
8706 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8708 case LVM_SETITEMTEXTW:
8709 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8711 /* case LVN_SETOUTLINECOLOR: */
8713 /* case LVN_SETSELECTEDCOLUMN: */
8715 case LVM_SETSELECTIONMARK:
8716 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
8718 case LVM_SETTEXTBKCOLOR:
8719 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
8721 case LVM_SETTEXTCOLOR:
8722 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
8724 /* case LVN_SETTILEINFO: */
8726 /* case LVN_SETTILEVIEWINFO: */
8728 /* case LVN_SETTILEWIDTH: */
8730 /* case LVM_SETTOOLTIPS: */
8732 /* case LVM_SETUNICODEFORMAT: */
8734 /* case LVN_SETVIEW: */
8736 /* case LVM_SETWORKAREAS: */
8738 /* case LVN_SORTGROUPS: */
8741 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8743 case LVM_SUBITEMHITTEST:
8744 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE);
8747 return LISTVIEW_Update(infoPtr, (INT)wParam);
8750 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
8753 return LISTVIEW_Command(infoPtr, wParam, lParam);
8756 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8759 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
8762 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8765 return infoPtr->hFont;
8768 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8771 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
8774 return LISTVIEW_KillFocus(infoPtr);
8776 case WM_LBUTTONDBLCLK:
8777 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8779 case WM_LBUTTONDOWN:
8780 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8783 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8786 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8789 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8792 return LISTVIEW_NCDestroy(infoPtr);
8795 return LISTVIEW_Notify(infoPtr, (INT)wParam, (LPNMHDR)lParam);
8797 case WM_NOTIFYFORMAT:
8798 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
8801 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
8803 case WM_RBUTTONDBLCLK:
8804 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8806 case WM_RBUTTONDOWN:
8807 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8810 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, MAKEPOINTS(lParam));
8813 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
8818 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
8821 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
8824 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
8827 return LISTVIEW_Size(infoPtr, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8829 case WM_STYLECHANGED:
8830 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
8832 case WM_SYSCOLORCHANGE:
8833 COMCTL32_RefreshSysColors();
8836 /* case WM_TIMER: */
8839 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
8842 if (wParam & (MK_SHIFT | MK_CONTROL))
8843 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8844 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
8846 case WM_WINDOWPOSCHANGED:
8847 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
8848 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
8849 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
8850 LISTVIEW_UpdateSize(infoPtr);
8851 LISTVIEW_UpdateScroll(infoPtr);
8853 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8855 /* case WM_WININICHANGE: */
8858 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
8859 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
8862 /* call default window procedure */
8863 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
8871 * Registers the window class.
8879 void LISTVIEW_Register(void)
8883 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
8884 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
8885 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
8886 wndClass.cbClsExtra = 0;
8887 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
8888 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
8889 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
8890 wndClass.lpszClassName = WC_LISTVIEWW;
8891 RegisterClassW(&wndClass);
8896 * Unregisters the window class.
8904 void LISTVIEW_Unregister(void)
8906 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
8911 * Handle any WM_COMMAND messages
8917 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
8919 switch (HIWORD(wParam))
8924 * Adjust the edit window size
8927 HDC hdc = GetDC(infoPtr->hwndEdit);
8928 HFONT hFont, hOldFont = 0;
8933 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
8934 GetWindowRect(infoPtr->hwndEdit, &rect);
8936 /* Select font to get the right dimension of the string */
8937 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
8940 hOldFont = SelectObject(hdc, hFont);
8943 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
8945 TEXTMETRICW textMetric;
8947 /* Add Extra spacing for the next character */
8948 GetTextMetricsW(hdc, &textMetric);
8949 sz.cx += (textMetric.tmMaxCharWidth * 2);
8957 rect.bottom - rect.top,
8958 SWP_DRAWFRAME|SWP_NOMOVE);
8961 SelectObject(hdc, hOldFont);
8963 ReleaseDC(infoPtr->hwndSelf, hdc);
8969 return SendMessageW (GetParent (infoPtr->hwndSelf), WM_COMMAND, wParam, lParam);
8978 * Subclassed edit control windproc function
8984 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
8985 WPARAM wParam, LPARAM lParam, BOOL isW)
8987 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
8988 static BOOL bIgnoreKillFocus = FALSE;
8989 BOOL cancel = FALSE;
8991 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
8992 hwnd, uMsg, wParam, lParam, isW);
8997 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9000 if(bIgnoreKillFocus) return TRUE;
9005 WNDPROC editProc = infoPtr->EditWndProc;
9006 infoPtr->EditWndProc = 0;
9007 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9008 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9012 if (VK_ESCAPE == (INT)wParam)
9017 else if (VK_RETURN == (INT)wParam)
9021 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9024 if (infoPtr->bEditing)
9026 LPWSTR buffer = NULL;
9030 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9034 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9036 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9037 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9041 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9042 /* eg. Using a messagebox */
9043 bIgnoreKillFocus = TRUE;
9044 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9046 if (buffer) COMCTL32_Free(buffer);
9048 bIgnoreKillFocus = FALSE;
9051 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9057 * Subclassed edit control windproc function
9063 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9065 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9070 * Subclassed edit control windproc function
9076 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9078 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9083 * Creates a subclassed edit cotrol
9089 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9090 INT x, INT y, INT width, INT height, BOOL isW)
9092 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9097 TEXTMETRICW textMetric;
9098 HINSTANCE hinst = GetWindowLongW(infoPtr->hwndSelf, GWL_HINSTANCE);
9100 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9102 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9103 hdc = GetDC(infoPtr->hwndSelf);
9105 /* Select the font to get appropriate metric dimensions */
9106 if(infoPtr->hFont != 0)
9107 hOldFont = SelectObject(hdc, infoPtr->hFont);
9109 /*Get String Lenght in pixels */
9110 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9112 /*Add Extra spacing for the next character */
9113 GetTextMetricsW(hdc, &textMetric);
9114 sz.cx += (textMetric.tmMaxCharWidth * 2);
9116 if(infoPtr->hFont != 0)
9117 SelectObject(hdc, hOldFont);
9119 ReleaseDC(infoPtr->hwndSelf, hdc);
9121 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9123 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9125 if (!hedit) return 0;
9127 infoPtr->EditWndProc = (WNDPROC)
9128 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9129 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9131 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);