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 * 1. No horizontal scrolling when header is larger than the client area.
29 * 2. Drawing optimizations.
30 * 3. Hot item handling.
33 * LISTVIEW_Notify : most notifications from children (editbox and header)
36 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
38 * Advanced functionality:
39 * LISTVIEW_GetNumberOfWorkAreas : not implemented
40 * LISTVIEW_GetHotCursor : not implemented
41 * LISTVIEW_GetISearchString : not implemented
42 * LISTVIEW_GetBkImage : not implemented
43 * LISTVIEW_SetBkImage : not implemented
44 * LISTVIEW_GetColumnOrderArray : simple hack only
45 * LISTVIEW_SetColumnOrderArray : simple hack only
46 * LISTVIEW_Arrange : empty stub
47 * LISTVIEW_ApproximateViewRect : incomplete
48 * LISTVIEW_Update : not completed
50 * Known differences in message stream from native control (not known if
51 * these differences cause problems):
52 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
53 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
54 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
55 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
56 * does *not* invoke DefWindowProc
57 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
58 * processing for "USEDOUBLECLICKTIME".
63 #include "wine/port.h"
75 #include "wine/debug.h"
77 WINE_DEFAULT_DEBUG_CHANNEL(listview);
79 /* Some definitions for inline edit control */
80 typedef BOOL (*EditlblCallbackW)(HWND, LPWSTR, DWORD);
81 typedef BOOL (*EditlblCallbackA)(HWND, LPWSTR, DWORD);
83 typedef struct tagLV_INTHIT
86 DWORD distance; /* distance to closest item */
87 INT iDistItem; /* item number that is closest */
88 } LV_INTHIT, *LPLV_INTHIT;
91 typedef struct tagEDITLABEL_ITEM
95 EditlblCallbackW EditLblCb;
98 typedef struct tagLISTVIEW_SUBITEM
105 typedef struct tagLISTVIEW_ITEM
116 typedef struct tagLISTVIEW_SELECTION
120 } LISTVIEW_SELECTION;
122 typedef struct tagLISTVIEW_INFO
128 HIMAGELIST himlNormal;
129 HIMAGELIST himlSmall;
130 HIMAGELIST himlState;
134 HDPA hdpaSelectionRanges;
148 INT ntmHeight; /* from GetTextMetrics from above font */
149 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
151 DWORD dwExStyle; /* extended listview style */
153 PFNLVCOMPARE pfnCompare;
158 EDITLABEL_ITEM *pedititem;
160 INT nColumnCount; /* the number of columns in this control */
162 DWORD lastKeyPressTimestamp; /* Added */
163 WPARAM charCode; /* Added */
164 INT nSearchParamLength; /* Added */
165 WCHAR szSearchParam[ MAX_PATH ]; /* Added */
173 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
174 #define SB_INTERNAL_UP -1
175 #define SB_INTERNAL_DOWN -2
176 #define SB_INTERNAL_RIGHT -3
177 #define SB_INTERNAL_LEFT -4
179 /* maximum size of a label */
180 #define DISP_TEXT_SIZE 512
182 /* padding for items in list and small icon display modes */
183 #define WIDTH_PADDING 12
185 /* padding for items in list, report and small icon display modes */
186 #define HEIGHT_PADDING 1
188 /* offset of items in report display mode */
189 #define REPORT_MARGINX 2
191 /* padding for icon in large icon display mode
192 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
193 * that HITTEST will see.
194 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
195 * ICON_TOP_PADDING - sum of the two above.
196 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
197 * LABEL_VERT_OFFSET - between bottom of text and end of box
199 #define ICON_TOP_PADDING_NOTHITABLE 2
200 #define ICON_TOP_PADDING_HITABLE 2
201 #define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE
202 #define ICON_BOTTOM_PADDING 4
203 #define LABEL_VERT_OFFSET 10
205 /* default label width for items in list and small icon display modes */
206 #define DEFAULT_LABEL_WIDTH 40
208 /* default column width for items in list display mode */
209 #define DEFAULT_COLUMN_WIDTH 128
211 /* Size of "line" scroll for V & H scrolls */
212 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
214 /* Padding betwen image and label */
215 #define IMAGE_PADDING 2
217 /* Padding behind the label */
218 #define TRAILING_PADDING 5
220 /* Border for the icon caption */
221 #define CAPTION_BORDER 2
225 /* retrieve the number of items in the listview */
226 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
227 #define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA )
229 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
230 INT width, INT height, HWND parent, HINSTANCE hinst,
231 EditlblCallbackW EditLblCb, DWORD param, BOOL isW);
234 * forward declarations
236 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW);
237 static INT LISTVIEW_SuperHitTestItem(HWND, LPLV_INTHIT, BOOL);
238 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL);
239 static INT LISTVIEW_GetCountPerRow(HWND);
240 static INT LISTVIEW_GetCountPerColumn(HWND);
241 static VOID LISTVIEW_AlignLeft(HWND);
242 static VOID LISTVIEW_AlignTop(HWND);
243 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
244 static VOID LISTVIEW_AddSelection(HWND, INT);
245 static BOOL LISTVIEW_AddSubItemT(HWND, LPLVITEMW, BOOL);
246 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
247 static INT LISTVIEW_GetItemHeight(HWND);
248 static BOOL LISTVIEW_GetItemBoundBox(HWND, INT, LPRECT);
249 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
250 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
251 static LRESULT LISTVIEW_GetSubItemRect(HWND, INT, INT, INT, LPRECT);
252 static INT LISTVIEW_GetItemWidth(HWND);
253 static INT LISTVIEW_GetLabelWidth(HWND, INT);
254 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
255 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
256 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
257 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
258 static BOOL LISTVIEW_InitItemT(HWND, LISTVIEW_ITEM *, LPLVITEMW, BOOL);
259 static BOOL LISTVIEW_InitSubItemT(HWND, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL);
260 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
261 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
262 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
263 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
264 static BOOL LISTVIEW_SetItemT(HWND, LPLVITEMW, BOOL);
265 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
266 static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG);
267 static VOID LISTVIEW_UpdateScroll(HWND);
268 static VOID LISTVIEW_SetSelection(HWND, INT);
269 static BOOL LISTVIEW_UpdateSize(HWND);
270 static BOOL LISTVIEW_SetSubItemT(HWND, LPLVITEMW, BOOL);
271 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
272 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
273 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
274 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW);
275 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem);
276 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem);
277 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
278 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
279 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW);
280 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
281 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
282 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
283 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem);
284 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem);
285 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
286 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
287 static void ListView_UpdateLargeItemLabelRect (HWND hwnd, const LISTVIEW_INFO* infoPtr, int nItem, RECT *rect);
288 static LRESULT LISTVIEW_GetColumnT(HWND, INT, LPLVCOLUMNW, BOOL);
289 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos, HWND hScrollWnd);
290 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos, HWND hScrollWnd);
291 static INT LISTVIEW_GetTopIndex(HWND hwnd);
292 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial);
294 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
295 #define KEY_DELAY 450
297 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
299 static inline BOOL is_textW(LPCWSTR text)
301 return text != NULL && text != LPSTR_TEXTCALLBACKW;
304 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
306 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
307 return is_textW(text);
310 static inline int textlenT(LPCWSTR text, BOOL isW)
312 return !is_textT(text, isW) ? 0 :
313 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
316 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
319 if (isSrcW) lstrcpynW(dest, src, max);
320 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
322 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
323 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
326 static inline LPCSTR debugstr_t(LPCWSTR text, BOOL isW)
328 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
331 static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n)
333 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
336 static inline LPCSTR lv_dmp_str(LPCWSTR text, BOOL isW, INT n)
338 return debugstr_tn(text, isW, min(n, textlenT(text, isW)));
341 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
343 LPWSTR wstr = (LPWSTR)text;
345 TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW);
348 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
349 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
350 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
352 TRACE(" wstr=%s\n", debugstr_w(wstr));
356 static inline void textfreeT(LPWSTR wstr, BOOL isW)
358 if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
362 * dest is a pointer to a Unicode string
363 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
365 static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
367 LPWSTR pszText = textdupTtoW(src, isW);
369 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
370 bResult = Str_SetPtrW(dest, pszText);
371 textfreeT(pszText, isW);
375 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
376 WPARAM wParam, LPARAM lParam, BOOL isW)
379 return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
381 return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
384 static inline BOOL notify(HWND self, INT code, LPNMHDR pnmh)
386 pnmh->hwndFrom = self;
387 pnmh->idFrom = GetWindowLongW(self, GWL_ID);
389 return (BOOL)SendMessageW(GetParent(self), WM_NOTIFY,
390 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
393 static inline BOOL hdr_notify(HWND self, INT code)
396 return notify(self, code, &nmh);
399 static inline BOOL listview_notify(HWND self, INT code, LPNMLISTVIEW plvnm)
401 return notify(self, code, (LPNMHDR)plvnm);
404 static int tabNotification[] = {
405 LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
406 LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
407 LVN_GETDISPINFOW, LVN_GETDISPINFOA,
408 LVN_SETDISPINFOW, LVN_SETDISPINFOA,
409 LVN_ODFINDITEMW, LVN_ODFINDITEMA,
410 LVN_GETINFOTIPW, LVN_GETINFOTIPA,
414 static int get_ansi_notification(INT unicodeNotificationCode)
416 int *pTabNotif = tabNotification;
417 while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
418 if (*pTabNotif) return *pTabNotif;
419 ERR("unknown notification %x\n", unicodeNotificationCode);
420 return unicodeNotificationCode;
424 Send notification. depends on dispinfoW having same
425 structure as dispinfoA.
426 self : listview handle
427 notificationCode : *Unicode* notification code
428 pdi : dispinfo structure (can be unicode or ansi)
429 isW : TRUE if dispinfo is Unicode
431 static BOOL dispinfo_notifyT(HWND self, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
433 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(self, 0);
434 BOOL bResult = FALSE;
435 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
437 INT cchTempBufMax = 0, savCchTextMax = 0;
438 LPWSTR pszTempBuf = NULL, savPszText = NULL;
440 TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self, notificationCode, pdi, isW);
441 TRACE(" notifyFormat=%s\n",
442 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
443 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
444 if (infoPtr->notifyFormat == NFR_ANSI)
445 realNotifCode = get_ansi_notification(notificationCode);
447 realNotifCode = notificationCode;
449 if (is_textT(pdi->item.pszText, isW))
451 if (isW && infoPtr->notifyFormat == NFR_ANSI)
452 convertToAnsi = TRUE;
453 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
454 convertToUnicode = TRUE;
457 if (convertToAnsi || convertToUnicode)
459 TRACE(" we have to convert the text to the correct format\n");
460 if (notificationCode != LVN_GETDISPINFOW)
461 { /* length of existing text */
462 cchTempBufMax = convertToUnicode ?
463 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
464 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
467 cchTempBufMax = pdi->item.cchTextMax;
469 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
470 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
471 if (!pszTempBuf) return FALSE;
472 if (convertToUnicode)
473 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
474 pszTempBuf, cchTempBufMax);
476 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
477 cchTempBufMax, NULL, NULL);
478 TRACE(" text=%s\n", debugstr_t(pszTempBuf, convertToUnicode));
479 savCchTextMax = pdi->item.cchTextMax;
480 savPszText = pdi->item.pszText;
481 pdi->item.pszText = pszTempBuf;
482 pdi->item.cchTextMax = cchTempBufMax;
485 bResult = notify(self, realNotifCode, (LPNMHDR)pdi);
487 if (convertToUnicode || convertToAnsi)
488 { /* convert back result */
489 TRACE(" returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode));
490 if (convertToUnicode) /* note : pointer can be changed by app ! */
491 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
492 savCchTextMax, NULL, NULL);
494 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
495 savPszText, savCchTextMax);
496 pdi->item.pszText = savPszText; /* restores our buffer */
497 pdi->item.cchTextMax = savCchTextMax;
498 HeapFree(GetProcessHeap(), 0, pszTempBuf);
503 static inline LRESULT LISTVIEW_GetItemW(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal)
505 return LISTVIEW_GetItemT(hwnd, lpLVItem, internal, TRUE);
508 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
512 n = min(min(n, strlenW(s1)), strlenW(s2));
513 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
514 return res ? res - 2 : res;
517 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
519 static int index = 0;
520 static char buffers[20][256];
521 char* buf = buffers[index++ % 20];
522 if (lpLVItem == NULL) return "(null)";
523 snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
524 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
525 lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
526 lpLVItem->state, lpLVItem->stateMask,
527 lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
528 lv_dmp_str(lpLVItem->pszText, isW, 80),
529 lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
534 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
536 static int index = 0;
537 static char buffers[20][256];
538 char* buf = buffers[index++ % 20];
539 if (lpColumn == NULL) return "(null)";
540 snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
541 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
542 lpColumn->mask, lpColumn->fmt, lpColumn->cx,
543 lpColumn->mask & LVCF_TEXT ? lpColumn->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
544 lv_dmp_str(lpColumn->pszText, isW, 80): "",
545 lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
549 static void LISTVIEW_DumpListview(LISTVIEW_INFO *iP, INT line)
551 DWORD dwStyle = GetWindowLongW (iP->hwndSelf, GWL_STYLE);
552 TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n",
553 iP->hwndSelf, line, iP->clrBk, iP->clrText, iP->clrTextBk,
554 iP->nItemHeight, iP->nItemWidth, dwStyle);
555 TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n",
556 iP->hwndSelf, line, iP->himlNormal, iP->himlSmall, iP->himlState,
557 iP->nFocusedItem, iP->nHotItem, iP->dwExStyle);
558 TRACE("listview %08x at line %d, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld\n",
559 iP->hwndSelf, line, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy,
560 iP->iconSpacing.cx, iP->iconSpacing.cy);
564 LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
567 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
568 NMLVCUSTOMDRAW nmcdhdr;
571 TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd, dwDrawStage, hdc);
573 nmcd= & nmcdhdr.nmcd;
574 nmcd->hdr.hwndFrom = hwnd;
575 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
576 nmcd->hdr.code = NM_CUSTOMDRAW;
577 nmcd->dwDrawStage= dwDrawStage;
579 nmcd->rc.left = rc.left;
580 nmcd->rc.right = rc.right;
581 nmcd->rc.bottom = rc.bottom;
582 nmcd->rc.top = rc.top;
583 nmcd->dwItemSpec = 0;
584 nmcd->uItemState = 0;
585 nmcd->lItemlParam= 0;
586 nmcdhdr.clrText = infoPtr->clrText;
587 nmcdhdr.clrTextBk= infoPtr->clrBk;
589 return (BOOL)SendMessageW (GetParent (hwnd), WM_NOTIFY,
590 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
594 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
595 UINT iItem, UINT iSubItem,
598 LISTVIEW_INFO *infoPtr;
599 NMLVCUSTOMDRAW nmcdhdr;
601 DWORD dwDrawStage,dwItemSpec;
607 infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
609 ZeroMemory(&item,sizeof(item));
611 item.mask = LVIF_PARAM;
612 ListView_GetItemW(hwnd,&item);
614 dwDrawStage=CDDS_ITEM | uItemDrawState;
618 if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
619 if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
620 if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
622 itemRect.left = LVIR_BOUNDS;
623 LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
625 nmcd= & nmcdhdr.nmcd;
626 nmcd->hdr.hwndFrom = hwnd;
627 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
628 nmcd->hdr.code = NM_CUSTOMDRAW;
629 nmcd->dwDrawStage= dwDrawStage;
631 nmcd->rc.left = itemRect.left;
632 nmcd->rc.right = itemRect.right;
633 nmcd->rc.bottom = itemRect.bottom;
634 nmcd->rc.top = itemRect.top;
635 nmcd->dwItemSpec = dwItemSpec;
636 nmcd->uItemState = uItemState;
637 nmcd->lItemlParam= item.lParam;
638 nmcdhdr.clrText = infoPtr->clrText;
639 nmcdhdr.clrTextBk= infoPtr->clrBk;
640 nmcdhdr.iSubItem =iSubItem;
642 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
643 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
644 nmcd->uItemState, nmcd->lItemlParam);
646 retval=SendMessageW (GetParent (hwnd), WM_NOTIFY,
647 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
649 infoPtr->clrText=nmcdhdr.clrText;
650 infoPtr->clrBk =nmcdhdr.clrTextBk;
651 return (BOOL) retval;
655 /*************************************************************************
656 * LISTVIEW_ProcessLetterKeys
658 * Processes keyboard messages generated by pressing the letter keys
660 * What this does is perform a case insensitive search from the
661 * current position with the following quirks:
662 * - If two chars or more are pressed in quick succession we search
663 * for the corresponding string (e.g. 'abc').
664 * - If there is a delay we wipe away the current search string and
665 * restart with just that char.
666 * - If the user keeps pressing the same character, whether slowly or
667 * fast, so that the search string is entirely composed of this
668 * character ('aaaaa' for instance), then we search for first item
669 * that starting with that character.
670 * - If the user types the above character in quick succession, then
671 * we must also search for the corresponding string ('aaaaa'), and
672 * go to that string if there is a match.
680 * - The current implementation has a list of characters it will
681 * accept and it ignores averything else. In particular it will
682 * ignore accentuated characters which seems to match what
683 * Windows does. But I'm not sure it makes sense to follow
685 * - We don't sound a beep when the search fails.
689 * TREEVIEW_ProcessLetterKeys
691 static INT LISTVIEW_ProcessLetterKeys(
692 HWND hwnd, /* handle to the window */
693 WPARAM charCode, /* the character code, the actual character */
694 LPARAM keyData /* key data */
697 LISTVIEW_INFO *infoPtr;
702 WCHAR buffer[MAX_PATH];
703 DWORD timestamp,elapsed;
705 /* simple parameter checking */
706 if (!hwnd || !charCode || !keyData)
709 infoPtr=(LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
713 /* only allow the valid WM_CHARs through */
714 if (!isalnum(charCode) &&
715 charCode != '.' && charCode != '`' && charCode != '!' &&
716 charCode != '@' && charCode != '#' && charCode != '$' &&
717 charCode != '%' && charCode != '^' && charCode != '&' &&
718 charCode != '*' && charCode != '(' && charCode != ')' &&
719 charCode != '-' && charCode != '_' && charCode != '+' &&
720 charCode != '=' && charCode != '\\'&& charCode != ']' &&
721 charCode != '}' && charCode != '[' && charCode != '{' &&
722 charCode != '/' && charCode != '?' && charCode != '>' &&
723 charCode != '<' && charCode != ',' && charCode != '~')
726 nSize=GETITEMCOUNT(infoPtr);
727 /* if there's one item or less, there is no where to go */
731 /* compute how much time elapsed since last keypress */
732 timestamp=GetTickCount();
733 if (timestamp > infoPtr->lastKeyPressTimestamp) {
734 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
736 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
739 /* update the search parameters */
740 infoPtr->lastKeyPressTimestamp=timestamp;
741 if (elapsed < KEY_DELAY) {
742 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
743 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
745 if (infoPtr->charCode != charCode) {
746 infoPtr->charCode=charCode=0;
749 infoPtr->charCode=charCode;
750 infoPtr->szSearchParam[0]=charCode;
751 infoPtr->nSearchParamLength=1;
752 /* Redundant with the 1 char string */
756 /* and search from the current position */
758 if (infoPtr->nFocusedItem >= 0) {
759 endidx=infoPtr->nFocusedItem;
761 /* if looking for single character match,
762 * then we must always move forward
764 if (infoPtr->nSearchParamLength == 1)
778 ZeroMemory(&item, sizeof(item));
779 item.mask = LVIF_TEXT;
782 item.pszText = buffer;
783 item.cchTextMax = COUNTOF(buffer);
784 ListView_GetItemW( hwnd, &item );
786 /* check for a match */
787 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
790 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
791 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
792 /* This would work but we must keep looking for a longer match */
796 } while (idx != endidx);
799 if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
800 /* refresh client area */
801 InvalidateRect(hwnd, NULL, TRUE);
809 /*************************************************************************
810 * LISTVIEW_UpdateHeaderSize [Internal]
812 * Function to resize the header control
815 * hwnd [I] handle to a window
816 * nNewScrollPos [I] Scroll Pos to Set
823 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
825 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
829 GetWindowRect(infoPtr->hwndHeader, &winRect);
830 point[0].x = winRect.left;
831 point[0].y = winRect.top;
832 point[1].x = winRect.right;
833 point[1].y = winRect.bottom;
835 MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
836 point[0].x = -nNewScrollPos;
837 point[1].x += nNewScrollPos;
839 SetWindowPos(infoPtr->hwndHeader,0,
840 point[0].x,point[0].y,point[1].x,point[1].y,
841 SWP_NOZORDER | SWP_NOACTIVATE);
846 * Update the scrollbars. This functions should be called whenever
847 * the content, size or view changes.
850 * [I] HWND : window handle
855 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
857 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
858 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
859 UINT uView = lStyle & LVS_TYPEMASK;
860 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
861 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
862 SCROLLINFO scrollInfo;
864 if (lStyle & LVS_NOSCROLL) return;
866 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
867 scrollInfo.cbSize = sizeof(SCROLLINFO);
869 if (uView == LVS_LIST)
871 /* update horizontal scrollbar */
873 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
874 INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
875 INT nNumOfItems = GETITEMCOUNT(infoPtr);
877 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
878 TRACE("items=%d, perColumn=%d, perRow=%d\n",
879 nNumOfItems, nCountPerColumn, nCountPerRow);
880 if((nNumOfItems % nCountPerColumn) == 0)
884 scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
885 scrollInfo.nPage = nCountPerRow;
886 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
888 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
889 ShowScrollBar(hwnd, SB_VERT, FALSE);
891 else if (uView == LVS_REPORT)
895 /* update vertical scrollbar */
897 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
898 scrollInfo.nPos = ListView_GetTopIndex(hwnd);
899 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
900 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
901 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
902 TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
903 scrollInfo.nMax, scrollInfo.nPage, test);
904 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
905 ShowScrollBar(hwnd, SB_VERT, (test) ? FALSE : TRUE);
907 /* update horizontal scrollbar */
908 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
909 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
910 || GETITEMCOUNT(infoPtr) == 0)
915 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
916 scrollInfo.nPage = nListWidth;
917 scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
918 test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
919 TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
920 scrollInfo.nMax, scrollInfo.nPage, test);
921 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
922 ShowScrollBar(hwnd, SB_HORZ, (test) ? FALSE : TRUE);
924 /* Update the Header Control */
925 scrollInfo.fMask = SIF_POS;
926 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
927 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
934 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
936 INT nViewWidth = rcView.right - rcView.left;
937 INT nViewHeight = rcView.bottom - rcView.top;
939 /* Update Horizontal Scrollbar */
940 scrollInfo.fMask = SIF_POS;
941 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
942 || GETITEMCOUNT(infoPtr) == 0)
946 scrollInfo.nMax = max(nViewWidth, 0)-1;
948 scrollInfo.nPage = nListWidth;
949 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
950 TRACE("LVS_ICON/SMALLICON Horz.\n");
951 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
953 /* Update Vertical Scrollbar */
954 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
955 scrollInfo.fMask = SIF_POS;
956 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
957 || GETITEMCOUNT(infoPtr) == 0)
961 scrollInfo.nMax = max(nViewHeight,0)-1;
963 scrollInfo.nPage = nListHeight;
964 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
965 TRACE("LVS_ICON/SMALLICON Vert.\n");
966 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
973 * Prints a message for unsupported window styles.
974 * A kind of TODO list for window styles.
977 * [I] LONG : window style
982 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
984 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
985 FIXME(" LVS_NOSCROLL\n");
987 if (lStyle & LVS_NOLABELWRAP)
988 FIXME(" LVS_NOLABELWRAP\n");
990 if (!(lStyle & LVS_SHAREIMAGELISTS))
991 FIXME(" !LVS_SHAREIMAGELISTS\n");
993 if (lStyle & LVS_SORTASCENDING)
994 FIXME(" LVS_SORTASCENDING\n");
996 if (lStyle & LVS_SORTDESCENDING)
997 FIXME(" LVS_SORTDESCENDING\n");
1002 * Aligns the items with the top edge of the window.
1005 * [I] HWND : window handle
1010 static VOID LISTVIEW_AlignTop(HWND hwnd)
1012 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1013 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1014 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1017 INT i, off_x=0, off_y=0;
1019 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1021 /* Since SetItemPosition uses upper-left of icon, and for
1022 style=LVS_ICON the icon is not left adjusted, get the offset */
1023 if (uView == LVS_ICON)
1025 off_y = ICON_TOP_PADDING;
1026 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1030 ZeroMemory(&rcView, sizeof(RECT));
1031 TRACE("Icon off.x=%d, off.y=%d, left=%d, right=%d\n",
1033 infoPtr->rcList.left, infoPtr->rcList.right);
1035 if (nListWidth > infoPtr->nItemWidth)
1037 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1039 if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1042 ptItem.y += infoPtr->nItemHeight;
1045 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1046 ptItem.x += infoPtr->nItemWidth;
1047 rcView.right = max(rcView.right, ptItem.x);
1050 rcView.right -= off_x;
1051 rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1055 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1057 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1058 ptItem.y += infoPtr->nItemHeight;
1061 rcView.right = infoPtr->nItemWidth;
1062 rcView.bottom = ptItem.y-off_y;
1065 LISTVIEW_SetViewRect(hwnd, &rcView);
1071 * Aligns the items with the left edge of the window.
1074 * [I] HWND : window handle
1079 static VOID LISTVIEW_AlignLeft(HWND hwnd)
1081 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1082 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1083 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1086 INT i, off_x=0, off_y=0;
1088 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1090 /* Since SetItemPosition uses upper-left of icon, and for
1091 style=LVS_ICON the icon is not left adjusted, get the offset */
1092 if (uView == LVS_ICON)
1094 off_y = ICON_TOP_PADDING;
1095 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1099 ZeroMemory(&rcView, sizeof(RECT));
1100 TRACE("Icon off.x=%d, off.y=%d\n", off_x, off_y);
1102 if (nListHeight > infoPtr->nItemHeight)
1104 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1106 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1109 ptItem.x += infoPtr->nItemWidth;
1112 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1113 ptItem.y += infoPtr->nItemHeight;
1114 rcView.bottom = max(rcView.bottom, ptItem.y);
1117 rcView.right = ptItem.x + infoPtr->nItemWidth;
1121 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1123 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1124 ptItem.x += infoPtr->nItemWidth;
1127 rcView.bottom = infoPtr->nItemHeight;
1128 rcView.right = ptItem.x;
1131 LISTVIEW_SetViewRect(hwnd, &rcView);
1137 * Set the bounding rectangle of all the items.
1140 * [I] HWND : window handle
1141 * [I] LPRECT : bounding rectangle
1147 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
1149 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1150 BOOL bResult = FALSE;
1152 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
1153 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1155 if (lprcView != NULL)
1158 infoPtr->rcView.left = lprcView->left;
1159 infoPtr->rcView.top = lprcView->top;
1160 infoPtr->rcView.right = lprcView->right;
1161 infoPtr->rcView.bottom = lprcView->bottom;
1169 * Retrieves the bounding rectangle of all the items.
1172 * [I] HWND : window handle
1173 * [O] LPRECT : bounding rectangle
1179 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
1181 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1182 BOOL bResult = FALSE;
1185 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
1187 if (lprcView != NULL)
1189 bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
1190 if (bResult != FALSE)
1192 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1193 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1194 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1195 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1198 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1199 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1207 * Retrieves the subitem pointer associated with the subitem index.
1210 * [I] HDPA : DPA handle for a specific item
1211 * [I] INT : index of subitem
1214 * SUCCESS : subitem pointer
1217 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1220 LISTVIEW_SUBITEM *lpSubItem;
1223 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1225 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1226 if (lpSubItem != NULL)
1228 if (lpSubItem->iSubItem == nSubItem)
1240 * Calculates the width of an item.
1243 * [I] HWND : window handle
1244 * [I] LONG : window style
1247 * Returns item width.
1249 static INT LISTVIEW_GetItemWidth(HWND hwnd)
1251 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1252 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
1253 UINT uView = style & LVS_TYPEMASK;
1254 INT nHeaderItemCount;
1260 TRACE("(hwnd=%x)\n", hwnd);
1262 if (uView == LVS_ICON)
1264 nItemWidth = infoPtr->iconSpacing.cx;
1266 else if (uView == LVS_REPORT)
1268 /* calculate width of header */
1269 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1270 for (i = 0; i < nHeaderItemCount; i++)
1272 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1274 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1278 else /* for LVS_SMALLICON and LVS_LIST */
1280 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1282 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
1283 nItemWidth = max(nItemWidth, nLabelWidth);
1286 /* default label size */
1287 if (GETITEMCOUNT(infoPtr) == 0)
1289 nItemWidth = DEFAULT_COLUMN_WIDTH;
1293 if (nItemWidth == 0)
1295 nItemWidth = DEFAULT_LABEL_WIDTH;
1300 nItemWidth += WIDTH_PADDING;
1302 if (infoPtr->himlSmall != NULL)
1304 nItemWidth += infoPtr->iconSize.cx;
1307 if (infoPtr->himlState != NULL)
1309 nItemWidth += infoPtr->iconSize.cx;
1311 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1317 /* nItemWidth Cannot be Zero */
1325 * Calculates the width of a specific item.
1328 * [I] HWND : window handle
1329 * [I] LPSTR : string
1332 * Returns the width of an item width a specified string.
1334 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1336 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1337 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1338 INT nHeaderItemCount;
1343 TRACE("(hwnd=%x)\n", hwnd);
1345 if (uView == LVS_ICON)
1347 nItemWidth = infoPtr->iconSpacing.cx;
1349 else if (uView == LVS_REPORT)
1351 /* calculate width of header */
1352 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1353 for (i = 0; i < nHeaderItemCount; i++)
1355 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1357 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1363 /* get width of string */
1364 nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1366 /* default label size */
1367 if (GETITEMCOUNT(infoPtr) == 0)
1369 nItemWidth = DEFAULT_COLUMN_WIDTH;
1373 if (nItemWidth == 0)
1375 nItemWidth = DEFAULT_LABEL_WIDTH;
1380 nItemWidth += WIDTH_PADDING;
1382 if (infoPtr->himlSmall != NULL)
1384 nItemWidth += infoPtr->iconSize.cx;
1387 if (infoPtr->himlState != NULL)
1389 nItemWidth += infoPtr->iconSize.cx;
1400 * Retrieves and saves important text metrics info for the current
1404 * [I] HWND : window handle
1407 static VOID LISTVIEW_SaveTextMetrics(HWND hwnd)
1409 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1411 HDC hdc = GetDC(hwnd);
1412 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1413 INT oldHeight, oldACW;
1415 GetTextMetricsW(hdc, &tm);
1417 oldHeight = infoPtr->ntmHeight;
1418 oldACW = infoPtr->ntmAveCharWidth;
1419 infoPtr->ntmHeight = tm.tmHeight;
1420 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1422 SelectObject(hdc, hOldFont);
1423 ReleaseDC(hwnd, hdc);
1424 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1425 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1431 * Calculates the height of an item.
1434 * [I] HWND : window handle
1437 * Returns item height.
1439 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1441 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1442 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1443 INT nItemHeight = 0;
1445 if (uView == LVS_ICON)
1447 nItemHeight = infoPtr->iconSpacing.cy;
1451 if(infoPtr->himlState || infoPtr->himlSmall)
1452 nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1454 nItemHeight = infoPtr->ntmHeight;
1461 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1463 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1464 LISTVIEW_SELECTION *selection;
1465 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1468 TRACE("Selections are:\n");
1469 for (i = 0; i < topSelection; i++)
1471 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1472 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1478 * A compare function for selection ranges
1481 * [I] LPVOID : Item 1;
1482 * [I] LPVOID : Item 2;
1483 * [I] LPARAM : flags
1486 * >0 : if Item 1 > Item 2
1487 * <0 : if Item 2 > Item 1
1488 * 0 : if Item 1 == Item 2
1490 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1493 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1494 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1495 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1496 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1510 * Adds a selection range.
1513 * [I] HWND : window handle
1514 * [I] INT : lower item index
1515 * [I] INT : upper item index
1520 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1522 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1523 LISTVIEW_SELECTION *selection;
1524 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1525 BOOL lowerzero=FALSE;
1527 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1528 selection->lower = lItem;
1529 selection->upper = uItem;
1531 TRACE("Add range %i - %i\n", lItem, uItem);
1534 LISTVIEW_SELECTION *checkselection,*checkselection2;
1535 INT index,mergeindex;
1537 /* find overlapping selections */
1538 /* we want to catch adjacent ranges so expand our range by 1 */
1541 if (selection->lower == 0)
1546 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1547 LISTVIEW_CompareSelectionRanges,
1549 selection->upper --;
1553 selection->lower ++;
1557 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1558 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1559 checkselection->upper);
1561 checkselection->lower = min(selection->lower,checkselection->lower);
1562 checkselection->upper = max(selection->upper,checkselection->upper);
1564 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1565 checkselection->upper);
1567 COMCTL32_Free(selection);
1569 /* merge now common selection ranges in the lower group*/
1572 checkselection->upper ++;
1573 if (checkselection->lower == 0)
1576 checkselection->lower --;
1578 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1579 checkselection->upper);
1581 /* not sorted yet */
1582 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1583 LISTVIEW_CompareSelectionRanges, 0,
1586 checkselection->upper --;
1590 checkselection->lower ++;
1592 if (mergeindex >=0 && mergeindex != index)
1594 TRACE("Merge with index %i\n",mergeindex);
1595 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1597 checkselection->lower = min(checkselection->lower,
1598 checkselection2->lower);
1599 checkselection->upper = max(checkselection->upper,
1600 checkselection2->upper);
1601 COMCTL32_Free(checkselection2);
1602 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1606 while (mergeindex > -1 && mergeindex <index);
1608 /* merge now common selection ranges in the upper group*/
1611 checkselection->upper ++;
1612 if (checkselection->lower == 0)
1615 checkselection->lower --;
1617 TRACE("search upper range %i (%lu - %lu)\n",index,
1618 checkselection->lower, checkselection->upper);
1620 /* not sorted yet */
1621 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1623 LISTVIEW_CompareSelectionRanges, 0,
1626 checkselection->upper --;
1630 checkselection->lower ++;
1632 if (mergeindex >=0 && mergeindex !=index)
1634 TRACE("Merge with index %i\n",mergeindex);
1635 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1637 checkselection->lower = min(checkselection->lower,
1638 checkselection2->lower);
1639 checkselection->upper = max(checkselection->upper,
1640 checkselection2->upper);
1641 COMCTL32_Free(checkselection2);
1642 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1645 while (mergeindex > -1);
1650 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1651 LISTVIEW_CompareSelectionRanges, 0,
1654 TRACE("Insert before index %i\n",index);
1657 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1662 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1667 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1668 LISTVIEW_PrintSelectionRanges(hwnd);
1673 * check if a specified index is selected.
1676 * [I] HWND : window handle
1677 * [I] INT : item index
1682 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1684 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1685 LISTVIEW_SELECTION selection;
1688 selection.upper = nItem;
1689 selection.lower = nItem;
1691 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1692 LISTVIEW_CompareSelectionRanges,
1702 * Removes all selection ranges
1705 * HWND: window handle
1711 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1713 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1714 LISTVIEW_SELECTION *selection;
1718 TRACE("(0x%x)\n",hwnd);
1720 ZeroMemory(&item,sizeof(item));
1721 item.stateMask = LVIS_SELECTED;
1725 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1728 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1729 for (i = selection->lower; i<=selection->upper; i++)
1730 LISTVIEW_SetItemState(hwnd,i,&item);
1731 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1734 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1742 * Removes a range selections.
1745 * [I] HWND : window handle
1746 * [I] INT : lower item index
1747 * [I] INT : upper item index
1752 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1754 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1755 LISTVIEW_SELECTION removeselection,*checkselection;
1758 removeselection.lower = lItem;
1759 removeselection.upper = uItem;
1761 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1762 LISTVIEW_PrintSelectionRanges(hwnd);
1764 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1765 LISTVIEW_CompareSelectionRanges,
1772 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1775 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1776 checkselection->upper);
1779 if ((checkselection->upper == removeselection.upper) &&
1780 (checkselection->lower == removeselection.lower))
1782 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1785 /* case 2: engulf */
1786 else if (((checkselection->upper < removeselection.upper) &&
1787 (checkselection->lower > removeselection.lower))||
1788 ((checkselection->upper <= removeselection.upper) &&
1789 (checkselection->lower > removeselection.lower)) ||
1790 ((checkselection->upper < removeselection.upper) &&
1791 (checkselection->lower >= removeselection.lower)))
1794 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1795 /* do it again because others may also get caught */
1797 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1799 /* case 3: overlap upper */
1800 else if ((checkselection->upper < removeselection.upper) &&
1801 (checkselection->lower < removeselection.lower))
1803 checkselection->upper = removeselection.lower - 1;
1805 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1807 /* case 4: overlap lower */
1808 else if ((checkselection->upper > removeselection.upper) &&
1809 (checkselection->lower > removeselection.lower))
1811 checkselection->lower = removeselection.upper + 1;
1813 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1815 /* case 5: fully internal */
1816 else if (checkselection->upper == removeselection.upper)
1817 checkselection->upper = removeselection.lower - 1;
1818 else if (checkselection->lower == removeselection.lower)
1819 checkselection->lower = removeselection.upper + 1;
1822 /* bisect the range */
1823 LISTVIEW_SELECTION *newselection;
1825 newselection = (LISTVIEW_SELECTION *)
1826 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1827 newselection -> lower = checkselection->lower;
1828 newselection -> upper = removeselection.lower - 1;
1829 checkselection -> lower = removeselection.upper + 1;
1830 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1832 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1834 LISTVIEW_PrintSelectionRanges(hwnd);
1839 * Updates the various indices after an item has been inserted or deleted.
1842 * [I] HWND : window handle
1843 * [I] INT : item index
1844 * [I] INT : Direction of shift, +1 or -1.
1849 static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
1851 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1852 LISTVIEW_SELECTION selection,*checkselection;
1855 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1857 selection.upper = nItem;
1858 selection.lower = nItem;
1860 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1861 LISTVIEW_CompareSelectionRanges,
1862 0,DPAS_SORTED|DPAS_INSERTAFTER);
1864 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1866 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1867 if ((checkselection->lower >= nItem)&&
1868 ((int)(checkselection->lower + direction) >= 0))
1869 checkselection->lower += direction;
1870 if ((checkselection->upper >= nItem)&&
1871 ((int)(checkselection->upper + direction) >= 0))
1872 checkselection->upper += direction;
1876 /* Note that the following will fail if direction != +1 and -1 */
1877 if (infoPtr->nSelectionMark > nItem)
1878 infoPtr->nSelectionMark += direction;
1879 else if (infoPtr->nSelectionMark == nItem)
1882 infoPtr->nSelectionMark += direction;
1883 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1884 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1887 if (infoPtr->nFocusedItem > nItem)
1888 infoPtr->nFocusedItem += direction;
1889 else if (infoPtr->nFocusedItem == nItem)
1892 infoPtr->nFocusedItem += direction;
1895 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1896 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1897 if (infoPtr->nFocusedItem >= 0)
1898 LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
1901 /* But we are not supposed to modify nHotItem! */
1907 * Adds a block of selections.
1910 * [I] HWND : window handle
1911 * [I] INT : item index
1916 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1918 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1919 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1920 INT nLast = max(infoPtr->nSelectionMark, nItem);
1927 ZeroMemory(&item,sizeof(item));
1928 item.stateMask = LVIS_SELECTED;
1929 item.state = LVIS_SELECTED;
1931 for (i = nFirst; i <= nLast; i++)
1932 LISTVIEW_SetItemState(hwnd,i,&item);
1934 LISTVIEW_SetItemFocus(hwnd, nItem);
1935 infoPtr->nSelectionMark = nItem;
1941 * Adds a single selection.
1944 * [I] HWND : window handle
1945 * [I] INT : item index
1950 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1952 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1955 ZeroMemory(&item,sizeof(item));
1956 item.state = LVIS_SELECTED;
1957 item.stateMask = LVIS_SELECTED;
1959 LISTVIEW_SetItemState(hwnd,nItem,&item);
1961 LISTVIEW_SetItemFocus(hwnd, nItem);
1962 infoPtr->nSelectionMark = nItem;
1967 * Selects or unselects an item.
1970 * [I] HWND : window handle
1971 * [I] INT : item index
1977 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1979 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1983 ZeroMemory(&item,sizeof(item));
1984 item.stateMask = LVIS_SELECTED;
1986 if (LISTVIEW_IsSelected(hwnd,nItem))
1988 LISTVIEW_SetItemState(hwnd,nItem,&item);
1993 item.state = LVIS_SELECTED;
1994 LISTVIEW_SetItemState(hwnd,nItem,&item);
1998 LISTVIEW_SetItemFocus(hwnd, nItem);
1999 infoPtr->nSelectionMark = nItem;
2006 * Selects items based on view coordinates.
2009 * [I] HWND : window handle
2010 * [I] RECT : selection rectangle
2015 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
2017 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2022 ZeroMemory(&item,sizeof(item));
2023 item.stateMask = LVIS_SELECTED;
2025 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
2027 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
2029 if (PtInRect(&rcSelRect, ptItem) != FALSE)
2030 item.state = LVIS_SELECTED;
2033 LISTVIEW_SetItemState(hwnd,i,&item);
2039 * Sets a single group selection.
2042 * [I] HWND : window handle
2043 * [I] INT : item index
2048 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
2050 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2051 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2054 ZeroMemory(&item,sizeof(item));
2055 item.stateMask = LVIS_SELECTED;
2057 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2062 if (infoPtr->nSelectionMark == -1)
2064 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2068 nFirst = min(infoPtr->nSelectionMark, nItem);
2069 nLast = max(infoPtr->nSelectionMark, nItem);
2072 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2074 if ((i < nFirst) || (i > nLast))
2077 item.state = LVIS_SELECTED;
2078 LISTVIEW_SetItemState(hwnd,i,&item);
2086 LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcItem);
2087 LISTVIEW_GetItemBoundBox(hwnd, infoPtr->nSelectionMark, &rcSelMark);
2088 rcSel.left = min(rcSelMark.left, rcItem.left);
2089 rcSel.top = min(rcSelMark.top, rcItem.top);
2090 rcSel.right = max(rcSelMark.right, rcItem.right);
2091 rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
2092 LISTVIEW_SetSelectionRect(hwnd, rcSel);
2093 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2094 nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
2095 infoPtr->nSelectionMark,
2096 rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
2097 rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2101 LISTVIEW_SetItemFocus(hwnd, nItem);
2106 * Manages the item focus.
2109 * [I] HWND : window handle
2110 * [I] INT : item index
2113 * TRUE : focused item changed
2114 * FALSE : focused item has NOT changed
2116 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
2118 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2119 BOOL bResult = FALSE;
2122 if (infoPtr->nFocusedItem != nItem)
2124 if (infoPtr->nFocusedItem >= 0)
2126 INT oldFocus = infoPtr->nFocusedItem;
2128 infoPtr->nFocusedItem = -1;
2129 ZeroMemory(&lvItem, sizeof(lvItem));
2130 lvItem.stateMask = LVIS_FOCUSED;
2131 LISTVIEW_SetItemState(hwnd, oldFocus, &lvItem);
2135 lvItem.state = LVIS_FOCUSED;
2136 lvItem.stateMask = LVIS_FOCUSED;
2137 LISTVIEW_SetItemState(hwnd, nItem, &lvItem);
2139 infoPtr->nFocusedItem = nItem;
2140 LISTVIEW_EnsureVisible(hwnd, nItem, FALSE);
2148 * Sets a single selection.
2151 * [I] HWND : window handle
2152 * [I] INT : item index
2157 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
2159 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2162 ZeroMemory(&lvItem, sizeof(lvItem));
2163 lvItem.stateMask = LVIS_FOCUSED;
2164 LISTVIEW_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
2166 LISTVIEW_RemoveAllSelections(hwnd);
2168 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
2169 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2170 LISTVIEW_SetItemState(hwnd, nItem, &lvItem);
2172 infoPtr->nFocusedItem = nItem;
2173 infoPtr->nSelectionMark = nItem;
2178 * Set selection(s) with keyboard.
2181 * [I] HWND : window handle
2182 * [I] INT : item index
2185 * SUCCESS : TRUE (needs to be repainted)
2186 * FAILURE : FALSE (nothing has changed)
2188 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
2190 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2191 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2192 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2193 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2194 BOOL bResult = FALSE;
2196 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2198 if (lStyle & LVS_SINGLESEL)
2201 LISTVIEW_SetSelection(hwnd, nItem);
2202 ListView_EnsureVisible(hwnd, nItem, FALSE);
2209 LISTVIEW_SetGroupSelection(hwnd, nItem);
2213 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
2218 LISTVIEW_SetSelection(hwnd, nItem);
2219 ListView_EnsureVisible(hwnd, nItem, FALSE);
2229 * Called when the mouse is being actively tracked and has hovered for a specified
2233 * [I] HWND : window handle
2234 * [I] wParam : key indicator
2235 * [I] lParam : mouse position
2238 * 0 if the message was processed, non-zero if there was an error
2241 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2242 * over the item for a certain period of time.
2245 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
2247 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2250 pt.x = (INT)LOWORD(lParam);
2251 pt.y = (INT)HIWORD(lParam);
2253 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2254 /* select the item under the cursor */
2255 LISTVIEW_MouseSelection(hwnd, pt);
2263 * Called whenever WM_MOUSEMOVE is received.
2266 * [I] HWND : window handle
2267 * [I] wParam : key indicators
2268 * [I] lParam : cursor position
2271 * 0 if the message is processed, non-zero if there was an error
2273 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
2275 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2276 TRACKMOUSEEVENT trackinfo;
2278 /* see if we are supposed to be tracking mouse hovering */
2279 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2280 /* fill in the trackinfo struct */
2281 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2282 trackinfo.dwFlags = TME_QUERY;
2283 trackinfo.hwndTrack = hwnd;
2284 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2286 /* see if we are already tracking this hwnd */
2287 _TrackMouseEvent(&trackinfo);
2289 if(!(trackinfo.dwFlags & TME_HOVER)) {
2290 trackinfo.dwFlags = TME_HOVER;
2292 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2293 _TrackMouseEvent(&trackinfo);
2302 * Selects an item based on coordinates.
2305 * [I] HWND : window handle
2306 * [I] POINT : mouse click ccordinates
2309 * SUCCESS : item index
2312 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
2314 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2316 INT i,topindex,bottomindex;
2317 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2318 UINT uView = lStyle & LVS_TYPEMASK;
2320 topindex = LISTVIEW_GetTopIndex(hwnd);
2321 if (uView == LVS_REPORT)
2323 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
2324 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
2328 bottomindex = GETITEMCOUNT(infoPtr);
2331 for (i = topindex; i < bottomindex; i++)
2333 rcItem.left = LVIR_SELECTBOUNDS;
2334 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
2336 if (PtInRect(&rcItem, pt) != FALSE)
2351 * [IO] HDPA : dynamic pointer array handle
2352 * [I] INT : column index (subitem index)
2358 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2360 BOOL bResult = TRUE;
2364 for (i = 0; i < hdpaItems->nItemCount; i++)
2366 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2367 if (hdpaSubItems != NULL)
2369 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2381 * Removes a subitem at a given position.
2384 * [IO] HDPA : dynamic pointer array handle
2385 * [I] INT : subitem index
2391 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2393 LISTVIEW_SUBITEM *lpSubItem;
2396 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2398 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2399 if (lpSubItem != NULL)
2401 if (lpSubItem->iSubItem == nSubItem)
2404 if (is_textW(lpSubItem->pszText))
2405 COMCTL32_Free(lpSubItem->pszText);
2408 COMCTL32_Free(lpSubItem);
2410 /* free dpa memory */
2411 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2414 else if (lpSubItem->iSubItem > nSubItem)
2424 * Compares the item information.
2427 * [I] LISTVIEW_ITEM *: destination item
2428 * [I] LPLVITEM : source item
2429 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2432 * SUCCCESS : TRUE (EQUAL)
2433 * FAILURE : FALSE (NOT EQUAL)
2435 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2439 if ((lpItem != NULL) && (lpLVItem != NULL))
2441 if (lpLVItem->mask & LVIF_STATE)
2443 if ((lpItem->state & lpLVItem->stateMask) !=
2444 (lpLVItem->state & lpLVItem->stateMask))
2445 uChanged |= LVIF_STATE;
2448 if (lpLVItem->mask & LVIF_IMAGE)
2450 if (lpItem->iImage != lpLVItem->iImage)
2451 uChanged |= LVIF_IMAGE;
2454 if (lpLVItem->mask & LVIF_PARAM)
2456 if (lpItem->lParam != lpLVItem->lParam)
2457 uChanged |= LVIF_PARAM;
2460 if (lpLVItem->mask & LVIF_INDENT)
2462 if (lpItem->iIndent != lpLVItem->iIndent)
2463 uChanged |= LVIF_INDENT;
2466 if (lpLVItem->mask & LVIF_TEXT)
2468 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2470 if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2471 uChanged |= LVIF_TEXT;
2475 if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2477 uChanged |= LVIF_TEXT;
2481 if (lpLVItem->pszText)
2483 if (lpItem->pszText)
2485 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2486 if (pszText && strcmpW(pszText, lpItem->pszText))
2487 uChanged |= LVIF_TEXT;
2488 textfreeT(pszText, isW);
2492 uChanged |= LVIF_TEXT;
2497 if (lpItem->pszText)
2498 uChanged |= LVIF_TEXT;
2509 * Initializes item attributes.
2512 * [I] HWND : window handle
2513 * [O] LISTVIEW_ITEM *: destination item
2514 * [I] LPLVITEM : source item
2515 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2521 static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem,
2522 LPLVITEMW lpLVItem, BOOL isW)
2524 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2525 BOOL bResult = FALSE;
2527 if ((lpItem != NULL) && (lpLVItem != NULL))
2531 if (lpLVItem->mask & LVIF_STATE)
2533 lpItem->state &= ~lpLVItem->stateMask;
2534 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2537 if (lpLVItem->mask & LVIF_IMAGE)
2538 lpItem->iImage = lpLVItem->iImage;
2540 if (lpLVItem->mask & LVIF_PARAM)
2541 lpItem->lParam = lpLVItem->lParam;
2543 if (lpLVItem->mask & LVIF_INDENT)
2544 lpItem->iIndent = lpLVItem->iIndent;
2546 if (lpLVItem->mask & LVIF_TEXT)
2548 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2550 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2553 if (is_textW(lpItem->pszText))
2554 COMCTL32_Free(lpItem->pszText);
2556 lpItem->pszText = LPSTR_TEXTCALLBACKW;
2559 bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
2568 * Initializes subitem attributes.
2570 * NOTE: The documentation specifies that the operation fails if the user
2571 * tries to set the indent of a subitem.
2574 * [I] HWND : window handle
2575 * [O] LISTVIEW_SUBITEM *: destination subitem
2576 * [I] LPLVITEM : source subitem
2577 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2583 static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2584 LPLVITEMW lpLVItem, BOOL isW)
2586 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2587 BOOL bResult = FALSE;
2589 TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
2590 hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW);
2592 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2594 if (!(lpLVItem->mask & LVIF_INDENT))
2598 lpSubItem->iSubItem = lpLVItem->iSubItem;
2600 if (lpLVItem->mask & LVIF_IMAGE)
2601 lpSubItem->iImage = lpLVItem->iImage;
2603 if (lpLVItem->mask & LVIF_TEXT)
2605 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2607 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2610 if (is_textW(lpSubItem->pszText))
2611 COMCTL32_Free(lpSubItem->pszText);
2613 lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2616 bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
2626 * Adds a subitem at a given position (column index).
2629 * [I] HWND : window handle
2630 * [I] LPLVITEM : new subitem atttributes
2631 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2637 static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2639 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2640 LISTVIEW_SUBITEM *lpSubItem = NULL;
2641 BOOL bResult = FALSE;
2643 INT nPosition, nItem;
2644 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2646 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2648 if (lStyle & LVS_OWNERDATA)
2651 if (lpLVItem != NULL)
2653 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2654 if (hdpaSubItems != NULL)
2656 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2657 if (lpSubItem != NULL)
2659 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2660 if (LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW))
2662 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2663 lpSubItem->iSubItem);
2664 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2665 if (nItem != -1) bResult = TRUE;
2671 /* cleanup if unsuccessful */
2672 if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2679 * Finds the dpa insert position (array index).
2682 * [I] HWND : window handle
2683 * [I] INT : subitem index
2689 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2691 LISTVIEW_SUBITEM *lpSubItem;
2694 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2696 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2697 if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2701 return hdpaSubItems->nItemCount;
2706 * Retrieves a listview subitem at a given position (column index).
2709 * [I] HWND : window handle
2710 * [I] INT : subitem index
2716 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2718 LISTVIEW_SUBITEM *lpSubItem;
2721 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2723 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2724 if (lpSubItem != NULL)
2726 if (lpSubItem->iSubItem == nSubItem)
2728 else if (lpSubItem->iSubItem > nSubItem)
2738 * Sets item attributes.
2741 * [I] HWND : window handle
2742 * [I] LPLVITEM : new item atttributes
2743 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2749 static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2751 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2752 BOOL bResult = FALSE;
2754 LISTVIEW_ITEM *lpItem;
2756 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2758 UINT uView = lStyle & LVS_TYPEMASK;
2762 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2764 if (lStyle & LVS_OWNERDATA)
2766 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2770 ZeroMemory(&itm, sizeof(itm));
2771 itm.mask = LVIF_STATE | LVIF_PARAM;
2772 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2773 itm.iItem = lpLVItem->iItem;
2775 ListView_GetItemW(hwnd, &itm);
2778 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2779 nmlv.uNewState = lpLVItem->state;
2780 nmlv.uOldState = itm.state;
2781 nmlv.uChanged = LVIF_STATE;
2782 nmlv.lParam = itm.lParam;
2783 nmlv.iItem = lpLVItem->iItem;
2785 if ((itm.state & lpLVItem->stateMask) !=
2786 (lpLVItem->state & lpLVItem->stateMask))
2789 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent
2790 * by LVS_OWERNDATA list controls
2792 if (lpLVItem->stateMask & LVIS_FOCUSED)
2794 if (lpLVItem->state & LVIS_FOCUSED)
2795 infoPtr->nFocusedItem = lpLVItem->iItem;
2796 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2797 infoPtr->nFocusedItem = -1;
2799 if (lpLVItem->stateMask & LVIS_SELECTED)
2801 if (lpLVItem->state & LVIS_SELECTED)
2803 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd);
2804 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2807 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2811 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2813 rcItem.left = LVIR_BOUNDS;
2814 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2815 if (!infoPtr->bIsDrawing)
2816 InvalidateRect(hwnd, &rcItem, TRUE);
2823 if (lpLVItem != NULL)
2825 if (lpLVItem->iSubItem == 0)
2827 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2828 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2830 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2833 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2834 nmlv.lParam = lpItem->lParam;
2835 uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2838 if (uChanged & LVIF_STATE)
2840 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2841 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2843 if (nmlv.uNewState & LVIS_SELECTED)
2846 * This is redundant if called through SetSelection
2848 * however is required if the used directly calls SetItem
2849 * to set the selection.
2851 if (lStyle & LVS_SINGLESEL)
2852 LISTVIEW_RemoveAllSelections(hwnd);
2854 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2857 else if (lpLVItem->stateMask & LVIS_SELECTED)
2859 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2862 if (nmlv.uNewState & LVIS_FOCUSED)
2865 * This is a fun hoop to jump to try to catch if
2866 * the user is calling us directly to call focus or if
2867 * this function is being called as a result of a
2868 * SetItemFocus call.
2870 if (infoPtr->nFocusedItem >= 0)
2871 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2875 nmlv.uChanged = uChanged;
2876 nmlv.iItem = lpLVItem->iItem;
2877 nmlv.lParam = lpItem->lParam;
2878 /* send LVN_ITEMCHANGING notification */
2879 listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv);
2881 /* copy information */
2882 bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW);
2884 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2885 based on the width of the items text */
2886 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2888 item_width = LISTVIEW_GetStringWidthT(hwnd, lpItem->pszText, TRUE);
2890 if(item_width > infoPtr->nItemWidth)
2891 infoPtr->nItemWidth = item_width;
2894 /* send LVN_ITEMCHANGED notification */
2895 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2904 rcItem.left = LVIR_BOUNDS;
2905 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2906 if (!infoPtr->bIsDrawing)
2907 InvalidateRect(hwnd, &rcItem, TRUE);
2919 * Sets subitem attributes.
2922 * [I] HWND : window handle
2923 * [I] LPLVITEM : new subitem atttributes
2924 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2930 static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2932 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2933 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2934 BOOL bResult = FALSE;
2936 LISTVIEW_SUBITEM *lpSubItem;
2939 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2941 if (lStyle & LVS_OWNERDATA)
2944 if (lpLVItem != NULL)
2946 if (lpLVItem->iSubItem > 0)
2948 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2949 if (hdpaSubItems != NULL)
2951 /* set subitem only if column is present */
2952 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2954 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2955 if (lpSubItem != NULL)
2956 bResult = LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW);
2958 bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW);
2960 rcItem.left = LVIR_BOUNDS;
2961 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2962 InvalidateRect(hwnd, &rcItem, FALSE);
2973 * Sets item attributes.
2976 * [I] HWND : window handle
2977 * [I] LPLVITEM : new item atttributes
2978 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2984 static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2986 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2988 if (!lpLVItem || lpLVItem->iItem < 0 ||
2989 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2991 if (lpLVItem->iSubItem == 0)
2992 return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW);
2994 return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW);
2999 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3002 * [I] HWND : window handle
3007 static INT LISTVIEW_GetTopIndex(HWND hwnd)
3009 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3010 UINT uView = lStyle & LVS_TYPEMASK;
3012 SCROLLINFO scrollInfo;
3014 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3015 scrollInfo.cbSize = sizeof(SCROLLINFO);
3016 scrollInfo.fMask = SIF_POS;
3018 if (uView == LVS_LIST)
3020 if ((lStyle & WS_HSCROLL) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo))
3021 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
3023 else if (uView == LVS_REPORT)
3025 if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo))
3026 nItem = scrollInfo.nPos;
3037 * [I] HWND : window handle
3038 * [I] HDC : device context handle
3039 * [I] INT : item index
3040 * [I] INT : subitem index
3041 * [I] RECT * : clipping rectangle
3046 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
3047 RECT rcItem, BOOL Selected)
3049 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3050 WCHAR szDispText[DISP_TEXT_SIZE];
3053 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
3056 INT nLabelWidth = 0;
3058 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
3061 /* get information needed for drawing the item */
3062 ZeroMemory(&lvItem, sizeof(lvItem));
3063 lvItem.mask = LVIF_TEXT;
3064 lvItem.iItem = nItem;
3065 lvItem.iSubItem = nSubItem;
3066 lvItem.cchTextMax = DISP_TEXT_SIZE;
3067 lvItem.pszText = szDispText;
3068 *lvItem.pszText = '\0';
3069 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3070 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3072 ZeroMemory(&lvColumn, sizeof(lvColumn));
3073 lvColumn.mask = LVCF_FMT;
3074 LISTVIEW_GetColumnT(hwnd, nSubItem, &lvColumn, TRUE);
3075 textLeft = rcItem.left;
3076 TRACE("lvColumn.fmt=%d\n", lvColumn.fmt);
3077 if (lvColumn.fmt != LVCFMT_LEFT)
3079 if ((nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE)))
3081 if (lvColumn.fmt == LVCFMT_RIGHT)
3082 textLeft = rcItem.right - nLabelWidth;
3084 textLeft = rcItem.left + (rcItem.right-rcItem.left-nLabelWidth)/2;
3089 /* redraw the background of the item */
3091 if(infoPtr->nColumnCount == (nSubItem + 1))
3092 rcTemp.right = infoPtr->rcList.right;
3094 rcTemp.right += WIDTH_PADDING;
3096 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3098 /* set item colors */
3099 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
3101 if (infoPtr->bFocus)
3103 SetBkColor(hdc, comctl32_color.clrHighlight);
3104 SetTextColor(hdc, comctl32_color.clrHighlightText);
3108 SetBkColor(hdc, comctl32_color.clr3dFace);
3109 SetTextColor(hdc, comctl32_color.clrBtnText);
3114 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3116 SetBkMode(hdc, TRANSPARENT);
3117 textoutOptions &= ~ETO_OPAQUE;
3121 SetBkMode(hdc, OPAQUE);
3122 SetBkColor(hdc, infoPtr->clrTextBk);
3125 SetTextColor(hdc, infoPtr->clrText);
3128 TRACE("drawing text %s, l=%d, t=%d, rect=(%d,%d)-(%d,%d)\n",
3129 debugstr_w(lvItem.pszText), textLeft, rcItem.top,
3130 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3131 ExtTextOutW(hdc, textLeft, rcItem.top, textoutOptions,
3132 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3136 /* fill in the gap */
3138 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
3140 CopyRect(&rec,&rcItem);
3141 rec.left = rec.right;
3142 rec.right = rec.left+REPORT_MARGINX;
3143 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3144 &rec, NULL, 0, NULL);
3146 CopyRect(&rec,&rcItem);
3147 rec.right = rec.left;
3148 rec.left = rec.left - REPORT_MARGINX;
3149 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3150 &rec, NULL, 0, NULL);
3160 * [I] HWND : window handle
3161 * [I] HDC : device context handle
3162 * [I] INT : item index
3163 * [I] RECT * : clipping rectangle
3168 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
3170 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3171 WCHAR szDispText[DISP_TEXT_SIZE];
3176 DWORD dwTextColor,dwTextX;
3177 BOOL bImage = FALSE;
3179 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
3182 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
3185 /* get information needed for drawing the item */
3186 ZeroMemory(&lvItem, sizeof(lvItem));
3187 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3188 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
3189 lvItem.iItem = nItem;
3190 lvItem.iSubItem = 0;
3191 lvItem.cchTextMax = DISP_TEXT_SIZE;
3192 lvItem.pszText = szDispText;
3193 *lvItem.pszText = '\0';
3194 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3195 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3197 /* redraw the background of the item */
3199 if(infoPtr->nColumnCount == (nItem + 1))
3200 rcTemp.right = infoPtr->rcList.right;
3202 rcTemp.right+=WIDTH_PADDING;
3204 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3207 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3209 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3212 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3216 if (infoPtr->himlState != NULL)
3218 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3219 if (uStateImage > 0)
3221 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3222 rcItem.top, ILD_NORMAL);
3225 rcItem.left += infoPtr->iconSize.cx;
3227 SuggestedFocus->left += infoPtr->iconSize.cx;
3232 if (infoPtr->himlSmall != NULL)
3234 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
3237 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3238 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3239 rcItem.top, ILD_SELECTED);
3241 else if (lvItem.iImage>=0)
3243 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3244 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3245 rcItem.top, ILD_NORMAL);
3248 rcItem.left += infoPtr->iconSize.cx;
3251 SuggestedFocus->left += infoPtr->iconSize.cx;
3255 /* Don't bother painting item being edited */
3256 if (infoPtr->Editing && lvItem.state & LVIS_FOCUSED && !FullSelect)
3259 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
3261 /* set item colors */
3262 dwBkColor = SetBkColor(hdc, comctl32_color.clrHighlight);
3263 dwTextColor = SetTextColor(hdc, comctl32_color.clrHighlightText);
3264 /* set raster mode */
3265 nMixMode = SetROP2(hdc, R2_XORPEN);
3267 else if ((GetWindowLongW(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3268 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
3270 dwBkColor = SetBkColor(hdc, comctl32_color.clr3dFace);
3271 dwTextColor = SetTextColor(hdc, comctl32_color.clrBtnText);
3272 /* set raster mode */
3273 nMixMode = SetROP2(hdc, R2_COPYPEN);
3277 /* set item colors */
3278 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3280 dwBkColor = GetBkColor(hdc);
3281 iBkMode = SetBkMode(hdc, TRANSPARENT);
3282 textoutOptions &= ~ETO_OPAQUE;
3286 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3287 iBkMode = SetBkMode(hdc, OPAQUE);
3290 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3291 /* set raster mode */
3292 nMixMode = SetROP2(hdc, R2_COPYPEN);
3295 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
3296 if (rcItem.left + nLabelWidth < rcItem.right)
3299 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3301 rcItem.right += IMAGE_PADDING;
3305 dwTextX = rcItem.left + 1;
3307 dwTextX += IMAGE_PADDING;
3309 if (lvItem.pszText) {
3310 TRACE("drawing text hwnd=%x, rect=(%d,%d)-(%d,%d)\n",
3311 hwnd, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3312 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3313 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3316 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3318 /* fill in the gap */
3320 CopyRect(&rec,&rcItem);
3321 rec.left = rec.right;
3322 rec.right = rec.left+REPORT_MARGINX;
3323 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3324 &rec, NULL, 0, NULL);
3328 CopyRect(SuggestedFocus,&rcItem);
3332 SetROP2(hdc, R2_COPYPEN);
3333 SetBkColor(hdc, dwBkColor);
3334 SetTextColor(hdc, dwTextColor);
3336 SetBkMode(hdc, iBkMode);
3342 * Draws an item when in large icon display mode.
3345 * [I] HWND : window handle
3346 * [I] HDC : device context handle
3347 * [I] INT : item index
3348 * [I] RECT : clipping rectangle
3349 * [O] RECT * : The text rectangle about which to draw the focus
3354 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3355 RECT *SuggestedFocus)
3357 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3358 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3360 UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
3362 /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
3365 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3366 hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3368 /* get information needed for drawing the item */
3369 ZeroMemory(&lvItem, sizeof(lvItem));
3370 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3371 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3372 lvItem.iItem = nItem;
3373 lvItem.iSubItem = 0;
3374 lvItem.cchTextMax = DISP_TEXT_SIZE;
3375 lvItem.pszText = szDispText;
3376 *lvItem.pszText = '\0';
3377 LISTVIEW_GetItemW(hwnd, &lvItem, FALSE);
3378 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3380 /* redraw the background of the item */
3382 if(infoPtr->nColumnCount == (nItem + 1))
3383 rcTemp.right = infoPtr->rcList.right;
3385 rcTemp.right+=WIDTH_PADDING;
3386 /* The comment doesn't say WIDTH_PADDING applies to large icons */
3388 TRACE("background rect (%d,%d)-(%d,%d)\n",
3389 rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
3391 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3394 /* Figure out text colours etc. depending on state
3395 * At least the following states exist; there may be more.
3396 * Many items may be selected
3397 * At most one item may have the focus
3398 * The application may not actually be active currently
3399 * 1. The item is not selected in any way
3400 * 2. The cursor is flying over the icon or text and the text is being
3401 * expanded because it is not fully displayed currently.
3402 * 3. The item is selected and is focussed, i.e. the user has not clicked
3403 * in the blank area of the window, and the window (or application?)
3404 * still has the focus.
3405 * 4. As 3 except that a different window has the focus
3406 * 5. The item is the selected item of all the items, but the user has
3407 * clicked somewhere else on the window.
3408 * Only a few of these are handled currently. In particular 2 is not yet
3409 * handled since we do not support the functionality currently (or at least
3410 * we didn't when I wrote this)
3413 if (lvItem.state & LVIS_SELECTED)
3415 /* set item colors */
3416 SetBkColor(hdc, comctl32_color.clrHighlight);
3417 SetTextColor(hdc, comctl32_color.clrHighlightText);
3418 SetBkMode (hdc, OPAQUE);
3419 /* set raster mode */
3420 SetROP2(hdc, R2_XORPEN);
3421 /* When exactly is it in XOR? while being dragged? */
3425 /* set item colors */
3426 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3428 SetBkMode(hdc, TRANSPARENT);
3432 SetBkMode(hdc, OPAQUE);
3433 SetBkColor(hdc, infoPtr->clrTextBk);
3436 SetTextColor(hdc, infoPtr->clrText);
3437 /* set raster mode */
3438 SetROP2(hdc, R2_COPYPEN);
3441 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3442 * wrapping and long words split.
3443 * In cases 1 and 4 only a portion of the text is displayed with word
3444 * wrapping and both word and end ellipsis. (I don't yet know about path
3447 uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
3450 DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3453 if (infoPtr->himlNormal != NULL)
3455 if (lvItem.iImage >= 0)
3457 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3459 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3463 /* Draw the text below the icon */
3465 /* Don't bother painting item being edited */
3466 if ((infoPtr->Editing && (lvItem.state & LVIS_FOCUSED)) ||
3467 !lstrlenW(lvItem.pszText))
3469 SetRectEmpty(SuggestedFocus);
3473 /* Since rcItem.left is left point of icon, compute left point of item box */
3474 rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3475 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3476 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3477 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3478 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3479 infoPtr->iconSize.cx, infoPtr->nItemWidth);
3480 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3481 infoPtr->rcList.left, infoPtr->rcList.top,
3482 infoPtr->rcList.right, infoPtr->rcList.bottom,
3483 infoPtr->rcView.left, infoPtr->rcView.top,
3484 infoPtr->rcView.right, infoPtr->rcView.bottom);
3486 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3487 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3492 /* I am sure of most of the uFormat values. However I am not sure about
3493 * whether we need or do not need the following:
3494 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3495 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3496 * We certainly do not need
3497 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3498 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3501 /* If the text is being drawn without clipping (i.e. the full text) then we
3502 * need to jump through a few hoops to ensure that it all gets displayed and
3503 * that the background is complete
3505 if (uFormat & DT_NOCLIP)
3508 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3509 int dx, dy, old_wid, new_wid;
3510 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3511 /* Microsoft, in their great wisdom, have decided that the rectangle
3512 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3513 * not the location. So we have to do the centring ourselves (and take
3514 * responsibility for agreeing off-by-one consistency with them).
3516 old_wid = rcItem.right-rcItem.left;
3517 new_wid = rcBack.right - rcBack.left;
3518 dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
3519 dy = rcBack.top - rcItem.top;
3520 OffsetRect (&rcItem, dx, dy);
3521 FillRect(hdc, &rcItem, hBrush);
3522 DeleteObject(hBrush);
3524 /* else ? What if we are losing the focus? will we not get a complete
3527 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3529 CopyRect(SuggestedFocus, &rcItem);
3534 * Draws listview items when in report display mode.
3537 * [I] HWND : window handle
3538 * [I] HDC : device context handle
3543 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3545 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3546 SCROLLINFO scrollInfo;
3547 INT nDrawPosY = infoPtr->rcList.top;
3549 RECT rcItem, rcTemp;
3554 DWORD cditemmode = CDRF_DODEFAULT;
3555 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3559 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3560 scrollInfo.cbSize = sizeof(SCROLLINFO);
3561 scrollInfo.fMask = SIF_POS;
3563 nItem = ListView_GetTopIndex(hwnd);
3565 /* add 1 for displaying a partial item at the bottom */
3566 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3567 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3569 /* send cache hint notification */
3570 if (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3574 nmlv.hdr.hwndFrom = hwnd;
3575 nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID);
3576 nmlv.hdr.code = LVN_ODCACHEHINT;
3580 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3584 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3585 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3586 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3588 /* clear the background of any part of the control that doesn't contain items */
3589 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3590 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3592 /* nothing to draw */
3593 if(GETITEMCOUNT(infoPtr) == 0)
3596 /* Get scroll bar info once before loop */
3597 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3598 scrollOffset = scrollInfo.nPos;
3600 for (; nItem < nLast; nItem++)
3602 RECT SuggestedFocusRect;
3605 if (lStyle & LVS_OWNERDRAWFIXED)
3607 UINT uID = GetWindowLongW( hwnd, GWL_ID);
3612 TRACE("Owner Drawn\n");
3613 dis.CtlType = ODT_LISTVIEW;
3616 dis.itemAction = ODA_DRAWENTIRE;
3619 if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3620 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3622 dis.hwndItem = hwnd;
3625 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3627 dis.rcItem.left = -scrollOffset;
3628 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3629 dis.rcItem.top = nDrawPosY;
3630 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3632 ZeroMemory(&item,sizeof(item));
3634 item.mask = LVIF_PARAM;
3635 ListView_GetItemW(hwnd, &item);
3637 dis.itemData = item.lParam;
3639 if (SendMessageW(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3641 nDrawPosY += infoPtr->nItemHeight;
3650 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3651 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3653 ir.left += REPORT_MARGINX;
3654 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3656 ir.bottom = ir.top + infoPtr->nItemHeight;
3658 CopyRect(&SuggestedFocusRect,&ir);
3661 for (j = 0; j < nColumnCount; j++)
3663 if (cdmode & CDRF_NOTIFYITEMDRAW)
3664 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3666 if (cditemmode & CDRF_SKIPDEFAULT)
3669 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3671 rcItem.left += REPORT_MARGINX;
3672 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3673 rcItem.top = nDrawPosY;
3674 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3676 /* Offset the Scroll Bar Pos */
3677 rcItem.left -= scrollOffset;
3678 rcItem.right -= scrollOffset;
3682 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3683 &SuggestedFocusRect);
3687 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, FullSelected);
3690 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3691 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3692 CDDS_ITEMPOSTPAINT);
3697 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3700 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3701 rop = SetROP2(hdc, R2_XORPEN);
3703 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3704 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3707 SetROP2(hdc, R2_COPYPEN);
3709 nDrawPosY += infoPtr->nItemHeight;
3715 * Retrieves the number of items that can fit vertically in the client area.
3718 * [I] HWND : window handle
3721 * Number of items per row.
3723 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3725 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3726 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3727 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3728 INT nCountPerRow = 1;
3732 if (uView != LVS_REPORT)
3734 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3735 if (nCountPerRow == 0) nCountPerRow = 1;
3739 return nCountPerRow;
3744 * Retrieves the number of items that can fit horizontally in the client
3748 * [I] HWND : window handle
3751 * Number of items per column.
3753 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3755 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3756 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3757 INT nCountPerColumn = 1;
3759 if (nListHeight > 0)
3761 TRACE("rcList=(%d,%d)-(%d,%d), nItemHeight=%d, CYHSCROLL=%d\n",
3762 infoPtr->rcList.left,infoPtr->rcList.top,
3763 infoPtr->rcList.right,infoPtr->rcList.bottom,
3764 infoPtr->nItemHeight,
3765 GetSystemMetrics(SM_CYHSCROLL));
3766 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3767 if (nCountPerColumn == 0) nCountPerColumn = 1;
3770 return nCountPerColumn;
3775 * Retrieves the number of columns needed to display all the items when in
3776 * list display mode.
3779 * [I] HWND : window handle
3782 * Number of columns.
3784 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3786 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3787 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3788 INT nColumnCount = 0;
3790 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3792 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3793 if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3796 return nColumnCount;
3802 * Draws listview items when in list display mode.
3805 * [I] HWND : window handle
3806 * [I] HDC : device context handle
3811 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3813 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3814 RECT rcItem, FocusRect, rcTemp;
3818 INT nCountPerColumn;
3819 INT nItemWidth = infoPtr->nItemWidth;
3820 INT nItemHeight = infoPtr->nItemHeight;
3821 DWORD cditemmode = CDRF_DODEFAULT;
3823 /* get number of fully visible columns */
3824 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3825 infoPtr->nColumnCount = nColumnCount;
3826 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3827 nItem = ListView_GetTopIndex(hwnd);
3828 TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3829 nColumnCount, nCountPerColumn, nItem);
3831 /* paint the background of the control that doesn't contain any items */
3832 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3833 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3835 /* nothing to draw, return here */
3836 if(GETITEMCOUNT(infoPtr) == 0)
3839 for (i = 0; i < nColumnCount; i++)
3841 for (j = 0; j < nCountPerColumn; j++, nItem++)
3843 if (nItem >= GETITEMCOUNT(infoPtr))
3846 if (cdmode & CDRF_NOTIFYITEMDRAW)
3847 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3849 if (cditemmode & CDRF_SKIPDEFAULT)
3852 rcItem.top = j * nItemHeight;
3853 rcItem.left = i * nItemWidth;
3854 rcItem.bottom = rcItem.top + nItemHeight;
3855 rcItem.right = rcItem.left + nItemWidth;
3856 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3860 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3861 Rectangle(hdc, FocusRect.left, FocusRect.top,
3862 FocusRect.right,FocusRect.bottom);
3864 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3865 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3866 CDDS_ITEMPOSTPAINT);
3874 * Draws listview items when in icon or small icon display mode.
3877 * [I] HWND : window handle
3878 * [I] HDC : device context handle
3883 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3885 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3888 RECT rcItem, SuggestedFocus, rcTemp;
3890 DWORD cditemmode = CDRF_DODEFAULT;
3893 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3894 /* DrawItem from erasing the incorrect background area */
3896 /* paint the background of the control that doesn't contain any items */
3897 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3898 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3900 /* nothing to draw, return here */
3901 if(GETITEMCOUNT(infoPtr) == 0)
3904 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3905 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3907 if (cdmode & CDRF_NOTIFYITEMDRAW)
3908 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3910 if (cditemmode & CDRF_SKIPDEFAULT)
3913 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3914 ptPosition.x += ptOrigin.x;
3915 ptPosition.y += ptOrigin.y;
3917 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3919 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3921 if (ptPosition.y < infoPtr->rcList.bottom)
3923 if (ptPosition.x < infoPtr->rcList.right)
3925 rcItem.top = ptPosition.y;
3926 rcItem.left = ptPosition.x;
3927 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3928 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3930 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3932 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3936 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3937 infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
3938 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3939 SuggestedFocus.right,SuggestedFocus.bottom);
3944 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3945 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3946 CDDS_ITEMPOSTPAINT);
3952 * Draws listview items.
3955 * [I] HWND : window handle
3956 * [I] HDC : device context handle
3961 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3963 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3964 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3970 infoPtr->bIsDrawing = TRUE;
3971 LISTVIEW_DumpListview (infoPtr, __LINE__);
3973 GetClientRect(hwnd, &rect);
3974 cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3976 if (cdmode == CDRF_SKIPDEFAULT) return;
3979 hOldFont = SelectObject(hdc, infoPtr->hFont);
3981 /* select the dotted pen (for drawing the focus box) */
3982 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3983 hOldPen = SelectObject(hdc, hPen);
3985 /* select transparent brush (for drawing the focus box) */
3986 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3989 if (uView == LVS_LIST)
3990 LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3991 else if (uView == LVS_REPORT)
3992 LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3993 else if (uView == LVS_SMALLICON)
3994 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3995 else if (uView == LVS_ICON)
3996 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3998 /* unselect objects */
3999 SelectObject(hdc, hOldFont);
4000 SelectObject(hdc, hOldPen);
4005 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4006 LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
4008 infoPtr->bIsDrawing = FALSE;
4014 * Calculates the approximate width and height of a given number of items.
4017 * [I] HWND : window handle
4018 * [I] INT : number of items
4023 * Returns a DWORD. The width in the low word and the height in high word.
4025 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
4026 WORD wWidth, WORD wHeight)
4028 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4029 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4030 INT nItemCountPerColumn = 1;
4031 INT nColumnCount = 0;
4032 DWORD dwViewRect = 0;
4034 if (nItemCount == -1)
4035 nItemCount = GETITEMCOUNT(infoPtr);
4037 if (uView == LVS_LIST)
4039 if (wHeight == 0xFFFF)
4041 /* use current height */
4042 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4045 if (wHeight < infoPtr->nItemHeight)
4046 wHeight = infoPtr->nItemHeight;
4050 if (infoPtr->nItemHeight > 0)
4052 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4053 if (nItemCountPerColumn == 0)
4054 nItemCountPerColumn = 1;
4056 if (nItemCount % nItemCountPerColumn != 0)
4057 nColumnCount = nItemCount / nItemCountPerColumn;
4059 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4063 /* Microsoft padding magic */
4064 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4065 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4067 dwViewRect = MAKELONG(wWidth, wHeight);
4069 else if (uView == LVS_REPORT)
4070 FIXME("uView == LVS_REPORT: not implemented\n");
4071 else if (uView == LVS_SMALLICON)
4072 FIXME("uView == LVS_SMALLICON: not implemented\n");
4073 else if (uView == LVS_ICON)
4074 FIXME("uView == LVS_ICON: not implemented\n");
4081 * Arranges listview items in icon display mode.
4084 * [I] HWND : window handle
4085 * [I] INT : alignment code
4091 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
4093 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4094 BOOL bResult = FALSE;
4096 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4101 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
4104 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
4107 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4109 case LVA_SNAPTOGRID:
4110 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4118 /* << LISTVIEW_CreateDragImage >> */
4123 * Removes all listview items and subitems.
4126 * [I] HWND : window handle
4132 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
4134 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4135 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4136 UINT uView = lStyle & LVS_TYPEMASK;
4137 LISTVIEW_ITEM *lpItem;
4138 LISTVIEW_SUBITEM *lpSubItem;
4141 BOOL bResult = FALSE;
4144 TRACE("(hwnd=%x,)\n", hwnd);
4146 LISTVIEW_RemoveAllSelections(hwnd);
4147 infoPtr->nSelectionMark=-1;
4148 infoPtr->nFocusedItem=-1;
4149 /* But we are supposed to leave nHotItem as is! */
4151 if (lStyle & LVS_OWNERDATA)
4153 infoPtr->hdpaItems->nItemCount = 0;
4154 InvalidateRect(hwnd, NULL, TRUE);
4158 if (GETITEMCOUNT(infoPtr) > 0)
4162 /* send LVN_DELETEALLITEMS notification */
4163 /* verify if subsequent LVN_DELETEITEM notifications should be
4165 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4167 bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &nmlv);
4169 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4171 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4172 if (hdpaSubItems != NULL)
4174 for (j = 1; j < hdpaSubItems->nItemCount; j++)
4176 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
4177 if (lpSubItem != NULL)
4179 /* free subitem string */
4180 if (is_textW(lpSubItem->pszText))
4181 COMCTL32_Free(lpSubItem->pszText);
4184 COMCTL32_Free(lpSubItem);
4188 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4193 /* send LVN_DELETEITEM notification */
4195 nmlv.lParam = lpItem->lParam;
4196 listview_notify(hwnd, LVN_DELETEITEM, &nmlv);
4199 /* free item string */
4200 if (is_textW(lpItem->pszText))
4201 COMCTL32_Free(lpItem->pszText);
4204 COMCTL32_Free(lpItem);
4207 DPA_Destroy(hdpaSubItems);
4211 /* reinitialize listview memory */
4212 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
4214 /* align items (set position of each item) */
4215 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4217 if (lStyle & LVS_ALIGNLEFT)
4219 LISTVIEW_AlignLeft(hwnd);
4223 LISTVIEW_AlignTop(hwnd);
4227 LISTVIEW_UpdateScroll(hwnd);
4229 /* invalidate client area (optimization needed) */
4230 InvalidateRect(hwnd, NULL, TRUE);
4238 * Removes a column from the listview control.
4241 * [I] HWND : window handle
4242 * [I] INT : column index
4248 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
4250 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4251 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4252 UINT uOwnerData = GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA;
4253 BOOL bResult = FALSE;
4255 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
4257 bResult = uOwnerData ? TRUE : LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
4259 /* Need to reset the item width when deleting a column */
4260 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
4262 /* reset scroll parameters */
4263 if (uView == LVS_REPORT)
4265 /* update scrollbar(s) */
4266 LISTVIEW_UpdateScroll(hwnd);
4268 /* refresh client area */
4269 InvalidateRect(hwnd, NULL, FALSE);
4278 * Removes an item from the listview control.
4281 * [I] HWND : window handle
4282 * [I] INT : item index
4288 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
4290 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4291 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4292 UINT uView = lStyle & LVS_TYPEMASK;
4293 LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID);
4295 BOOL bResult = FALSE;
4297 LISTVIEW_ITEM *lpItem;
4298 LISTVIEW_SUBITEM *lpSubItem;
4302 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
4305 /* First, send LVN_DELETEITEM notification. */
4306 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4307 nmlv.hdr.hwndFrom = hwnd;
4308 nmlv.hdr.idFrom = lCtrlId;
4309 nmlv.hdr.code = LVN_DELETEITEM;
4311 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
4315 /* remove it from the selection range */
4316 ZeroMemory(&item,sizeof(item));
4317 item.stateMask = LVIS_SELECTED;
4318 LISTVIEW_SetItemState(hwnd,nItem,&item);
4320 if (lStyle & LVS_OWNERDATA)
4322 infoPtr->hdpaItems->nItemCount --;
4323 InvalidateRect(hwnd, NULL, TRUE);
4327 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4329 /* initialize memory */
4330 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4332 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4333 if (hdpaSubItems != NULL)
4335 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4337 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4338 if (lpSubItem != NULL)
4340 /* free item string */
4341 if (is_textW(lpSubItem->pszText))
4342 COMCTL32_Free(lpSubItem->pszText);
4345 COMCTL32_Free(lpSubItem);
4349 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4352 /* free item string */
4353 if (is_textW(lpItem->pszText))
4354 COMCTL32_Free(lpItem->pszText);
4357 COMCTL32_Free(lpItem);
4360 bResult = DPA_Destroy(hdpaSubItems);
4363 LISTVIEW_ShiftIndices(hwnd,nItem,-1);
4365 /* align items (set position of each item) */
4366 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4368 if (lStyle & LVS_ALIGNLEFT)
4369 LISTVIEW_AlignLeft(hwnd);
4371 LISTVIEW_AlignTop(hwnd);
4374 LISTVIEW_UpdateScroll(hwnd);
4376 /* refresh client area */
4377 InvalidateRect(hwnd, NULL, TRUE);
4386 * Return edit control handle of current edit label
4389 * [I] HWND : window handle
4395 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4397 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4398 return infoPtr->hwndEdit;
4404 * Callback implementation for editlabel control
4407 * [I] HWND : window handle
4408 * [I] LPSTR : modified text
4409 * [I] DWORD : item index
4410 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4416 static BOOL LISTVIEW_EndEditLabelT(HWND hwnd, LPWSTR pszText, DWORD nItem, BOOL isW)
4418 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4419 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4420 NMLVDISPINFOW dispInfo;
4421 LISTVIEW_ITEM *lpItem;
4423 LISTVIEW_ITEM lvItemRef;
4425 BOOL bResult = TRUE;
4427 TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd, debugstr_t(pszText, isW), nItem, isW);
4429 if (!(lStyle & LVS_OWNERDATA))
4431 if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4434 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4439 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4440 ZeroMemory(&item,sizeof(item));
4443 item.mask = LVIF_PARAM | LVIF_STATE;
4444 ListView_GetItemW(hwnd, &item);
4445 lvItemRef.state = item.state;
4446 lvItemRef.iImage = item.iImage;
4447 lvItemRef.lParam = item.lParam;
4448 lpItem = &lvItemRef;
4451 ZeroMemory(&dispInfo, sizeof(dispInfo));
4452 dispInfo.item.mask = 0;
4453 dispInfo.item.iItem = nItem;
4454 dispInfo.item.state = lpItem->state;
4455 dispInfo.item.stateMask = 0;
4456 dispInfo.item.pszText = pszText;
4457 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4458 dispInfo.item.iImage = lpItem->iImage;
4459 dispInfo.item.lParam = lpItem->lParam;
4460 infoPtr->Editing = FALSE;
4462 /* Do we need to update the Item Text */
4463 if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, isW))
4464 if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
4465 bResult = textsetptrT(&lpItem->pszText, pszText, isW);
4472 * Callback implementation for editlabel control
4475 * [I] HWND : window handle
4476 * [I] LPSTR : modified text
4477 * [I] DWORD : item index
4483 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem)
4485 return LISTVIEW_EndEditLabelT(hwnd, pszText, nItem, TRUE);
4490 * Callback implementation for editlabel control
4493 * [I] HWND : window handle
4494 * [I] LPSTR : modified text
4495 * [I] DWORD : item index
4501 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem)
4503 return LISTVIEW_EndEditLabelT(hwnd, (LPWSTR)pszText, nItem, FALSE);
4508 * Begin in place editing of specified list view item
4511 * [I] HWND : window handle
4512 * [I] INT : item index
4513 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4519 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW)
4521 NMLVDISPINFOW dispInfo;
4523 LISTVIEW_ITEM *lpItem;
4525 HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE);
4526 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4528 WCHAR szDispText[DISP_TEXT_SIZE];
4530 LISTVIEW_ITEM lvItemRef;
4531 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4533 if (~GetWindowLongW(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4536 /* Is the EditBox still there, if so remove it */
4537 if(infoPtr->hwndEdit != 0)
4540 infoPtr->hwndEdit = 0;
4543 LISTVIEW_SetSelection(hwnd, nItem);
4544 LISTVIEW_SetItemFocus(hwnd, nItem);
4546 if (!(lStyle & LVS_OWNERDATA))
4548 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4551 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4557 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4558 ZeroMemory(&item, sizeof(item));
4561 item.mask = LVIF_PARAM | LVIF_STATE;
4562 ListView_GetItemW(hwnd, &item);
4563 lvItemRef.iImage = item.iImage;
4564 lvItemRef.state = item.state;
4565 lvItemRef.lParam = item.lParam;
4566 lpItem = &lvItemRef;
4569 /* get information needed for drawing the item */
4570 ZeroMemory(&lvItem, sizeof(lvItem));
4571 lvItem.mask = LVIF_TEXT;
4572 lvItem.iItem = nItem;
4573 lvItem.iSubItem = 0;
4574 lvItem.cchTextMax = DISP_TEXT_SIZE;
4575 lvItem.pszText = szDispText;
4576 *lvItem.pszText = '\0';
4577 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW);
4579 ZeroMemory(&dispInfo, sizeof(dispInfo));
4580 dispInfo.item.mask = 0;
4581 dispInfo.item.iItem = nItem;
4582 dispInfo.item.state = lpItem->state;
4583 dispInfo.item.stateMask = 0;
4584 dispInfo.item.pszText = lvItem.pszText;
4585 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4586 dispInfo.item.iImage = lpItem->iImage;
4587 dispInfo.item.lParam = lpItem->lParam;
4589 if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW))
4592 rect.left = LVIR_LABEL;
4593 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4596 if (!(hedit = CreateEditLabelT(szDispText , WS_VISIBLE,
4597 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, hwnd, hinst,
4598 isW ? LISTVIEW_EndEditLabelW : (EditlblCallbackW)LISTVIEW_EndEditLabelA,
4602 infoPtr->hwndEdit = hedit;
4604 ShowWindow(infoPtr->hwndEdit,SW_NORMAL);
4605 infoPtr->Editing = TRUE;
4606 SetFocus(infoPtr->hwndEdit);
4607 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4608 return infoPtr->hwndEdit;
4614 * Ensures the specified item is visible, scrolling into view if necessary.
4617 * [I] HWND : window handle
4618 * [I] INT : item index
4619 * [I] BOOL : partially or entirely visible
4625 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4627 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4628 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4629 INT nScrollPosHeight = 0;
4630 INT nScrollPosWidth = 0;
4631 SCROLLINFO scrollInfo;
4633 BOOL bRedraw = FALSE;
4635 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4636 scrollInfo.cbSize = sizeof(SCROLLINFO);
4637 scrollInfo.fMask = SIF_POS;
4639 /* ALWAYS bPartial == FALSE, FOR NOW! */
4641 rcItem.left = LVIR_BOUNDS;
4642 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4644 if (rcItem.left < infoPtr->rcList.left)
4646 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4650 if (uView == LVS_LIST)
4652 nScrollPosWidth = infoPtr->nItemWidth;
4653 rcItem.left += infoPtr->rcList.left;
4655 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4657 nScrollPosWidth = 1;
4658 rcItem.left += infoPtr->rcList.left;
4661 /* When in LVS_REPORT view, the scroll position should
4663 if (nScrollPosWidth != 0)
4665 if (rcItem.left % nScrollPosWidth == 0)
4666 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4668 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4670 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4674 else if (rcItem.right > infoPtr->rcList.right)
4676 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4680 if (uView == LVS_LIST)
4682 rcItem.right -= infoPtr->rcList.right;
4683 nScrollPosWidth = infoPtr->nItemWidth;
4685 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4687 rcItem.right -= infoPtr->rcList.right;
4688 nScrollPosWidth = 1;
4691 /* When in LVS_REPORT view, the scroll position should
4693 if (nScrollPosWidth != 0)
4695 if (rcItem.right % nScrollPosWidth == 0)
4696 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4698 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4700 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4705 if (rcItem.top < infoPtr->rcList.top)
4709 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4711 if (uView == LVS_REPORT)
4713 rcItem.top -= infoPtr->rcList.top;
4714 nScrollPosHeight = infoPtr->nItemHeight;
4716 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4718 nScrollPosHeight = 1;
4719 rcItem.top += infoPtr->rcList.top;
4722 if (rcItem.top % nScrollPosHeight == 0)
4723 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4725 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4727 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4730 else if (rcItem.bottom > infoPtr->rcList.bottom)
4734 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4736 if (uView == LVS_REPORT)
4738 rcItem.bottom -= infoPtr->rcList.bottom;
4739 nScrollPosHeight = infoPtr->nItemHeight;
4741 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4743 nScrollPosHeight = 1;
4744 rcItem.bottom -= infoPtr->rcList.bottom;
4747 if (rcItem.bottom % nScrollPosHeight == 0)
4748 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4750 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4752 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4758 InvalidateRect(hwnd,NULL,TRUE);
4764 * Retrieves the nearest item, given a position and a direction.
4767 * [I] HWND : window handle
4768 * [I] POINT : start position
4769 * [I] UINT : direction
4772 * Item index if successdful, -1 otherwise.
4774 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4776 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4781 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4782 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4783 ((vkDirection == VK_UP) ? "VK_UP" :
4784 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4786 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4788 ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4789 LISTVIEW_GetOrigin(hwnd, &lvIntHit.ht.pt);
4790 lvIntHit.ht.pt.x += pt.x;
4791 lvIntHit.ht.pt.y += pt.y;
4793 if (vkDirection == VK_DOWN)
4794 lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4795 else if (vkDirection == VK_UP)
4796 lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4797 else if (vkDirection == VK_LEFT)
4798 lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4799 else if (vkDirection == VK_RIGHT)
4800 lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4802 if (PtInRect(&rcView, lvIntHit.ht.pt) == FALSE)
4806 nItem = LISTVIEW_SuperHitTestItem(hwnd, &lvIntHit, TRUE);
4807 return nItem == -1 ? lvIntHit.iDistItem : nItem;
4816 * Searches for an item with specific characteristics.
4819 * [I] hwnd : window handle
4820 * [I] nStart : base item index
4821 * [I] lpFindInfo : item information to look for
4824 * SUCCESS : index of item
4827 static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart,
4828 LPLVFINDINFOW lpFindInfo)
4830 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4832 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4836 INT nLast = GETITEMCOUNT(infoPtr);
4838 if ((nItem >= -1) && (lpFindInfo != NULL))
4840 ZeroMemory(&lvItem, sizeof(lvItem));
4842 if (lpFindInfo->flags & LVFI_PARAM)
4844 lvItem.mask |= LVIF_PARAM;
4847 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4849 lvItem.mask |= LVIF_TEXT;
4850 lvItem.pszText = szDispText;
4851 lvItem.cchTextMax = DISP_TEXT_SIZE;
4854 if (lpFindInfo->flags & LVFI_WRAP)
4857 if (lpFindInfo->flags & LVFI_NEARESTXY)
4859 ptItem.x = lpFindInfo->pt.x;
4860 ptItem.y = lpFindInfo->pt.y;
4865 while (nItem < nLast)
4867 if (lpFindInfo->flags & LVFI_NEARESTXY)
4869 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4870 lpFindInfo->vkDirection);
4873 /* get position of the new item index */
4874 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4885 lvItem.iItem = nItem;
4886 lvItem.iSubItem = 0;
4887 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
4889 if (lvItem.mask & LVIF_TEXT)
4891 if (lpFindInfo->flags & LVFI_PARTIAL)
4893 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4898 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4903 if (lvItem.mask & LVIF_PARAM)
4905 if (lpFindInfo->lParam != lvItem.lParam)
4931 * Searches for an item with specific characteristics.
4934 * [I] hwnd : window handle
4935 * [I] nStart : base item index
4936 * [I] lpFindInfo : item information to look for
4939 * SUCCESS : index of item
4942 static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart,
4943 LPLVFINDINFOA lpFindInfo)
4945 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4949 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4950 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4951 res = LISTVIEW_FindItemW(hwnd, nStart, &fiw);
4952 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4958 * Retrieves the background color of the listview control.
4961 * [I] HWND : window handle
4964 * COLORREF associated with the background.
4966 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4968 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4970 return infoPtr->clrBk;
4975 * Retrieves the background image of the listview control.
4978 * [I] HWND : window handle
4979 * [O] LPLVMKBIMAGE : background image attributes
4985 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4987 /* FIXME (listview, "empty stub!\n"); */
4993 * Retrieves the callback mask.
4996 * [I] HWND : window handle
5001 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
5003 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5005 return infoPtr->uCallbackMask;
5010 * Retrieves column attributes.
5013 * [I] HWND : window handle
5014 * [I] INT : column index
5015 * [IO] LPLVCOLUMNW : column information
5016 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5017 * otherwise it is in fact a LPLVCOLUMNA
5023 static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
5025 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5027 BOOL bResult = FALSE;
5029 if (lpColumn != NULL)
5032 /* initialize memory */
5033 ZeroMemory(&hdi, sizeof(hdi));
5035 if (lpColumn->mask & LVCF_FMT)
5036 hdi.mask |= HDI_FORMAT;
5038 if (lpColumn->mask & LVCF_WIDTH)
5039 hdi.mask |= HDI_WIDTH;
5041 if (lpColumn->mask & LVCF_TEXT)
5043 hdi.mask |= HDI_TEXT;
5044 hdi.cchTextMax = lpColumn->cchTextMax;
5045 hdi.pszText = lpColumn->pszText;
5048 if (lpColumn->mask & LVCF_IMAGE)
5049 hdi.mask |= HDI_IMAGE;
5051 if (lpColumn->mask & LVCF_ORDER)
5052 hdi.mask |= HDI_ORDER;
5055 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
5057 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
5059 if (bResult != FALSE)
5061 if (lpColumn->mask & LVCF_FMT)
5065 if (hdi.fmt & HDF_LEFT)
5066 lpColumn->fmt |= LVCFMT_LEFT;
5067 else if (hdi.fmt & HDF_RIGHT)
5068 lpColumn->fmt |= LVCFMT_RIGHT;
5069 else if (hdi.fmt & HDF_CENTER)
5070 lpColumn->fmt |= LVCFMT_CENTER;
5072 if (hdi.fmt & HDF_IMAGE)
5073 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
5075 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
5076 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
5079 if (lpColumn->mask & LVCF_WIDTH)
5080 lpColumn->cx = hdi.cxy;
5082 if (lpColumn->mask & LVCF_IMAGE)
5083 lpColumn->iImage = hdi.iImage;
5085 if (lpColumn->mask & LVCF_ORDER)
5086 lpColumn->iOrder = hdi.iOrder;
5088 TRACE("(hwnd=%x, col=%d, lpColumn=%s, isW=%d)\n",
5089 hwnd, nItem, debuglvcolumn_t(lpColumn, isW), isW);
5098 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
5100 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
5107 for (i = 0; i < iCount; i++)
5115 * Retrieves the column width.
5118 * [I] HWND : window handle
5119 * [I] int : column index
5122 * SUCCESS : column width
5125 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
5127 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5128 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5129 INT nColumnWidth = 0;
5132 if (uView == LVS_LIST)
5134 nColumnWidth = infoPtr->nItemWidth;
5136 else if (uView == LVS_REPORT)
5138 /* get column width from header */
5139 ZeroMemory(&hdi, sizeof(hdi));
5140 hdi.mask = HDI_WIDTH;
5141 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
5142 nColumnWidth = hdi.cxy;
5145 return nColumnWidth;
5150 * In list or report display mode, retrieves the number of items that can fit
5151 * vertically in the visible area. In icon or small icon display mode,
5152 * retrieves the total number of visible items.
5155 * [I] HWND : window handle
5158 * Number of fully visible items.
5160 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
5162 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5163 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5166 if (uView == LVS_LIST)
5168 if (infoPtr->rcList.right > infoPtr->nItemWidth)
5170 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
5171 LISTVIEW_GetCountPerColumn(hwnd);
5174 else if (uView == LVS_REPORT)
5176 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
5180 nItemCount = GETITEMCOUNT(infoPtr);
5186 /* LISTVIEW_GetEditControl */
5190 * Retrieves the extended listview style.
5193 * [I] HWND : window handle
5196 * SUCCESS : previous style
5199 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
5201 LISTVIEW_INFO *infoPtr;
5203 /* make sure we can get the listview info */
5204 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
5207 return (infoPtr->dwExStyle);
5212 * Retrieves the handle to the header control.
5215 * [I] HWND : window handle
5220 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
5222 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5224 return infoPtr->hwndHeader;
5227 /* LISTVIEW_GetHotCursor */
5231 * Returns the time that the mouse cursor must hover over an item
5232 * before it is selected.
5235 * [I] HWND : window handle
5238 * Returns the previously set hover time or (DWORD)-1 to indicate that the
5239 * hover time is set to the default hover time.
5241 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
5243 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5245 return infoPtr->dwHoverTime;
5250 * Retrieves an image list handle.
5253 * [I] HWND : window handle
5254 * [I] INT : image list identifier
5257 * SUCCESS : image list handle
5260 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
5262 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5263 HIMAGELIST himl = NULL;
5268 himl = infoPtr->himlNormal;
5271 himl = infoPtr->himlSmall;
5274 himl = infoPtr->himlState;
5278 return (LRESULT)himl;
5281 /* LISTVIEW_GetISearchString */
5285 * Retrieves item attributes.
5288 * [I] hwnd : window handle
5289 * [IO] lpLVItem : item info
5290 * [I] internal : if true then we will use tricks that avoid copies
5291 * but are not compatible with the regular interface
5292 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5293 * if FALSE, the lpLVItem is a LPLVITEMA.
5299 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
5301 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5302 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5303 NMLVDISPINFOW dispInfo;
5304 LISTVIEW_SUBITEM *lpSubItem;
5305 LISTVIEW_ITEM *lpItem;
5308 INT* piImage = (INT*)&null;
5309 LPWSTR* ppszText= (LPWSTR*)&null;
5310 LPARAM* plParam = (LPARAM*)&null;
5312 if (internal && !isW)
5314 ERR("We can't have internal non-Unicode GetItem!\n");
5318 /* In the following:
5319 * lpLVItem describes the information requested by the user
5320 * lpItem/lpSubItem is what we have
5321 * dispInfo is a structure we use to request the missing
5322 * information from the application
5325 TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n",
5326 hwnd, debuglvitem_t(lpLVItem, isW), internal, isW);
5328 if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5329 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5332 ZeroMemory(&dispInfo, sizeof(dispInfo));
5334 if (lStyle & LVS_OWNERDATA)
5336 if (lpLVItem->mask & ~LVIF_STATE)
5338 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5339 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5340 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5341 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5344 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5346 lpLVItem->state = 0;
5347 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5348 lpLVItem->state |= LVIS_FOCUSED;
5349 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5350 lpLVItem->state |= LVIS_SELECTED;
5356 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5357 if (hdpaSubItems == NULL) return FALSE;
5359 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5362 ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5363 if (lpLVItem->iSubItem == 0)
5365 piImage=&lpItem->iImage;
5366 ppszText=&lpItem->pszText;
5367 plParam=&lpItem->lParam;
5368 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5370 dispInfo.item.mask |= LVIF_STATE;
5371 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5376 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5377 if (lpSubItem != NULL)
5379 piImage=&lpSubItem->iImage;
5380 ppszText=&lpSubItem->pszText;
5384 if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
5386 dispInfo.item.mask |= LVIF_IMAGE;
5389 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5391 dispInfo.item.mask |= LVIF_TEXT;
5392 dispInfo.item.pszText = lpLVItem->pszText;
5393 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5394 if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5395 *dispInfo.item.pszText = '\0';
5396 if (dispInfo.item.pszText && (*ppszText == NULL))
5397 *dispInfo.item.pszText = '\0';
5400 if (dispInfo.item.mask != 0)
5402 /* We don't have all the requested info, query the application */
5403 dispInfo.item.iItem = lpLVItem->iItem;
5404 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5405 dispInfo.item.lParam = lpItem->lParam;
5406 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5407 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5410 if (dispInfo.item.mask & LVIF_IMAGE)
5412 lpLVItem->iImage = dispInfo.item.iImage;
5413 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
5414 *piImage = dispInfo.item.iImage;
5416 else if (lpLVItem->mask & LVIF_IMAGE)
5418 lpLVItem->iImage = *piImage;
5421 if (dispInfo.item.mask & LVIF_PARAM)
5423 lpLVItem->lParam = dispInfo.item.lParam;
5424 if (dispInfo.item.mask & LVIF_DI_SETITEM)
5425 *plParam = dispInfo.item.lParam;
5427 else if (lpLVItem->mask & LVIF_PARAM)
5428 lpLVItem->lParam = lpItem->lParam;
5430 if (dispInfo.item.mask & LVIF_TEXT)
5432 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5433 textsetptrT(ppszText, dispInfo.item.pszText, isW);
5435 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5436 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5437 if (lpLVItem->pszText != dispInfo.item.pszText)
5438 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5441 else if (lpLVItem->mask & LVIF_TEXT)
5443 if (internal) lpLVItem->pszText = *ppszText;
5444 else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5447 if (lpLVItem->iSubItem == 0)
5449 if (dispInfo.item.mask & LVIF_STATE)
5451 lpLVItem->state = lpItem->state;
5452 lpLVItem->state &= ~dispInfo.item.stateMask;
5453 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5455 lpLVItem->state &= ~LVIS_SELECTED;
5456 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5457 LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem))
5458 lpLVItem->state |= LVIS_SELECTED;
5460 else if (lpLVItem->mask & LVIF_STATE)
5462 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5464 lpLVItem->state &= ~LVIS_SELECTED;
5465 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5466 LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5467 lpLVItem->state |= LVIS_SELECTED;
5470 if (lpLVItem->mask & LVIF_PARAM)
5471 lpLVItem->lParam = lpItem->lParam;
5473 if (lpLVItem->mask & LVIF_INDENT)
5474 lpLVItem->iIndent = lpItem->iIndent;
5480 /* LISTVIEW_GetHotCursor */
5484 * Retrieves the index of the hot item.
5487 * [I] HWND : window handle
5490 * SUCCESS : hot item index
5491 * FAILURE : -1 (no hot item)
5493 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5495 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5497 return infoPtr->nHotItem;
5500 /* LISTVIEW_GetHoverTime */
5504 * Retrieves the number of items in the listview control.
5507 * [I] HWND : window handle
5512 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5514 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5516 return GETITEMCOUNT(infoPtr);
5521 * Retrieves the rectangle enclosing the item icon and text.
5524 * [I] HWND : window handle
5525 * [I] INT : item index
5526 * [O] LPRECT : coordinate information
5532 static BOOL LISTVIEW_GetItemBoundBox(HWND hwnd, INT nItem, LPRECT lpRect)
5534 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5535 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5536 UINT uView = lStyle & LVS_TYPEMASK;
5537 BOOL bResult = FALSE;
5539 LISTVIEW_ITEM *lpItem;
5540 INT nCountPerColumn;
5543 TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd, nItem, lpRect);
5545 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5548 if (uView == LVS_LIST)
5551 nItem = nItem - ListView_GetTopIndex(hwnd);
5552 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5555 nRow = nItem % nCountPerColumn;
5558 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5563 lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5564 lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5569 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5570 lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
5573 else if (uView == LVS_REPORT)
5576 lpRect->left = REPORT_MARGINX;
5577 lpRect->top = ((nItem - ListView_GetTopIndex(hwnd)) *
5578 infoPtr->nItemHeight) + infoPtr->rcList.top;
5580 if (!(lStyle & LVS_NOSCROLL))
5582 SCROLLINFO scrollInfo;
5583 /* Adjust position by scrollbar offset */
5584 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5585 scrollInfo.cbSize = sizeof(SCROLLINFO);
5586 scrollInfo.fMask = SIF_POS;
5587 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5588 lpRect->left -= scrollInfo.nPos;
5591 else /* either LVS_ICON or LVS_SMALLICON */
5593 if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
5595 if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
5598 lpRect->left = lpItem->ptPosition.x;
5599 lpRect->top = lpItem->ptPosition.y;
5604 lpRect->right = lpRect->left + infoPtr->nItemWidth;
5605 lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
5606 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5607 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
5613 * Retrieves the position (upper-left) of the listview control item.
5614 * Note that for LVS_ICON style, the upper-left is that of the icon
5615 * and not the bounding box.
5618 * [I] HWND : window handle
5619 * [I] INT : item index
5620 * [O] LPPOINT : coordinate information
5626 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, LPPOINT lpptPosition)
5628 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5629 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5630 BOOL bResult = FALSE;
5633 TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd, nItem, lpptPosition);
5635 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5636 (lpptPosition != NULL))
5638 bResult = LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcBounding);
5639 lpptPosition->x = rcBounding.left;
5640 lpptPosition->y = rcBounding.top;
5641 if (uView == LVS_ICON)
5643 lpptPosition->y += ICON_TOP_PADDING;
5644 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5646 TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
5647 lpptPosition->x, lpptPosition->y);
5653 * Update the bounding rectangle around the text under a large icon.
5654 * This depends on whether it has the focus or not.
5655 * On entry the rectangle's top, left and right should be set.
5656 * On return the bottom will also be set and the width may have been
5659 * This appears to be weird, even in the Microsoft implementation.
5662 static void ListView_UpdateLargeItemLabelRect (
5663 HWND hwnd, /* The window of the listview */
5664 const LISTVIEW_INFO *infoPtr, /* The listview itself */
5665 int nItem, /* The item for which we are calculating this */
5666 RECT *rect) /* The rectangle to be updated */
5668 HDC hdc = GetDC (hwnd);
5669 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
5671 if (infoPtr->bFocus && infoPtr->nFocusedItem == nItem)
5673 /* We (aim to) display the full text. In Windows 95 it appears to
5674 * calculate the size assuming the specified font and then it draws
5675 * the text in that region with the specified font except scaled to
5676 * 10 point (or the height of the system font or ...). Thus if the
5677 * window has 24 point Helvetica the highlit rectangle will be
5678 * taller than the text and if it is 7 point Helvetica then the text
5680 * For now we will simply say that it is the correct size to display
5681 * the text in the specified font.
5684 lvItem.mask = LVIF_TEXT;
5685 lvItem.iItem = nItem;
5686 lvItem.iSubItem = 0;
5687 /* We will specify INTERNAL and so will receive back a const
5688 * pointer to the text, rather than specifying a buffer to which
5691 LISTVIEW_GetItemW (hwnd, &lvItem, TRUE);
5692 DrawTextW (hdc, lvItem.pszText, -1, rect, DT_CALCRECT |
5693 DT_NOCLIP | DT_EDITCONTROL | DT_TOP | DT_CENTER |
5694 DT_WORDBREAK | DT_NOPREFIX);
5695 /* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
5699 /* As far as I can see the text region seems to be trying to be
5700 * "tall enough for two lines of text". Once again (comctl32.dll ver
5701 * 5.81?) it measures this on the basis of the selected font and then
5702 * draws it with the same font except in 10 point size. This can lead
5703 * to more or less than the two rows appearing.
5704 * Question; are we supposed to be including DT_EXTERNALLEADING?
5705 * Question; should the width be shrunk to the space required to
5706 * display the two lines?
5708 rect->bottom = rect->top + 2 * infoPtr->ntmHeight;
5711 SelectObject (hdc, hOldFont);
5712 ReleaseDC (hwnd, hdc);
5717 * Retrieves the bounding rectangle for a listview control item.
5720 * [I] HWND : window handle
5721 * [I] INT : item index
5722 * [IO] LPRECT : bounding rectangle coordinates
5723 * lprc->left specifies the portion of the item for which the bounding
5724 * rectangle will be retrieved.
5726 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5727 * including the icon and label.
5728 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5729 * LVIR_LABEL Returns the bounding rectangle of the item text.
5730 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5731 * rectangles, but excludes columns in report view.
5738 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5739 * upon whether the window has the focus currently and on whether the item
5740 * is the one with the focus. Ensure that the control's record of which
5741 * item has the focus agrees with the items' records.
5743 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5745 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5746 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5747 BOOL bResult = FALSE;
5756 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5758 if (uView & LVS_REPORT)
5760 ZeroMemory(&lvItem, sizeof(lvItem));
5761 lvItem.mask = LVIF_INDENT;
5762 lvItem.iItem = nItem;
5763 lvItem.iSubItem = 0;
5764 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
5767 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5768 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5775 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5780 if (!LISTVIEW_GetItemPosition(hwnd, nItem, &ptItem)) break;
5781 if (uView == LVS_ICON)
5783 if (infoPtr->himlNormal != NULL)
5785 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5788 lprc->left = ptItem.x + ptOrigin.x;
5789 lprc->top = ptItem.y + ptOrigin.y;
5790 lprc->right = lprc->left + infoPtr->iconSize.cx;
5791 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5792 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5796 else if (uView == LVS_SMALLICON)
5798 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5801 lprc->left = ptItem.x + ptOrigin.x;
5802 lprc->top = ptItem.y + ptOrigin.y;
5803 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5805 if (infoPtr->himlState != NULL)
5806 lprc->left += infoPtr->iconSize.cx;
5808 if (infoPtr->himlSmall != NULL)
5809 lprc->right = lprc->left + infoPtr->iconSize.cx;
5811 lprc->right = lprc->left;
5817 lprc->left = ptItem.x;
5818 if (uView & LVS_REPORT)
5819 lprc->left += nIndent;
5820 lprc->top = ptItem.y;
5821 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5823 if (infoPtr->himlState != NULL)
5824 lprc->left += infoPtr->iconSize.cx;
5826 if (infoPtr->himlSmall != NULL)
5827 lprc->right = lprc->left + infoPtr->iconSize.cx;
5829 lprc->right = lprc->left;
5834 if (!LISTVIEW_GetItemPosition(hwnd, nItem, &ptItem)) break;
5835 if (uView == LVS_ICON)
5837 if (infoPtr->himlNormal != NULL)
5839 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5842 lprc->left = ptItem.x + ptOrigin.x;
5843 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5844 ICON_BOTTOM_PADDING);
5845 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5846 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5848 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5849 lprc->right = lprc->left + nLabelWidth;
5854 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5855 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, lprc);
5857 lprc->bottom = lprc->top + infoPtr->ntmHeight + HEIGHT_PADDING;
5861 else if (uView == LVS_SMALLICON)
5863 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5866 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5867 lprc->top = ptItem.y + ptOrigin.y;
5868 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5870 if (infoPtr->himlState != NULL)
5871 lprc->left += infoPtr->iconSize.cx;
5873 if (infoPtr->himlSmall != NULL)
5874 lprc->left += infoPtr->iconSize.cx;
5876 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5877 nLabelWidth += TRAILING_PADDING;
5878 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5879 lprc->right = lprc->left + nLabelWidth;
5881 lprc->right = nLeftPos + infoPtr->nItemWidth;
5887 if (uView == LVS_REPORT)
5888 nLeftPos = lprc->left = ptItem.x + nIndent;
5890 nLeftPos = lprc->left = ptItem.x;
5891 lprc->top = ptItem.y;
5892 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5894 if (infoPtr->himlState != NULL)
5895 lprc->left += infoPtr->iconSize.cx;
5897 if (infoPtr->himlSmall != NULL)
5898 lprc->left += infoPtr->iconSize.cx;
5900 if (uView != LVS_REPORT)
5902 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5903 nLabelWidth += TRAILING_PADDING;
5904 if (infoPtr->himlSmall)
5905 nLabelWidth += IMAGE_PADDING;
5908 nLabelWidth = LISTVIEW_GetColumnWidth(hwnd, 0)-lprc->left;
5909 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5910 lprc->right = lprc->left + nLabelWidth;
5912 lprc->right = nLeftPos + infoPtr->nItemWidth;
5917 if (!LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcInternal)) break;
5918 ptItem.x = rcInternal.left;
5919 ptItem.y = rcInternal.top;
5920 if (uView == LVS_ICON)
5922 if (infoPtr->himlNormal != NULL)
5924 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5927 INT text_left, text_right, icon_left, text_pos_x;
5928 /* for style LVS_ICON bounds
5929 * left = min(icon.left, text.left)
5930 * right = max(icon.right, text.right)
5931 * top = boundbox.top + NOTHITABLE
5932 * bottom = text.bottom + 1
5935 icon_left = text_left = ptItem.x;
5937 /* Correct ptItem to icon upper-left */
5938 icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5939 ptItem.y += ICON_TOP_PADDING;
5941 /* Compute the label left and right */
5942 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5943 text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
5946 text_left += text_pos_x / 2;
5947 text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
5952 text_right = text_left + infoPtr->iconSpacing.cx - 1;
5955 /* Compute rectangle w/o the text height */
5956 lprc->left = min(icon_left, text_left) + ptOrigin.x;
5957 lprc->right = max(icon_left + infoPtr->iconSize.cx,
5958 text_right) + ptOrigin.x;
5959 lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
5960 lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
5961 + infoPtr->iconSize.cy + 1
5962 + ICON_BOTTOM_PADDING;
5964 CopyRect (&label_rect, lprc);
5965 label_rect.top = lprc->bottom;
5966 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, &label_rect);
5967 UnionRect (lprc, lprc, &label_rect);
5971 else if (uView == LVS_SMALLICON)
5973 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5976 lprc->left = ptItem.x + ptOrigin.x;
5977 lprc->right = lprc->left;
5978 lprc->top = ptItem.y + ptOrigin.y;
5979 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5980 if (infoPtr->himlState != NULL)
5981 lprc->right += infoPtr->iconSize.cx;
5982 if (infoPtr->himlSmall != NULL)
5983 lprc->right += infoPtr->iconSize.cx;
5985 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5986 nLabelWidth += TRAILING_PADDING;
5987 if (infoPtr->himlSmall)
5988 nLabelWidth += IMAGE_PADDING;
5989 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5990 lprc->right += nLabelWidth;
5992 lprc->right = lprc->left + infoPtr->nItemWidth;
5998 lprc->left = ptItem.x;
5999 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
6000 lprc->left += nIndent;
6001 lprc->right = lprc->left;
6002 lprc->top = ptItem.y;
6003 lprc->bottom = lprc->top + infoPtr->nItemHeight;
6005 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
6008 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
6009 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
6011 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
6015 if (infoPtr->himlState != NULL)
6016 lprc->right += infoPtr->iconSize.cx;
6018 if (infoPtr->himlSmall != NULL)
6019 lprc->right += infoPtr->iconSize.cx;
6021 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6022 nLabelWidth += TRAILING_PADDING;
6023 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
6024 lprc->right += nLabelWidth;
6026 lprc->right = lprc->left + infoPtr->nItemWidth;
6031 case LVIR_SELECTBOUNDS:
6032 if (!LISTVIEW_GetItemPosition(hwnd, nItem, &ptItem)) break;
6033 if (uView == LVS_ICON)
6035 if (infoPtr->himlNormal != NULL)
6037 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
6040 lprc->left = ptItem.x + ptOrigin.x;
6041 lprc->top = ptItem.y + ptOrigin.y;
6042 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
6043 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
6047 else if (uView == LVS_SMALLICON)
6049 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
6052 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
6053 lprc->top = ptItem.y + ptOrigin.y;
6054 lprc->bottom = lprc->top + infoPtr->nItemHeight;
6056 if (infoPtr->himlState != NULL)
6057 lprc->left += infoPtr->iconSize.cx;
6059 lprc->right = lprc->left;
6061 if (infoPtr->himlSmall != NULL)
6062 lprc->right += infoPtr->iconSize.cx;
6064 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6065 nLabelWidth += TRAILING_PADDING;
6066 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6067 lprc->right += nLabelWidth;
6069 lprc->right = nLeftPos + infoPtr->nItemWidth;
6075 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
6076 nLeftPos = lprc->left = ptItem.x + nIndent;
6078 nLeftPos = lprc->left = ptItem.x;
6079 lprc->top = ptItem.y;
6080 lprc->bottom = lprc->top + infoPtr->nItemHeight;
6082 if (infoPtr->himlState != NULL)
6083 lprc->left += infoPtr->iconSize.cx;
6085 lprc->right = lprc->left;
6087 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
6090 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
6091 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
6093 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
6097 if (infoPtr->himlSmall != NULL)
6098 lprc->right += infoPtr->iconSize.cx;
6100 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6101 nLabelWidth += TRAILING_PADDING;
6102 if (infoPtr->himlSmall)
6103 nLabelWidth += IMAGE_PADDING;
6104 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6105 lprc->right += nLabelWidth;
6107 lprc->right = nLeftPos + infoPtr->nItemWidth;
6112 TRACE("result %s (%d,%d)-(%d,%d)\n",
6113 (bResult) ? "TRUE" : "FALSE",
6114 lprc->left, lprc->top, lprc->right, lprc->bottom);
6117 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
6118 lprc->left, lprc->top, lprc->right, lprc->bottom);
6124 static LRESULT LISTVIEW_GetSubItemRect(HWND hwnd, INT nItem, INT nSubItem, INT
6127 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6130 TRACE("(hwnd=%x, nItem=%d, nSubItem=%d lprc=%p)\n", hwnd, nItem, nSubItem,
6133 if (!(uView & LVS_REPORT))
6136 if (flags & LVIR_ICON)
6138 FIXME("Unimplemented LVIR_ICON\n");
6143 int top = min(((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0))->nColumnCount,
6146 LISTVIEW_GetItemRect(hwnd,nItem,lprc);
6147 for (count = 0; count < top; count++)
6148 lprc->left += LISTVIEW_GetColumnWidth(hwnd,count);
6150 lprc->right = LISTVIEW_GetColumnWidth(hwnd,(nSubItem-1)) +
6159 * Retrieves the width of a label.
6162 * [I] HWND : window handle
6165 * SUCCESS : string width (in pixels)
6168 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
6170 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6171 INT nLabelWidth = 0;
6174 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
6176 ZeroMemory(&lvItem, sizeof(lvItem));
6177 lvItem.mask = LVIF_TEXT;
6178 lvItem.iItem = nItem;
6179 lvItem.cchTextMax = DISP_TEXT_SIZE;
6180 lvItem.pszText = szDispText;
6181 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6182 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
6189 * Retrieves the spacing between listview control items.
6192 * [I] HWND : window handle
6193 * [I] BOOL : flag for small or large icon
6196 * Horizontal + vertical spacing
6198 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
6200 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6203 if (bSmall == FALSE)
6205 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6209 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
6210 if ((style & LVS_TYPEMASK) == LVS_ICON)
6211 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6213 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6220 * Retrieves the state of a listview control item.
6223 * [I] HWND : window handle
6224 * [I] INT : item index
6225 * [I] UINT : state mask
6228 * State specified by the mask.
6230 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
6232 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6236 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6238 ZeroMemory(&lvItem, sizeof(lvItem));
6239 lvItem.iItem = nItem;
6240 lvItem.stateMask = uMask;
6241 lvItem.mask = LVIF_STATE;
6242 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6243 uState = lvItem.state;
6251 * Retrieves the text of a listview control item or subitem.
6254 * [I] hwnd : window handle
6255 * [I] nItem : item index
6256 * [IO] lpLVItem : item information
6257 * [I] isW : TRUE if lpLVItem is Unicode
6260 * SUCCESS : string length
6263 static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6265 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6268 if (lpLVItem != NULL)
6270 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6272 lpLVItem->mask = LVIF_TEXT;
6273 lpLVItem->iItem = nItem;
6274 if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
6275 nLength = textlenT(lpLVItem->pszText, isW);
6284 * Searches for an item based on properties + relationships.
6287 * [I] HWND : window handle
6288 * [I] INT : item index
6289 * [I] INT : relationship flag
6292 * SUCCESS : item index
6295 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
6297 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6298 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6300 LVFINDINFOW lvFindInfo;
6301 INT nCountPerColumn;
6304 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6306 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6308 if (uFlags & LVNI_CUT)
6311 if (uFlags & LVNI_DROPHILITED)
6312 uMask |= LVIS_DROPHILITED;
6314 if (uFlags & LVNI_FOCUSED)
6315 uMask |= LVIS_FOCUSED;
6317 if (uFlags & LVNI_SELECTED)
6318 uMask |= LVIS_SELECTED;
6320 if (uFlags & LVNI_ABOVE)
6322 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6327 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6333 lvFindInfo.flags = LVFI_NEARESTXY;
6334 lvFindInfo.vkDirection = VK_UP;
6335 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6336 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6338 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6343 else if (uFlags & LVNI_BELOW)
6345 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6347 while (nItem < GETITEMCOUNT(infoPtr))
6350 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6356 lvFindInfo.flags = LVFI_NEARESTXY;
6357 lvFindInfo.vkDirection = VK_DOWN;
6358 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6359 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6361 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6366 else if (uFlags & LVNI_TOLEFT)
6368 if (uView == LVS_LIST)
6370 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6371 while (nItem - nCountPerColumn >= 0)
6373 nItem -= nCountPerColumn;
6374 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6378 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6380 lvFindInfo.flags = LVFI_NEARESTXY;
6381 lvFindInfo.vkDirection = VK_LEFT;
6382 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6383 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6385 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6390 else if (uFlags & LVNI_TORIGHT)
6392 if (uView == LVS_LIST)
6394 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6395 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6397 nItem += nCountPerColumn;
6398 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6402 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6404 lvFindInfo.flags = LVFI_NEARESTXY;
6405 lvFindInfo.vkDirection = VK_RIGHT;
6406 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6407 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6409 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6418 /* search by index */
6419 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6421 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
6430 /* LISTVIEW_GetNumberOfWorkAreas */
6434 * Retrieves the origin coordinates when in icon or small icon display mode.
6437 * [I] HWND : window handle
6438 * [O] LPPOINT : coordinate information
6444 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6446 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6447 UINT uView = lStyle & LVS_TYPEMASK;
6448 BOOL bResult = FALSE;
6450 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6452 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6454 SCROLLINFO scrollInfo;
6455 ZeroMemory(lpptOrigin, sizeof(POINT));
6456 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6457 scrollInfo.cbSize = sizeof(SCROLLINFO);
6459 if (lStyle & WS_HSCROLL)
6461 scrollInfo.fMask = SIF_POS;
6462 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6463 lpptOrigin->x = -scrollInfo.nPos;
6466 if (lStyle & WS_VSCROLL)
6468 scrollInfo.fMask = SIF_POS;
6469 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6470 lpptOrigin->y = -scrollInfo.nPos;
6475 TRACE("(hwnd=%x, pt=(%ld,%ld))\n", hwnd, lpptOrigin->x, lpptOrigin->y);
6484 * Retrieves the number of items that are marked as selected.
6487 * [I] HWND : window handle
6490 * Number of items selected.
6492 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6495 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6496 INT nSelectedCount = 0;
6499 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6501 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6505 return nSelectedCount;
6510 * Retrieves item index that marks the start of a multiple selection.
6513 * [I] HWND : window handle
6516 * Index number or -1 if there is no selection mark.
6518 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6520 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6522 return infoPtr->nSelectionMark;
6528 * Retrieves the width of a string.
6531 * [I] hwnd : window handle
6532 * [I] lpszText : text string to process
6533 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6536 * SUCCESS : string width (in pixels)
6539 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
6541 if (is_textT(lpszText, isW))
6543 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6544 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6545 HDC hdc = GetDC(hwnd);
6546 HFONT hOldFont = SelectObject(hdc, hFont);
6548 ZeroMemory(&stringSize, sizeof(SIZE));
6550 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6552 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6553 SelectObject(hdc, hOldFont);
6554 ReleaseDC(hwnd, hdc);
6555 return stringSize.cx;
6562 * Retrieves the text backgound color.
6565 * [I] HWND : window handle
6568 * COLORREF associated with the the background.
6570 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6572 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6574 return infoPtr->clrTextBk;
6579 * Retrieves the text color.
6582 * [I] HWND : window handle
6585 * COLORREF associated with the text.
6587 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6589 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6591 return infoPtr->clrText;
6596 * Determines item if a hit or closest if not
6599 * [I] HWND : window handle
6600 * [IO] LPLV_INTHIT : hit test information
6601 * [I] subitem : fill out iSubItem.
6604 * SUCCESS : item index of hit
6607 static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem)
6609 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6610 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6611 UINT uView = lStyle & LVS_TYPEMASK;
6612 INT i,j,topindex,bottomindex;
6613 RECT rcItem,rcSubItem;
6614 DWORD xterm, yterm, dist;
6616 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.pt.y);
6618 topindex = LISTVIEW_GetTopIndex(hwnd);
6619 if (uView == LVS_REPORT)
6621 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6622 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6626 bottomindex = GETITEMCOUNT(infoPtr);
6629 lpInt->distance = 0x7fffffff;
6630 lpInt->iDistItem = -1;
6632 for (i = topindex; i < bottomindex; i++)
6634 rcItem.left = LVIR_BOUNDS;
6635 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6637 if (PtInRect(&rcItem, lpInt->ht.pt))
6640 rcItem.left = LVIR_ICON;
6641 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6643 if (PtInRect(&rcItem, lpInt->ht.pt))
6645 lpInt->ht.flags = LVHT_ONITEMICON;
6646 lpInt->ht.iItem = i;
6651 rcItem.left = LVIR_LABEL;
6652 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6654 if (PtInRect(&rcItem, lpInt->ht.pt))
6656 lpInt->ht.flags = LVHT_ONITEMLABEL;
6657 lpInt->ht.iItem = i;
6662 lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6663 lpInt->ht.iItem = i;
6667 lpInt->ht.iSubItem = 0;
6668 rcSubItem.right = rcSubItem.left;
6669 for (j = 0; j < infoPtr->nColumnCount; j++)
6671 rcSubItem.left = rcSubItem.right;
6672 rcSubItem.right += LISTVIEW_GetColumnWidth(hwnd, j);
6673 if (PtInRect(&rcSubItem, lpInt->ht.pt))
6675 lpInt->ht.iSubItem = j;
6685 * Now compute distance from point to center of boundary
6686 * box. Since we are only interested in the relative
6687 * distance, we can skip the nasty square root operation
6689 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6690 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6691 dist = xterm * xterm + yterm * yterm;
6692 if (dist < lpInt->distance)
6694 lpInt->distance = dist;
6695 lpInt->iDistItem = i;
6701 lpInt->ht.flags = LVHT_NOWHERE;
6702 TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6709 * Determines which section of the item was selected (if any).
6712 * [I] HWND : window handle
6713 * [IO] LPLVHITTESTINFO : hit test information
6714 * [I] subitem : fill out iSubItem.
6717 * SUCCESS : item index
6720 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6723 LV_INTHIT lv_inthit;
6725 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6726 lpHitTestInfo->pt.y);
6728 memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6729 ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem);
6730 memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6736 * Determines which listview item is located at the specified position.
6739 * [I] HWND : window handle
6740 * [IO} LPLVHITTESTINFO : hit test information
6743 * SUCCESS : item index
6746 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6748 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6751 lpHitTestInfo->flags = 0;
6753 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6754 lpHitTestInfo->flags = LVHT_TOLEFT;
6755 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6756 lpHitTestInfo->flags = LVHT_TORIGHT;
6757 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6758 lpHitTestInfo->flags |= LVHT_ABOVE;
6759 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6760 lpHitTestInfo->flags |= LVHT_BELOW;
6762 if (lpHitTestInfo->flags == 0)
6764 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6765 * an app might pass only a structure with space up to iItem!
6766 * (MS Office 97 does that for instance in the file open dialog)
6768 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6776 * Determines which listview subitem is located at the specified position.
6779 * [I] HWND : window handle
6780 * [IO} LPLVHITTESTINFO : hit test information
6783 * SUCCESS : item index
6786 static LRESULT LISTVIEW_SubItemHitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6788 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6791 lpHitTestInfo->flags = 0;
6793 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6794 lpHitTestInfo->flags = LVHT_TOLEFT;
6795 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6796 lpHitTestInfo->flags = LVHT_TORIGHT;
6797 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6798 lpHitTestInfo->flags |= LVHT_ABOVE;
6799 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6800 lpHitTestInfo->flags |= LVHT_BELOW;
6802 if (lpHitTestInfo->flags == 0)
6803 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, TRUE);
6810 * Inserts a new column.
6813 * [I] HWND : window handle
6814 * [I] INT : column index
6815 * [I] LPLVCOLUMNW : column information
6818 * SUCCESS : new column index
6821 static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn,
6822 LPLVCOLUMNW lpColumn, BOOL isW)
6824 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6825 INT nNewColumn = -1;
6828 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
6830 if (lpColumn != NULL)
6832 /* initialize memory */
6833 ZeroMemory(&hdi, sizeof(hdi));
6835 if (lpColumn->mask & LVCF_FMT)
6837 /* format member is valid */
6838 hdi.mask |= HDI_FORMAT;
6840 /* set text alignment (leftmost column must be left-aligned) */
6843 hdi.fmt |= HDF_LEFT;
6847 if (lpColumn->fmt & LVCFMT_LEFT)
6849 hdi.fmt |= HDF_LEFT;
6851 else if (lpColumn->fmt & LVCFMT_RIGHT)
6853 hdi.fmt |= HDF_RIGHT;
6855 else if (lpColumn->fmt & LVCFMT_CENTER)
6857 hdi.fmt |= HDF_CENTER;
6861 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6863 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6867 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6872 if (lpColumn->fmt & LVCFMT_IMAGE)
6874 hdi.fmt |= HDF_IMAGE;
6875 hdi.iImage = I_IMAGECALLBACK;
6879 if (lpColumn->mask & LVCF_WIDTH)
6881 hdi.mask |= HDI_WIDTH;
6882 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6884 /* make it fill the remainder of the controls width */
6889 ZeroMemory(&hdit, sizeof(hdit));
6891 /* get the width of every item except the current one */
6892 hdit.mask = HDI_WIDTH;
6895 for(item_index = 0; item_index < (nColumn - 1); item_index++) {
6896 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
6900 /* retrieve the layout of the header */
6901 GetClientRect(hwnd, &rcHeader);
6902 /* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6903 TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
6905 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
6908 hdi.cxy = lpColumn->cx;
6911 if (lpColumn->mask & LVCF_TEXT)
6913 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6914 hdi.pszText = lpColumn->pszText;
6915 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6916 hdi.fmt |= HDF_STRING;
6919 if (lpColumn->mask & LVCF_IMAGE)
6921 hdi.mask |= HDI_IMAGE;
6922 hdi.iImage = lpColumn->iImage;
6925 if (lpColumn->mask & LVCF_ORDER)
6927 hdi.mask |= HDI_ORDER;
6928 hdi.iOrder = lpColumn->iOrder;
6931 /* insert item in header control */
6932 nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6933 (WPARAM)nColumn, (LPARAM)&hdi);
6935 /* Need to reset the item width when inserting a new column */
6936 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6938 LISTVIEW_UpdateScroll(hwnd);
6939 InvalidateRect(hwnd, NULL, FALSE);
6945 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6946 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6947 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6948 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6949 their own sort proc. when sending LVM_SORTITEMS.
6952 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6954 LVS_SORTXXX must be specified,
6955 LVS_OWNERDRAW is not set,
6956 <item>.pszText is not LPSTR_TEXTCALLBACK.
6958 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6959 are sorted based on item text..."
6961 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6963 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6964 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6965 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6966 INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6967 /* if we're sorting descending, negate the return value */
6968 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6973 * Inserts a new item in the listview control.
6976 * [I] HWND : window handle
6977 * [I] LPLVITEMW : item information
6978 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6981 * SUCCESS : new item index
6984 static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
6986 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6987 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6988 UINT uView = lStyle & LVS_TYPEMASK;
6992 LISTVIEW_ITEM *lpItem = NULL;
6994 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
6995 hwnd, debuglvitem_t(lpLVItem, isW), isW);
6997 if (lStyle & LVS_OWNERDATA)
6999 nItem = infoPtr->hdpaItems->nItemCount;
7000 infoPtr->hdpaItems->nItemCount ++;
7004 if (lpLVItem != NULL)
7006 /* make sure it's not a subitem; cannot insert a subitem */
7007 if (lpLVItem->iSubItem == 0)
7009 if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
7011 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
7012 if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
7014 /* insert item in listview control data structure */
7015 if ( (hdpaSubItems = DPA_Create(8)) )
7017 if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
7019 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
7020 && !(lStyle & LVS_OWNERDRAWFIXED)
7021 && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
7023 /* Insert the item in the proper sort order based on the pszText
7024 member. See comments for LISTVIEW_InsertCompare() for greater detail */
7025 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
7026 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
7027 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
7028 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
7032 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
7039 LISTVIEW_ShiftIndices(hwnd,nItem,1);
7041 /* manage item focus */
7042 if (lpLVItem->mask & LVIF_STATE)
7044 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
7045 if (lpLVItem->stateMask & LVIS_SELECTED)
7046 LISTVIEW_SetSelection(hwnd, nItem);
7047 else if (lpLVItem->stateMask & LVIS_FOCUSED)
7048 LISTVIEW_SetItemFocus(hwnd, nItem);
7051 /* send LVN_INSERTITEM notification */
7052 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7054 nmlv.lParam = lpItem->lParam;
7055 listview_notify(hwnd, LVN_INSERTITEM, &nmlv);
7057 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
7059 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
7060 if (nItemWidth > infoPtr->nItemWidth)
7061 infoPtr->nItemWidth = nItemWidth;
7064 /* align items (set position of each item) */
7065 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
7067 if (lStyle & LVS_ALIGNLEFT)
7068 LISTVIEW_AlignLeft(hwnd);
7070 LISTVIEW_AlignTop(hwnd);
7073 LISTVIEW_UpdateScroll(hwnd);
7074 /* refresh client area */
7075 InvalidateRect(hwnd, NULL, FALSE);
7084 /* free memory if unsuccessful */
7085 if ((nItem == -1) && (lpItem != NULL))
7086 COMCTL32_Free(lpItem);
7093 * Redraws a range of items.
7096 * [I] HWND : window handle
7097 * [I] INT : first item
7098 * [I] INT : last item
7104 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
7106 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7107 BOOL bResult = FALSE;
7111 if (nFirst <= nLast)
7113 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
7115 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
7117 for (i = nFirst; i <= nLast; i++)
7119 rcItem.left = LVIR_BOUNDS;
7120 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
7121 InvalidateRect(hwnd, &rcItem, TRUE);
7132 * Scroll the content of a listview.
7135 * [I] HWND : window handle
7136 * [I] INT : horizontal scroll amount in pixels
7137 * [I] INT : vertical scroll amount in pixels
7144 * If the control is in report mode (LVS_REPORT) the control can
7145 * be scrolled only in line increments. "dy" will be rounded to the
7146 * nearest number of pixels that are a whole line. Ex: if line height
7147 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
7148 * is passed the the scroll will be 0. (per MSDN 7/2002)
7150 * For: (per experimentaion with native control and CSpy ListView)
7151 * LVS_ICON dy=1 = 1 pixel (vertical only)
7153 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
7155 * LVS_LIST dx=1 = 1 column (horizontal only)
7156 * but will only scroll 1 column per message
7157 * no matter what the value.
7158 * dy must be 0 or FALSE returned.
7159 * LVS_REPORT dx=1 = 1 pixel
7163 static LRESULT LISTVIEW_Scroll(HWND hwnd, INT dx, INT dy)
7165 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7166 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7167 UINT uView = lStyle & LVS_TYPEMASK;
7170 if (uView == LVS_REPORT)
7172 rows = (abs(dy) + infoPtr->nItemHeight/2) / infoPtr->nItemHeight;
7175 mode = (dy>0) ? SB_INTERNAL_DOWN : SB_INTERNAL_UP;
7176 for ( i=0; i<rows; i++)
7177 LISTVIEW_VScroll(hwnd, mode, 0, hwnd);
7182 mode = (dx>0) ? SB_INTERNAL_RIGHT : SB_INTERNAL_LEFT;
7183 for ( i=0; i<abs(dx); i++)
7184 LISTVIEW_HScroll(hwnd, mode, 0, hwnd);
7188 else if (uView == LVS_ICON)
7192 mode = (dy>0) ? SB_INTERNAL_DOWN : SB_INTERNAL_UP;
7193 for(i=0; i<abs(dy); i++)
7194 LISTVIEW_VScroll(hwnd, mode, 0, hwnd);
7197 else if (uView == LVS_SMALLICON)
7201 mode = (dy>0) ? SB_INTERNAL_DOWN : SB_INTERNAL_UP;
7202 for(i=0; i<abs(dy); i++)
7203 LISTVIEW_VScroll(hwnd, mode, 0, hwnd);
7206 else if (uView == LVS_LIST)
7208 if (dy != 0) return FALSE;
7209 if (dx == 0) return TRUE;
7210 mode = (dx>0) ? SB_INTERNAL_RIGHT : SB_INTERNAL_LEFT;
7211 LISTVIEW_HScroll(hwnd, mode, 0, hwnd);
7219 * Sets the background color.
7222 * [I] HWND : window handle
7223 * [I] COLORREF : background color
7229 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
7231 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7233 if(infoPtr->clrBk!=clrBk){
7234 infoPtr->clrBk = clrBk;
7235 InvalidateRect(hwnd, NULL, TRUE);
7241 /* LISTVIEW_SetBkImage */
7245 * Sets the callback mask. This mask will be used when the parent
7246 * window stores state information (some or all).
7249 * [I] HWND : window handle
7250 * [I] UINT : state mask
7256 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
7258 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7260 infoPtr->uCallbackMask = uMask;
7267 * Sets the attributes of a header item.
7270 * [I] HWND : window handle
7271 * [I] INT : column index
7272 * [I] LPLVCOLUMNW : column attributes
7273 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
7274 * otherwise it is in fact a LPLVCOLUMNA
7280 static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn,
7281 LPLVCOLUMNW lpColumn, BOOL isW)
7283 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7284 BOOL bResult = FALSE;
7285 HDITEMW hdi, hdiget;
7287 if ((lpColumn != NULL) && (nColumn >= 0) &&
7288 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
7290 /* initialize memory */
7291 ZeroMemory(&hdi, sizeof(hdi));
7293 if (lpColumn->mask & LVCF_FMT)
7295 /* format member is valid */
7296 hdi.mask |= HDI_FORMAT;
7298 /* get current format first */
7299 hdiget.mask = HDI_FORMAT;
7300 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7301 /* preserve HDF_STRING if present */
7302 hdi.fmt = hdiget.fmt & HDF_STRING;
7304 /* set text alignment (leftmost column must be left-aligned) */
7307 hdi.fmt |= HDF_LEFT;
7311 if (lpColumn->fmt & LVCFMT_LEFT)
7312 hdi.fmt |= HDF_LEFT;
7313 else if (lpColumn->fmt & LVCFMT_RIGHT)
7314 hdi.fmt |= HDF_RIGHT;
7315 else if (lpColumn->fmt & LVCFMT_CENTER)
7316 hdi.fmt |= HDF_CENTER;
7319 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7320 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
7322 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7323 hdi.fmt |= HDF_IMAGE;
7325 if (lpColumn->fmt & LVCFMT_IMAGE)
7327 hdi.fmt |= HDF_IMAGE;
7328 hdi.iImage = I_IMAGECALLBACK;
7332 if (lpColumn->mask & LVCF_WIDTH)
7334 hdi.mask |= HDI_WIDTH;
7335 hdi.cxy = lpColumn->cx;
7338 if (lpColumn->mask & LVCF_TEXT)
7340 hdi.mask |= HDI_TEXT | HDI_FORMAT;
7341 hdi.pszText = lpColumn->pszText;
7342 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
7343 hdi.fmt |= HDF_STRING;
7346 if (lpColumn->mask & LVCF_IMAGE)
7348 hdi.mask |= HDI_IMAGE;
7349 hdi.iImage = lpColumn->iImage;
7352 if (lpColumn->mask & LVCF_ORDER)
7354 hdi.mask |= HDI_ORDER;
7355 hdi.iOrder = lpColumn->iOrder;
7358 /* set header item attributes */
7360 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7362 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
7370 * Sets the column order array
7373 * [I] HWND : window handle
7374 * [I] INT : number of elements in column order array
7375 * [I] INT : pointer to column order array
7381 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
7383 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
7385 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7396 * Sets the width of a column
7399 * [I] HWND : window handle
7400 * [I] INT : column index
7401 * [I] INT : column width
7407 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
7409 LISTVIEW_INFO *infoPtr;
7412 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7413 UINT uView = lStyle & LVS_TYPEMASK;
7418 WCHAR text_buffer[DISP_TEXT_SIZE];
7419 INT header_item_count;
7424 WCHAR szDispText[DISP_TEXT_SIZE];
7426 /* make sure we can get the listview info */
7427 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
7430 if (!infoPtr->hwndHeader) /* make sure we have a header */
7433 /* set column width only if in report or list mode */
7434 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7437 TRACE("(hwnd=%x, iCol=%d, cx=%d\n", hwnd, iCol, cx);
7439 /* take care of invalid cx values */
7440 if((uView == LVS_REPORT) && (cx < -2))
7441 cx = LVSCW_AUTOSIZE;
7442 else if (uView == LVS_LIST && (cx < 1))
7445 /* resize all columns if in LVS_LIST mode */
7446 if(uView == LVS_LIST) {
7447 infoPtr->nItemWidth = cx;
7448 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7452 /* autosize based on listview items width */
7453 if(cx == LVSCW_AUTOSIZE)
7455 /* set the width of the column to the width of the widest item */
7456 if (iCol == 0 || uView == LVS_LIST)
7459 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7461 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, item_index);
7462 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7464 if (infoPtr->himlSmall)
7465 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
7469 ZeroMemory(&lvItem, sizeof(lvItem));
7470 lvItem.iSubItem = iCol;
7471 lvItem.mask = LVIF_TEXT;
7472 lvItem.cchTextMax = DISP_TEXT_SIZE;
7473 lvItem.pszText = szDispText;
7474 *lvItem.pszText = '\0';
7476 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7478 lvItem.iItem = item_index;
7479 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7480 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7481 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7484 cx += TRAILING_PADDING;
7485 } /* autosize based on listview header width */
7486 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7488 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7490 /* if iCol is the last column make it fill the remainder of the controls width */
7491 if(iCol == (header_item_count - 1)) {
7492 /* get the width of every item except the current one */
7493 hdi.mask = HDI_WIDTH;
7496 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7497 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7501 /* retrieve the layout of the header */
7502 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7504 cx = (rcHeader.right - rcHeader.left) - cx;
7508 /* Despite what the MS docs say, if this is not the last
7509 column, then MS resizes the column to the width of the
7510 largest text string in the column, including headers
7511 and items. This is different from LVSCW_AUTOSIZE in that
7512 LVSCW_AUTOSIZE ignores the header string length.
7515 /* retrieve header font */
7516 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7518 /* retrieve header text */
7519 hdi.mask = HDI_TEXT;
7520 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7521 hdi.pszText = text_buffer;
7523 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7525 /* determine the width of the text in the header */
7527 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7529 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7531 SelectObject(hdc, old_font); /* restore the old font */
7532 ReleaseDC(hwnd, hdc);
7534 ZeroMemory(&lvItem, sizeof(lvItem));
7535 lvItem.iSubItem = iCol;
7536 lvItem.mask = LVIF_TEXT;
7537 lvItem.cchTextMax = DISP_TEXT_SIZE;
7538 lvItem.pszText = szDispText;
7539 *lvItem.pszText = '\0';
7541 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7543 lvItem.iItem = item_index;
7544 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7545 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7546 nLabelWidth += TRAILING_PADDING;
7547 /* While it is possible for subitems to have icons, even MS messes
7548 up the positioning, so I suspect no applications actually use
7550 if (item_index == 0 && infoPtr->himlSmall)
7551 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
7552 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7557 /* call header to update the column change */
7558 hdi.mask = HDI_WIDTH;
7561 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7563 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7570 * Sets the extended listview style.
7573 * [I] HWND : window handle
7578 * SUCCESS : previous style
7581 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
7583 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7584 DWORD dwOldStyle = infoPtr->dwExStyle;
7588 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7590 infoPtr->dwExStyle = dwStyle;
7595 /* LISTVIEW_SetHotCursor */
7599 * Sets the hot item index.
7602 * [I] HWND : window handle
7606 * SUCCESS : previous hot item index
7607 * FAILURE : -1 (no hot item)
7609 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7611 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7612 INT iOldIndex = infoPtr->nHotItem;
7615 infoPtr->nHotItem = iIndex;
7622 * Sets the amount of time the cursor must hover over an item before it is selected.
7625 * [I] HWND : window handle
7626 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7629 * Returns the previous hover time
7631 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7633 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7634 DWORD oldHoverTime = infoPtr->dwHoverTime;
7636 infoPtr->dwHoverTime = dwHoverTime;
7638 return oldHoverTime;
7643 * Sets spacing for icons of LVS_ICON style.
7646 * [I] HWND : window handle
7647 * [I] DWORD : MAKELONG(cx, cy)
7650 * MAKELONG(oldcx, oldcy)
7652 static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing)
7654 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7655 INT cy = HIWORD(spacing);
7656 INT cx = LOWORD(spacing);
7658 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7659 UINT uView = lStyle & LVS_TYPEMASK;
7661 oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7662 if (cx == -1) /* set to default */
7663 cx = GetSystemMetrics(SM_CXICONSPACING);
7664 if (cy == -1) /* set to default */
7665 cy = GetSystemMetrics(SM_CYICONSPACING);
7668 infoPtr->iconSpacing.cx = cx;
7670 { /* if 0 then compute width */
7671 if (uView == LVS_ICON)
7672 FIXME("width computation not yet done\n");
7674 * Should scan each item and determine max width of
7675 * icon or label, then make that the width
7677 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7678 infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd);
7681 infoPtr->iconSpacing.cy = cy;
7683 { /* if 0 then compute height */
7684 if (uView == LVS_ICON)
7685 infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight
7686 + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7687 /* FIXME. I don't think so; I think it is based on twice the ntmHeight */
7688 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7689 infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd);
7692 TRACE("old=(%d,%d), new=(%ld,%ld), iconSize=(%ld,%ld), ntmH=%d\n",
7693 LOWORD(oldspacing), HIWORD(oldspacing),
7694 infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy,
7695 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7696 infoPtr->ntmHeight);
7698 /* these depend on the iconSpacing */
7699 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7700 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7710 * [I] HWND : window handle
7711 * [I] INT : image list type
7712 * [I] HIMAGELIST : image list handle
7715 * SUCCESS : old image list
7718 static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7720 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7721 HIMAGELIST himlOld = 0;
7723 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7728 himlOld = infoPtr->himlNormal;
7729 infoPtr->himlNormal = himl;
7730 if(himl && (LVS_ICON == uView))
7733 ImageList_GetIconSize(himl, &cx, &cy);
7734 TRACE("icon old size=(%ld,%ld), new size=(%d,%d)\n",
7735 infoPtr->iconSize.cx, infoPtr->iconSize.cy, cx, cy);
7736 infoPtr->iconSize.cx = cx;
7737 infoPtr->iconSize.cy = cy;
7738 LISTVIEW_SetIconSpacing(hwnd,0);
7743 himlOld = infoPtr->himlSmall;
7744 infoPtr->himlSmall = himl;
7748 himlOld = infoPtr->himlState;
7749 infoPtr->himlState = himl;
7750 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7754 oldHeight = infoPtr->nItemHeight;
7755 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7756 if (infoPtr->nItemHeight != oldHeight)
7757 LISTVIEW_UpdateScroll(hwnd);
7764 * Preallocates memory (does *not* set the actual count of items !)
7767 * [I] HWND : window handle
7768 * [I] INT : item count (projected number of items to allocate)
7769 * [I] DWORD : update flags
7775 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7777 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7779 TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
7781 if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7783 int precount,topvisible;
7785 TRACE("LVS_OWNERDATA is set!\n");
7786 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7787 FIXME("flags %s %s not implemented\n",
7788 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7790 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7793 * Internally remove all the selections.
7797 LISTVIEW_SELECTION *selection;
7798 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7800 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7803 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7805 precount = infoPtr->hdpaItems->nItemCount;
7806 topvisible = ListView_GetTopIndex(hwnd) +
7807 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7809 infoPtr->hdpaItems->nItemCount = nItems;
7811 infoPtr->nItemWidth = max(LISTVIEW_GetItemWidth(hwnd),
7812 DEFAULT_COLUMN_WIDTH);
7814 LISTVIEW_UpdateSize(hwnd);
7815 LISTVIEW_UpdateScroll(hwnd);
7817 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7818 InvalidateRect(hwnd, NULL, TRUE);
7822 /* According to MSDN for non-LVS_OWNERDATA this is just
7823 * a performance issue. The control allocates its internal
7824 * data structures for the number of items specified. It
7825 * cuts down on the number of memory allocations. Therefore
7826 * we will just issue a WARN here
7828 WARN("for non-ownerdata performance option not implemented.\n");
7836 * Sets the position of an item.
7839 * [I] HWND : window handle
7840 * [I] INT : item index
7841 * [I] LONG : x coordinate
7842 * [I] LONG : y coordinate
7848 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7849 LONG nPosX, LONG nPosY)
7851 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7852 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7853 UINT uView = lStyle & LVS_TYPEMASK;
7854 LISTVIEW_ITEM *lpItem;
7856 BOOL bResult = FALSE;
7858 TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7860 if (lStyle & LVS_OWNERDATA)
7863 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7865 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7867 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7869 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7873 orig = lpItem->ptPosition;
7874 if ((nPosX == -1) && (nPosY == -1))
7876 /* This point value seems to be an undocumented feature. The
7877 * best guess is that it means either at the origin, or at
7878 * the true beginning of the list. I will assume the origin.
7881 if (!LISTVIEW_GetOrigin(hwnd, &pt1))
7888 if (uView == LVS_ICON)
7890 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7891 nPosY += ICON_TOP_PADDING;
7893 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7897 lpItem->ptPosition.x = nPosX;
7898 lpItem->ptPosition.y = nPosY;
7899 if (uView == LVS_ICON)
7901 lpItem->ptPosition.y -= ICON_TOP_PADDING;
7902 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7903 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7905 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7906 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7909 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7910 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7915 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7916 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7929 * Sets the state of one or many items.
7932 * [I] HWND : window handle
7933 * [I]INT : item index
7934 * [I] LPLVITEM : item or subitem info
7940 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem)
7942 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7943 BOOL bResult = TRUE;
7946 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
7947 hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
7949 ZeroMemory(&lvItem, sizeof(lvItem));
7950 lvItem.mask = LVIF_STATE;
7951 lvItem.state = lpLVItem->state;
7952 lvItem.stateMask = lpLVItem->stateMask ;
7953 lvItem.iItem = nItem;
7957 /* apply to all items */
7958 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7959 if (!LISTVIEW_SetItemT(hwnd, &lvItem, TRUE)) bResult = FALSE;
7962 bResult = LISTVIEW_SetItemT(hwnd, &lvItem, TRUE);
7969 * Sets the text of an item or subitem.
7972 * [I] hwnd : window handle
7973 * [I] nItem : item index
7974 * [I] lpLVItem : item or subitem info
7975 * [I] isW : TRUE if input is Unicode
7981 static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7983 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7984 BOOL bResult = FALSE;
7987 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
7988 hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
7990 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7992 ZeroMemory(&lvItem, sizeof(LVITEMW));
7993 lvItem.mask = LVIF_TEXT;
7994 lvItem.pszText = lpLVItem->pszText;
7995 lvItem.iItem = nItem;
7996 lvItem.iSubItem = lpLVItem->iSubItem;
7997 if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
7998 else bResult = ListView_SetItemA(hwnd, &lvItem);
8006 * Set item index that marks the start of a multiple selection.
8009 * [I] HWND : window handle
8013 * Index number or -1 if there is no selection mark.
8015 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
8017 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8018 INT nOldIndex = infoPtr->nSelectionMark;
8020 TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
8022 infoPtr->nSelectionMark = nIndex;
8029 * Sets the text background color.
8032 * [I] HWND : window handle
8033 * [I] COLORREF : text background color
8039 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
8041 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8043 TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
8045 infoPtr->clrTextBk = clrTextBk;
8046 InvalidateRect(hwnd, NULL, TRUE);
8053 * Sets the text foreground color.
8056 * [I] HWND : window handle
8057 * [I] COLORREF : text color
8063 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
8065 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8067 TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
8069 infoPtr->clrText = clrText;
8070 InvalidateRect(hwnd, NULL, TRUE);
8075 /* LISTVIEW_SetToolTips */
8076 /* LISTVIEW_SetUnicodeFormat */
8077 /* LISTVIEW_SetWorkAreas */
8081 * Callback internally used by LISTVIEW_SortItems()
8084 * [I] LPVOID : first LISTVIEW_ITEM to compare
8085 * [I] LPVOID : second LISTVIEW_ITEM to compare
8086 * [I] LPARAM : HWND of control
8089 * if first comes before second : negative
8090 * if first comes after second : positive
8091 * if first and second are equivalent : zero
8093 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
8095 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
8096 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
8097 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
8099 /* Forward the call to the client defined callback */
8100 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
8105 * Sorts the listview items.
8108 * [I] HWND : window handle
8109 * [I] WPARAM : application-defined value
8110 * [I] LPARAM : pointer to comparision callback
8116 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
8118 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8119 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8120 HDPA hdpaSubItems=NULL;
8121 LISTVIEW_ITEM *pLVItem=NULL;
8122 LPVOID selectionMarkItem;
8125 TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
8127 if (lStyle & LVS_OWNERDATA) return FALSE;
8129 if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
8131 nCount = GETITEMCOUNT(infoPtr);
8132 /* if there are 0 or 1 items, there is no need to sort */
8136 infoPtr->pfnCompare = pfnCompare;
8137 infoPtr->lParamSort = lParamSort;
8138 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
8140 /* Adjust selections and indices so that they are the way they should
8141 * be after the sort (otherwise, the list items move around, but
8142 * whatever is at the item's previous original position will be
8145 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
8146 for (i=0; i < nCount; i++)
8148 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
8149 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
8151 if (pLVItem->state & LVIS_SELECTED)
8152 LISTVIEW_AddSelectionRange(hwnd, i, i);
8154 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
8155 if (pLVItem->state & LVIS_FOCUSED)
8156 infoPtr->nFocusedItem=i;
8158 if (selectionMarkItem != NULL)
8159 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
8160 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8162 /* align the items */
8163 LISTVIEW_AlignTop(hwnd);
8165 /* refresh the display */
8166 InvalidateRect(hwnd, NULL, TRUE);
8171 /* LISTVIEW_SubItemHitTest */
8175 * Updates an items or rearranges the listview control.
8178 * [I] HWND : window handle
8179 * [I] INT : item index
8185 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
8187 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8188 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8189 BOOL bResult = FALSE;
8192 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
8194 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8198 /* rearrange with default alignment style */
8199 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
8200 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
8202 ListView_Arrange(hwnd, 0);
8206 /* get item bounding rectangle */
8207 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
8208 InvalidateRect(hwnd, &rc, TRUE);
8217 * Creates the listview control.
8220 * [I] HWND : window handle
8225 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
8227 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8228 UINT uView = lpcs->style & LVS_TYPEMASK;
8231 TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
8233 /* initialize info pointer */
8234 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
8236 /* determine the type of structures to use */
8237 infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT,
8238 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8240 /* initialize color information */
8241 infoPtr->clrBk = comctl32_color.clrWindow;
8242 infoPtr->clrText = comctl32_color.clrWindowText;
8243 infoPtr->clrTextBk = CLR_DEFAULT;
8245 /* set default values */
8246 infoPtr->hwndSelf = hwnd;
8247 infoPtr->uCallbackMask = 0;
8248 infoPtr->nFocusedItem = -1;
8249 infoPtr->nSelectionMark = -1;
8250 infoPtr->nHotItem = -1;
8251 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8252 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8253 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
8254 infoPtr->hwndEdit = 0;
8255 infoPtr->Editing = FALSE;
8256 infoPtr->pedititem = NULL;
8257 infoPtr->nEditLabelItem = -1;
8258 infoPtr->bIsDrawing = FALSE;
8260 /* get default font (icon title) */
8261 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8262 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8263 infoPtr->hFont = infoPtr->hDefaultFont;
8264 LISTVIEW_SaveTextMetrics(hwnd);
8267 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
8268 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
8269 0, 0, 0, 0, hwnd, (HMENU)0,
8270 lpcs->hInstance, NULL);
8272 /* set header unicode format */
8273 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
8275 /* set header font */
8276 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
8279 if (uView == LVS_ICON)
8281 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8282 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8284 else if (uView == LVS_REPORT)
8286 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8288 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8292 /* set HDS_HIDDEN flag to hide the header bar */
8293 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
8294 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
8298 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8299 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8303 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8304 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8307 /* display unsupported listview window styles */
8308 LISTVIEW_UnsupportedStyles(lpcs->style);
8310 /* allocate memory for the data structure */
8311 infoPtr->hdpaItems = DPA_Create(10);
8313 /* allocate memory for the selection ranges */
8314 infoPtr->hdpaSelectionRanges = DPA_Create(10);
8316 /* initialize size of items */
8317 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8318 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8320 /* initialize the hover time to -1(indicating the default system hover time) */
8321 infoPtr->dwHoverTime = -1;
8328 * Erases the background of the listview control.
8331 * [I] HWND : window handle
8332 * [I] WPARAM : device context handle
8333 * [I] LPARAM : not used
8339 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
8342 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8345 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8347 if (infoPtr->clrBk == CLR_NONE)
8349 bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
8354 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8355 GetClientRect(hwnd, &rc);
8356 FillRect((HDC)wParam, &rc, hBrush);
8357 DeleteObject(hBrush);
8365 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
8367 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8369 TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
8371 if (infoPtr->clrBk != CLR_NONE)
8373 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8374 FillRect(hdc, rc, hBrush);
8375 DeleteObject(hBrush);
8381 * Retrieves the listview control font.
8384 * [I] HWND : window handle
8389 static LRESULT LISTVIEW_GetFont(HWND hwnd)
8391 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8393 TRACE("(hwnd=%x)\n", hwnd);
8395 return infoPtr->hFont;
8400 * Performs vertical scrolling.
8403 * [I] HWND : window handle
8404 * [I] INT : scroll code
8405 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8407 * [I] HWND : scrollbar control window handle
8413 * SB_LINEUP/SB_LINEDOWN:
8414 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8415 * for LVS_REPORT is 1 line --> infoPtr->nItemHeight
8416 * for LVS_LIST cannot occur ??? (implemented as LVS_REPORT)
8419 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8422 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8423 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8424 SCROLLINFO scrollInfo;
8427 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8428 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8430 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8432 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8433 scrollInfo.cbSize = sizeof(SCROLLINFO);
8434 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8436 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8438 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8440 INT nOldScrollPos = scrollInfo.nPos;
8441 switch (nScrollCode)
8443 case SB_INTERNAL_UP:
8444 if (scrollInfo.nPos > scrollInfo.nMin)
8448 case SB_INTERNAL_DOWN:
8449 if (scrollInfo.nPos < scrollInfo.nMax)
8454 if (scrollInfo.nPos > scrollInfo.nMin)
8455 scrollInfo.nPos -= (is_an_icon) ?
8456 LISTVIEW_SCROLL_ICON_LINE_SIZE : infoPtr->nItemHeight;
8460 if (scrollInfo.nPos < scrollInfo.nMax)
8461 scrollInfo.nPos += (is_an_icon) ?
8462 LISTVIEW_SCROLL_ICON_LINE_SIZE : infoPtr->nItemHeight;
8466 if (scrollInfo.nPos > scrollInfo.nMin)
8468 if (scrollInfo.nPos >= scrollInfo.nPage)
8469 scrollInfo.nPos -= scrollInfo.nPage;
8471 scrollInfo.nPos = scrollInfo.nMin;
8476 if (scrollInfo.nPos < scrollInfo.nMax)
8478 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8479 scrollInfo.nPos += scrollInfo.nPage;
8481 scrollInfo.nPos = scrollInfo.nMax;
8485 case SB_THUMBPOSITION:
8487 scrollInfo.nPos = nCurrentPos;
8488 if (scrollInfo.nPos > scrollInfo.nMax)
8489 scrollInfo.nPos=scrollInfo.nMax;
8491 if (scrollInfo.nPos < scrollInfo.nMin)
8492 scrollInfo.nPos=scrollInfo.nMin;
8497 if (nOldScrollPos != scrollInfo.nPos)
8499 scrollInfo.fMask = SIF_POS;
8500 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
8502 /* Get real position value, the value we set might have been changed
8503 * by SetScrollInfo (especially if we went too far.
8505 scrollInfo.fMask = SIF_POS;
8506 GetScrollInfo(hwnd, SB_VERT, &scrollInfo);
8508 /* only if the scroll position really changed, do we update screen */
8509 if (nOldScrollPos != scrollInfo.nPos)
8511 if (IsWindowVisible(infoPtr->hwndHeader))
8513 RECT rListview, rcHeader, rDest;
8514 GetClientRect(hwnd, &rListview);
8515 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
8516 MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2);
8517 SubtractRect(&rDest, &rListview, &rcHeader);
8518 InvalidateRect(hwnd, &rDest, TRUE);
8521 InvalidateRect(hwnd, NULL, TRUE);
8531 * Performs horizontal scrolling.
8534 * [I] HWND : window handle
8535 * [I] INT : scroll code
8536 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8538 * [I] HWND : scrollbar control window handle
8544 * SB_LINELEFT/SB_LINERIGHT:
8545 * for LVS_ICON, LVS_SMALLICON ??? (implemented as 1 pixel)
8546 * for LVS_REPORT is 1 pixel
8547 * for LVS_LIST is 1 column --> which is a 1 because the
8548 * scroll is based on columns not pixels
8551 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8554 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8555 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8556 SCROLLINFO scrollInfo;
8559 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8560 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8562 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8564 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8565 scrollInfo.cbSize = sizeof(SCROLLINFO);
8566 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8568 is_a_list = (uView == LVS_LIST);
8570 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
8572 INT nOldScrollPos = scrollInfo.nPos;
8574 switch (nScrollCode)
8576 case SB_INTERNAL_LEFT:
8577 if (scrollInfo.nPos > scrollInfo.nMin)
8581 case SB_INTERNAL_RIGHT:
8582 if (scrollInfo.nPos < scrollInfo.nMax)
8587 if (scrollInfo.nPos > scrollInfo.nMin)
8592 if (scrollInfo.nPos < scrollInfo.nMax)
8597 if (scrollInfo.nPos > scrollInfo.nMin)
8599 if (scrollInfo.nPos >= scrollInfo.nPage)
8600 scrollInfo.nPos -= scrollInfo.nPage;
8602 scrollInfo.nPos = scrollInfo.nMin;
8607 if (scrollInfo.nPos < scrollInfo.nMax)
8609 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8610 scrollInfo.nPos += scrollInfo.nPage;
8612 scrollInfo.nPos = scrollInfo.nMax;
8616 case SB_THUMBPOSITION:
8618 scrollInfo.nPos = nCurrentPos;
8620 if (scrollInfo.nPos > scrollInfo.nMax)
8621 scrollInfo.nPos=scrollInfo.nMax;
8623 if (scrollInfo.nPos < scrollInfo.nMin)
8624 scrollInfo.nPos=scrollInfo.nMin;
8628 if (nOldScrollPos != scrollInfo.nPos)
8630 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8631 scrollInfo.fMask = SIF_POS;
8632 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
8634 /* Get real position value, the value we set might have been changed
8635 * by SetScrollInfo (especially if we went too far.
8637 scrollInfo.fMask = SIF_POS;
8638 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
8639 if(uView == LVS_REPORT)
8641 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
8644 /* only if the scroll position really changed, do we update screen */
8645 if (nOldScrollPos != scrollInfo.nPos)
8646 InvalidateRect(hwnd, NULL, TRUE);
8653 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
8655 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8656 INT gcWheelDelta = 0;
8657 UINT pulScrollLines = 3;
8658 SCROLLINFO scrollInfo;
8660 TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
8662 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8663 gcWheelDelta -= wheelDelta;
8665 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8666 scrollInfo.cbSize = sizeof(SCROLLINFO);
8667 scrollInfo.fMask = SIF_POS | SIF_RANGE;
8674 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8675 * should be fixed in the future.
8677 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8678 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION,
8679 scrollInfo.nPos + (gcWheelDelta < 0) ?
8680 LISTVIEW_SCROLL_ICON_LINE_SIZE :
8681 -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8685 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8687 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8689 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
8690 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8691 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8697 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8708 * [I] HWND : window handle
8709 * [I] INT : virtual key
8710 * [I] LONG : key data
8715 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
8717 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8718 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8720 NMLVKEYDOWN nmKeyDown;
8722 TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
8724 /* send LVN_KEYDOWN notification */
8725 nmKeyDown.wVKey = nVirtualKey;
8726 nmKeyDown.flags = 0;
8727 notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
8729 switch (nVirtualKey)
8732 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8734 hdr_notify(hwnd, NM_RETURN); /* NM_RETURN notification */
8735 hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
8740 if (GETITEMCOUNT(infoPtr) > 0)
8745 if (GETITEMCOUNT(infoPtr) > 0)
8746 nItem = GETITEMCOUNT(infoPtr) - 1;
8750 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8754 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8758 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8762 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8766 if (uView == LVS_REPORT)
8767 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8769 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8770 * LISTVIEW_GetCountPerRow(hwnd);
8771 if(nItem < 0) nItem = 0;
8775 if (uView == LVS_REPORT)
8776 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8778 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8779 * LISTVIEW_GetCountPerRow(hwnd);
8780 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8784 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8786 if (LISTVIEW_KeySelection(hwnd, nItem))
8787 UpdateWindow(hwnd); /* update client area */
8798 * [I] HWND : window handle
8803 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8805 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
8806 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8809 TRACE("(hwnd=%x)\n", hwnd);
8811 /* send NM_KILLFOCUS notification */
8812 hdr_notify(hwnd, NM_KILLFOCUS);
8814 /* set window focus flag */
8815 infoPtr->bFocus = FALSE;
8817 /* NEED drawing optimization ; redraw the selected items */
8818 if (uView & LVS_REPORT)
8820 nTop = LISTVIEW_GetTopIndex(hwnd);
8822 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8827 nBottom = GETITEMCOUNT(infoPtr);
8829 for (i = nTop; i<nBottom; i++)
8831 if (LISTVIEW_IsSelected(hwnd,i))
8834 rcItem.left = LVIR_BOUNDS;
8835 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8836 InvalidateRect(hwnd, &rcItem, FALSE);
8845 * Processes double click messages (left mouse button).
8848 * [I] HWND : window handle
8849 * [I] WORD : key flag
8850 * [I] WORD : x coordinate
8851 * [I] WORD : y coordinate
8856 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8859 LVHITTESTINFO htInfo;
8862 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8864 htInfo.pt.x = wPosX;
8865 htInfo.pt.y = wPosY;
8867 /* send NM_DBLCLK notification */
8868 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8869 if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -1)
8871 nmlv.iItem = htInfo.iItem;
8872 nmlv.iSubItem = htInfo.iSubItem;
8879 nmlv.ptAction.x = wPosX;
8880 nmlv.ptAction.y = wPosY;
8881 listview_notify(hwnd, NM_DBLCLK, &nmlv);
8884 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8885 if(nmlv.iItem != -1)
8886 hdr_notify(hwnd, LVN_ITEMACTIVATE);
8893 * Processes mouse down messages (left mouse button).
8896 * [I] HWND : window handle
8897 * [I] WORD : key flag
8898 * [I] WORD : x coordinate
8899 * [I] WORD : y coordinate
8904 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8907 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8908 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8909 static BOOL bGroupSelect = TRUE;
8913 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8915 /* send NM_RELEASEDCAPTURE notification */
8916 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8918 if (infoPtr->bFocus == FALSE)
8921 /* set left button down flag */
8922 infoPtr->bLButtonDown = TRUE;
8924 ptPosition.x = wPosX;
8925 ptPosition.y = wPosY;
8926 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8927 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8929 if (lStyle & LVS_SINGLESEL)
8931 if ((LISTVIEW_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8932 && infoPtr->nEditLabelItem == -1)
8933 infoPtr->nEditLabelItem = nItem;
8935 LISTVIEW_SetSelection(hwnd, nItem);
8939 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8942 LISTVIEW_AddGroupSelection(hwnd, nItem);
8944 LISTVIEW_AddSelection(hwnd, nItem);
8946 else if (wKey & MK_CONTROL)
8948 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8950 else if (wKey & MK_SHIFT)
8952 LISTVIEW_SetGroupSelection(hwnd, nItem);
8957 (LISTVIEW_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8959 /* set selection (clears other pre-existing selections) */
8960 LISTVIEW_SetSelection(hwnd, nItem);
8962 if (was_selected && infoPtr->nEditLabelItem == -1)
8963 infoPtr->nEditLabelItem = nItem;
8969 /* remove all selections */
8970 LISTVIEW_RemoveAllSelections(hwnd);
8973 /* redraw if we could have possibly selected something */
8974 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8981 * Processes mouse up messages (left mouse button).
8984 * [I] HWND : window handle
8985 * [I] WORD : key flag
8986 * [I] WORD : x coordinate
8987 * [I] WORD : y coordinate
8992 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8995 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8997 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8999 if (infoPtr->bLButtonDown != FALSE)
9001 LVHITTESTINFO lvHitTestInfo;
9004 lvHitTestInfo.pt.x = wPosX;
9005 lvHitTestInfo.pt.y = wPosY;
9007 /* send NM_CLICK notification */
9008 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9009 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
9011 nmlv.iItem = lvHitTestInfo.iItem;
9012 nmlv.iSubItem = lvHitTestInfo.iSubItem;
9019 nmlv.ptAction.x = wPosX;
9020 nmlv.ptAction.y = wPosY;
9021 listview_notify(hwnd, NM_CLICK, &nmlv);
9023 /* set left button flag */
9024 infoPtr->bLButtonDown = FALSE;
9026 if(infoPtr->nEditLabelItem != -1)
9028 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL)
9029 LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
9030 infoPtr->nEditLabelItem = -1;
9039 * Creates the listview control (called before WM_CREATE).
9042 * [I] HWND : window handle
9043 * [I] WPARAM : unhandled
9044 * [I] LPARAM : widow creation info
9049 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
9051 LISTVIEW_INFO *infoPtr;
9053 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
9055 /* allocate memory for info structure */
9056 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
9057 if (infoPtr == NULL)
9059 ERR("could not allocate info memory!\n");
9063 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
9064 if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr)
9066 ERR("pointer assignment error!\n");
9070 return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam);
9075 * Destroys the listview control (called after WM_DESTROY).
9078 * [I] HWND : window handle
9083 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
9085 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9086 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9088 TRACE("(hwnd=%x)\n", hwnd);
9090 /* delete all items */
9091 LISTVIEW_DeleteAllItems(hwnd);
9093 /* destroy data structure */
9094 DPA_Destroy(infoPtr->hdpaItems);
9095 DPA_Destroy(infoPtr->hdpaSelectionRanges);
9097 /* destroy image lists */
9098 if (!(lStyle & LVS_SHAREIMAGELISTS))
9101 /* FIXME: If the caller does a ImageList_Destroy and then we
9102 * do this code the area will be freed twice. Currently
9103 * this generates an "err:heap:HEAP_ValidateInUseArena
9104 * Heap xxxxxxxx: in-use arena yyyyyyyy next block
9105 * has PREV_FREE flag" sometimes.
9107 * We will leak the memory till we figure out how to fix
9109 if (infoPtr->himlNormal)
9110 ImageList_Destroy(infoPtr->himlNormal);
9111 if (infoPtr->himlSmall)
9112 ImageList_Destroy(infoPtr->himlSmall);
9113 if (infoPtr->himlState)
9114 ImageList_Destroy(infoPtr->himlState);
9119 infoPtr->hFont = (HFONT)0;
9120 if (infoPtr->hDefaultFont)
9122 DeleteObject(infoPtr->hDefaultFont);
9125 /* free listview info pointer*/
9126 COMCTL32_Free(infoPtr);
9128 SetWindowLongW(hwnd, 0, 0);
9134 * Handles notifications from children.
9137 * [I] HWND : window handle
9138 * [I] INT : control identifier
9139 * [I] LPNMHDR : notification information
9144 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
9146 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9148 TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
9150 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
9152 /* handle notification from header control */
9153 if (lpnmh->code == HDN_ENDTRACKW)
9155 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9156 InvalidateRect(hwnd, NULL, TRUE);
9158 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
9160 /* Handle sorting by Header Column */
9163 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9165 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
9166 listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv);
9168 else if(lpnmh->code == NM_RELEASEDCAPTURE)
9170 /* Idealy this should be done in HDN_ENDTRACKA
9171 * but since SetItemBounds in Header.c is called after
9172 * the notification is sent, it is neccessary to handle the
9173 * update of the scroll bar here (Header.c works fine as it is,
9174 * no need to disturb it)
9176 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9177 LISTVIEW_UpdateScroll(hwnd);
9178 InvalidateRect(hwnd, NULL, TRUE);
9188 * Determines the type of structure to use.
9191 * [I] HWND : window handle of the sender
9192 * [I] HWND : listview window handle
9193 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
9198 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
9200 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9202 TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
9204 if (nCommand == NF_REQUERY)
9205 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
9206 (WPARAM)hwnd, (LPARAM)NF_QUERY);
9212 * Paints/Repaints the listview control.
9215 * [I] HWND : window handle
9216 * [I] HDC : device context handle
9221 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
9225 TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc);
9229 hdc = BeginPaint(hwnd, &ps);
9230 LISTVIEW_Refresh(hwnd, hdc);
9231 EndPaint(hwnd, &ps);
9235 LISTVIEW_Refresh(hwnd, hdc);
9243 * Processes double click messages (right mouse button).
9246 * [I] HWND : window handle
9247 * [I] WORD : key flag
9248 * [I] WORD : x coordinate
9249 * [I] WORD : y coordinate
9254 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
9257 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9259 /* send NM_RELEASEDCAPTURE notification */
9260 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
9262 /* send NM_RDBLCLK notification */
9263 hdr_notify(hwnd, NM_RDBLCLK);
9270 * Processes mouse down messages (right mouse button).
9273 * [I] HWND : window handle
9274 * [I] WORD : key flag
9275 * [I] WORD : x coordinate
9276 * [I] WORD : y coordinate
9281 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
9284 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9288 LVHITTESTINFO lvHitTestInfo;
9290 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9292 /* send NM_RELEASEDCAPTURE notification */
9293 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
9295 /* make sure the listview control window has the focus */
9296 if (infoPtr->bFocus == FALSE)
9299 /* set right button down flag */
9300 infoPtr->bRButtonDown = TRUE;
9302 /* determine the index of the selected item */
9303 ptPosition.x = wPosX;
9304 ptPosition.y = wPosY;
9305 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
9306 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
9308 LISTVIEW_SetItemFocus(hwnd,nItem);
9309 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9310 !LISTVIEW_IsSelected(hwnd,nItem))
9311 LISTVIEW_SetSelection(hwnd, nItem);
9315 LISTVIEW_RemoveAllSelections(hwnd);
9318 lvHitTestInfo.pt.x = wPosX;
9319 lvHitTestInfo.pt.y = wPosY;
9321 /* Send NM_RClICK notification */
9322 ZeroMemory(&nmlv, sizeof(nmlv));
9323 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
9325 nmlv.iItem = lvHitTestInfo.iItem;
9326 nmlv.iSubItem = lvHitTestInfo.iSubItem;
9333 nmlv.ptAction.x = wPosX;
9334 nmlv.ptAction.y = wPosY;
9335 listview_notify(hwnd, NM_RCLICK, &nmlv);
9342 * Processes mouse up messages (right mouse button).
9345 * [I] HWND : window handle
9346 * [I] WORD : key flag
9347 * [I] WORD : x coordinate
9348 * [I] WORD : y coordinate
9353 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
9356 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9358 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9360 if (infoPtr->bRButtonDown)
9367 /* set button flag */
9368 infoPtr->bRButtonDown = FALSE;
9370 /* Change to screen coordinate for WM_CONTEXTMENU */
9371 ClientToScreen(hwnd, &pt);
9373 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9374 SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
9385 * [I] HWND : window handle
9386 * [I] HWND : window handle of previously focused window
9391 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
9393 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9395 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
9397 /* send NM_SETFOCUS notification */
9398 hdr_notify(hwnd, NM_SETFOCUS);
9400 /* set window focus flag */
9401 infoPtr->bFocus = TRUE;
9413 * [I] HWND : window handle
9414 * [I] HFONT : font handle
9415 * [I] WORD : redraw flag
9420 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
9422 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9423 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
9425 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
9427 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9428 LISTVIEW_SaveTextMetrics(hwnd);
9430 if (uView == LVS_REPORT)
9432 /* set header font */
9433 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
9434 MAKELPARAM(fRedraw, 0));
9437 /* invalidate listview control client area */
9438 InvalidateRect(hwnd, NULL, TRUE);
9440 if (fRedraw != FALSE)
9448 * Message handling for WM_SETREDRAW.
9449 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9452 * [I] HWND : window handle
9453 * [I] bRedraw: state of redraw flag
9456 * DefWinProc return value
9458 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
9460 LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0);
9462 RedrawWindow(hwnd, NULL, 0,
9463 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
9469 * Resizes the listview control. This function processes WM_SIZE
9470 * messages. At this time, the width and height are not used.
9473 * [I] HWND : window handle
9474 * [I] WORD : new width
9475 * [I] WORD : new height
9480 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
9482 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9483 UINT uView = lStyle & LVS_TYPEMASK;
9485 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
9487 if (LISTVIEW_UpdateSize(hwnd))
9489 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
9491 if (lStyle & LVS_ALIGNLEFT)
9492 LISTVIEW_AlignLeft(hwnd);
9494 LISTVIEW_AlignTop(hwnd);
9497 LISTVIEW_UpdateScroll(hwnd);
9499 /* invalidate client area + erase background */
9500 InvalidateRect(hwnd, NULL, TRUE);
9508 * Sets the size information.
9511 * [I] HWND : window handle
9514 * Zero if no size change
9517 static BOOL LISTVIEW_UpdateSize(HWND hwnd)
9519 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9520 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9521 UINT uView = lStyle & LVS_TYPEMASK;
9525 TRACE("(hwnd=%x)\n", hwnd);
9527 GetClientRect(hwnd, &rcList);
9528 CopyRect(&rcOld,&(infoPtr->rcList));
9529 infoPtr->rcList.left = 0;
9530 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
9531 infoPtr->rcList.top = 0;
9532 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
9534 if (uView == LVS_LIST)
9536 /* Apparently the "LIST" style is supposed to have the same
9537 * number of items in a column even if there is no scroll bar.
9538 * Since if a scroll bar already exists then the bottom is already
9539 * reduced, only reduce if the scroll bar does not currently exist.
9540 * The "2" is there to mimic the native control. I think it may be
9541 * related to either padding or edges. (GLA 7/2002)
9543 if (!(lStyle & WS_HSCROLL))
9545 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
9546 if (infoPtr->rcList.bottom > nHScrollHeight)
9547 infoPtr->rcList.bottom -= (nHScrollHeight + 2);
9551 if (infoPtr->rcList.bottom > 2)
9552 infoPtr->rcList.bottom -= 2;
9555 else if (uView == LVS_REPORT)
9562 Header_Layout(infoPtr->hwndHeader, &hl);
9564 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9566 if (!(LVS_NOCOLUMNHEADER & lStyle))
9567 infoPtr->rcList.top = max(wp.cy, 0);
9569 return (EqualRect(&rcOld,&(infoPtr->rcList)));
9574 * Processes WM_STYLECHANGED messages.
9577 * [I] HWND : window handle
9578 * [I] WPARAM : window style type (normal or extended)
9579 * [I] LPSTYLESTRUCT : window style information
9584 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
9587 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9588 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9589 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9590 RECT rcList = infoPtr->rcList;
9592 TRACE("(hwnd=%x, styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
9593 hwnd, wStyleType, lpss->styleOld, lpss->styleNew);
9595 if (wStyleType == GWL_STYLE)
9597 if (uOldView == LVS_REPORT)
9598 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9600 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9601 ((lpss->styleNew & WS_HSCROLL) == 0))
9602 ShowScrollBar(hwnd, SB_HORZ, FALSE);
9604 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9605 ((lpss->styleNew & WS_VSCROLL) == 0))
9606 ShowScrollBar(hwnd, SB_VERT, FALSE);
9608 /* If switching modes, then start with no scroll bars and then
9611 if (uNewView != uOldView)
9612 ShowScrollBar(hwnd, SB_BOTH, FALSE);
9614 if (uNewView == LVS_ICON)
9618 /* First readjust the iconSize and if necessary the iconSpacing */
9619 oldcx = infoPtr->iconSize.cx;
9620 oldcy = infoPtr->iconSize.cy;
9621 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9622 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9623 if (infoPtr->himlNormal != NULL)
9626 ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
9627 infoPtr->iconSize.cx = cx;
9628 infoPtr->iconSize.cy = cy;
9630 if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
9632 TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
9633 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9634 LISTVIEW_SetIconSpacing(hwnd,0);
9637 /* Now update the full item width and height */
9638 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9639 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9640 if (lpss->styleNew & LVS_ALIGNLEFT)
9641 LISTVIEW_AlignLeft(hwnd);
9643 LISTVIEW_AlignTop(hwnd);
9645 else if (uNewView == LVS_REPORT)
9652 Header_Layout(infoPtr->hwndHeader, &hl);
9653 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
9655 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9656 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9658 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9659 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9660 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9661 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9663 else if (uNewView == LVS_LIST)
9665 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9666 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9667 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9668 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9672 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9673 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9674 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9675 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9676 if (lpss->styleNew & LVS_ALIGNLEFT)
9677 LISTVIEW_AlignLeft(hwnd);
9679 LISTVIEW_AlignTop(hwnd);
9682 /* update the size of the client area */
9683 LISTVIEW_UpdateSize(hwnd);
9685 /* add scrollbars if needed */
9686 LISTVIEW_UpdateScroll(hwnd);
9688 /* invalidate client area + erase background */
9689 InvalidateRect(hwnd, NULL, TRUE);
9691 /* print the list of unsupported window styles */
9692 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9695 /* If they change the view and we have an active edit control
9696 we will need to kill the control since the redraw will
9697 misplace the edit control.
9699 if (infoPtr->Editing &&
9700 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9701 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9703 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9711 * Window procedure of the listview control.
9714 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9717 TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
9718 if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
9719 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9722 case LVM_APPROXIMATEVIEWRECT:
9723 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9724 LOWORD(lParam), HIWORD(lParam));
9726 return LISTVIEW_Arrange(hwnd, (INT)wParam);
9728 /* case LVM_CREATEDRAGIMAGE: */
9730 case LVM_DELETEALLITEMS:
9731 return LISTVIEW_DeleteAllItems(hwnd);
9733 case LVM_DELETECOLUMN:
9734 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9736 case LVM_DELETEITEM:
9737 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9739 case LVM_EDITLABELW:
9740 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
9742 case LVM_EDITLABELA:
9743 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
9745 case LVM_ENSUREVISIBLE:
9746 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9749 return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
9752 return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
9754 case LVM_GETBKCOLOR:
9755 return LISTVIEW_GetBkColor(hwnd);
9757 /* case LVM_GETBKIMAGE: */
9759 case LVM_GETCALLBACKMASK:
9760 return LISTVIEW_GetCallbackMask(hwnd);
9762 case LVM_GETCOLUMNA:
9763 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9765 case LVM_GETCOLUMNW:
9766 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9768 case LVM_GETCOLUMNORDERARRAY:
9769 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9771 case LVM_GETCOLUMNWIDTH:
9772 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9774 case LVM_GETCOUNTPERPAGE:
9775 return LISTVIEW_GetCountPerPage(hwnd);
9777 case LVM_GETEDITCONTROL:
9778 return LISTVIEW_GetEditControl(hwnd);
9780 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9781 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9784 return LISTVIEW_GetHeader(hwnd);
9786 case LVM_GETHOTCURSOR:
9787 FIXME("LVM_GETHOTCURSOR: unimplemented\n");
9790 case LVM_GETHOTITEM:
9791 return LISTVIEW_GetHotItem(hwnd);
9793 case LVM_GETHOVERTIME:
9794 return LISTVIEW_GetHoverTime(hwnd);
9796 case LVM_GETIMAGELIST:
9797 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9799 case LVM_GETISEARCHSTRINGA:
9800 case LVM_GETISEARCHSTRINGW:
9801 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9805 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
9808 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
9810 case LVM_GETITEMCOUNT:
9811 return LISTVIEW_GetItemCount(hwnd);
9813 case LVM_GETITEMPOSITION:
9814 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9816 case LVM_GETITEMRECT:
9817 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9819 case LVM_GETITEMSPACING:
9820 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9822 case LVM_GETITEMSTATE:
9823 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9825 case LVM_GETITEMTEXTA:
9826 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9828 case LVM_GETITEMTEXTW:
9829 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9831 case LVM_GETNEXTITEM:
9832 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9834 case LVM_GETNUMBEROFWORKAREAS:
9835 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9839 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9841 case LVM_GETSELECTEDCOUNT:
9842 return LISTVIEW_GetSelectedCount(hwnd);
9844 case LVM_GETSELECTIONMARK:
9845 return LISTVIEW_GetSelectionMark(hwnd);
9847 case LVM_GETSTRINGWIDTHA:
9848 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
9850 case LVM_GETSTRINGWIDTHW:
9851 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
9853 case LVM_GETSUBITEMRECT:
9854 return LISTVIEW_GetSubItemRect(hwnd, (UINT)wParam, ((LPRECT)lParam)->top,
9855 ((LPRECT)lParam)->left, (LPRECT)lParam);
9857 case LVM_GETTEXTBKCOLOR:
9858 return LISTVIEW_GetTextBkColor(hwnd);
9860 case LVM_GETTEXTCOLOR:
9861 return LISTVIEW_GetTextColor(hwnd);
9863 case LVM_GETTOOLTIPS:
9864 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9867 case LVM_GETTOPINDEX:
9868 return LISTVIEW_GetTopIndex(hwnd);
9870 /*case LVM_GETUNICODEFORMAT:
9871 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9874 case LVM_GETVIEWRECT:
9875 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9877 case LVM_GETWORKAREAS:
9878 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9882 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9884 case LVM_INSERTCOLUMNA:
9885 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9887 case LVM_INSERTCOLUMNW:
9888 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9890 case LVM_INSERTITEMA:
9891 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9893 case LVM_INSERTITEMW:
9894 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9896 case LVM_REDRAWITEMS:
9897 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9900 return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam);
9902 case LVM_SETBKCOLOR:
9903 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9905 /* case LVM_SETBKIMAGE: */
9907 case LVM_SETCALLBACKMASK:
9908 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9910 case LVM_SETCOLUMNA:
9911 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9913 case LVM_SETCOLUMNW:
9914 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9916 case LVM_SETCOLUMNORDERARRAY:
9917 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9919 case LVM_SETCOLUMNWIDTH:
9920 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9922 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9923 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9925 /* case LVM_SETHOTCURSOR: */
9927 case LVM_SETHOTITEM:
9928 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9930 case LVM_SETHOVERTIME:
9931 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9933 case LVM_SETICONSPACING:
9934 return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam);
9936 case LVM_SETIMAGELIST:
9937 return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9940 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9943 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9945 case LVM_SETITEMCOUNT:
9946 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9948 case LVM_SETITEMPOSITION:
9949 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9950 (INT)HIWORD(lParam));
9952 case LVM_SETITEMPOSITION32:
9953 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9954 ((POINT*)lParam)->y);
9956 case LVM_SETITEMSTATE:
9957 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam);
9959 case LVM_SETITEMTEXTA:
9960 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9962 case LVM_SETITEMTEXTW:
9963 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9965 case LVM_SETSELECTIONMARK:
9966 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9968 case LVM_SETTEXTBKCOLOR:
9969 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9971 case LVM_SETTEXTCOLOR:
9972 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9974 /* case LVM_SETTOOLTIPS: */
9975 /* case LVM_SETUNICODEFORMAT: */
9976 /* case LVM_SETWORKAREAS: */
9979 return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9981 case LVM_SUBITEMHITTEST:
9982 return LISTVIEW_SubItemHitTest(hwnd, (LPLVHITTESTINFO)lParam);
9985 return LISTVIEW_Update(hwnd, (INT)wParam);
9988 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9991 return LISTVIEW_Command(hwnd, wParam, lParam);
9994 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9997 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
10000 return DLGC_WANTCHARS | DLGC_WANTARROWS;
10003 return LISTVIEW_GetFont(hwnd);
10006 if (SLOWORD(wParam) < 0) return 0; /* validate not internal codes */
10007 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
10008 (INT)HIWORD(wParam), (HWND)lParam);
10011 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
10014 return LISTVIEW_KillFocus(hwnd);
10016 case WM_LBUTTONDBLCLK:
10017 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
10020 case WM_LBUTTONDOWN:
10021 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
10024 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
10027 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
10029 case WM_MOUSEHOVER:
10030 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
10033 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
10036 return LISTVIEW_NCDestroy(hwnd);
10039 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
10041 case WM_NOTIFYFORMAT:
10042 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
10045 return LISTVIEW_Paint(hwnd, (HDC)wParam);
10047 case WM_RBUTTONDBLCLK:
10048 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
10051 case WM_RBUTTONDOWN:
10052 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
10056 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
10060 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
10063 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
10066 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
10069 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
10071 case WM_STYLECHANGED:
10072 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
10074 case WM_SYSCOLORCHANGE:
10075 COMCTL32_RefreshSysColors();
10078 /* case WM_TIMER: */
10081 if (SLOWORD(wParam) < 0) return 0; /* validate not internal codes */
10082 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
10083 (INT)HIWORD(wParam), (HWND)lParam);
10085 case WM_MOUSEWHEEL:
10086 if (wParam & (MK_SHIFT | MK_CONTROL))
10087 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
10088 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));
10090 case WM_WINDOWPOSCHANGED:
10091 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
10092 SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10093 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10094 LISTVIEW_UpdateSize(hwnd);
10095 LISTVIEW_UpdateScroll(hwnd);
10097 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10099 /* case WM_WININICHANGE: */
10102 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
10104 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
10108 /* call default window procedure */
10109 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10117 * Registers the window class.
10125 VOID LISTVIEW_Register(void)
10127 WNDCLASSW wndClass;
10129 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10130 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10131 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
10132 wndClass.cbClsExtra = 0;
10133 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10134 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
10135 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10136 wndClass.lpszClassName = WC_LISTVIEWW;
10137 RegisterClassW(&wndClass);
10142 * Unregisters the window class.
10150 VOID LISTVIEW_Unregister(void)
10152 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
10157 * Handle any WM_COMMAND messages
10163 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
10165 switch (HIWORD(wParam))
10170 * Adjust the edit window size
10172 WCHAR buffer[1024];
10173 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
10174 HDC hdc = GetDC(infoPtr->hwndEdit);
10175 HFONT hFont, hOldFont = 0;
10180 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10181 GetWindowRect(infoPtr->hwndEdit, &rect);
10183 /* Select font to get the right dimension of the string */
10184 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10187 hOldFont = SelectObject(hdc, hFont);
10190 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10192 TEXTMETRICW textMetric;
10194 /* Add Extra spacing for the next character */
10195 GetTextMetricsW(hdc, &textMetric);
10196 sz.cx += (textMetric.tmMaxCharWidth * 2);
10204 rect.bottom - rect.top,
10205 SWP_DRAWFRAME|SWP_NOMOVE);
10208 SelectObject(hdc, hOldFont);
10210 ReleaseDC(hwnd, hdc);
10216 return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
10225 * Subclassed edit control windproc function
10231 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
10232 WPARAM wParam, LPARAM lParam, BOOL isW)
10234 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
10235 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
10236 static BOOL bIgnoreKillFocus = FALSE;
10237 BOOL cancel = FALSE;
10239 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
10240 hwnd, uMsg, wParam, lParam, isW);
10244 case WM_GETDLGCODE:
10245 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10248 if(bIgnoreKillFocus) return TRUE;
10253 WNDPROC editProc = einfo->EditWndProc;
10254 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
10255 COMCTL32_Free(einfo);
10256 infoPtr->pedititem = NULL;
10257 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10261 if (VK_ESCAPE == (INT)wParam)
10266 else if (VK_RETURN == (INT)wParam)
10270 return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10273 if (einfo->EditLblCb)
10275 LPWSTR buffer = NULL;
10279 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10283 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10285 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10286 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10290 /* Processing LVN_ENDLABELEDIT message could kill the focus */
10291 /* eg. Using a messagebox */
10292 bIgnoreKillFocus = TRUE;
10293 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
10295 if (buffer) COMCTL32_Free(buffer);
10297 einfo->EditLblCb = NULL;
10298 bIgnoreKillFocus = FALSE;
10301 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10307 * Subclassed edit control windproc function
10313 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10315 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10320 * Subclassed edit control windproc function
10326 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10328 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10333 * Creates a subclassed edit cotrol
10339 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
10340 INT width, INT height, HWND parent, HINSTANCE hinst,
10341 EditlblCallbackW EditLblCb, DWORD param, BOOL isW)
10343 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
10344 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10349 TEXTMETRICW textMetric;
10351 TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
10353 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
10356 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
10357 hdc = GetDC(parent);
10359 /* Select the font to get appropriate metric dimensions */
10360 if(infoPtr->hFont != 0)
10361 hOldFont = SelectObject(hdc, infoPtr->hFont);
10363 /*Get String Lenght in pixels */
10364 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10366 /*Add Extra spacing for the next character */
10367 GetTextMetricsW(hdc, &textMetric);
10368 sz.cx += (textMetric.tmMaxCharWidth * 2);
10370 if(infoPtr->hFont != 0)
10371 SelectObject(hdc, hOldFont);
10373 ReleaseDC(parent, hdc);
10375 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
10377 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
10381 COMCTL32_Free(infoPtr->pedititem);
10385 infoPtr->pedititem->param = param;
10386 infoPtr->pedititem->EditLblCb = EditLblCb;
10387 infoPtr->pedititem->EditWndProc = (WNDPROC)
10388 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
10389 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
10391 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);