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_Scroll : not implemented
49 * LISTVIEW_Update : not completed
51 * Known differences in message stream from native control (not known if
52 * these differences cause problems):
53 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
54 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
55 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
56 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
57 * does *not* invoke DefWindowProc
58 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
59 * processing for "USEDOUBLECLICKTIME".
71 #include "wine/debug.h"
73 WINE_DEFAULT_DEBUG_CHANNEL(listview);
75 /* Some definitions for inline edit control */
76 typedef BOOL (*EditlblCallbackW)(HWND, LPWSTR, DWORD);
77 typedef BOOL (*EditlblCallbackA)(HWND, LPWSTR, DWORD);
79 typedef struct tagLV_INTHIT
82 DWORD distance; /* distance to closest item */
83 INT iDistItem; /* item number that is closest */
84 } LV_INTHIT, *LPLV_INTHIT;
87 typedef struct tagEDITLABEL_ITEM
91 EditlblCallbackW EditLblCb;
94 typedef struct tagLISTVIEW_SUBITEM
101 typedef struct tagLISTVIEW_ITEM
112 typedef struct tagLISTVIEW_SELECTION
116 } LISTVIEW_SELECTION;
118 typedef struct tagLISTVIEW_INFO
124 HIMAGELIST himlNormal;
125 HIMAGELIST himlSmall;
126 HIMAGELIST himlState;
130 HDPA hdpaSelectionRanges;
144 INT ntmHeight; /* from GetTextMetrics from above font */
145 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
147 DWORD dwExStyle; /* extended listview style */
149 PFNLVCOMPARE pfnCompare;
153 EDITLABEL_ITEM *pedititem;
155 INT nColumnCount; /* the number of columns in this control */
157 DWORD lastKeyPressTimestamp; /* Added */
158 WPARAM charCode; /* Added */
159 INT nSearchParamLength; /* Added */
160 WCHAR szSearchParam[ MAX_PATH ]; /* Added */
167 /* maximum size of a label */
168 #define DISP_TEXT_SIZE 512
170 /* padding for items in list and small icon display modes */
171 #define WIDTH_PADDING 12
173 /* padding for items in list, report and small icon display modes */
174 #define HEIGHT_PADDING 1
176 /* offset of items in report display mode */
177 #define REPORT_MARGINX 2
179 /* padding for icon in large icon display mode
180 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
181 * that HITTEST will see.
182 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
183 * ICON_TOP_PADDING - sum of the two above.
184 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
185 * LABEL_VERT_OFFSET - between bottom of text and end of box
187 #define ICON_TOP_PADDING_NOTHITABLE 2
188 #define ICON_TOP_PADDING_HITABLE 2
189 #define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE
190 #define ICON_BOTTOM_PADDING 4
191 #define LABEL_VERT_OFFSET 10
193 /* default label width for items in list and small icon display modes */
194 #define DEFAULT_LABEL_WIDTH 40
196 /* default column width for items in list display mode */
197 #define DEFAULT_COLUMN_WIDTH 96
199 /* Increment size of the horizontal scroll bar */
200 #define LISTVIEW_SCROLL_DIV_SIZE 10
202 /* Padding betwen image and label */
203 #define IMAGE_PADDING 2
205 /* Padding behind the label */
206 #define TRAILING_PADDING 5
208 /* Border for the icon caption */
209 #define CAPTION_BORDER 2
213 /* retrieve the number of items in the listview */
214 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
215 #define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA )
217 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
218 INT width, INT height, HWND parent, HINSTANCE hinst,
219 EditlblCallbackW EditLblCb, DWORD param, BOOL isW);
222 * forward declarations
224 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW);
225 static INT LISTVIEW_SuperHitTestItem(HWND, LPLV_INTHIT, BOOL);
226 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL);
227 static INT LISTVIEW_GetCountPerRow(HWND);
228 static INT LISTVIEW_GetCountPerColumn(HWND);
229 static VOID LISTVIEW_AlignLeft(HWND);
230 static VOID LISTVIEW_AlignTop(HWND);
231 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
232 static VOID LISTVIEW_AddSelection(HWND, INT);
233 static BOOL LISTVIEW_AddSubItemT(HWND, LPLVITEMW, BOOL);
234 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
235 static INT LISTVIEW_GetItemHeight(HWND);
236 static BOOL LISTVIEW_GetItemBoundBox(HWND, INT, LPRECT);
237 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
238 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
239 static INT LISTVIEW_GetItemWidth(HWND);
240 static INT LISTVIEW_GetLabelWidth(HWND, INT);
241 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
242 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
243 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
244 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
245 static BOOL LISTVIEW_InitItemT(HWND, LISTVIEW_ITEM *, LPLVITEMW, BOOL);
246 static BOOL LISTVIEW_InitSubItemT(HWND, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL);
247 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
248 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
249 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
250 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
251 static BOOL LISTVIEW_SetItemT(HWND, LPLVITEMW, BOOL);
252 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
253 static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG);
254 static VOID LISTVIEW_UpdateScroll(HWND);
255 static VOID LISTVIEW_SetSelection(HWND, INT);
256 static VOID LISTVIEW_UpdateSize(HWND);
257 static BOOL LISTVIEW_SetSubItemT(HWND, LPLVITEMW, BOOL);
258 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
259 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
260 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
261 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW);
262 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem);
263 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem);
264 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
265 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
266 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW);
267 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
268 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
269 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
270 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem);
271 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem);
272 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
273 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
274 static void ListView_UpdateLargeItemLabelRect (HWND hwnd, const LISTVIEW_INFO* infoPtr, int nItem, RECT *rect);
275 static LRESULT LISTVIEW_GetColumnT(HWND, INT, LPLVCOLUMNW, BOOL);
277 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
278 #define KEY_DELAY 450
280 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
282 static inline BOOL is_textW(LPCWSTR text)
284 return text != NULL && text != LPSTR_TEXTCALLBACKW;
287 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
289 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
290 return is_textW(text);
293 static inline int textlenT(LPCWSTR text, BOOL isW)
295 return !is_textT(text, isW) ? 0 :
296 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
299 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
302 if (isSrcW) lstrcpynW(dest, src, max);
303 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
305 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
306 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
309 static inline LPCSTR debugstr_t(LPCWSTR text, BOOL isW)
311 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
314 static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n)
316 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
319 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
321 LPWSTR wstr = (LPWSTR)text;
323 TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW);
326 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
327 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
328 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
330 TRACE(" wstr=%s\n", debugstr_w(wstr));
334 static inline void textfreeT(LPWSTR wstr, BOOL isW)
336 if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
340 * dest is a pointer to a Unicode string
341 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
343 static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
345 LPWSTR pszText = textdupTtoW(src, isW);
347 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
348 bResult = Str_SetPtrW(dest, pszText);
349 textfreeT(pszText, isW);
353 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
354 WPARAM wParam, LPARAM lParam, BOOL isW)
357 return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
359 return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
362 static inline BOOL notify(HWND self, INT code, LPNMHDR pnmh)
364 pnmh->hwndFrom = self;
365 pnmh->idFrom = GetWindowLongW(self, GWL_ID);
367 return (BOOL)SendMessageW(GetParent(self), WM_NOTIFY,
368 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
371 static inline BOOL hdr_notify(HWND self, INT code)
374 return notify(self, code, &nmh);
377 static inline BOOL listview_notify(HWND self, INT code, LPNMLISTVIEW plvnm)
379 return notify(self, code, (LPNMHDR)plvnm);
382 static int tabNotification[] = {
383 LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
384 LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
385 LVN_GETDISPINFOW, LVN_GETDISPINFOA,
386 LVN_SETDISPINFOW, LVN_SETDISPINFOA,
387 LVN_ODFINDITEMW, LVN_ODFINDITEMA,
388 LVN_GETINFOTIPW, LVN_GETINFOTIPA,
392 static int get_ansi_notification(INT unicodeNotificationCode)
394 int *pTabNotif = tabNotification;
395 while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
396 if (*pTabNotif) return *pTabNotif;
397 ERR("unknown notification %x\n", unicodeNotificationCode);
398 return unicodeNotificationCode;
402 Send notification. depends on dispinfoW having same
403 structure as dispinfoA.
404 self : listview handle
405 notificationCode : *Unicode* notification code
406 pdi : dispinfo structure (can be unicode or ansi)
407 isW : TRUE if dispinfo is Unicode
409 static BOOL dispinfo_notifyT(HWND self, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
411 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(self, 0);
412 BOOL bResult = FALSE;
413 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
415 INT cchTempBufMax = 0, savCchTextMax = 0;
416 LPWSTR pszTempBuf = NULL, savPszText = NULL;
418 TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self, notificationCode, pdi, isW);
419 TRACE(" notifyFormat=%s\n",
420 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
421 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
422 if (infoPtr->notifyFormat == NFR_ANSI)
423 realNotifCode = get_ansi_notification(notificationCode);
425 realNotifCode = notificationCode;
427 if (is_textT(pdi->item.pszText, isW))
429 if (isW && infoPtr->notifyFormat == NFR_ANSI)
430 convertToAnsi = TRUE;
431 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
432 convertToUnicode = TRUE;
435 if (convertToAnsi || convertToUnicode)
437 TRACE(" we have to convert the text to the correct format\n");
438 if (notificationCode != LVN_GETDISPINFOW)
439 { /* length of existing text */
440 cchTempBufMax = convertToUnicode ?
441 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
442 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
445 cchTempBufMax = pdi->item.cchTextMax;
447 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
448 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
449 if (!pszTempBuf) return FALSE;
450 if (convertToUnicode)
451 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
452 pszTempBuf, cchTempBufMax);
454 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
455 cchTempBufMax, NULL, NULL);
456 TRACE(" text=%s\n", debugstr_t(pszTempBuf, convertToUnicode));
457 savCchTextMax = pdi->item.cchTextMax;
458 savPszText = pdi->item.pszText;
459 pdi->item.pszText = pszTempBuf;
460 pdi->item.cchTextMax = cchTempBufMax;
463 bResult = notify(self, realNotifCode, (LPNMHDR)pdi);
465 if (convertToUnicode || convertToAnsi)
466 { /* convert back result */
467 TRACE(" returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode));
468 if (convertToUnicode) /* note : pointer can be changed by app ! */
469 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
470 savCchTextMax, NULL, NULL);
472 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
473 savPszText, savCchTextMax);
474 pdi->item.pszText = savPszText; /* restores our buffer */
475 pdi->item.cchTextMax = savCchTextMax;
476 HeapFree(GetProcessHeap(), 0, pszTempBuf);
481 static inline LRESULT LISTVIEW_GetItemW(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal)
483 return LISTVIEW_GetItemT(hwnd, lpLVItem, internal, TRUE);
486 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
490 n = min(min(n, strlenW(s1)), strlenW(s2));
491 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
492 return res ? res - 2 : res;
495 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
497 static int index = 0;
498 static char buffers[20][256];
499 char* buf = buffers[index++ % 20];
500 if (lpLVItem == NULL) return "(null)";
501 snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
502 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
503 lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
504 lpLVItem->state, lpLVItem->stateMask,
505 lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
506 debugstr_tn(lpLVItem->pszText, isW, 80),
507 lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
512 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
514 static int index = 0;
515 static char buffers[20][256];
516 char* buf = buffers[index++ % 20];
517 if (lpColumn == NULL) return "(null)";
518 snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
519 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
520 lpColumn->mask, lpColumn->fmt, lpColumn->cx,
521 lpColumn->mask & LVCF_TEXT ? lpColumn->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
522 debugstr_tn(lpColumn->pszText, isW, 80): "",
523 lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
527 static void LISTVIEW_DumpListview(LISTVIEW_INFO *iP, INT line)
529 DWORD dwStyle = GetWindowLongW (iP->hwndSelf, GWL_STYLE);
530 TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n",
531 iP->hwndSelf, line, iP->clrBk, iP->clrText, iP->clrTextBk,
532 iP->nItemHeight, iP->nItemWidth, dwStyle);
533 TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n",
534 iP->hwndSelf, line, iP->himlNormal, iP->himlSmall, iP->himlState,
535 iP->nFocusedItem, iP->nHotItem, iP->dwExStyle);
539 LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
542 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
543 NMLVCUSTOMDRAW nmcdhdr;
546 TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd, dwDrawStage, hdc);
548 nmcd= & nmcdhdr.nmcd;
549 nmcd->hdr.hwndFrom = hwnd;
550 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
551 nmcd->hdr.code = NM_CUSTOMDRAW;
552 nmcd->dwDrawStage= dwDrawStage;
554 nmcd->rc.left = rc.left;
555 nmcd->rc.right = rc.right;
556 nmcd->rc.bottom = rc.bottom;
557 nmcd->rc.top = rc.top;
558 nmcd->dwItemSpec = 0;
559 nmcd->uItemState = 0;
560 nmcd->lItemlParam= 0;
561 nmcdhdr.clrText = infoPtr->clrText;
562 nmcdhdr.clrTextBk= infoPtr->clrBk;
564 return (BOOL)SendMessageW (GetParent (hwnd), WM_NOTIFY,
565 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
569 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
570 UINT iItem, UINT iSubItem,
573 LISTVIEW_INFO *infoPtr;
574 NMLVCUSTOMDRAW nmcdhdr;
576 DWORD dwDrawStage,dwItemSpec;
582 infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
584 ZeroMemory(&item,sizeof(item));
586 item.mask = LVIF_PARAM;
587 ListView_GetItemW(hwnd,&item);
589 dwDrawStage=CDDS_ITEM | uItemDrawState;
593 if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
594 if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
595 if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
597 itemRect.left = LVIR_BOUNDS;
598 LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
600 nmcd= & nmcdhdr.nmcd;
601 nmcd->hdr.hwndFrom = hwnd;
602 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
603 nmcd->hdr.code = NM_CUSTOMDRAW;
604 nmcd->dwDrawStage= dwDrawStage;
606 nmcd->rc.left = itemRect.left;
607 nmcd->rc.right = itemRect.right;
608 nmcd->rc.bottom = itemRect.bottom;
609 nmcd->rc.top = itemRect.top;
610 nmcd->dwItemSpec = dwItemSpec;
611 nmcd->uItemState = uItemState;
612 nmcd->lItemlParam= item.lParam;
613 nmcdhdr.clrText = infoPtr->clrText;
614 nmcdhdr.clrTextBk= infoPtr->clrBk;
615 nmcdhdr.iSubItem =iSubItem;
617 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
618 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
619 nmcd->uItemState, nmcd->lItemlParam);
621 retval=SendMessageW (GetParent (hwnd), WM_NOTIFY,
622 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
624 infoPtr->clrText=nmcdhdr.clrText;
625 infoPtr->clrBk =nmcdhdr.clrTextBk;
626 return (BOOL) retval;
630 /*************************************************************************
631 * LISTVIEW_ProcessLetterKeys
633 * Processes keyboard messages generated by pressing the letter keys
635 * What this does is perform a case insensitive search from the
636 * current position with the following quirks:
637 * - If two chars or more are pressed in quick succession we search
638 * for the corresponding string (e.g. 'abc').
639 * - If there is a delay we wipe away the current search string and
640 * restart with just that char.
641 * - If the user keeps pressing the same character, whether slowly or
642 * fast, so that the search string is entirely composed of this
643 * character ('aaaaa' for instance), then we search for first item
644 * that starting with that character.
645 * - If the user types the above character in quick succession, then
646 * we must also search for the corresponding string ('aaaaa'), and
647 * go to that string if there is a match.
655 * - The current implementation has a list of characters it will
656 * accept and it ignores averything else. In particular it will
657 * ignore accentuated characters which seems to match what
658 * Windows does. But I'm not sure it makes sense to follow
660 * - We don't sound a beep when the search fails.
664 * TREEVIEW_ProcessLetterKeys
666 static INT LISTVIEW_ProcessLetterKeys(
667 HWND hwnd, /* handle to the window */
668 WPARAM charCode, /* the character code, the actual character */
669 LPARAM keyData /* key data */
672 LISTVIEW_INFO *infoPtr;
677 WCHAR buffer[MAX_PATH];
678 DWORD timestamp,elapsed;
680 /* simple parameter checking */
681 if (!hwnd || !charCode || !keyData)
684 infoPtr=(LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
688 /* only allow the valid WM_CHARs through */
689 if (!isalnum(charCode) &&
690 charCode != '.' && charCode != '`' && charCode != '!' &&
691 charCode != '@' && charCode != '#' && charCode != '$' &&
692 charCode != '%' && charCode != '^' && charCode != '&' &&
693 charCode != '*' && charCode != '(' && charCode != ')' &&
694 charCode != '-' && charCode != '_' && charCode != '+' &&
695 charCode != '=' && charCode != '\\'&& charCode != ']' &&
696 charCode != '}' && charCode != '[' && charCode != '{' &&
697 charCode != '/' && charCode != '?' && charCode != '>' &&
698 charCode != '<' && charCode != ',' && charCode != '~')
701 nSize=GETITEMCOUNT(infoPtr);
702 /* if there's one item or less, there is no where to go */
706 /* compute how much time elapsed since last keypress */
707 timestamp=GetTickCount();
708 if (timestamp > infoPtr->lastKeyPressTimestamp) {
709 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
711 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
714 /* update the search parameters */
715 infoPtr->lastKeyPressTimestamp=timestamp;
716 if (elapsed < KEY_DELAY) {
717 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
718 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
720 if (infoPtr->charCode != charCode) {
721 infoPtr->charCode=charCode=0;
724 infoPtr->charCode=charCode;
725 infoPtr->szSearchParam[0]=charCode;
726 infoPtr->nSearchParamLength=1;
727 /* Redundant with the 1 char string */
731 /* and search from the current position */
733 if (infoPtr->nFocusedItem >= 0) {
734 endidx=infoPtr->nFocusedItem;
736 /* if looking for single character match,
737 * then we must always move forward
739 if (infoPtr->nSearchParamLength == 1)
753 ZeroMemory(&item, sizeof(item));
754 item.mask = LVIF_TEXT;
757 item.pszText = buffer;
758 item.cchTextMax = COUNTOF(buffer);
759 ListView_GetItemW( hwnd, &item );
761 /* check for a match */
762 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
765 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
766 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
767 /* This would work but we must keep looking for a longer match */
771 } while (idx != endidx);
774 if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
775 /* refresh client area */
776 InvalidateRect(hwnd, NULL, TRUE);
784 /*************************************************************************
785 * LISTVIEW_UpdateHeaderSize [Internal]
787 * Function to resize the header control
790 * hwnd [I] handle to a window
791 * nNewScrollPos [I] Scroll Pos to Set
798 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
800 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
804 GetWindowRect(infoPtr->hwndHeader, &winRect);
805 point[0].x = winRect.left;
806 point[0].y = winRect.top;
807 point[1].x = winRect.right;
808 point[1].y = winRect.bottom;
810 MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
811 point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
812 point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
814 SetWindowPos(infoPtr->hwndHeader,0,
815 point[0].x,point[0].y,point[1].x,point[1].y,
816 SWP_NOZORDER | SWP_NOACTIVATE);
821 * Update the scrollbars. This functions should be called whenever
822 * the content, size or view changes.
825 * [I] HWND : window handle
830 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
832 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
833 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
834 UINT uView = lStyle & LVS_TYPEMASK;
835 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
836 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
837 SCROLLINFO scrollInfo;
839 if (lStyle & LVS_NOSCROLL) return;
841 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
842 scrollInfo.cbSize = sizeof(SCROLLINFO);
844 if (uView == LVS_LIST)
846 /* update horizontal scrollbar */
848 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
849 INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
850 INT nNumOfItems = GETITEMCOUNT(infoPtr);
852 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
853 if((nNumOfItems % nCountPerColumn) == 0)
857 scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
858 scrollInfo.nPage = nCountPerRow;
859 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
860 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
861 ShowScrollBar(hwnd, SB_VERT, FALSE);
863 else if (uView == LVS_REPORT)
865 /* update vertical scrollbar */
867 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
868 scrollInfo.nPos = ListView_GetTopIndex(hwnd);
869 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
870 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
871 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
873 /* update horizontal scrollbar */
874 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
875 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
876 || GETITEMCOUNT(infoPtr) == 0)
881 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
882 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
883 scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
884 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
886 /* Update the Header Control */
887 scrollInfo.fMask = SIF_POS;
888 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
889 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
896 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
898 INT nViewWidth = rcView.right - rcView.left;
899 INT nViewHeight = rcView.bottom - rcView.top;
901 /* Update Horizontal Scrollbar */
902 scrollInfo.fMask = SIF_POS;
903 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
904 || GETITEMCOUNT(infoPtr) == 0)
908 scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
910 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
911 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
912 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
914 /* Update Vertical Scrollbar */
915 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
916 scrollInfo.fMask = SIF_POS;
917 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
918 || GETITEMCOUNT(infoPtr) == 0)
922 scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
924 scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
925 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
926 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
933 * Prints a message for unsupported window styles.
934 * A kind of TODO list for window styles.
937 * [I] LONG : window style
942 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
944 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
945 FIXME(" LVS_NOSCROLL\n");
947 if (lStyle & LVS_EDITLABELS)
948 FIXME(" LVS_EDITLABELS\n");
950 if (lStyle & LVS_NOLABELWRAP)
951 FIXME(" LVS_NOLABELWRAP\n");
953 if (lStyle & LVS_SHAREIMAGELISTS)
954 FIXME(" LVS_SHAREIMAGELISTS\n");
956 if (lStyle & LVS_SORTASCENDING)
957 FIXME(" LVS_SORTASCENDING\n");
959 if (lStyle & LVS_SORTDESCENDING)
960 FIXME(" LVS_SORTDESCENDING\n");
965 * Aligns the items with the top edge of the window.
968 * [I] HWND : window handle
973 static VOID LISTVIEW_AlignTop(HWND hwnd)
975 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
976 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
977 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
980 INT i, off_x=0, off_y=0;
982 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
984 /* Since SetItemPosition uses upper-left of icon, and for
985 style=LVS_ICON the icon is not left adjusted, get the offset */
986 if (uView == LVS_ICON)
988 off_y = ICON_TOP_PADDING;
989 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
993 ZeroMemory(&rcView, sizeof(RECT));
995 if (nListWidth > infoPtr->nItemWidth)
997 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
999 if (ptItem.x + infoPtr->nItemWidth > nListWidth)
1002 ptItem.y += infoPtr->nItemHeight;
1005 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1006 ptItem.x += infoPtr->nItemWidth;
1007 rcView.right = max(rcView.right, ptItem.x);
1010 rcView.bottom = ptItem.y + infoPtr->nItemHeight;
1014 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1016 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1017 ptItem.y += infoPtr->nItemHeight;
1020 rcView.right = infoPtr->nItemWidth;
1021 rcView.bottom = ptItem.y;
1024 LISTVIEW_SetViewRect(hwnd, &rcView);
1030 * Aligns the items with the left edge of the window.
1033 * [I] HWND : window handle
1038 static VOID LISTVIEW_AlignLeft(HWND hwnd)
1040 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1041 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1042 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1045 INT i, off_x=0, off_y=0;
1047 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1049 /* Since SetItemPosition uses upper-left of icon, and for
1050 style=LVS_ICON the icon is not left adjusted, get the offset */
1051 if (uView == LVS_ICON)
1053 off_y = ICON_TOP_PADDING;
1054 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1058 ZeroMemory(&rcView, sizeof(RECT));
1060 if (nListHeight > infoPtr->nItemHeight)
1062 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1064 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1067 ptItem.x += infoPtr->nItemWidth;
1070 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1071 ptItem.y += infoPtr->nItemHeight;
1072 rcView.bottom = max(rcView.bottom, ptItem.y);
1075 rcView.right = ptItem.x + infoPtr->nItemWidth;
1079 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1081 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1082 ptItem.x += infoPtr->nItemWidth;
1085 rcView.bottom = infoPtr->nItemHeight;
1086 rcView.right = ptItem.x;
1089 LISTVIEW_SetViewRect(hwnd, &rcView);
1095 * Set the bounding rectangle of all the items.
1098 * [I] HWND : window handle
1099 * [I] LPRECT : bounding rectangle
1105 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
1107 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1108 BOOL bResult = FALSE;
1110 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
1111 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1113 if (lprcView != NULL)
1116 infoPtr->rcView.left = lprcView->left;
1117 infoPtr->rcView.top = lprcView->top;
1118 infoPtr->rcView.right = lprcView->right;
1119 infoPtr->rcView.bottom = lprcView->bottom;
1127 * Retrieves the bounding rectangle of all the items.
1130 * [I] HWND : window handle
1131 * [O] LPRECT : bounding rectangle
1137 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
1139 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1140 BOOL bResult = FALSE;
1143 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
1145 if (lprcView != NULL)
1147 bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
1148 if (bResult != FALSE)
1150 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1151 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1152 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1153 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1156 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1157 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1165 * Retrieves the subitem pointer associated with the subitem index.
1168 * [I] HDPA : DPA handle for a specific item
1169 * [I] INT : index of subitem
1172 * SUCCESS : subitem pointer
1175 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1178 LISTVIEW_SUBITEM *lpSubItem;
1181 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1183 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1184 if (lpSubItem != NULL)
1186 if (lpSubItem->iSubItem == nSubItem)
1198 * Calculates the width of an item.
1201 * [I] HWND : window handle
1202 * [I] LONG : window style
1205 * Returns item width.
1207 static INT LISTVIEW_GetItemWidth(HWND hwnd)
1209 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1210 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
1211 UINT uView = style & LVS_TYPEMASK;
1212 INT nHeaderItemCount;
1218 TRACE("(hwnd=%x)\n", hwnd);
1220 if (uView == LVS_ICON)
1222 nItemWidth = infoPtr->iconSpacing.cx;
1224 else if (uView == LVS_REPORT)
1226 /* calculate width of header */
1227 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1228 for (i = 0; i < nHeaderItemCount; i++)
1230 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1232 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1238 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1240 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
1241 nItemWidth = max(nItemWidth, nLabelWidth);
1244 /* default label size */
1245 if (GETITEMCOUNT(infoPtr) == 0)
1247 nItemWidth = DEFAULT_COLUMN_WIDTH;
1251 if (nItemWidth == 0)
1253 nItemWidth = DEFAULT_LABEL_WIDTH;
1258 nItemWidth += WIDTH_PADDING;
1260 if (infoPtr->himlSmall != NULL)
1262 nItemWidth += infoPtr->iconSize.cx;
1265 if (infoPtr->himlState != NULL)
1267 nItemWidth += infoPtr->iconSize.cx;
1274 /* nItemWidth Cannot be Zero */
1282 * Calculates the width of a specific item.
1285 * [I] HWND : window handle
1286 * [I] LPSTR : string
1289 * Returns the width of an item width a specified string.
1291 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1293 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1294 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1295 INT nHeaderItemCount;
1300 TRACE("(hwnd=%x)\n", hwnd);
1302 if (uView == LVS_ICON)
1304 nItemWidth = infoPtr->iconSpacing.cx;
1306 else if (uView == LVS_REPORT)
1308 /* calculate width of header */
1309 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1310 for (i = 0; i < nHeaderItemCount; i++)
1312 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1314 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1320 /* get width of string */
1321 nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1323 /* default label size */
1324 if (GETITEMCOUNT(infoPtr) == 0)
1326 nItemWidth = DEFAULT_COLUMN_WIDTH;
1330 if (nItemWidth == 0)
1332 nItemWidth = DEFAULT_LABEL_WIDTH;
1337 nItemWidth += WIDTH_PADDING;
1339 if (infoPtr->himlSmall != NULL)
1341 nItemWidth += infoPtr->iconSize.cx;
1344 if (infoPtr->himlState != NULL)
1346 nItemWidth += infoPtr->iconSize.cx;
1357 * Retrieves and saves important text metrics info for the current
1361 * [I] HWND : window handle
1364 static VOID LISTVIEW_SaveTextMetrics(HWND hwnd)
1366 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1368 HDC hdc = GetDC(hwnd);
1369 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1370 INT oldHeight, oldACW;
1372 GetTextMetricsW(hdc, &tm);
1374 oldHeight = infoPtr->ntmHeight;
1375 oldACW = infoPtr->ntmAveCharWidth;
1376 infoPtr->ntmHeight = tm.tmHeight;
1377 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1379 SelectObject(hdc, hOldFont);
1380 ReleaseDC(hwnd, hdc);
1381 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1382 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1388 * Calculates the height of an item.
1391 * [I] HWND : window handle
1394 * Returns item height.
1396 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1398 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1399 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1400 INT nItemHeight = 0;
1402 if (uView == LVS_ICON)
1404 nItemHeight = infoPtr->iconSpacing.cy;
1408 if(infoPtr->himlState || infoPtr->himlSmall)
1409 nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1411 nItemHeight = infoPtr->ntmHeight;
1418 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1420 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1421 LISTVIEW_SELECTION *selection;
1422 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1425 TRACE("Selections are:\n");
1426 for (i = 0; i < topSelection; i++)
1428 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1429 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1435 * A compare function for selection ranges
1438 * [I] LPVOID : Item 1;
1439 * [I] LPVOID : Item 2;
1440 * [I] LPARAM : flags
1443 * >0 : if Item 1 > Item 2
1444 * <0 : if Item 2 > Item 1
1445 * 0 : if Item 1 == Item 2
1447 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1450 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1451 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1452 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1453 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1467 * Adds a selection range.
1470 * [I] HWND : window handle
1471 * [I] INT : lower item index
1472 * [I] INT : upper item index
1477 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1479 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1480 LISTVIEW_SELECTION *selection;
1481 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1482 BOOL lowerzero=FALSE;
1484 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1485 selection->lower = lItem;
1486 selection->upper = uItem;
1488 TRACE("Add range %i - %i\n", lItem, uItem);
1491 LISTVIEW_SELECTION *checkselection,*checkselection2;
1492 INT index,mergeindex;
1494 /* find overlapping selections */
1495 /* we want to catch adjacent ranges so expand our range by 1 */
1498 if (selection->lower == 0)
1503 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1504 LISTVIEW_CompareSelectionRanges,
1506 selection->upper --;
1510 selection->lower ++;
1514 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1515 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1516 checkselection->upper);
1518 checkselection->lower = min(selection->lower,checkselection->lower);
1519 checkselection->upper = max(selection->upper,checkselection->upper);
1521 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1522 checkselection->upper);
1524 COMCTL32_Free(selection);
1526 /* merge now common selection ranges in the lower group*/
1529 checkselection->upper ++;
1530 if (checkselection->lower == 0)
1533 checkselection->lower --;
1535 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1536 checkselection->upper);
1538 /* not sorted yet */
1539 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1540 LISTVIEW_CompareSelectionRanges, 0,
1543 checkselection->upper --;
1547 checkselection->lower ++;
1549 if (mergeindex >=0 && mergeindex != index)
1551 TRACE("Merge with index %i\n",mergeindex);
1552 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1554 checkselection->lower = min(checkselection->lower,
1555 checkselection2->lower);
1556 checkselection->upper = max(checkselection->upper,
1557 checkselection2->upper);
1558 COMCTL32_Free(checkselection2);
1559 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1563 while (mergeindex > -1 && mergeindex <index);
1565 /* merge now common selection ranges in the upper group*/
1568 checkselection->upper ++;
1569 if (checkselection->lower == 0)
1572 checkselection->lower --;
1574 TRACE("search upper range %i (%lu - %lu)\n",index,
1575 checkselection->lower, checkselection->upper);
1577 /* not sorted yet */
1578 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1580 LISTVIEW_CompareSelectionRanges, 0,
1583 checkselection->upper --;
1587 checkselection->lower ++;
1589 if (mergeindex >=0 && mergeindex !=index)
1591 TRACE("Merge with index %i\n",mergeindex);
1592 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1594 checkselection->lower = min(checkselection->lower,
1595 checkselection2->lower);
1596 checkselection->upper = max(checkselection->upper,
1597 checkselection2->upper);
1598 COMCTL32_Free(checkselection2);
1599 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1602 while (mergeindex > -1);
1607 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1608 LISTVIEW_CompareSelectionRanges, 0,
1611 TRACE("Insert before index %i\n",index);
1614 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1619 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1624 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1625 LISTVIEW_PrintSelectionRanges(hwnd);
1630 * check if a specified index is selected.
1633 * [I] HWND : window handle
1634 * [I] INT : item index
1639 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1641 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1642 LISTVIEW_SELECTION selection;
1645 selection.upper = nItem;
1646 selection.lower = nItem;
1648 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1649 LISTVIEW_CompareSelectionRanges,
1659 * Removes all selection ranges
1662 * HWND: window handle
1668 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1670 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1671 LISTVIEW_SELECTION *selection;
1675 TRACE("(0x%x)\n",hwnd);
1677 ZeroMemory(&item,sizeof(item));
1678 item.stateMask = LVIS_SELECTED;
1682 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1685 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1686 for (i = selection->lower; i<=selection->upper; i++)
1687 LISTVIEW_SetItemState(hwnd,i,&item);
1688 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1691 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1699 * Removes a range selections.
1702 * [I] HWND : window handle
1703 * [I] INT : lower item index
1704 * [I] INT : upper item index
1709 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1711 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1712 LISTVIEW_SELECTION removeselection,*checkselection;
1715 removeselection.lower = lItem;
1716 removeselection.upper = uItem;
1718 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1719 LISTVIEW_PrintSelectionRanges(hwnd);
1721 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1722 LISTVIEW_CompareSelectionRanges,
1729 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1732 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1733 checkselection->upper);
1736 if ((checkselection->upper == removeselection.upper) &&
1737 (checkselection->lower == removeselection.lower))
1739 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1742 /* case 2: engulf */
1743 else if (((checkselection->upper < removeselection.upper) &&
1744 (checkselection->lower > removeselection.lower))||
1745 ((checkselection->upper <= removeselection.upper) &&
1746 (checkselection->lower > removeselection.lower)) ||
1747 ((checkselection->upper < removeselection.upper) &&
1748 (checkselection->lower >= removeselection.lower)))
1751 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1752 /* do it again because others may also get caught */
1754 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1756 /* case 3: overlap upper */
1757 else if ((checkselection->upper < removeselection.upper) &&
1758 (checkselection->lower < removeselection.lower))
1760 checkselection->upper = removeselection.lower - 1;
1762 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1764 /* case 4: overlap lower */
1765 else if ((checkselection->upper > removeselection.upper) &&
1766 (checkselection->lower > removeselection.lower))
1768 checkselection->lower = removeselection.upper + 1;
1770 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1772 /* case 5: fully internal */
1773 else if (checkselection->upper == removeselection.upper)
1774 checkselection->upper = removeselection.lower - 1;
1775 else if (checkselection->lower == removeselection.lower)
1776 checkselection->lower = removeselection.upper + 1;
1779 /* bisect the range */
1780 LISTVIEW_SELECTION *newselection;
1782 newselection = (LISTVIEW_SELECTION *)
1783 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1784 newselection -> lower = checkselection->lower;
1785 newselection -> upper = removeselection.lower - 1;
1786 checkselection -> lower = removeselection.upper + 1;
1787 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1789 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1791 LISTVIEW_PrintSelectionRanges(hwnd);
1796 * Updates the various indices after an item has been inserted or deleted.
1799 * [I] HWND : window handle
1800 * [I] INT : item index
1801 * [I] INT : Direction of shift, +1 or -1.
1806 static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
1808 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1809 LISTVIEW_SELECTION selection,*checkselection;
1812 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1814 selection.upper = nItem;
1815 selection.lower = nItem;
1817 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1818 LISTVIEW_CompareSelectionRanges,
1819 0,DPAS_SORTED|DPAS_INSERTAFTER);
1821 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1823 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1824 if ((checkselection->lower >= nItem)&&
1825 (checkselection->lower + direction >= 0))
1826 checkselection->lower += direction;
1827 if ((checkselection->upper >= nItem)&&
1828 (checkselection->upper + direction >=0))
1829 checkselection->upper += direction;
1833 /* Note that the following will fail if direction != +1 and -1 */
1834 if (infoPtr->nSelectionMark > nItem)
1835 infoPtr->nSelectionMark += direction;
1836 else if (infoPtr->nSelectionMark == nItem)
1839 infoPtr->nSelectionMark += direction;
1840 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1841 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1844 if (infoPtr->nFocusedItem > nItem)
1845 infoPtr->nFocusedItem += direction;
1846 else if (infoPtr->nFocusedItem == nItem)
1849 infoPtr->nFocusedItem += direction;
1852 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1853 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1854 if (infoPtr->nFocusedItem >= 0)
1855 LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
1858 /* But we are not supposed to modify nHotItem! */
1864 * Adds a block of selections.
1867 * [I] HWND : window handle
1868 * [I] INT : item index
1873 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1875 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1876 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1877 INT nLast = max(infoPtr->nSelectionMark, nItem);
1884 ZeroMemory(&item,sizeof(item));
1885 item.stateMask = LVIS_SELECTED;
1886 item.state = LVIS_SELECTED;
1888 for (i = nFirst; i <= nLast; i++)
1889 LISTVIEW_SetItemState(hwnd,i,&item);
1891 LISTVIEW_SetItemFocus(hwnd, nItem);
1892 infoPtr->nSelectionMark = nItem;
1898 * Adds a single selection.
1901 * [I] HWND : window handle
1902 * [I] INT : item index
1907 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1909 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1912 ZeroMemory(&item,sizeof(item));
1913 item.state = LVIS_SELECTED;
1914 item.stateMask = LVIS_SELECTED;
1916 LISTVIEW_SetItemState(hwnd,nItem,&item);
1918 LISTVIEW_SetItemFocus(hwnd, nItem);
1919 infoPtr->nSelectionMark = nItem;
1924 * Selects or unselects an item.
1927 * [I] HWND : window handle
1928 * [I] INT : item index
1934 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1936 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1940 ZeroMemory(&item,sizeof(item));
1941 item.stateMask = LVIS_SELECTED;
1943 if (LISTVIEW_IsSelected(hwnd,nItem))
1945 LISTVIEW_SetItemState(hwnd,nItem,&item);
1950 item.state = LVIS_SELECTED;
1951 LISTVIEW_SetItemState(hwnd,nItem,&item);
1955 LISTVIEW_SetItemFocus(hwnd, nItem);
1956 infoPtr->nSelectionMark = nItem;
1963 * Selects items based on view coordinates.
1966 * [I] HWND : window handle
1967 * [I] RECT : selection rectangle
1972 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
1974 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1979 ZeroMemory(&item,sizeof(item));
1980 item.stateMask = LVIS_SELECTED;
1982 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1984 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
1986 if (PtInRect(&rcSelRect, ptItem) != FALSE)
1987 item.state = LVIS_SELECTED;
1990 LISTVIEW_SetItemState(hwnd,i,&item);
1996 * Sets a single group selection.
1999 * [I] HWND : window handle
2000 * [I] INT : item index
2005 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
2007 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2008 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2011 ZeroMemory(&item,sizeof(item));
2012 item.stateMask = LVIS_SELECTED;
2014 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2019 if (infoPtr->nSelectionMark == -1)
2021 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2025 nFirst = min(infoPtr->nSelectionMark, nItem);
2026 nLast = max(infoPtr->nSelectionMark, nItem);
2029 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2031 if ((i < nFirst) || (i > nLast))
2034 item.state = LVIS_SELECTED;
2035 LISTVIEW_SetItemState(hwnd,i,&item);
2043 LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcItem);
2044 LISTVIEW_GetItemBoundBox(hwnd, infoPtr->nSelectionMark, &rcSelMark);
2045 rcSel.left = min(rcSelMark.left, rcItem.left);
2046 rcSel.top = min(rcSelMark.top, rcItem.top);
2047 rcSel.right = max(rcSelMark.right, rcItem.right);
2048 rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
2049 LISTVIEW_SetSelectionRect(hwnd, rcSel);
2050 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2051 nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
2052 infoPtr->nSelectionMark,
2053 rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
2054 rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2058 LISTVIEW_SetItemFocus(hwnd, nItem);
2063 * Manages the item focus.
2066 * [I] HWND : window handle
2067 * [I] INT : item index
2070 * TRUE : focused item changed
2071 * FALSE : focused item has NOT changed
2073 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
2075 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2076 BOOL bResult = FALSE;
2079 if (infoPtr->nFocusedItem != nItem)
2081 if (infoPtr->nFocusedItem >= 0)
2083 INT oldFocus = infoPtr->nFocusedItem;
2085 infoPtr->nFocusedItem = -1;
2086 ZeroMemory(&lvItem, sizeof(lvItem));
2087 lvItem.stateMask = LVIS_FOCUSED;
2088 ListView_SetItemState(hwnd, oldFocus, &lvItem);
2092 lvItem.state = LVIS_FOCUSED;
2093 lvItem.stateMask = LVIS_FOCUSED;
2094 ListView_SetItemState(hwnd, nItem, &lvItem);
2096 infoPtr->nFocusedItem = nItem;
2097 ListView_EnsureVisible(hwnd, nItem, FALSE);
2105 * Sets a single selection.
2108 * [I] HWND : window handle
2109 * [I] INT : item index
2114 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
2116 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2119 ZeroMemory(&lvItem, sizeof(lvItem));
2120 lvItem.stateMask = LVIS_FOCUSED;
2121 ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
2123 LISTVIEW_RemoveAllSelections(hwnd);
2125 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
2126 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2127 ListView_SetItemState(hwnd, nItem, &lvItem);
2129 infoPtr->nFocusedItem = nItem;
2130 infoPtr->nSelectionMark = nItem;
2135 * Set selection(s) with keyboard.
2138 * [I] HWND : window handle
2139 * [I] INT : item index
2142 * SUCCESS : TRUE (needs to be repainted)
2143 * FAILURE : FALSE (nothing has changed)
2145 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
2147 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2148 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2149 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2150 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2151 BOOL bResult = FALSE;
2153 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2155 if (lStyle & LVS_SINGLESEL)
2158 LISTVIEW_SetSelection(hwnd, nItem);
2159 ListView_EnsureVisible(hwnd, nItem, FALSE);
2166 LISTVIEW_SetGroupSelection(hwnd, nItem);
2170 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
2175 LISTVIEW_SetSelection(hwnd, nItem);
2176 ListView_EnsureVisible(hwnd, nItem, FALSE);
2186 * Called when the mouse is being actively tracked and has hovered for a specified
2190 * [I] HWND : window handle
2191 * [I] wParam : key indicator
2192 * [I] lParam : mouse position
2195 * 0 if the message was processed, non-zero if there was an error
2198 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2199 * over the item for a certain period of time.
2202 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
2204 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2207 pt.x = (INT)LOWORD(lParam);
2208 pt.y = (INT)HIWORD(lParam);
2210 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2211 /* select the item under the cursor */
2212 LISTVIEW_MouseSelection(hwnd, pt);
2220 * Called whenever WM_MOUSEMOVE is received.
2223 * [I] HWND : window handle
2224 * [I] wParam : key indicators
2225 * [I] lParam : cursor position
2228 * 0 if the message is processed, non-zero if there was an error
2230 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
2232 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2233 TRACKMOUSEEVENT trackinfo;
2235 /* see if we are supposed to be tracking mouse hovering */
2236 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2237 /* fill in the trackinfo struct */
2238 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2239 trackinfo.dwFlags = TME_QUERY;
2240 trackinfo.hwndTrack = hwnd;
2241 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2243 /* see if we are already tracking this hwnd */
2244 _TrackMouseEvent(&trackinfo);
2246 if(!(trackinfo.dwFlags & TME_HOVER)) {
2247 trackinfo.dwFlags = TME_HOVER;
2249 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2250 _TrackMouseEvent(&trackinfo);
2259 * Selects an item based on coordinates.
2262 * [I] HWND : window handle
2263 * [I] POINT : mouse click ccordinates
2266 * SUCCESS : item index
2269 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
2271 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2273 INT i,topindex,bottomindex;
2274 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2275 UINT uView = lStyle & LVS_TYPEMASK;
2277 topindex = ListView_GetTopIndex(hwnd);
2278 if (uView == LVS_REPORT)
2280 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
2281 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
2285 bottomindex = GETITEMCOUNT(infoPtr);
2288 for (i = topindex; i < bottomindex; i++)
2290 rcItem.left = LVIR_SELECTBOUNDS;
2291 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
2293 if (PtInRect(&rcItem, pt) != FALSE)
2308 * [IO] HDPA : dynamic pointer array handle
2309 * [I] INT : column index (subitem index)
2315 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2317 BOOL bResult = TRUE;
2321 for (i = 0; i < hdpaItems->nItemCount; i++)
2323 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2324 if (hdpaSubItems != NULL)
2326 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2338 * Removes a subitem at a given position.
2341 * [IO] HDPA : dynamic pointer array handle
2342 * [I] INT : subitem index
2348 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2350 LISTVIEW_SUBITEM *lpSubItem;
2353 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2355 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2356 if (lpSubItem != NULL)
2358 if (lpSubItem->iSubItem == nSubItem)
2361 if (is_textW(lpSubItem->pszText))
2362 COMCTL32_Free(lpSubItem->pszText);
2365 COMCTL32_Free(lpSubItem);
2367 /* free dpa memory */
2368 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2371 else if (lpSubItem->iSubItem > nSubItem)
2381 * Compares the item information.
2384 * [I] LISTVIEW_ITEM *: destination item
2385 * [I] LPLVITEM : source item
2386 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2389 * SUCCCESS : TRUE (EQUAL)
2390 * FAILURE : FALSE (NOT EQUAL)
2392 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2396 if ((lpItem != NULL) && (lpLVItem != NULL))
2398 if (lpLVItem->mask & LVIF_STATE)
2400 if ((lpItem->state & lpLVItem->stateMask) !=
2401 (lpLVItem->state & lpLVItem->stateMask))
2402 uChanged |= LVIF_STATE;
2405 if (lpLVItem->mask & LVIF_IMAGE)
2407 if (lpItem->iImage != lpLVItem->iImage)
2408 uChanged |= LVIF_IMAGE;
2411 if (lpLVItem->mask & LVIF_PARAM)
2413 if (lpItem->lParam != lpLVItem->lParam)
2414 uChanged |= LVIF_PARAM;
2417 if (lpLVItem->mask & LVIF_INDENT)
2419 if (lpItem->iIndent != lpLVItem->iIndent)
2420 uChanged |= LVIF_INDENT;
2423 if (lpLVItem->mask & LVIF_TEXT)
2425 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2427 if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2428 uChanged |= LVIF_TEXT;
2432 if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2434 uChanged |= LVIF_TEXT;
2438 if (lpLVItem->pszText)
2440 if (lpItem->pszText)
2442 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2443 if (pszText && strcmpW(pszText, lpItem->pszText))
2444 uChanged |= LVIF_TEXT;
2445 textfreeT(pszText, isW);
2449 uChanged |= LVIF_TEXT;
2454 if (lpItem->pszText)
2455 uChanged |= LVIF_TEXT;
2466 * Initializes item attributes.
2469 * [I] HWND : window handle
2470 * [O] LISTVIEW_ITEM *: destination item
2471 * [I] LPLVITEM : source item
2472 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2478 static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem,
2479 LPLVITEMW lpLVItem, BOOL isW)
2481 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2482 BOOL bResult = FALSE;
2484 if ((lpItem != NULL) && (lpLVItem != NULL))
2488 if (lpLVItem->mask & LVIF_STATE)
2490 lpItem->state &= ~lpLVItem->stateMask;
2491 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2494 if (lpLVItem->mask & LVIF_IMAGE)
2495 lpItem->iImage = lpLVItem->iImage;
2497 if (lpLVItem->mask & LVIF_PARAM)
2498 lpItem->lParam = lpLVItem->lParam;
2500 if (lpLVItem->mask & LVIF_INDENT)
2501 lpItem->iIndent = lpLVItem->iIndent;
2503 if (lpLVItem->mask & LVIF_TEXT)
2505 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2507 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2510 if (is_textW(lpItem->pszText))
2511 COMCTL32_Free(lpItem->pszText);
2513 lpItem->pszText = LPSTR_TEXTCALLBACKW;
2516 bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
2525 * Initializes subitem attributes.
2527 * NOTE: The documentation specifies that the operation fails if the user
2528 * tries to set the indent of a subitem.
2531 * [I] HWND : window handle
2532 * [O] LISTVIEW_SUBITEM *: destination subitem
2533 * [I] LPLVITEM : source subitem
2534 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2540 static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2541 LPLVITEMW lpLVItem, BOOL isW)
2543 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2544 BOOL bResult = FALSE;
2546 TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
2547 hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW);
2549 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2551 if (!(lpLVItem->mask & LVIF_INDENT))
2555 lpSubItem->iSubItem = lpLVItem->iSubItem;
2557 if (lpLVItem->mask & LVIF_IMAGE)
2558 lpSubItem->iImage = lpLVItem->iImage;
2560 if (lpLVItem->mask & LVIF_TEXT)
2562 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2564 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2567 if (is_textW(lpSubItem->pszText))
2568 COMCTL32_Free(lpSubItem->pszText);
2570 lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2573 bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
2583 * Adds a subitem at a given position (column index).
2586 * [I] HWND : window handle
2587 * [I] LPLVITEM : new subitem atttributes
2588 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2594 static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2596 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2597 LISTVIEW_SUBITEM *lpSubItem = NULL;
2598 BOOL bResult = FALSE;
2600 INT nPosition, nItem;
2601 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2603 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2605 if (lStyle & LVS_OWNERDATA)
2608 if (lpLVItem != NULL)
2610 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2611 if (hdpaSubItems != NULL)
2613 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2614 if (lpSubItem != NULL)
2616 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2617 if (LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW))
2619 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2620 lpSubItem->iSubItem);
2621 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2622 if (nItem != -1) bResult = TRUE;
2628 /* cleanup if unsuccessful */
2629 if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2636 * Finds the dpa insert position (array index).
2639 * [I] HWND : window handle
2640 * [I] INT : subitem index
2646 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2648 LISTVIEW_SUBITEM *lpSubItem;
2651 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2653 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2654 if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2658 return hdpaSubItems->nItemCount;
2663 * Retrieves a listview subitem at a given position (column index).
2666 * [I] HWND : window handle
2667 * [I] INT : subitem index
2673 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2675 LISTVIEW_SUBITEM *lpSubItem;
2678 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2680 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2681 if (lpSubItem != NULL)
2683 if (lpSubItem->iSubItem == nSubItem)
2685 else if (lpSubItem->iSubItem > nSubItem)
2695 * Sets item attributes.
2698 * [I] HWND : window handle
2699 * [I] LPLVITEM : new item atttributes
2700 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2706 static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2708 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2709 BOOL bResult = FALSE;
2711 LISTVIEW_ITEM *lpItem;
2713 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2715 UINT uView = lStyle & LVS_TYPEMASK;
2719 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2721 if (lStyle & LVS_OWNERDATA)
2723 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2727 ZeroMemory(&itm, sizeof(itm));
2728 itm.mask = LVIF_STATE | LVIF_PARAM;
2729 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2730 itm.iItem = lpLVItem->iItem;
2732 ListView_GetItemW(hwnd, &itm);
2735 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2736 nmlv.uNewState = lpLVItem->state;
2737 nmlv.uOldState = itm.state;
2738 nmlv.uChanged = LVIF_STATE;
2739 nmlv.lParam = itm.lParam;
2740 nmlv.iItem = lpLVItem->iItem;
2742 if ((itm.state & lpLVItem->stateMask) !=
2743 (lpLVItem->state & lpLVItem->stateMask))
2745 /* send LVN_ITEMCHANGING notification */
2746 if (!listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv))
2748 if (lpLVItem->stateMask & LVIS_FOCUSED)
2750 if (lpLVItem->state & LVIS_FOCUSED)
2751 infoPtr->nFocusedItem = lpLVItem->iItem;
2752 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2753 infoPtr->nFocusedItem = -1;
2755 if (lpLVItem->stateMask & LVIS_SELECTED)
2757 if (lpLVItem->state & LVIS_SELECTED)
2759 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd);
2760 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2763 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2767 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2769 rcItem.left = LVIR_BOUNDS;
2770 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2771 InvalidateRect(hwnd, &rcItem, TRUE);
2779 if (lpLVItem != NULL)
2781 if (lpLVItem->iSubItem == 0)
2783 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2784 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2786 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2789 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2790 nmlv.lParam = lpItem->lParam;
2791 uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2794 if (uChanged & LVIF_STATE)
2796 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2797 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2799 if (nmlv.uNewState & LVIS_SELECTED)
2802 * This is redundant if called through SetSelection
2804 * however is required if the used directly calls SetItem
2805 * to set the selection.
2807 if (lStyle & LVS_SINGLESEL)
2808 LISTVIEW_RemoveAllSelections(hwnd);
2810 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2813 else if (lpLVItem->stateMask & LVIS_SELECTED)
2815 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2818 if (nmlv.uNewState & LVIS_FOCUSED)
2821 * This is a fun hoop to jump to try to catch if
2822 * the user is calling us directly to call focus or if
2823 * this function is being called as a result of a
2824 * SetItemFocus call.
2826 if (infoPtr->nFocusedItem >= 0)
2827 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2831 nmlv.uChanged = uChanged;
2832 nmlv.iItem = lpLVItem->iItem;
2833 nmlv.lParam = lpItem->lParam;
2834 /* send LVN_ITEMCHANGING notification */
2835 listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv);
2837 /* copy information */
2838 bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW);
2840 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2841 based on the width of the items text */
2842 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2844 item_width = LISTVIEW_GetStringWidthT(hwnd, lpItem->pszText, TRUE);
2846 if(item_width > infoPtr->nItemWidth)
2847 infoPtr->nItemWidth = item_width;
2850 /* send LVN_ITEMCHANGED notification */
2851 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2860 rcItem.left = LVIR_BOUNDS;
2861 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2862 InvalidateRect(hwnd, &rcItem, TRUE);
2874 * Sets subitem attributes.
2877 * [I] HWND : window handle
2878 * [I] LPLVITEM : new subitem atttributes
2879 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2885 static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2887 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2888 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2889 BOOL bResult = FALSE;
2891 LISTVIEW_SUBITEM *lpSubItem;
2894 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2896 if (lStyle & LVS_OWNERDATA)
2899 if (lpLVItem != NULL)
2901 if (lpLVItem->iSubItem > 0)
2903 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2904 if (hdpaSubItems != NULL)
2906 /* set subitem only if column is present */
2907 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2909 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2910 if (lpSubItem != NULL)
2911 bResult = LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW);
2913 bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW);
2915 rcItem.left = LVIR_BOUNDS;
2916 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2917 InvalidateRect(hwnd, &rcItem, FALSE);
2928 * Sets item attributes.
2931 * [I] HWND : window handle
2932 * [I] LPLVITEM : new item atttributes
2933 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2939 static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2941 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2943 if (!lpLVItem || lpLVItem->iItem < 0 ||
2944 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2946 if (lpLVItem->iSubItem == 0)
2947 return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW);
2949 return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW);
2954 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2957 * [I] HWND : window handle
2962 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2964 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2965 UINT uView = lStyle & LVS_TYPEMASK;
2967 SCROLLINFO scrollInfo;
2969 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2970 scrollInfo.cbSize = sizeof(SCROLLINFO);
2971 scrollInfo.fMask = SIF_POS;
2973 if (uView == LVS_LIST)
2975 if ((lStyle & WS_HSCROLL) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo))
2976 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2978 else if (uView == LVS_REPORT)
2980 if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo))
2981 nItem = scrollInfo.nPos;
2992 * [I] HWND : window handle
2993 * [I] HDC : device context handle
2994 * [I] INT : item index
2995 * [I] INT : subitem index
2996 * [I] RECT * : clipping rectangle
3001 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
3002 RECT rcItem, BOOL Selected)
3004 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3005 WCHAR szDispText[DISP_TEXT_SIZE];
3008 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
3011 INT nLabelWidth = 0;
3013 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
3016 /* get information needed for drawing the item */
3017 ZeroMemory(&lvItem, sizeof(lvItem));
3018 lvItem.mask = LVIF_TEXT;
3019 lvItem.iItem = nItem;
3020 lvItem.iSubItem = nSubItem;
3021 lvItem.cchTextMax = DISP_TEXT_SIZE;
3022 lvItem.pszText = szDispText;
3023 *lvItem.pszText = '\0';
3024 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3025 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3027 ZeroMemory(&lvColumn, sizeof(lvColumn));
3028 lvColumn.mask = LVCF_FMT;
3029 LISTVIEW_GetColumnT(hwnd, nSubItem, &lvColumn, TRUE);
3030 textLeft = rcItem.left;
3031 if (lvColumn.fmt != LVCFMT_LEFT)
3033 if ((nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE)))
3035 if (lvColumn.fmt == LVCFMT_RIGHT)
3036 textLeft = rcItem.right - nLabelWidth;
3038 textLeft = rcItem.left + (rcItem.right-rcItem.left-nLabelWidth)/2;
3043 /* redraw the background of the item */
3045 if(infoPtr->nColumnCount == (nSubItem + 1))
3046 rcTemp.right = infoPtr->rcList.right;
3048 rcTemp.right += WIDTH_PADDING;
3050 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3052 /* set item colors */
3053 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
3055 if (infoPtr->bFocus)
3057 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3058 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3062 SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3063 SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3068 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3070 SetBkMode(hdc, TRANSPARENT);
3071 textoutOptions &= ~ETO_OPAQUE;
3075 SetBkMode(hdc, OPAQUE);
3076 SetBkColor(hdc, infoPtr->clrTextBk);
3079 SetTextColor(hdc, infoPtr->clrText);
3082 ExtTextOutW(hdc, textLeft, rcItem.top, textoutOptions,
3083 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3087 /* fill in the gap */
3089 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
3091 CopyRect(&rec,&rcItem);
3092 rec.left = rec.right;
3093 rec.right = rec.left+REPORT_MARGINX;
3094 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3095 &rec, NULL, 0, NULL);
3097 CopyRect(&rec,&rcItem);
3098 rec.right = rec.left;
3099 rec.left = rec.left - REPORT_MARGINX;
3100 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3101 &rec, NULL, 0, NULL);
3111 * [I] HWND : window handle
3112 * [I] HDC : device context handle
3113 * [I] INT : item index
3114 * [I] RECT * : clipping rectangle
3119 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
3121 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3122 WCHAR szDispText[DISP_TEXT_SIZE];
3127 DWORD dwTextColor,dwTextX;
3128 BOOL bImage = FALSE;
3130 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
3133 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
3136 /* get information needed for drawing the item */
3137 ZeroMemory(&lvItem, sizeof(lvItem));
3138 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3139 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
3140 lvItem.iItem = nItem;
3141 lvItem.iSubItem = 0;
3142 lvItem.cchTextMax = DISP_TEXT_SIZE;
3143 lvItem.pszText = szDispText;
3144 *lvItem.pszText = '\0';
3145 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3146 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3148 /* redraw the background of the item */
3150 if(infoPtr->nColumnCount == (nItem + 1))
3151 rcTemp.right = infoPtr->rcList.right;
3153 rcTemp.right+=WIDTH_PADDING;
3155 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3158 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3160 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3163 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3167 if (infoPtr->himlState != NULL)
3169 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3170 if (uStateImage > 0)
3172 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3173 rcItem.top, ILD_NORMAL);
3176 rcItem.left += infoPtr->iconSize.cx;
3178 SuggestedFocus->left += infoPtr->iconSize.cx;
3183 if (infoPtr->himlSmall != NULL)
3185 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
3188 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3189 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3190 rcItem.top, ILD_SELECTED);
3192 else if (lvItem.iImage>=0)
3194 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3195 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3196 rcItem.top, ILD_NORMAL);
3199 rcItem.left += infoPtr->iconSize.cx;
3202 SuggestedFocus->left += infoPtr->iconSize.cx;
3206 /* Don't bother painting item being edited */
3207 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
3210 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
3212 /* set item colors */
3213 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3214 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3215 /* set raster mode */
3216 nMixMode = SetROP2(hdc, R2_XORPEN);
3218 else if ((GetWindowLongW(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3219 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
3221 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3222 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3223 /* set raster mode */
3224 nMixMode = SetROP2(hdc, R2_COPYPEN);
3228 /* set item colors */
3229 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3231 dwBkColor = GetBkColor(hdc);
3232 iBkMode = SetBkMode(hdc, TRANSPARENT);
3233 textoutOptions &= ~ETO_OPAQUE;
3237 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3238 iBkMode = SetBkMode(hdc, OPAQUE);
3241 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3242 /* set raster mode */
3243 nMixMode = SetROP2(hdc, R2_COPYPEN);
3246 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
3247 if (rcItem.left + nLabelWidth < rcItem.right)
3250 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3252 rcItem.right += IMAGE_PADDING;
3256 dwTextX = rcItem.left + 1;
3258 dwTextX += IMAGE_PADDING;
3261 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3262 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3264 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3266 /* fill in the gap */
3268 CopyRect(&rec,&rcItem);
3269 rec.left = rec.right;
3270 rec.right = rec.left+REPORT_MARGINX;
3271 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3272 &rec, NULL, 0, NULL);
3276 CopyRect(SuggestedFocus,&rcItem);
3280 SetROP2(hdc, R2_COPYPEN);
3281 SetBkColor(hdc, dwBkColor);
3282 SetTextColor(hdc, dwTextColor);
3284 SetBkMode(hdc, iBkMode);
3290 * Draws an item when in large icon display mode.
3293 * [I] HWND : window handle
3294 * [I] HDC : device context handle
3295 * [I] INT : item index
3296 * [I] RECT : clipping rectangle
3297 * [O] RECT * : The text rectangle about which to draw the focus
3302 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3303 RECT *SuggestedFocus)
3305 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3306 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3308 UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
3310 /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
3313 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3314 hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3316 /* get information needed for drawing the item */
3317 ZeroMemory(&lvItem, sizeof(lvItem));
3318 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3319 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3320 lvItem.iItem = nItem;
3321 lvItem.iSubItem = 0;
3322 lvItem.cchTextMax = DISP_TEXT_SIZE;
3323 lvItem.pszText = szDispText;
3324 *lvItem.pszText = '\0';
3325 LISTVIEW_GetItemW(hwnd, &lvItem, FALSE);
3326 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3328 /* redraw the background of the item */
3330 if(infoPtr->nColumnCount == (nItem + 1))
3331 rcTemp.right = infoPtr->rcList.right;
3333 rcTemp.right+=WIDTH_PADDING;
3334 /* The comment doesn't say WIDTH_PADDING applies to large icons */
3336 TRACE("background rect (%d,%d)-(%d,%d)\n",
3337 rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
3339 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3342 /* Figure out text colours etc. depending on state
3343 * At least the following states exist; there may be more.
3344 * Many items may be selected
3345 * At most one item may have the focus
3346 * The application may not actually be active currently
3347 * 1. The item is not selected in any way
3348 * 2. The cursor is flying over the icon or text and the text is being
3349 * expanded because it is not fully displayed currently.
3350 * 3. The item is selected and is focussed, i.e. the user has not clicked
3351 * in the blank area of the window, and the window (or application?)
3352 * still has the focus.
3353 * 4. As 3 except that a different window has the focus
3354 * 5. The item is the selected item of all the items, but the user has
3355 * clicked somewhere else on the window.
3356 * Only a few of these are handled currently. In particular 2 is not yet
3357 * handled since we do not support the functionality currently (or at least
3358 * we didn't when I wrote this)
3361 if (lvItem.state & LVIS_SELECTED)
3363 /* set item colors */
3364 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3365 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3366 SetBkMode (hdc, OPAQUE);
3367 /* set raster mode */
3368 SetROP2(hdc, R2_XORPEN);
3369 /* When exactly is it in XOR? while being dragged? */
3373 /* set item colors */
3374 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3376 SetBkMode(hdc, TRANSPARENT);
3380 SetBkMode(hdc, OPAQUE);
3381 SetBkColor(hdc, infoPtr->clrTextBk);
3384 SetTextColor(hdc, infoPtr->clrText);
3385 /* set raster mode */
3386 SetROP2(hdc, R2_COPYPEN);
3389 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3390 * wrapping and long words split.
3391 * In cases 1 and 4 only a portion of the text is displayed with word
3392 * wrapping and both word and end ellipsis. (I don't yet know about path
3395 uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
3398 DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3401 if (infoPtr->himlNormal != NULL)
3403 if (lvItem.iImage >= 0)
3405 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3407 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3411 /* Draw the text below the icon */
3413 /* Don't bother painting item being edited */
3414 if ((infoPtr->hwndEdit && (lvItem.state & LVIS_FOCUSED)) ||
3415 !lstrlenW(lvItem.pszText))
3417 SetRectEmpty(SuggestedFocus);
3421 /* Since rcItem.left is left point of icon, compute left point of item box */
3422 rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3423 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3424 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3425 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3426 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3427 infoPtr->iconSize.cx, infoPtr->nItemWidth);
3428 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3429 infoPtr->rcList.left, infoPtr->rcList.top,
3430 infoPtr->rcList.right, infoPtr->rcList.bottom,
3431 infoPtr->rcView.left, infoPtr->rcView.top,
3432 infoPtr->rcView.right, infoPtr->rcView.bottom);
3434 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3435 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3440 /* I am sure of most of the uFormat values. However I am not sure about
3441 * whether we need or do not need the following:
3442 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3443 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3444 * We certainly do not need
3445 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3446 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3449 /* If the text is being drawn without clipping (i.e. the full text) then we
3450 * need to jump through a few hoops to ensure that it all gets displayed and
3451 * that the background is complete
3453 if (uFormat & DT_NOCLIP)
3456 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3457 int dx, dy, old_wid, new_wid;
3458 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3459 /* Microsoft, in their great wisdom, have decided that the rectangle
3460 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3461 * not the location. So we have to do the centring ourselves (and take
3462 * responsibility for agreeing off-by-one consistency with them).
3464 old_wid = rcItem.right-rcItem.left;
3465 new_wid = rcBack.right - rcBack.left;
3466 dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
3467 dy = rcBack.top - rcItem.top;
3468 OffsetRect (&rcItem, dx, dy);
3469 FillRect(hdc, &rcItem, hBrush);
3470 DeleteObject(hBrush);
3472 /* else ? What if we are losing the focus? will we not get a complete
3475 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3477 CopyRect(SuggestedFocus, &rcItem);
3482 * Draws listview items when in report display mode.
3485 * [I] HWND : window handle
3486 * [I] HDC : device context handle
3491 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3493 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3494 SCROLLINFO scrollInfo;
3495 INT nDrawPosY = infoPtr->rcList.top;
3497 RECT rcItem, rcTemp;
3502 DWORD cditemmode = CDRF_DODEFAULT;
3503 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3506 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3507 scrollInfo.cbSize = sizeof(SCROLLINFO);
3508 scrollInfo.fMask = SIF_POS;
3510 nItem = ListView_GetTopIndex(hwnd);
3512 /* add 1 for displaying a partial item at the bottom */
3513 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3514 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3516 /* send cache hint notification */
3517 if (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3521 nmlv.hdr.hwndFrom = hwnd;
3522 nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID);
3523 nmlv.hdr.code = LVN_ODCACHEHINT;
3527 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3531 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3532 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3533 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3535 /* clear the background of any part of the control that doesn't contain items */
3536 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3537 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3539 /* nothing to draw */
3540 if(GETITEMCOUNT(infoPtr) == 0)
3543 /* Get scroll bar info once before loop */
3544 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3545 scrollOffset = scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
3547 for (; nItem < nLast; nItem++)
3549 RECT SuggestedFocusRect;
3552 if (lStyle & LVS_OWNERDRAWFIXED)
3554 UINT uID = GetWindowLongW( hwnd, GWL_ID);
3559 TRACE("Owner Drawn\n");
3560 dis.CtlType = ODT_LISTVIEW;
3563 dis.itemAction = ODA_DRAWENTIRE;
3566 if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3567 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3569 dis.hwndItem = hwnd;
3572 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3574 dis.rcItem.left = -scrollOffset;
3575 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3576 dis.rcItem.top = nDrawPosY;
3577 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3579 ZeroMemory(&item,sizeof(item));
3581 item.mask = LVIF_PARAM;
3582 ListView_GetItemW(hwnd, &item);
3584 dis.itemData = item.lParam;
3586 if (SendMessageW(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3588 nDrawPosY += infoPtr->nItemHeight;
3597 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3598 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3600 ir.left += REPORT_MARGINX;
3601 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3603 ir.bottom = ir.top + infoPtr->nItemHeight;
3605 CopyRect(&SuggestedFocusRect,&ir);
3608 for (j = 0; j < nColumnCount; j++)
3610 if (cdmode & CDRF_NOTIFYITEMDRAW)
3611 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3613 if (cditemmode & CDRF_SKIPDEFAULT)
3616 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3618 rcItem.left += REPORT_MARGINX;
3619 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3620 rcItem.top = nDrawPosY;
3621 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3623 /* Offset the Scroll Bar Pos */
3624 rcItem.left -= scrollOffset;
3625 rcItem.right -= scrollOffset;
3629 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3630 &SuggestedFocusRect);
3634 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, FullSelected);
3637 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3638 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3639 CDDS_ITEMPOSTPAINT);
3644 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3647 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3648 rop = SetROP2(hdc, R2_XORPEN);
3650 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3651 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3654 SetROP2(hdc, R2_COPYPEN);
3656 nDrawPosY += infoPtr->nItemHeight;
3662 * Retrieves the number of items that can fit vertically in the client area.
3665 * [I] HWND : window handle
3668 * Number of items per row.
3670 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3672 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3673 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3674 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3675 INT nCountPerRow = 1;
3679 if (uView != LVS_REPORT)
3681 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3682 if (nCountPerRow == 0) nCountPerRow = 1;
3686 return nCountPerRow;
3691 * Retrieves the number of items that can fit horizontally in the client
3695 * [I] HWND : window handle
3698 * Number of items per column.
3700 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3702 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3703 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3704 INT nCountPerColumn = 1;
3706 if (nListHeight > 0)
3708 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3709 if (nCountPerColumn == 0) nCountPerColumn = 1;
3712 return nCountPerColumn;
3717 * Retrieves the number of columns needed to display all the items when in
3718 * list display mode.
3721 * [I] HWND : window handle
3724 * Number of columns.
3726 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3728 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3729 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3730 INT nColumnCount = 0;
3732 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3734 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3735 if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3738 return nColumnCount;
3744 * Draws listview items when in list display mode.
3747 * [I] HWND : window handle
3748 * [I] HDC : device context handle
3753 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3755 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3756 RECT rcItem, FocusRect, rcTemp;
3760 INT nCountPerColumn;
3761 INT nItemWidth = infoPtr->nItemWidth;
3762 INT nItemHeight = infoPtr->nItemHeight;
3763 DWORD cditemmode = CDRF_DODEFAULT;
3765 /* get number of fully visible columns */
3766 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3767 infoPtr->nColumnCount = nColumnCount;
3768 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3769 nItem = ListView_GetTopIndex(hwnd);
3771 /* paint the background of the control that doesn't contain any items */
3772 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3773 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3775 /* nothing to draw, return here */
3776 if(GETITEMCOUNT(infoPtr) == 0)
3779 for (i = 0; i < nColumnCount; i++)
3781 for (j = 0; j < nCountPerColumn; j++, nItem++)
3783 if (nItem >= GETITEMCOUNT(infoPtr))
3786 if (cdmode & CDRF_NOTIFYITEMDRAW)
3787 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3789 if (cditemmode & CDRF_SKIPDEFAULT)
3792 rcItem.top = j * nItemHeight;
3793 rcItem.left = i * nItemWidth;
3794 rcItem.bottom = rcItem.top + nItemHeight;
3795 rcItem.right = rcItem.left + nItemWidth;
3796 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3800 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3801 Rectangle(hdc, FocusRect.left, FocusRect.top,
3802 FocusRect.right,FocusRect.bottom);
3804 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3805 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3806 CDDS_ITEMPOSTPAINT);
3814 * Draws listview items when in icon or small icon display mode.
3817 * [I] HWND : window handle
3818 * [I] HDC : device context handle
3823 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3825 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3828 RECT rcItem, SuggestedFocus, rcTemp;
3830 DWORD cditemmode = CDRF_DODEFAULT;
3832 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3833 /* DrawItem from erasing the incorrect background area */
3835 /* paint the background of the control that doesn't contain any items */
3836 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3837 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3839 /* nothing to draw, return here */
3840 if(GETITEMCOUNT(infoPtr) == 0)
3843 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3844 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3846 if (cdmode & CDRF_NOTIFYITEMDRAW)
3847 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3849 if (cditemmode & CDRF_SKIPDEFAULT)
3852 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3853 ptPosition.x += ptOrigin.x;
3854 ptPosition.y += ptOrigin.y;
3856 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3858 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3860 if (ptPosition.y < infoPtr->rcList.bottom)
3862 if (ptPosition.x < infoPtr->rcList.right)
3864 rcItem.top = ptPosition.y;
3865 rcItem.left = ptPosition.x;
3866 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3867 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3869 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3871 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3875 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3876 infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
3877 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3878 SuggestedFocus.right,SuggestedFocus.bottom);
3883 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3884 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3885 CDDS_ITEMPOSTPAINT);
3891 * Draws listview items.
3894 * [I] HWND : window handle
3895 * [I] HDC : device context handle
3900 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3902 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3903 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3909 LISTVIEW_DumpListview (infoPtr, __LINE__);
3911 GetClientRect(hwnd, &rect);
3912 cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3914 if (cdmode == CDRF_SKIPDEFAULT) return;
3917 hOldFont = SelectObject(hdc, infoPtr->hFont);
3919 /* select the dotted pen (for drawing the focus box) */
3920 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3921 hOldPen = SelectObject(hdc, hPen);
3923 /* select transparent brush (for drawing the focus box) */
3924 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3926 if (uView == LVS_LIST)
3927 LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3928 else if (uView == LVS_REPORT)
3929 LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3930 else if (uView == LVS_SMALLICON)
3931 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3932 else if (uView == LVS_ICON)
3933 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3935 /* unselect objects */
3936 SelectObject(hdc, hOldFont);
3937 SelectObject(hdc, hOldPen);
3942 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3943 LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
3949 * Calculates the approximate width and height of a given number of items.
3952 * [I] HWND : window handle
3953 * [I] INT : number of items
3958 * Returns a DWORD. The width in the low word and the height in high word.
3960 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
3961 WORD wWidth, WORD wHeight)
3963 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3964 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3965 INT nItemCountPerColumn = 1;
3966 INT nColumnCount = 0;
3967 DWORD dwViewRect = 0;
3969 if (nItemCount == -1)
3970 nItemCount = GETITEMCOUNT(infoPtr);
3972 if (uView == LVS_LIST)
3974 if (wHeight == 0xFFFF)
3976 /* use current height */
3977 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3980 if (wHeight < infoPtr->nItemHeight)
3981 wHeight = infoPtr->nItemHeight;
3985 if (infoPtr->nItemHeight > 0)
3987 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3988 if (nItemCountPerColumn == 0)
3989 nItemCountPerColumn = 1;
3991 if (nItemCount % nItemCountPerColumn != 0)
3992 nColumnCount = nItemCount / nItemCountPerColumn;
3994 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3998 /* Microsoft padding magic */
3999 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4000 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4002 dwViewRect = MAKELONG(wWidth, wHeight);
4004 else if (uView == LVS_REPORT)
4005 FIXME("uView == LVS_REPORT: not implemented\n");
4006 else if (uView == LVS_SMALLICON)
4007 FIXME("uView == LVS_SMALLICON: not implemented\n");
4008 else if (uView == LVS_ICON)
4009 FIXME("uView == LVS_ICON: not implemented\n");
4016 * Arranges listview items in icon display mode.
4019 * [I] HWND : window handle
4020 * [I] INT : alignment code
4026 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
4028 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4029 BOOL bResult = FALSE;
4031 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4036 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
4039 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
4042 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4044 case LVA_SNAPTOGRID:
4045 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4053 /* << LISTVIEW_CreateDragImage >> */
4058 * Removes all listview items and subitems.
4061 * [I] HWND : window handle
4067 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
4069 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4070 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4071 UINT uView = lStyle & LVS_TYPEMASK;
4072 LISTVIEW_ITEM *lpItem;
4073 LISTVIEW_SUBITEM *lpSubItem;
4076 BOOL bResult = FALSE;
4079 TRACE("(hwnd=%x,)\n", hwnd);
4081 LISTVIEW_RemoveAllSelections(hwnd);
4082 infoPtr->nSelectionMark=-1;
4083 infoPtr->nFocusedItem=-1;
4084 /* But we are supposed to leave nHotItem as is! */
4086 if (lStyle & LVS_OWNERDATA)
4088 infoPtr->hdpaItems->nItemCount = 0;
4089 InvalidateRect(hwnd, NULL, TRUE);
4093 if (GETITEMCOUNT(infoPtr) > 0)
4097 /* send LVN_DELETEALLITEMS notification */
4098 /* verify if subsequent LVN_DELETEITEM notifications should be
4100 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4102 bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &nmlv);
4104 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4106 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4107 if (hdpaSubItems != NULL)
4109 for (j = 1; j < hdpaSubItems->nItemCount; j++)
4111 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
4112 if (lpSubItem != NULL)
4114 /* free subitem string */
4115 if (is_textW(lpSubItem->pszText))
4116 COMCTL32_Free(lpSubItem->pszText);
4119 COMCTL32_Free(lpSubItem);
4123 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4128 /* send LVN_DELETEITEM notification */
4130 nmlv.lParam = lpItem->lParam;
4131 listview_notify(hwnd, LVN_DELETEITEM, &nmlv);
4134 /* free item string */
4135 if (is_textW(lpItem->pszText))
4136 COMCTL32_Free(lpItem->pszText);
4139 COMCTL32_Free(lpItem);
4142 DPA_Destroy(hdpaSubItems);
4146 /* reinitialize listview memory */
4147 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
4149 /* align items (set position of each item) */
4150 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4152 if (lStyle & LVS_ALIGNLEFT)
4154 LISTVIEW_AlignLeft(hwnd);
4158 LISTVIEW_AlignTop(hwnd);
4162 LISTVIEW_UpdateScroll(hwnd);
4164 /* invalidate client area (optimization needed) */
4165 InvalidateRect(hwnd, NULL, TRUE);
4173 * Removes a column from the listview control.
4176 * [I] HWND : window handle
4177 * [I] INT : column index
4183 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
4185 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4186 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4187 UINT uOwnerData = GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA;
4188 BOOL bResult = FALSE;
4190 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
4193 bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
4195 /* Need to reset the item width when deleting a column */
4196 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
4198 /* reset scroll parameters */
4199 if (uView == LVS_REPORT)
4201 /* update scrollbar(s) */
4202 LISTVIEW_UpdateScroll(hwnd);
4204 /* refresh client area */
4205 InvalidateRect(hwnd, NULL, FALSE);
4214 * Removes an item from the listview control.
4217 * [I] HWND : window handle
4218 * [I] INT : item index
4224 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
4226 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4227 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4228 UINT uView = lStyle & LVS_TYPEMASK;
4229 LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID);
4231 BOOL bResult = FALSE;
4233 LISTVIEW_ITEM *lpItem;
4234 LISTVIEW_SUBITEM *lpSubItem;
4238 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
4241 /* First, send LVN_DELETEITEM notification. */
4242 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4243 nmlv.hdr.hwndFrom = hwnd;
4244 nmlv.hdr.idFrom = lCtrlId;
4245 nmlv.hdr.code = LVN_DELETEITEM;
4247 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
4251 /* remove it from the selection range */
4252 ZeroMemory(&item,sizeof(item));
4253 item.stateMask = LVIS_SELECTED;
4254 LISTVIEW_SetItemState(hwnd,nItem,&item);
4256 if (lStyle & LVS_OWNERDATA)
4258 infoPtr->hdpaItems->nItemCount --;
4259 InvalidateRect(hwnd, NULL, TRUE);
4263 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4265 /* initialize memory */
4266 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4268 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4269 if (hdpaSubItems != NULL)
4271 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4273 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4274 if (lpSubItem != NULL)
4276 /* free item string */
4277 if (is_textW(lpSubItem->pszText))
4278 COMCTL32_Free(lpSubItem->pszText);
4281 COMCTL32_Free(lpSubItem);
4285 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4288 /* free item string */
4289 if (is_textW(lpItem->pszText))
4290 COMCTL32_Free(lpItem->pszText);
4293 COMCTL32_Free(lpItem);
4296 bResult = DPA_Destroy(hdpaSubItems);
4299 LISTVIEW_ShiftIndices(hwnd,nItem,-1);
4301 /* align items (set position of each item) */
4302 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4304 if (lStyle & LVS_ALIGNLEFT)
4305 LISTVIEW_AlignLeft(hwnd);
4307 LISTVIEW_AlignTop(hwnd);
4310 LISTVIEW_UpdateScroll(hwnd);
4312 /* refresh client area */
4313 InvalidateRect(hwnd, NULL, TRUE);
4322 * Return edit control handle of current edit label
4325 * [I] HWND : window handle
4331 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4333 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4334 return infoPtr->hwndEdit;
4340 * Callback implementation for editlabel control
4343 * [I] HWND : window handle
4344 * [I] LPSTR : modified text
4345 * [I] DWORD : item index
4346 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4352 static BOOL LISTVIEW_EndEditLabelT(HWND hwnd, LPWSTR pszText, DWORD nItem, BOOL isW)
4354 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4355 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4356 NMLVDISPINFOW dispInfo;
4357 LISTVIEW_ITEM *lpItem;
4359 LISTVIEW_ITEM lvItemRef;
4361 BOOL bResult = TRUE;
4363 TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd, debugstr_t(pszText, isW), nItem, isW);
4365 if (!(lStyle & LVS_OWNERDATA))
4367 if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4370 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4375 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4376 ZeroMemory(&item,sizeof(item));
4379 item.mask = LVIF_PARAM | LVIF_STATE;
4380 ListView_GetItemW(hwnd, &item);
4381 lvItemRef.state = item.state;
4382 lvItemRef.iImage = item.iImage;
4383 lvItemRef.lParam = item.lParam;
4384 lpItem = &lvItemRef;
4387 ZeroMemory(&dispInfo, sizeof(dispInfo));
4388 dispInfo.item.mask = 0;
4389 dispInfo.item.iItem = nItem;
4390 dispInfo.item.state = lpItem->state;
4391 dispInfo.item.stateMask = 0;
4392 dispInfo.item.pszText = pszText;
4393 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4394 dispInfo.item.iImage = lpItem->iImage;
4395 dispInfo.item.lParam = lpItem->lParam;
4396 infoPtr->hwndEdit = 0;
4398 /* Do we need to update the Item Text */
4399 if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, isW))
4400 if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
4401 bResult = textsetptrT(&lpItem->pszText, pszText, isW);
4408 * Callback implementation for editlabel control
4411 * [I] HWND : window handle
4412 * [I] LPSTR : modified text
4413 * [I] DWORD : item index
4419 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem)
4421 return LISTVIEW_EndEditLabelT(hwnd, pszText, nItem, TRUE);
4426 * Callback implementation for editlabel control
4429 * [I] HWND : window handle
4430 * [I] LPSTR : modified text
4431 * [I] DWORD : item index
4437 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem)
4439 return LISTVIEW_EndEditLabelT(hwnd, (LPWSTR)pszText, nItem, FALSE);
4444 * Begin in place editing of specified list view item
4447 * [I] HWND : window handle
4448 * [I] INT : item index
4449 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4455 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW)
4457 NMLVDISPINFOW dispInfo;
4459 LISTVIEW_ITEM *lpItem;
4461 HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE);
4462 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4464 WCHAR szDispText[DISP_TEXT_SIZE];
4466 LISTVIEW_ITEM lvItemRef;
4467 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4469 if (~GetWindowLongW(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4472 /* Is the EditBox still there, if so remove it */
4473 if(infoPtr->hwndEdit != 0)
4476 LISTVIEW_SetSelection(hwnd, nItem);
4477 LISTVIEW_SetItemFocus(hwnd, nItem);
4479 if (!(lStyle & LVS_OWNERDATA))
4481 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4484 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4490 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4491 ZeroMemory(&item, sizeof(item));
4494 item.mask = LVIF_PARAM | LVIF_STATE;
4495 ListView_GetItemW(hwnd, &item);
4496 lvItemRef.iImage = item.iImage;
4497 lvItemRef.state = item.state;
4498 lvItemRef.lParam = item.lParam;
4499 lpItem = &lvItemRef;
4502 /* get information needed for drawing the item */
4503 ZeroMemory(&lvItem, sizeof(lvItem));
4504 lvItem.mask = LVIF_TEXT;
4505 lvItem.iItem = nItem;
4506 lvItem.iSubItem = 0;
4507 lvItem.cchTextMax = DISP_TEXT_SIZE;
4508 lvItem.pszText = szDispText;
4509 *lvItem.pszText = '\0';
4510 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW);
4512 ZeroMemory(&dispInfo, sizeof(dispInfo));
4513 dispInfo.item.mask = 0;
4514 dispInfo.item.iItem = nItem;
4515 dispInfo.item.state = lpItem->state;
4516 dispInfo.item.stateMask = 0;
4517 dispInfo.item.pszText = lvItem.pszText;
4518 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4519 dispInfo.item.iImage = lpItem->iImage;
4520 dispInfo.item.lParam = lpItem->lParam;
4522 if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW))
4525 rect.left = LVIR_LABEL;
4526 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4529 if (!(hedit = CreateEditLabelT(szDispText , WS_VISIBLE,
4530 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, hwnd, hinst,
4531 isW ? LISTVIEW_EndEditLabelW : (EditlblCallbackW)LISTVIEW_EndEditLabelA,
4535 infoPtr->hwndEdit = hedit;
4537 SendMessageW(hedit, EM_SETSEL, 0, -1);
4545 * Ensures the specified item is visible, scrolling into view if necessary.
4548 * [I] HWND : window handle
4549 * [I] INT : item index
4550 * [I] BOOL : partially or entirely visible
4556 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4558 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4559 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4560 INT nScrollPosHeight = 0;
4561 INT nScrollPosWidth = 0;
4562 SCROLLINFO scrollInfo;
4564 BOOL bRedraw = FALSE;
4566 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4567 scrollInfo.cbSize = sizeof(SCROLLINFO);
4568 scrollInfo.fMask = SIF_POS;
4570 /* ALWAYS bPartial == FALSE, FOR NOW! */
4572 rcItem.left = LVIR_BOUNDS;
4573 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4575 if (rcItem.left < infoPtr->rcList.left)
4577 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4581 if (uView == LVS_LIST)
4583 nScrollPosWidth = infoPtr->nItemWidth;
4584 rcItem.left += infoPtr->rcList.left;
4586 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4588 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4589 rcItem.left += infoPtr->rcList.left;
4592 /* When in LVS_REPORT view, the scroll position should
4594 if (nScrollPosWidth != 0)
4596 if (rcItem.left % nScrollPosWidth == 0)
4597 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4599 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4601 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4605 else if (rcItem.right > infoPtr->rcList.right)
4607 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4611 if (uView == LVS_LIST)
4613 rcItem.right -= infoPtr->rcList.right;
4614 nScrollPosWidth = infoPtr->nItemWidth;
4616 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4618 rcItem.right -= infoPtr->rcList.right;
4619 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4622 /* When in LVS_REPORT view, the scroll position should
4624 if (nScrollPosWidth != 0)
4626 if (rcItem.right % nScrollPosWidth == 0)
4627 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4629 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4631 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4636 if (rcItem.top < infoPtr->rcList.top)
4640 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4642 if (uView == LVS_REPORT)
4644 rcItem.top -= infoPtr->rcList.top;
4645 nScrollPosHeight = infoPtr->nItemHeight;
4647 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4649 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4650 rcItem.top += infoPtr->rcList.top;
4653 if (rcItem.top % nScrollPosHeight == 0)
4654 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4656 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4658 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4661 else if (rcItem.bottom > infoPtr->rcList.bottom)
4665 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4667 if (uView == LVS_REPORT)
4669 rcItem.bottom -= infoPtr->rcList.bottom;
4670 nScrollPosHeight = infoPtr->nItemHeight;
4672 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4674 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4675 rcItem.bottom -= infoPtr->rcList.bottom;
4678 if (rcItem.bottom % nScrollPosHeight == 0)
4679 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4681 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4683 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4689 InvalidateRect(hwnd,NULL,TRUE);
4695 * Retrieves the nearest item, given a position and a direction.
4698 * [I] HWND : window handle
4699 * [I] POINT : start position
4700 * [I] UINT : direction
4703 * Item index if successdful, -1 otherwise.
4705 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4707 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4712 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4713 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4714 ((vkDirection == VK_UP) ? "VK_UP" :
4715 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4717 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4719 ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4720 LISTVIEW_GetOrigin(hwnd, &lvIntHit.ht.pt);
4721 lvIntHit.ht.pt.x += pt.x;
4722 lvIntHit.ht.pt.y += pt.y;
4724 if (vkDirection == VK_DOWN)
4725 lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4726 else if (vkDirection == VK_UP)
4727 lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4728 else if (vkDirection == VK_LEFT)
4729 lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4730 else if (vkDirection == VK_RIGHT)
4731 lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4733 if (PtInRect(&rcView, lvIntHit.ht.pt) == FALSE)
4737 nItem = LISTVIEW_SuperHitTestItem(hwnd, &lvIntHit, TRUE);
4738 return nItem == -1 ? lvIntHit.iDistItem : nItem;
4747 * Searches for an item with specific characteristics.
4750 * [I] hwnd : window handle
4751 * [I] nStart : base item index
4752 * [I] lpFindInfo : item information to look for
4755 * SUCCESS : index of item
4758 static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart,
4759 LPLVFINDINFOW lpFindInfo)
4761 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4763 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4767 INT nLast = GETITEMCOUNT(infoPtr);
4769 if ((nItem >= -1) && (lpFindInfo != NULL))
4771 ZeroMemory(&lvItem, sizeof(lvItem));
4773 if (lpFindInfo->flags & LVFI_PARAM)
4775 lvItem.mask |= LVIF_PARAM;
4778 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4780 lvItem.mask |= LVIF_TEXT;
4781 lvItem.pszText = szDispText;
4782 lvItem.cchTextMax = DISP_TEXT_SIZE;
4785 if (lpFindInfo->flags & LVFI_WRAP)
4788 if (lpFindInfo->flags & LVFI_NEARESTXY)
4790 ptItem.x = lpFindInfo->pt.x;
4791 ptItem.y = lpFindInfo->pt.y;
4796 while (nItem < nLast)
4798 if (lpFindInfo->flags & LVFI_NEARESTXY)
4800 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4801 lpFindInfo->vkDirection);
4804 /* get position of the new item index */
4805 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4816 lvItem.iItem = nItem;
4817 lvItem.iSubItem = 0;
4818 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
4820 if (lvItem.mask & LVIF_TEXT)
4822 if (lpFindInfo->flags & LVFI_PARTIAL)
4824 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4829 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4834 if (lvItem.mask & LVIF_PARAM)
4836 if (lpFindInfo->lParam != lvItem.lParam)
4862 * Searches for an item with specific characteristics.
4865 * [I] hwnd : window handle
4866 * [I] nStart : base item index
4867 * [I] lpFindInfo : item information to look for
4870 * SUCCESS : index of item
4873 static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart,
4874 LPLVFINDINFOA lpFindInfo)
4876 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4880 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4881 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4882 res = LISTVIEW_FindItemW(hwnd, nStart, &fiw);
4883 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4889 * Retrieves the background color of the listview control.
4892 * [I] HWND : window handle
4895 * COLORREF associated with the background.
4897 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4899 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4901 return infoPtr->clrBk;
4906 * Retrieves the background image of the listview control.
4909 * [I] HWND : window handle
4910 * [O] LPLVMKBIMAGE : background image attributes
4916 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4918 /* FIXME (listview, "empty stub!\n"); */
4924 * Retrieves the callback mask.
4927 * [I] HWND : window handle
4932 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4934 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4936 return infoPtr->uCallbackMask;
4941 * Retrieves column attributes.
4944 * [I] HWND : window handle
4945 * [I] INT : column index
4946 * [IO] LPLVCOLUMNW : column information
4947 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4948 * otherwise it is in fact a LPLVCOLUMNA
4954 static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4956 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4958 BOOL bResult = FALSE;
4960 if (lpColumn != NULL)
4963 TRACE("(hwnd=%x, col=%d, lpColumn=%s, isW=%d)\n",
4964 hwnd, nItem, debuglvcolumn_t(lpColumn, isW), isW);
4966 /* initialize memory */
4967 ZeroMemory(&hdi, sizeof(hdi));
4969 if (lpColumn->mask & LVCF_FMT)
4970 hdi.mask |= HDI_FORMAT;
4972 if (lpColumn->mask & LVCF_WIDTH)
4973 hdi.mask |= HDI_WIDTH;
4975 if (lpColumn->mask & LVCF_TEXT)
4977 hdi.mask |= HDI_TEXT;
4978 hdi.cchTextMax = lpColumn->cchTextMax;
4979 hdi.pszText = lpColumn->pszText;
4982 if (lpColumn->mask & LVCF_IMAGE)
4983 hdi.mask |= HDI_IMAGE;
4985 if (lpColumn->mask & LVCF_ORDER)
4986 hdi.mask |= HDI_ORDER;
4989 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4991 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4993 if (bResult != FALSE)
4995 if (lpColumn->mask & LVCF_FMT)
4999 if (hdi.fmt & HDF_LEFT)
5000 lpColumn->fmt |= LVCFMT_LEFT;
5001 else if (hdi.fmt & HDF_RIGHT)
5002 lpColumn->fmt |= LVCFMT_RIGHT;
5003 else if (hdi.fmt & HDF_CENTER)
5004 lpColumn->fmt |= LVCFMT_CENTER;
5006 if (hdi.fmt & HDF_IMAGE)
5007 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
5009 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
5010 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
5013 if (lpColumn->mask & LVCF_WIDTH)
5014 lpColumn->cx = hdi.cxy;
5016 if (lpColumn->mask & LVCF_IMAGE)
5017 lpColumn->iImage = hdi.iImage;
5019 if (lpColumn->mask & LVCF_ORDER)
5020 lpColumn->iOrder = hdi.iOrder;
5028 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
5030 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
5037 for (i = 0; i < iCount; i++)
5045 * Retrieves the column width.
5048 * [I] HWND : window handle
5049 * [I] int : column index
5052 * SUCCESS : column width
5055 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
5057 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5058 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5059 INT nColumnWidth = 0;
5062 if (uView == LVS_LIST)
5064 nColumnWidth = infoPtr->nItemWidth;
5066 else if (uView == LVS_REPORT)
5068 /* get column width from header */
5069 ZeroMemory(&hdi, sizeof(hdi));
5070 hdi.mask = HDI_WIDTH;
5071 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
5072 nColumnWidth = hdi.cxy;
5075 return nColumnWidth;
5080 * In list or report display mode, retrieves the number of items that can fit
5081 * vertically in the visible area. In icon or small icon display mode,
5082 * retrieves the total number of visible items.
5085 * [I] HWND : window handle
5088 * Number of fully visible items.
5090 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
5092 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5093 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5096 if (uView == LVS_LIST)
5098 if (infoPtr->rcList.right > infoPtr->nItemWidth)
5100 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
5101 LISTVIEW_GetCountPerColumn(hwnd);
5104 else if (uView == LVS_REPORT)
5106 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
5110 nItemCount = GETITEMCOUNT(infoPtr);
5116 /* LISTVIEW_GetEditControl */
5120 * Retrieves the extended listview style.
5123 * [I] HWND : window handle
5126 * SUCCESS : previous style
5129 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
5131 LISTVIEW_INFO *infoPtr;
5133 /* make sure we can get the listview info */
5134 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
5137 return (infoPtr->dwExStyle);
5142 * Retrieves the handle to the header control.
5145 * [I] HWND : window handle
5150 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
5152 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5154 return infoPtr->hwndHeader;
5157 /* LISTVIEW_GetHotCursor */
5161 * Returns the time that the mouse cursor must hover over an item
5162 * before it is selected.
5165 * [I] HWND : window handle
5168 * Returns the previously set hover time or (DWORD)-1 to indicate that the
5169 * hover time is set to the default hover time.
5171 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
5173 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5175 return infoPtr->dwHoverTime;
5180 * Retrieves an image list handle.
5183 * [I] HWND : window handle
5184 * [I] INT : image list identifier
5187 * SUCCESS : image list handle
5190 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
5192 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5193 HIMAGELIST himl = NULL;
5198 himl = infoPtr->himlNormal;
5201 himl = infoPtr->himlSmall;
5204 himl = infoPtr->himlState;
5208 return (LRESULT)himl;
5211 /* LISTVIEW_GetISearchString */
5215 * Retrieves item attributes.
5218 * [I] hwnd : window handle
5219 * [IO] lpLVItem : item info
5220 * [I] internal : if true then we will use tricks that avoid copies
5221 * but are not compatible with the regular interface
5222 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5223 * if FALSE, the lpLVItem is a LPLVITEMA.
5229 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
5231 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5232 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5233 NMLVDISPINFOW dispInfo;
5234 LISTVIEW_SUBITEM *lpSubItem;
5235 LISTVIEW_ITEM *lpItem;
5238 INT* piImage = (INT*)&null;
5239 LPWSTR* ppszText= (LPWSTR*)&null;
5240 LPARAM* plParam = (LPARAM*)&null;
5242 if (internal && !isW)
5244 ERR("We can't have internal non-Unicode GetItem!\n");
5248 /* In the following:
5249 * lpLVItem describes the information requested by the user
5250 * lpItem/lpSubItem is what we have
5251 * dispInfo is a structure we use to request the missing
5252 * information from the application
5255 TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n",
5256 hwnd, debuglvitem_t(lpLVItem, isW), internal, isW);
5258 if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5259 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5262 ZeroMemory(&dispInfo, sizeof(dispInfo));
5264 if (lStyle & LVS_OWNERDATA)
5266 if (lpLVItem->mask & ~LVIF_STATE)
5268 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5269 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5270 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5271 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5274 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5276 lpLVItem->state = 0;
5277 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5278 lpLVItem->state |= LVIS_FOCUSED;
5279 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5280 lpLVItem->state |= LVIS_SELECTED;
5286 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5287 if (hdpaSubItems == NULL) return FALSE;
5289 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5292 ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5293 if (lpLVItem->iSubItem == 0)
5295 piImage=&lpItem->iImage;
5296 ppszText=&lpItem->pszText;
5297 plParam=&lpItem->lParam;
5298 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5300 dispInfo.item.mask |= LVIF_STATE;
5301 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5306 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5307 if (lpSubItem != NULL)
5309 piImage=&lpSubItem->iImage;
5310 ppszText=&lpSubItem->pszText;
5314 if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
5316 dispInfo.item.mask |= LVIF_IMAGE;
5319 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5321 dispInfo.item.mask |= LVIF_TEXT;
5322 dispInfo.item.pszText = lpLVItem->pszText;
5323 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5324 if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5325 *dispInfo.item.pszText = '\0';
5326 if (dispInfo.item.pszText && (*ppszText == NULL))
5327 *dispInfo.item.pszText = '\0';
5330 if (dispInfo.item.mask != 0)
5332 /* We don't have all the requested info, query the application */
5333 dispInfo.item.iItem = lpLVItem->iItem;
5334 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5335 dispInfo.item.lParam = lpItem->lParam;
5336 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5337 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5340 if (dispInfo.item.mask & LVIF_IMAGE)
5342 lpLVItem->iImage = dispInfo.item.iImage;
5343 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
5344 *piImage = dispInfo.item.iImage;
5346 else if (lpLVItem->mask & LVIF_IMAGE)
5348 lpLVItem->iImage = *piImage;
5351 if (dispInfo.item.mask & LVIF_PARAM)
5353 lpLVItem->lParam = dispInfo.item.lParam;
5354 if (dispInfo.item.mask & LVIF_DI_SETITEM)
5355 *plParam = dispInfo.item.lParam;
5357 else if (lpLVItem->mask & LVIF_PARAM)
5358 lpLVItem->lParam = lpItem->lParam;
5360 if (dispInfo.item.mask & LVIF_TEXT)
5362 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5363 textsetptrT(ppszText, dispInfo.item.pszText, isW);
5365 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5366 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5367 if (lpLVItem->pszText != dispInfo.item.pszText)
5368 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5371 else if (lpLVItem->mask & LVIF_TEXT)
5373 if (internal) lpLVItem->pszText = *ppszText;
5374 else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5377 if (lpLVItem->iSubItem == 0)
5379 if (dispInfo.item.mask & LVIF_STATE)
5381 lpLVItem->state = lpItem->state;
5382 lpLVItem->state &= ~dispInfo.item.stateMask;
5383 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5385 lpLVItem->state &= ~LVIS_SELECTED;
5386 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5387 LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem))
5388 lpLVItem->state |= LVIS_SELECTED;
5390 else if (lpLVItem->mask & LVIF_STATE)
5392 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5394 lpLVItem->state &= ~LVIS_SELECTED;
5395 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5396 LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5397 lpLVItem->state |= LVIS_SELECTED;
5400 if (lpLVItem->mask & LVIF_PARAM)
5401 lpLVItem->lParam = lpItem->lParam;
5403 if (lpLVItem->mask & LVIF_INDENT)
5404 lpLVItem->iIndent = lpItem->iIndent;
5410 /* LISTVIEW_GetHotCursor */
5414 * Retrieves the index of the hot item.
5417 * [I] HWND : window handle
5420 * SUCCESS : hot item index
5421 * FAILURE : -1 (no hot item)
5423 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5425 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5427 return infoPtr->nHotItem;
5430 /* LISTVIEW_GetHoverTime */
5434 * Retrieves the number of items in the listview control.
5437 * [I] HWND : window handle
5442 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5444 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5446 return GETITEMCOUNT(infoPtr);
5451 * Retrieves the rectangle enclosing the item icon and text.
5454 * [I] HWND : window handle
5455 * [I] INT : item index
5456 * [O] LPRECT : coordinate information
5462 static BOOL LISTVIEW_GetItemBoundBox(HWND hwnd, INT nItem, LPRECT lpRect)
5464 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5465 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5466 UINT uView = lStyle & LVS_TYPEMASK;
5467 BOOL bResult = FALSE;
5469 LISTVIEW_ITEM *lpItem;
5470 INT nCountPerColumn;
5473 TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd, nItem, lpRect);
5475 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5478 if (uView == LVS_LIST)
5481 nItem = nItem - ListView_GetTopIndex(hwnd);
5482 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5485 nRow = nItem % nCountPerColumn;
5488 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5493 lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5494 lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5499 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5500 lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
5503 else if (uView == LVS_REPORT)
5506 lpRect->left = REPORT_MARGINX;
5507 lpRect->top = ((nItem - ListView_GetTopIndex(hwnd)) *
5508 infoPtr->nItemHeight) + infoPtr->rcList.top;
5510 if (!(lStyle & LVS_NOSCROLL))
5512 SCROLLINFO scrollInfo;
5513 /* Adjust position by scrollbar offset */
5514 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5515 scrollInfo.cbSize = sizeof(SCROLLINFO);
5516 scrollInfo.fMask = SIF_POS;
5517 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5518 lpRect->left -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5521 else /* either LVS_ICON or LVS_SMALLICON */
5523 if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
5525 if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
5528 lpRect->left = lpItem->ptPosition.x;
5529 lpRect->top = lpItem->ptPosition.y;
5534 lpRect->right = lpRect->left + infoPtr->nItemWidth;
5535 lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
5536 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5537 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
5543 * Retrieves the position (upper-left) of the listview control item.
5544 * Note that for LVS_ICON style, the upper-left is that of the icon
5545 * and not the bounding box.
5548 * [I] HWND : window handle
5549 * [I] INT : item index
5550 * [O] LPPOINT : coordinate information
5556 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, LPPOINT lpptPosition)
5558 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5559 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5560 BOOL bResult = FALSE;
5563 TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd, nItem, lpptPosition);
5565 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5566 (lpptPosition != NULL))
5568 bResult = LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcBounding);
5569 lpptPosition->x = rcBounding.left;
5570 lpptPosition->y = rcBounding.top;
5571 if (uView == LVS_ICON)
5573 lpptPosition->y += ICON_TOP_PADDING;
5574 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5576 TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
5577 lpptPosition->x, lpptPosition->y);
5583 * Update the bounding rectangle around the text under a large icon.
5584 * This depends on whether it has the focus or not.
5585 * On entry the rectangle's top, left and right should be set.
5586 * On return the bottom will also be set and the width may have been
5589 * This appears to be weird, even in the Microsoft implementation.
5592 static void ListView_UpdateLargeItemLabelRect (
5593 HWND hwnd, /* The window of the listview */
5594 const LISTVIEW_INFO *infoPtr, /* The listview itself */
5595 int nItem, /* The item for which we are calculating this */
5596 RECT *rect) /* The rectangle to be updated */
5598 HDC hdc = GetDC (hwnd);
5599 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
5601 if (infoPtr->bFocus && infoPtr->nFocusedItem == nItem)
5603 /* We (aim to) display the full text. In Windows 95 it appears to
5604 * calculate the size assuming the specified font and then it draws
5605 * the text in that region with the specified font except scaled to
5606 * 10 point (or the height of the system font or ...). Thus if the
5607 * window has 24 point Helvetica the highlit rectangle will be
5608 * taller than the text and if it is 7 point Helvetica then the text
5610 * For now we will simply say that it is the correct size to display
5611 * the text in the specified font.
5614 lvItem.mask = LVIF_TEXT;
5615 lvItem.iItem = nItem;
5616 lvItem.iSubItem = 0;
5617 /* We will specify INTERNAL and so will receive back a const
5618 * pointer to the text, rather than specifying a buffer to which
5621 LISTVIEW_GetItemW (hwnd, &lvItem, TRUE);
5622 DrawTextW (hdc, lvItem.pszText, -1, rect, DT_CALCRECT |
5623 DT_NOCLIP | DT_EDITCONTROL | DT_TOP | DT_CENTER |
5624 DT_WORDBREAK | DT_NOPREFIX);
5625 /* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
5629 /* As far as I can see the text region seems to be trying to be
5630 * "tall enough for two lines of text". Once again (comctl32.dll ver
5631 * 5.81?) it measures this on the basis of the selected font and then
5632 * draws it with the same font except in 10 point size. This can lead
5633 * to more or less than the two rows appearing.
5634 * Question; are we supposed to be including DT_EXTERNALLEADING?
5635 * Question; should the width be shrunk to the space required to
5636 * display the two lines?
5638 rect->bottom = rect->top + 2 * infoPtr->ntmHeight;
5641 SelectObject (hdc, hOldFont);
5642 ReleaseDC (hwnd, hdc);
5647 * Retrieves the bounding rectangle for a listview control item.
5650 * [I] HWND : window handle
5651 * [I] INT : item index
5652 * [IO] LPRECT : bounding rectangle coordinates
5653 * lprc->left specifies the portion of the item for which the bounding
5654 * rectangle will be retrieved.
5656 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5657 * including the icon and label.
5658 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5659 * LVIR_LABEL Returns the bounding rectangle of the item text.
5660 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5661 * rectangles, but excludes columns in report view.
5668 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5669 * upon whether the window has the focus currently and on whether the item
5670 * is the one with the focus. Ensure that the control's record of which
5671 * item has the focus agrees with the items' records.
5673 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5675 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5676 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5677 BOOL bResult = FALSE;
5686 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5688 if (uView & LVS_REPORT)
5690 ZeroMemory(&lvItem, sizeof(lvItem));
5691 lvItem.mask = LVIF_INDENT;
5692 lvItem.iItem = nItem;
5693 lvItem.iSubItem = 0;
5694 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
5697 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5698 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5705 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5710 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5711 if (uView == LVS_ICON)
5713 if (infoPtr->himlNormal != NULL)
5715 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5718 lprc->left = ptItem.x + ptOrigin.x;
5719 lprc->top = ptItem.y + ptOrigin.y;
5720 lprc->right = lprc->left + infoPtr->iconSize.cx;
5721 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5722 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5726 else if (uView == LVS_SMALLICON)
5728 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5731 lprc->left = ptItem.x + ptOrigin.x;
5732 lprc->top = ptItem.y + ptOrigin.y;
5733 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5735 if (infoPtr->himlState != NULL)
5736 lprc->left += infoPtr->iconSize.cx;
5738 if (infoPtr->himlSmall != NULL)
5739 lprc->right = lprc->left + infoPtr->iconSize.cx;
5741 lprc->right = lprc->left;
5747 lprc->left = ptItem.x;
5748 if (uView & LVS_REPORT)
5749 lprc->left += nIndent;
5750 lprc->top = ptItem.y;
5751 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5753 if (infoPtr->himlState != NULL)
5754 lprc->left += infoPtr->iconSize.cx;
5756 if (infoPtr->himlSmall != NULL)
5757 lprc->right = lprc->left + infoPtr->iconSize.cx;
5759 lprc->right = lprc->left;
5764 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5765 if (uView == LVS_ICON)
5767 if (infoPtr->himlNormal != NULL)
5769 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5772 lprc->left = ptItem.x + ptOrigin.x;
5773 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5774 ICON_BOTTOM_PADDING);
5775 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5776 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5778 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5779 lprc->right = lprc->left + nLabelWidth;
5784 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5785 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, lprc);
5790 else if (uView == LVS_SMALLICON)
5792 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5795 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5796 lprc->top = ptItem.y + ptOrigin.y;
5797 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5799 if (infoPtr->himlState != NULL)
5800 lprc->left += infoPtr->iconSize.cx;
5802 if (infoPtr->himlSmall != NULL)
5803 lprc->left += infoPtr->iconSize.cx;
5805 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5806 nLabelWidth += TRAILING_PADDING;
5807 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5808 lprc->right = lprc->left + nLabelWidth;
5810 lprc->right = nLeftPos + infoPtr->nItemWidth;
5816 if (uView == LVS_REPORT)
5817 nLeftPos = lprc->left = ptItem.x + nIndent;
5819 nLeftPos = lprc->left = ptItem.x;
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->left += infoPtr->iconSize.cx;
5829 if (uView != LVS_REPORT)
5831 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5832 nLabelWidth += TRAILING_PADDING;
5833 if (infoPtr->himlSmall)
5834 nLabelWidth += IMAGE_PADDING;
5837 nLabelWidth = LISTVIEW_GetColumnWidth(hwnd, 0)-lprc->left;
5838 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5839 lprc->right = lprc->left + nLabelWidth;
5841 lprc->right = nLeftPos + infoPtr->nItemWidth;
5846 if (!LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcInternal)) break;
5847 ptItem.x = rcInternal.left;
5848 ptItem.y = rcInternal.top;
5849 if (uView == LVS_ICON)
5851 if (infoPtr->himlNormal != NULL)
5853 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5856 INT text_left, text_right, icon_left, text_pos_x;
5857 /* for style LVS_ICON bounds
5858 * left = min(icon.left, text.left)
5859 * right = max(icon.right, text.right)
5860 * top = boundbox.top + NOTHITABLE
5861 * bottom = text.bottom + 1
5864 icon_left = text_left = ptItem.x;
5866 /* Correct ptItem to icon upper-left */
5867 icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5868 ptItem.y += ICON_TOP_PADDING;
5870 /* Compute the label left and right */
5871 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5872 text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
5875 text_left += text_pos_x / 2;
5876 text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
5881 text_right = text_left + infoPtr->iconSpacing.cx - 1;
5884 /* Compute rectangle w/o the text height */
5885 lprc->left = min(icon_left, text_left) + ptOrigin.x;
5886 lprc->right = max(icon_left + infoPtr->iconSize.cx,
5887 text_right) + ptOrigin.x;
5888 lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
5889 lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
5890 + infoPtr->iconSize.cy + 1
5891 + ICON_BOTTOM_PADDING;
5893 CopyRect (&label_rect, lprc);
5894 label_rect.top = lprc->bottom;
5895 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, &label_rect);
5896 UnionRect (lprc, lprc, &label_rect);
5900 else if (uView == LVS_SMALLICON)
5902 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5905 lprc->left = ptItem.x + ptOrigin.x;
5906 lprc->right = lprc->left;
5907 lprc->top = ptItem.y + ptOrigin.y;
5908 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5909 if (infoPtr->himlState != NULL)
5910 lprc->right += infoPtr->iconSize.cx;
5911 if (infoPtr->himlSmall != NULL)
5912 lprc->right += infoPtr->iconSize.cx;
5914 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5915 nLabelWidth += TRAILING_PADDING;
5916 if (infoPtr->himlSmall)
5917 nLabelWidth += IMAGE_PADDING;
5918 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5919 lprc->right += nLabelWidth;
5921 lprc->right = lprc->left + infoPtr->nItemWidth;
5927 lprc->left = ptItem.x;
5928 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5929 lprc->left += nIndent;
5930 lprc->right = lprc->left;
5931 lprc->top = ptItem.y;
5932 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5934 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
5937 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5938 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5940 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5944 if (infoPtr->himlState != NULL)
5945 lprc->right += infoPtr->iconSize.cx;
5947 if (infoPtr->himlSmall != NULL)
5948 lprc->right += infoPtr->iconSize.cx;
5950 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5951 nLabelWidth += TRAILING_PADDING;
5952 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5953 lprc->right += nLabelWidth;
5955 lprc->right = lprc->left + infoPtr->nItemWidth;
5960 case LVIR_SELECTBOUNDS:
5961 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5962 if (uView == LVS_ICON)
5964 if (infoPtr->himlNormal != NULL)
5966 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5969 lprc->left = ptItem.x + ptOrigin.x;
5970 lprc->top = ptItem.y + ptOrigin.y;
5971 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5972 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5976 else if (uView == LVS_SMALLICON)
5978 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5981 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5982 lprc->top = ptItem.y + ptOrigin.y;
5983 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5985 if (infoPtr->himlState != NULL)
5986 lprc->left += infoPtr->iconSize.cx;
5988 lprc->right = lprc->left;
5990 if (infoPtr->himlSmall != NULL)
5991 lprc->right += infoPtr->iconSize.cx;
5993 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5994 nLabelWidth += TRAILING_PADDING;
5995 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5996 lprc->right += nLabelWidth;
5998 lprc->right = nLeftPos + infoPtr->nItemWidth;
6004 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
6005 nLeftPos = lprc->left = ptItem.x + nIndent;
6007 nLeftPos = lprc->left = ptItem.x;
6008 lprc->top = ptItem.y;
6009 lprc->bottom = lprc->top + infoPtr->nItemHeight;
6011 if (infoPtr->himlState != NULL)
6012 lprc->left += infoPtr->iconSize.cx;
6014 lprc->right = lprc->left;
6016 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
6019 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
6020 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
6022 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
6026 if (infoPtr->himlSmall != NULL)
6027 lprc->right += infoPtr->iconSize.cx;
6029 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6030 nLabelWidth += TRAILING_PADDING;
6031 if (infoPtr->himlSmall)
6032 nLabelWidth += IMAGE_PADDING;
6033 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6034 lprc->right += nLabelWidth;
6036 lprc->right = nLeftPos + infoPtr->nItemWidth;
6043 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
6044 lprc->left, lprc->top, lprc->right, lprc->bottom);
6051 * Retrieves the width of a label.
6054 * [I] HWND : window handle
6057 * SUCCESS : string width (in pixels)
6060 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
6062 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6063 INT nLabelWidth = 0;
6066 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
6068 ZeroMemory(&lvItem, sizeof(lvItem));
6069 lvItem.mask = LVIF_TEXT;
6070 lvItem.iItem = nItem;
6071 lvItem.cchTextMax = DISP_TEXT_SIZE;
6072 lvItem.pszText = szDispText;
6073 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6074 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
6081 * Retrieves the spacing between listview control items.
6084 * [I] HWND : window handle
6085 * [I] BOOL : flag for small or large icon
6088 * Horizontal + vertical spacing
6090 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
6092 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6095 if (bSmall == FALSE)
6097 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6101 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
6102 if ((style & LVS_TYPEMASK) == LVS_ICON)
6103 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6105 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6112 * Retrieves the state of a listview control item.
6115 * [I] HWND : window handle
6116 * [I] INT : item index
6117 * [I] UINT : state mask
6120 * State specified by the mask.
6122 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
6124 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6128 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6130 ZeroMemory(&lvItem, sizeof(lvItem));
6131 lvItem.iItem = nItem;
6132 lvItem.stateMask = uMask;
6133 lvItem.mask = LVIF_STATE;
6134 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6135 uState = lvItem.state;
6143 * Retrieves the text of a listview control item or subitem.
6146 * [I] hwnd : window handle
6147 * [I] nItem : item index
6148 * [IO] lpLVItem : item information
6149 * [I] isW : TRUE if lpLVItem is Unicode
6152 * SUCCESS : string length
6155 static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6157 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6160 if (lpLVItem != NULL)
6162 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6164 lpLVItem->mask = LVIF_TEXT;
6165 lpLVItem->iItem = nItem;
6166 if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
6167 nLength = textlenT(lpLVItem->pszText, isW);
6176 * Searches for an item based on properties + relationships.
6179 * [I] HWND : window handle
6180 * [I] INT : item index
6181 * [I] INT : relationship flag
6184 * SUCCESS : item index
6187 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
6189 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6190 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6192 LVFINDINFOW lvFindInfo;
6193 INT nCountPerColumn;
6196 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6198 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6200 if (uFlags & LVNI_CUT)
6203 if (uFlags & LVNI_DROPHILITED)
6204 uMask |= LVIS_DROPHILITED;
6206 if (uFlags & LVNI_FOCUSED)
6207 uMask |= LVIS_FOCUSED;
6209 if (uFlags & LVNI_SELECTED)
6210 uMask |= LVIS_SELECTED;
6212 if (uFlags & LVNI_ABOVE)
6214 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6219 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6225 lvFindInfo.flags = LVFI_NEARESTXY;
6226 lvFindInfo.vkDirection = VK_UP;
6227 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6228 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6230 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6235 else if (uFlags & LVNI_BELOW)
6237 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6239 while (nItem < GETITEMCOUNT(infoPtr))
6242 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6248 lvFindInfo.flags = LVFI_NEARESTXY;
6249 lvFindInfo.vkDirection = VK_DOWN;
6250 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6251 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6253 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6258 else if (uFlags & LVNI_TOLEFT)
6260 if (uView == LVS_LIST)
6262 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6263 while (nItem - nCountPerColumn >= 0)
6265 nItem -= nCountPerColumn;
6266 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6270 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6272 lvFindInfo.flags = LVFI_NEARESTXY;
6273 lvFindInfo.vkDirection = VK_LEFT;
6274 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6275 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6277 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6282 else if (uFlags & LVNI_TORIGHT)
6284 if (uView == LVS_LIST)
6286 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6287 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6289 nItem += nCountPerColumn;
6290 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6294 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6296 lvFindInfo.flags = LVFI_NEARESTXY;
6297 lvFindInfo.vkDirection = VK_RIGHT;
6298 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6299 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6301 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6310 /* search by index */
6311 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6313 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
6322 /* LISTVIEW_GetNumberOfWorkAreas */
6326 * Retrieves the origin coordinates when in icon or small icon display mode.
6329 * [I] HWND : window handle
6330 * [O] LPPOINT : coordinate information
6336 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6338 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6339 UINT uView = lStyle & LVS_TYPEMASK;
6340 BOOL bResult = FALSE;
6342 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6344 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6346 SCROLLINFO scrollInfo;
6347 ZeroMemory(lpptOrigin, sizeof(POINT));
6348 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6349 scrollInfo.cbSize = sizeof(SCROLLINFO);
6351 if (lStyle & WS_HSCROLL)
6353 scrollInfo.fMask = SIF_POS;
6354 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6355 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6358 if (lStyle & WS_VSCROLL)
6360 scrollInfo.fMask = SIF_POS;
6361 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6362 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6373 * Retrieves the number of items that are marked as selected.
6376 * [I] HWND : window handle
6379 * Number of items selected.
6381 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6384 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6385 INT nSelectedCount = 0;
6388 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6390 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6394 return nSelectedCount;
6399 * Retrieves item index that marks the start of a multiple selection.
6402 * [I] HWND : window handle
6405 * Index number or -1 if there is no selection mark.
6407 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6409 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6411 return infoPtr->nSelectionMark;
6417 * Retrieves the width of a string.
6420 * [I] hwnd : window handle
6421 * [I] lpszText : text string to process
6422 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6425 * SUCCESS : string width (in pixels)
6428 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
6430 if (is_textT(lpszText, isW))
6432 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6433 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6434 HDC hdc = GetDC(hwnd);
6435 HFONT hOldFont = SelectObject(hdc, hFont);
6437 ZeroMemory(&stringSize, sizeof(SIZE));
6439 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6441 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6442 SelectObject(hdc, hOldFont);
6443 ReleaseDC(hwnd, hdc);
6444 return stringSize.cx;
6451 * Retrieves the text backgound color.
6454 * [I] HWND : window handle
6457 * COLORREF associated with the the background.
6459 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6461 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6463 return infoPtr->clrTextBk;
6468 * Retrieves the text color.
6471 * [I] HWND : window handle
6474 * COLORREF associated with the text.
6476 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6478 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6480 return infoPtr->clrText;
6485 * Determines item if a hit or closest if not
6488 * [I] HWND : window handle
6489 * [IO] LPLV_INTHIT : hit test information
6490 * [I] subitem : fill out iSubItem.
6493 * SUCCESS : item index of hit
6496 static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem)
6498 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6499 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6500 UINT uView = lStyle & LVS_TYPEMASK;
6501 INT i,topindex,bottomindex;
6503 DWORD xterm, yterm, dist;
6505 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.pt.y);
6507 topindex = ListView_GetTopIndex(hwnd);
6508 if (uView == LVS_REPORT)
6510 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6511 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6515 bottomindex = GETITEMCOUNT(infoPtr);
6518 lpInt->distance = 0x7fffffff;
6519 lpInt->iDistItem = -1;
6521 for (i = topindex; i < bottomindex; i++)
6523 rcItem.left = LVIR_BOUNDS;
6524 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6526 if (PtInRect(&rcItem, lpInt->ht.pt))
6528 rcItem.left = LVIR_ICON;
6529 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6531 if (PtInRect(&rcItem, lpInt->ht.pt))
6533 lpInt->ht.flags = LVHT_ONITEMICON;
6534 lpInt->ht.iItem = i;
6535 if (subitem) lpInt->ht.iSubItem = 0;
6540 rcItem.left = LVIR_LABEL;
6541 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6543 if (PtInRect(&rcItem, lpInt->ht.pt))
6545 lpInt->ht.flags = LVHT_ONITEMLABEL;
6546 lpInt->ht.iItem = i;
6547 if (subitem) lpInt->ht.iSubItem = 0;
6552 lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6553 lpInt->ht.iItem = i;
6554 if (subitem) lpInt->ht.iSubItem = 0;
6560 * Now compute distance from point to center of boundary
6561 * box. Since we are only interested in the relative
6562 * distance, we can skip the nasty square root operation
6564 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6565 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6566 dist = xterm * xterm + yterm * yterm;
6567 if (dist < lpInt->distance)
6569 lpInt->distance = dist;
6570 lpInt->iDistItem = i;
6576 lpInt->ht.flags = LVHT_NOWHERE;
6577 TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6584 * Determines which section of the item was selected (if any).
6587 * [I] HWND : window handle
6588 * [IO] LPLVHITTESTINFO : hit test information
6589 * [I] subitem : fill out iSubItem.
6592 * SUCCESS : item index
6595 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6598 LV_INTHIT lv_inthit;
6600 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6601 lpHitTestInfo->pt.y);
6603 memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6604 ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem);
6605 memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6611 * Determines which listview item is located at the specified position.
6614 * [I] HWND : window handle
6615 * [IO} LPLVHITTESTINFO : hit test information
6618 * SUCCESS : item index
6621 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6623 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6626 lpHitTestInfo->flags = 0;
6628 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6629 lpHitTestInfo->flags = LVHT_TOLEFT;
6630 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6631 lpHitTestInfo->flags = LVHT_TORIGHT;
6632 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6633 lpHitTestInfo->flags |= LVHT_ABOVE;
6634 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6635 lpHitTestInfo->flags |= LVHT_BELOW;
6637 if (lpHitTestInfo->flags == 0)
6639 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6640 * an app might pass only a structure with space up to iItem!
6641 * (MS Office 97 does that for instance in the file open dialog)
6643 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6651 * Inserts a new column.
6654 * [I] HWND : window handle
6655 * [I] INT : column index
6656 * [I] LPLVCOLUMNW : column information
6659 * SUCCESS : new column index
6662 static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn,
6663 LPLVCOLUMNW lpColumn, BOOL isW)
6665 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6666 INT nNewColumn = -1;
6669 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
6671 if (lpColumn != NULL)
6673 /* initialize memory */
6674 ZeroMemory(&hdi, sizeof(hdi));
6676 if (lpColumn->mask & LVCF_FMT)
6678 /* format member is valid */
6679 hdi.mask |= HDI_FORMAT;
6681 /* set text alignment (leftmost column must be left-aligned) */
6684 hdi.fmt |= HDF_LEFT;
6688 if (lpColumn->fmt & LVCFMT_LEFT)
6690 hdi.fmt |= HDF_LEFT;
6692 else if (lpColumn->fmt & LVCFMT_RIGHT)
6694 hdi.fmt |= HDF_RIGHT;
6696 else if (lpColumn->fmt & LVCFMT_CENTER)
6698 hdi.fmt |= HDF_CENTER;
6702 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6704 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6708 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6713 if (lpColumn->fmt & LVCFMT_IMAGE)
6715 hdi.fmt |= HDF_IMAGE;
6716 hdi.iImage = I_IMAGECALLBACK;
6720 if (lpColumn->mask & LVCF_WIDTH)
6722 hdi.mask |= HDI_WIDTH;
6723 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6725 /* make it fill the remainder of the controls width */
6730 ZeroMemory(&hdit, sizeof(hdit));
6732 /* get the width of every item except the current one */
6733 hdit.mask = HDI_WIDTH;
6736 for(item_index = 0; item_index < (nColumn - 1); item_index++) {
6737 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
6741 /* retrieve the layout of the header */
6742 GetClientRect(hwnd, &rcHeader);
6743 /* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6744 TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
6746 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
6749 hdi.cxy = lpColumn->cx;
6752 if (lpColumn->mask & LVCF_TEXT)
6754 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6755 hdi.pszText = lpColumn->pszText;
6756 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6757 hdi.fmt |= HDF_STRING;
6760 if (lpColumn->mask & LVCF_IMAGE)
6762 hdi.mask |= HDI_IMAGE;
6763 hdi.iImage = lpColumn->iImage;
6766 if (lpColumn->mask & LVCF_ORDER)
6768 hdi.mask |= HDI_ORDER;
6769 hdi.iOrder = lpColumn->iOrder;
6772 /* insert item in header control */
6773 nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6774 (WPARAM)nColumn, (LPARAM)&hdi);
6776 /* Need to reset the item width when inserting a new column */
6777 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6779 LISTVIEW_UpdateScroll(hwnd);
6780 InvalidateRect(hwnd, NULL, FALSE);
6786 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6787 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6788 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6789 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6790 their own sort proc. when sending LVM_SORTITEMS.
6793 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6795 LVS_SORTXXX must be specified,
6796 LVS_OWNERDRAW is not set,
6797 <item>.pszText is not LPSTR_TEXTCALLBACK.
6799 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6800 are sorted based on item text..."
6802 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6804 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6805 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6806 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6807 INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6808 /* if we're sorting descending, negate the return value */
6809 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6814 * Inserts a new item in the listview control.
6817 * [I] HWND : window handle
6818 * [I] LPLVITEMW : item information
6819 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6822 * SUCCESS : new item index
6825 static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
6827 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6828 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6829 UINT uView = lStyle & LVS_TYPEMASK;
6833 LISTVIEW_ITEM *lpItem = NULL;
6835 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
6836 hwnd, debuglvitem_t(lpLVItem, isW), isW);
6838 if (lStyle & LVS_OWNERDATA)
6840 nItem = infoPtr->hdpaItems->nItemCount;
6841 infoPtr->hdpaItems->nItemCount ++;
6845 if (lpLVItem != NULL)
6847 /* make sure it's not a subitem; cannot insert a subitem */
6848 if (lpLVItem->iSubItem == 0)
6850 if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6852 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6853 if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
6855 /* insert item in listview control data structure */
6856 if ( (hdpaSubItems = DPA_Create(8)) )
6858 if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
6860 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6861 && !(lStyle & LVS_OWNERDRAWFIXED)
6862 && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
6864 /* Insert the item in the proper sort order based on the pszText
6865 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6866 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6867 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6868 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6869 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6873 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6880 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6882 /* manage item focus */
6883 if (lpLVItem->mask & LVIF_STATE)
6885 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6886 if (lpLVItem->stateMask & LVIS_SELECTED)
6887 LISTVIEW_SetSelection(hwnd, nItem);
6888 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6889 LISTVIEW_SetItemFocus(hwnd, nItem);
6892 /* send LVN_INSERTITEM notification */
6893 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6895 nmlv.lParam = lpItem->lParam;
6896 listview_notify(hwnd, LVN_INSERTITEM, &nmlv);
6898 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6900 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6901 if (nItemWidth > infoPtr->nItemWidth)
6902 infoPtr->nItemWidth = nItemWidth;
6905 /* align items (set position of each item) */
6906 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6908 if (lStyle & LVS_ALIGNLEFT)
6909 LISTVIEW_AlignLeft(hwnd);
6911 LISTVIEW_AlignTop(hwnd);
6914 LISTVIEW_UpdateScroll(hwnd);
6915 /* refresh client area */
6916 InvalidateRect(hwnd, NULL, FALSE);
6925 /* free memory if unsuccessful */
6926 if ((nItem == -1) && (lpItem != NULL))
6927 COMCTL32_Free(lpItem);
6934 * Redraws a range of items.
6937 * [I] HWND : window handle
6938 * [I] INT : first item
6939 * [I] INT : last item
6945 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6947 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6948 BOOL bResult = FALSE;
6952 if (nFirst <= nLast)
6954 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6956 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6958 for (i = nFirst; i <= nLast; i++)
6960 rcItem.left = LVIR_BOUNDS;
6961 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
6962 InvalidateRect(hwnd, &rcItem, TRUE);
6971 /* LISTVIEW_Scroll */
6975 * Sets the background color.
6978 * [I] HWND : window handle
6979 * [I] COLORREF : background color
6985 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6987 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6989 if(infoPtr->clrBk!=clrBk){
6990 infoPtr->clrBk = clrBk;
6991 InvalidateRect(hwnd, NULL, TRUE);
6997 /* LISTVIEW_SetBkImage */
7001 * Sets the callback mask. This mask will be used when the parent
7002 * window stores state information (some or all).
7005 * [I] HWND : window handle
7006 * [I] UINT : state mask
7012 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
7014 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7016 infoPtr->uCallbackMask = uMask;
7023 * Sets the attributes of a header item.
7026 * [I] HWND : window handle
7027 * [I] INT : column index
7028 * [I] LPLVCOLUMNW : column attributes
7029 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
7030 * otherwise it is in fact a LPLVCOLUMNA
7036 static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn,
7037 LPLVCOLUMNW lpColumn, BOOL isW)
7039 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7040 BOOL bResult = FALSE;
7041 HDITEMW hdi, hdiget;
7043 if ((lpColumn != NULL) && (nColumn >= 0) &&
7044 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
7046 /* initialize memory */
7047 ZeroMemory(&hdi, sizeof(hdi));
7049 if (lpColumn->mask & LVCF_FMT)
7051 /* format member is valid */
7052 hdi.mask |= HDI_FORMAT;
7054 /* get current format first */
7055 hdiget.mask = HDI_FORMAT;
7056 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7057 /* preserve HDF_STRING if present */
7058 hdi.fmt = hdiget.fmt & HDF_STRING;
7060 /* set text alignment (leftmost column must be left-aligned) */
7063 hdi.fmt |= HDF_LEFT;
7067 if (lpColumn->fmt & LVCFMT_LEFT)
7068 hdi.fmt |= HDF_LEFT;
7069 else if (lpColumn->fmt & LVCFMT_RIGHT)
7070 hdi.fmt |= HDF_RIGHT;
7071 else if (lpColumn->fmt & LVCFMT_CENTER)
7072 hdi.fmt |= HDF_CENTER;
7075 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7076 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
7078 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7079 hdi.fmt |= HDF_IMAGE;
7081 if (lpColumn->fmt & LVCFMT_IMAGE)
7083 hdi.fmt |= HDF_IMAGE;
7084 hdi.iImage = I_IMAGECALLBACK;
7088 if (lpColumn->mask & LVCF_WIDTH)
7090 hdi.mask |= HDI_WIDTH;
7091 hdi.cxy = lpColumn->cx;
7094 if (lpColumn->mask & LVCF_TEXT)
7096 hdi.mask |= HDI_TEXT | HDI_FORMAT;
7097 hdi.pszText = lpColumn->pszText;
7098 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
7099 hdi.fmt |= HDF_STRING;
7102 if (lpColumn->mask & LVCF_IMAGE)
7104 hdi.mask |= HDI_IMAGE;
7105 hdi.iImage = lpColumn->iImage;
7108 if (lpColumn->mask & LVCF_ORDER)
7110 hdi.mask |= HDI_ORDER;
7111 hdi.iOrder = lpColumn->iOrder;
7114 /* set header item attributes */
7115 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7123 * Sets the column order array
7126 * [I] HWND : window handle
7127 * [I] INT : number of elements in column order array
7128 * [I] INT : pointer to column order array
7134 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
7136 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
7138 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7149 * Sets the width of a column
7152 * [I] HWND : window handle
7153 * [I] INT : column index
7154 * [I] INT : column width
7160 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
7162 LISTVIEW_INFO *infoPtr;
7165 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7166 UINT uView = lStyle & LVS_TYPEMASK;
7171 WCHAR text_buffer[DISP_TEXT_SIZE];
7172 INT header_item_count;
7177 WCHAR szDispText[DISP_TEXT_SIZE];
7179 /* make sure we can get the listview info */
7180 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
7183 if (!infoPtr->hwndHeader) /* make sure we have a header */
7186 /* set column width only if in report or list mode */
7187 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7190 TRACE("(hwnd=%x, iCol=%d, cx=%d\n", hwnd, iCol, cx);
7192 /* take care of invalid cx values */
7193 if((uView == LVS_REPORT) && (cx < -2))
7194 cx = LVSCW_AUTOSIZE;
7195 else if (uView == LVS_LIST && (cx < 1))
7198 /* resize all columns if in LVS_LIST mode */
7199 if(uView == LVS_LIST) {
7200 infoPtr->nItemWidth = cx;
7201 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7205 /* autosize based on listview items width */
7206 if(cx == LVSCW_AUTOSIZE)
7208 /* set the width of the column to the width of the widest item */
7209 if (iCol == 0 || uView == LVS_LIST)
7212 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7214 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, item_index);
7215 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7217 if (infoPtr->himlSmall)
7218 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
7222 ZeroMemory(&lvItem, sizeof(lvItem));
7223 lvItem.iSubItem = iCol;
7224 lvItem.mask = LVIF_TEXT;
7225 lvItem.cchTextMax = DISP_TEXT_SIZE;
7226 lvItem.pszText = szDispText;
7227 *lvItem.pszText = '\0';
7229 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7231 lvItem.iItem = item_index;
7232 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7233 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7234 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7237 cx += TRAILING_PADDING;
7238 } /* autosize based on listview header width */
7239 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7241 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7243 /* if iCol is the last column make it fill the remainder of the controls width */
7244 if(iCol == (header_item_count - 1)) {
7245 /* get the width of every item except the current one */
7246 hdi.mask = HDI_WIDTH;
7249 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7250 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7254 /* retrieve the layout of the header */
7255 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7257 cx = (rcHeader.right - rcHeader.left) - cx;
7261 /* Despite what the MS docs say, if this is not the last
7262 column, then MS resizes the column to the width of the
7263 largest text string in the column, including headers
7264 and items. This is different from LVSCW_AUTOSIZE in that
7265 LVSCW_AUTOSIZE ignores the header string length.
7268 /* retrieve header font */
7269 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7271 /* retrieve header text */
7272 hdi.mask = HDI_TEXT;
7273 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7274 hdi.pszText = text_buffer;
7276 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7278 /* determine the width of the text in the header */
7280 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7282 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7284 SelectObject(hdc, old_font); /* restore the old font */
7285 ReleaseDC(hwnd, hdc);
7287 ZeroMemory(&lvItem, sizeof(lvItem));
7288 lvItem.iSubItem = iCol;
7289 lvItem.mask = LVIF_TEXT;
7290 lvItem.cchTextMax = DISP_TEXT_SIZE;
7291 lvItem.pszText = szDispText;
7292 *lvItem.pszText = '\0';
7294 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7296 lvItem.iItem = item_index;
7297 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7298 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7299 nLabelWidth += TRAILING_PADDING;
7300 /* While it is possible for subitems to have icons, even MS messes
7301 up the positioning, so I suspect no applications actually use
7303 if (item_index == 0 && infoPtr->himlSmall)
7304 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
7305 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7310 /* call header to update the column change */
7311 hdi.mask = HDI_WIDTH;
7314 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7316 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7323 * Sets the extended listview style.
7326 * [I] HWND : window handle
7331 * SUCCESS : previous style
7334 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
7336 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7337 DWORD dwOldStyle = infoPtr->dwExStyle;
7341 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7343 infoPtr->dwExStyle = dwStyle;
7348 /* LISTVIEW_SetHotCursor */
7352 * Sets the hot item index.
7355 * [I] HWND : window handle
7359 * SUCCESS : previous hot item index
7360 * FAILURE : -1 (no hot item)
7362 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7364 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7365 INT iOldIndex = infoPtr->nHotItem;
7368 infoPtr->nHotItem = iIndex;
7375 * Sets the amount of time the cursor must hover over an item before it is selected.
7378 * [I] HWND : window handle
7379 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7382 * Returns the previous hover time
7384 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7386 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7387 DWORD oldHoverTime = infoPtr->dwHoverTime;
7389 infoPtr->dwHoverTime = dwHoverTime;
7391 return oldHoverTime;
7396 * Sets spacing for icons of LVS_ICON style.
7399 * [I] HWND : window handle
7400 * [I] DWORD : MAKELONG(cx, cy)
7403 * MAKELONG(oldcx, oldcy)
7405 static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing)
7407 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7408 INT cy = HIWORD(spacing);
7409 INT cx = LOWORD(spacing);
7411 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7412 UINT uView = lStyle & LVS_TYPEMASK;
7414 oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7415 if (cx == -1) /* set to default */
7416 cx = GetSystemMetrics(SM_CXICONSPACING);
7417 if (cy == -1) /* set to default */
7418 cy = GetSystemMetrics(SM_CYICONSPACING);
7421 infoPtr->iconSpacing.cx = cx;
7423 { /* if 0 then compute width */
7424 if (uView == LVS_ICON)
7425 FIXME("width computation not yet done\n");
7427 * Should scan each item and determine max width of
7428 * icon or label, then make that the width
7430 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7431 infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd);
7434 infoPtr->iconSpacing.cy = cy;
7436 { /* if 0 then compute height */
7437 if (uView == LVS_ICON)
7438 infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + infoPtr->ntmHeight
7439 + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7440 /* FIXME. I don't think so; I think it is based on twice the ntmHeight */
7441 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7442 infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd);
7445 TRACE("old=(%d,%d), new=(%ld,%ld)\n", LOWORD(oldspacing), HIWORD(oldspacing),
7446 infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7448 /* these depend on the iconSpacing */
7449 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7450 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7460 * [I] HWND : window handle
7461 * [I] INT : image list type
7462 * [I] HIMAGELIST : image list handle
7465 * SUCCESS : old image list
7468 static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7470 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7471 HIMAGELIST himlOld = 0;
7477 himlOld = infoPtr->himlNormal;
7478 infoPtr->himlNormal = himl;
7482 himlOld = infoPtr->himlSmall;
7483 infoPtr->himlSmall = himl;
7487 himlOld = infoPtr->himlState;
7488 infoPtr->himlState = himl;
7489 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7493 oldHeight = infoPtr->nItemHeight;
7494 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7495 if (infoPtr->nItemHeight != oldHeight)
7496 LISTVIEW_UpdateScroll(hwnd);
7503 * Preallocates memory (does *not* set the actual count of items !)
7506 * [I] HWND : window handle
7507 * [I] INT : item count (projected number of items to allocate)
7508 * [I] DWORD : update flags
7514 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7516 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7518 TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
7520 if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7522 int precount,topvisible;
7524 TRACE("LVS_OWNERDATA is set!\n");
7525 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7526 FIXME("flags %s %s not implemented\n",
7527 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7529 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7532 * Internally remove all the selections.
7536 LISTVIEW_SELECTION *selection;
7537 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7539 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7542 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7544 precount = infoPtr->hdpaItems->nItemCount;
7545 topvisible = ListView_GetTopIndex(hwnd) +
7546 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7548 infoPtr->hdpaItems->nItemCount = nItems;
7550 LISTVIEW_UpdateSize(hwnd);
7551 LISTVIEW_UpdateScroll(hwnd);
7552 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7553 InvalidateRect(hwnd, NULL, TRUE);
7557 /* According to MSDN for non-LVS_OWNERDATA this is just
7558 * a performance issue. The control allocates its internal
7559 * data structures for the number of items specified. It
7560 * cuts down on the number of memory allocations. Therefore
7561 * we will just issue a WARN here
7563 WARN("for non-ownerdata performance option not implemented.\n");
7571 * Sets the position of an item.
7574 * [I] HWND : window handle
7575 * [I] INT : item index
7576 * [I] LONG : x coordinate
7577 * [I] LONG : y coordinate
7583 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7584 LONG nPosX, LONG nPosY)
7586 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7587 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7588 UINT uView = lStyle & LVS_TYPEMASK;
7589 LISTVIEW_ITEM *lpItem;
7591 BOOL bResult = FALSE;
7593 TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7595 if (lStyle & LVS_OWNERDATA)
7598 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7600 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7602 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7604 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7608 orig = lpItem->ptPosition;
7609 if ((nPosX == -1) && (nPosY == -1))
7611 /* This point value seems to be an undocumented feature. The
7612 * best guess is that it means either at the origin, or at
7613 * the true beginning of the list. I will assume the origin.
7616 if (!LISTVIEW_GetOrigin(hwnd, &pt1))
7623 if (uView == LVS_ICON)
7625 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7626 nPosY += ICON_TOP_PADDING;
7628 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7632 lpItem->ptPosition.x = nPosX;
7633 lpItem->ptPosition.y = nPosY;
7634 if (uView == LVS_ICON)
7636 lpItem->ptPosition.y -= ICON_TOP_PADDING;
7637 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7638 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7640 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7641 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7644 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7645 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7650 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7651 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7664 * Sets the state of one or many items.
7667 * [I] HWND : window handle
7668 * [I]INT : item index
7669 * [I] LPLVITEM : item or subitem info
7675 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem)
7677 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7678 BOOL bResult = TRUE;
7681 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
7682 hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
7684 ZeroMemory(&lvItem, sizeof(lvItem));
7685 lvItem.mask = LVIF_STATE;
7686 lvItem.state = lpLVItem->state;
7687 lvItem.stateMask = lpLVItem->stateMask ;
7688 lvItem.iItem = nItem;
7692 /* apply to all items */
7693 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7694 if (!ListView_SetItemW(hwnd, &lvItem)) bResult = FALSE;
7697 bResult = ListView_SetItemW(hwnd, &lvItem);
7704 * Sets the text of an item or subitem.
7707 * [I] hwnd : window handle
7708 * [I] nItem : item index
7709 * [I] lpLVItem : item or subitem info
7710 * [I] isW : TRUE if input is Unicode
7716 static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7718 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7719 BOOL bResult = FALSE;
7722 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
7723 hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
7725 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7727 ZeroMemory(&lvItem, sizeof(LVITEMW));
7728 lvItem.mask = LVIF_TEXT;
7729 lvItem.pszText = lpLVItem->pszText;
7730 lvItem.iItem = nItem;
7731 lvItem.iSubItem = lpLVItem->iSubItem;
7732 if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
7733 else bResult = ListView_SetItemA(hwnd, &lvItem);
7741 * Set item index that marks the start of a multiple selection.
7744 * [I] HWND : window handle
7748 * Index number or -1 if there is no selection mark.
7750 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7752 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7753 INT nOldIndex = infoPtr->nSelectionMark;
7755 TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
7757 infoPtr->nSelectionMark = nIndex;
7764 * Sets the text background color.
7767 * [I] HWND : window handle
7768 * [I] COLORREF : text background color
7774 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7776 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7778 TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
7780 infoPtr->clrTextBk = clrTextBk;
7781 InvalidateRect(hwnd, NULL, TRUE);
7788 * Sets the text foreground color.
7791 * [I] HWND : window handle
7792 * [I] COLORREF : text color
7798 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7800 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7802 TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
7804 infoPtr->clrText = clrText;
7805 InvalidateRect(hwnd, NULL, TRUE);
7810 /* LISTVIEW_SetToolTips */
7811 /* LISTVIEW_SetUnicodeFormat */
7812 /* LISTVIEW_SetWorkAreas */
7816 * Callback internally used by LISTVIEW_SortItems()
7819 * [I] LPVOID : first LISTVIEW_ITEM to compare
7820 * [I] LPVOID : second LISTVIEW_ITEM to compare
7821 * [I] LPARAM : HWND of control
7824 * if first comes before second : negative
7825 * if first comes after second : positive
7826 * if first and second are equivalent : zero
7828 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7830 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7831 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
7832 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
7834 /* Forward the call to the client defined callback */
7835 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7840 * Sorts the listview items.
7843 * [I] HWND : window handle
7844 * [I] WPARAM : application-defined value
7845 * [I] LPARAM : pointer to comparision callback
7851 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7853 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7854 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7855 HDPA hdpaSubItems=NULL;
7856 LISTVIEW_ITEM *pLVItem=NULL;
7857 LPVOID selectionMarkItem;
7860 TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
7862 if (lStyle & LVS_OWNERDATA) return FALSE;
7864 if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
7866 nCount = GETITEMCOUNT(infoPtr);
7867 /* if there are 0 or 1 items, there is no need to sort */
7871 infoPtr->pfnCompare = pfnCompare;
7872 infoPtr->lParamSort = lParamSort;
7873 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7875 /* Adjust selections and indices so that they are the way they should
7876 * be after the sort (otherwise, the list items move around, but
7877 * whatever is at the item's previous original position will be
7880 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7881 for (i=0; i < nCount; i++)
7883 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7884 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7886 if (pLVItem->state & LVIS_SELECTED)
7887 LISTVIEW_AddSelectionRange(hwnd, i, i);
7889 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7890 if (pLVItem->state & LVIS_FOCUSED)
7891 infoPtr->nFocusedItem=i;
7893 if (selectionMarkItem != NULL)
7894 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7895 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7897 /* align the items */
7898 LISTVIEW_AlignTop(hwnd);
7900 /* refresh the display */
7901 InvalidateRect(hwnd, NULL, TRUE);
7906 /* LISTVIEW_SubItemHitTest */
7910 * Updates an items or rearranges the listview control.
7913 * [I] HWND : window handle
7914 * [I] INT : item index
7920 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7922 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7923 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7924 BOOL bResult = FALSE;
7927 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
7929 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7933 /* rearrange with default alignment style */
7934 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7935 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7937 ListView_Arrange(hwnd, 0);
7941 /* get item bounding rectangle */
7942 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
7943 InvalidateRect(hwnd, &rc, TRUE);
7952 * Creates the listview control.
7955 * [I] HWND : window handle
7960 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7962 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7963 UINT uView = lpcs->style & LVS_TYPEMASK;
7966 TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
7968 /* initialize info pointer */
7969 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7971 /* determine the type of structures to use */
7972 infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT,
7973 (WPARAM)hwnd, (LPARAM)NF_QUERY);
7975 /* initialize color information */
7976 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7977 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7978 infoPtr->clrTextBk = CLR_DEFAULT;
7980 /* set default values */
7981 infoPtr->hwndSelf = hwnd;
7982 infoPtr->uCallbackMask = 0;
7983 infoPtr->nFocusedItem = -1;
7984 infoPtr->nSelectionMark = -1;
7985 infoPtr->nHotItem = -1;
7986 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7987 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7988 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7989 infoPtr->hwndEdit = 0;
7990 infoPtr->pedititem = NULL;
7991 infoPtr->nEditLabelItem = -1;
7993 /* get default font (icon title) */
7994 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7995 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7996 infoPtr->hFont = infoPtr->hDefaultFont;
7997 LISTVIEW_SaveTextMetrics(hwnd);
8000 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
8001 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
8002 0, 0, 0, 0, hwnd, (HMENU)0,
8003 lpcs->hInstance, NULL);
8005 /* set header font */
8006 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
8009 if (uView == LVS_ICON)
8011 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8012 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8014 else if (uView == LVS_REPORT)
8016 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8018 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8022 /* set HDS_HIDDEN flag to hide the header bar */
8023 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
8024 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
8028 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8029 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8033 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8034 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8037 /* display unsupported listview window styles */
8038 LISTVIEW_UnsupportedStyles(lpcs->style);
8040 /* allocate memory for the data structure */
8041 infoPtr->hdpaItems = DPA_Create(10);
8043 /* allocate memory for the selection ranges */
8044 infoPtr->hdpaSelectionRanges = DPA_Create(10);
8046 /* initialize size of items */
8047 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8048 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8050 /* initialize the hover time to -1(indicating the default system hover time) */
8051 infoPtr->dwHoverTime = -1;
8058 * Erases the background of the listview control.
8061 * [I] HWND : window handle
8062 * [I] WPARAM : device context handle
8063 * [I] LPARAM : not used
8069 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
8072 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8075 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8077 if (infoPtr->clrBk == CLR_NONE)
8079 bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
8084 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8085 GetClientRect(hwnd, &rc);
8086 FillRect((HDC)wParam, &rc, hBrush);
8087 DeleteObject(hBrush);
8095 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
8097 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8099 TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
8101 if (infoPtr->clrBk != CLR_NONE)
8103 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8104 FillRect(hdc, rc, hBrush);
8105 DeleteObject(hBrush);
8111 * Retrieves the listview control font.
8114 * [I] HWND : window handle
8119 static LRESULT LISTVIEW_GetFont(HWND hwnd)
8121 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8123 TRACE("(hwnd=%x)\n", hwnd);
8125 return infoPtr->hFont;
8130 * Performs vertical scrolling.
8133 * [I] HWND : window handle
8134 * [I] INT : scroll code
8135 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8137 * [I] HWND : scrollbar control window handle
8142 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8145 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8146 SCROLLINFO scrollInfo;
8148 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8149 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8151 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8153 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8154 scrollInfo.cbSize = sizeof(SCROLLINFO);
8155 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8157 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8159 INT nOldScrollPos = scrollInfo.nPos;
8160 switch (nScrollCode)
8163 if (scrollInfo.nPos > scrollInfo.nMin)
8168 if (scrollInfo.nPos < scrollInfo.nMax)
8173 if (scrollInfo.nPos > scrollInfo.nMin)
8175 if (scrollInfo.nPos >= scrollInfo.nPage)
8176 scrollInfo.nPos -= scrollInfo.nPage;
8178 scrollInfo.nPos = scrollInfo.nMin;
8183 if (scrollInfo.nPos < scrollInfo.nMax)
8185 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8186 scrollInfo.nPos += scrollInfo.nPage;
8188 scrollInfo.nPos = scrollInfo.nMax;
8192 case SB_THUMBPOSITION:
8194 scrollInfo.nPos = nCurrentPos;
8195 if (scrollInfo.nPos > scrollInfo.nMax)
8196 scrollInfo.nPos=scrollInfo.nMax;
8198 if (scrollInfo.nPos < scrollInfo.nMin)
8199 scrollInfo.nPos=scrollInfo.nMin;
8204 if (nOldScrollPos != scrollInfo.nPos)
8206 scrollInfo.fMask = SIF_POS;
8207 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
8208 if (IsWindowVisible(infoPtr->hwndHeader))
8210 RECT rListview, rcHeader, rDest;
8211 GetClientRect(hwnd, &rListview);
8212 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
8213 MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2);
8214 SubtractRect(&rDest, &rListview, &rcHeader);
8215 InvalidateRect(hwnd, &rDest, TRUE);
8218 InvalidateRect(hwnd, NULL, TRUE);
8227 * Performs horizontal scrolling.
8230 * [I] HWND : window handle
8231 * [I] INT : scroll code
8232 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8234 * [I] HWND : scrollbar control window handle
8239 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8242 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8243 SCROLLINFO scrollInfo;
8245 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8246 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8248 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8250 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8251 scrollInfo.cbSize = sizeof(SCROLLINFO);
8252 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8254 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
8256 INT nOldScrollPos = scrollInfo.nPos;
8258 switch (nScrollCode)
8261 if (scrollInfo.nPos > scrollInfo.nMin)
8266 if (scrollInfo.nPos < scrollInfo.nMax)
8271 if (scrollInfo.nPos > scrollInfo.nMin)
8273 if (scrollInfo.nPos >= scrollInfo.nPage)
8274 scrollInfo.nPos -= scrollInfo.nPage;
8276 scrollInfo.nPos = scrollInfo.nMin;
8281 if (scrollInfo.nPos < scrollInfo.nMax)
8283 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8284 scrollInfo.nPos += scrollInfo.nPage;
8286 scrollInfo.nPos = scrollInfo.nMax;
8290 case SB_THUMBPOSITION:
8292 scrollInfo.nPos = nCurrentPos;
8294 if (scrollInfo.nPos > scrollInfo.nMax)
8295 scrollInfo.nPos=scrollInfo.nMax;
8297 if (scrollInfo.nPos < scrollInfo.nMin)
8298 scrollInfo.nPos=scrollInfo.nMin;
8302 if (nOldScrollPos != scrollInfo.nPos)
8304 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8305 scrollInfo.fMask = SIF_POS;
8306 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
8307 if(uView == LVS_REPORT)
8309 scrollInfo.fMask = SIF_POS;
8310 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
8311 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
8313 InvalidateRect(hwnd, NULL, TRUE);
8320 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
8322 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8323 INT gcWheelDelta = 0;
8324 UINT pulScrollLines = 3;
8325 SCROLLINFO scrollInfo;
8327 TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
8329 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8330 gcWheelDelta -= wheelDelta;
8332 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8333 scrollInfo.cbSize = sizeof(SCROLLINFO);
8334 scrollInfo.fMask = SIF_POS | SIF_RANGE;
8341 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8342 * should be fixed in the future.
8344 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8345 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
8349 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8351 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8353 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
8354 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8355 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8361 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8372 * [I] HWND : window handle
8373 * [I] INT : virtual key
8374 * [I] LONG : key data
8379 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
8381 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8382 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8384 NMLVKEYDOWN nmKeyDown;
8386 TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
8388 /* send LVN_KEYDOWN notification */
8389 nmKeyDown.wVKey = nVirtualKey;
8390 nmKeyDown.flags = 0;
8391 notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
8393 switch (nVirtualKey)
8396 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8398 hdr_notify(hwnd, NM_RETURN); /* NM_RETURN notification */
8399 hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
8404 if (GETITEMCOUNT(infoPtr) > 0)
8409 if (GETITEMCOUNT(infoPtr) > 0)
8410 nItem = GETITEMCOUNT(infoPtr) - 1;
8414 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8418 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8422 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8426 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8430 if (uView == LVS_REPORT)
8431 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8433 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8434 * LISTVIEW_GetCountPerRow(hwnd);
8435 if(nItem < 0) nItem = 0;
8439 if (uView == LVS_REPORT)
8440 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8442 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8443 * LISTVIEW_GetCountPerRow(hwnd);
8444 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8448 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8450 if (LISTVIEW_KeySelection(hwnd, nItem))
8451 UpdateWindow(hwnd); /* update client area */
8462 * [I] HWND : window handle
8467 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8469 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
8470 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8473 TRACE("(hwnd=%x)\n", hwnd);
8475 /* send NM_KILLFOCUS notification */
8476 hdr_notify(hwnd, NM_KILLFOCUS);
8478 /* set window focus flag */
8479 infoPtr->bFocus = FALSE;
8481 /* NEED drawing optimization ; redraw the selected items */
8482 if (uView & LVS_REPORT)
8484 nTop = LISTVIEW_GetTopIndex(hwnd);
8486 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8491 nBottom = GETITEMCOUNT(infoPtr);
8493 for (i = nTop; i<nBottom; i++)
8495 if (LISTVIEW_IsSelected(hwnd,i))
8498 rcItem.left = LVIR_BOUNDS;
8499 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8500 InvalidateRect(hwnd, &rcItem, FALSE);
8509 * Processes double click messages (left mouse button).
8512 * [I] HWND : window handle
8513 * [I] WORD : key flag
8514 * [I] WORD : x coordinate
8515 * [I] WORD : y coordinate
8520 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8523 LVHITTESTINFO htInfo;
8526 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8528 htInfo.pt.x = wPosX;
8529 htInfo.pt.y = wPosY;
8531 /* send NM_DBLCLK notification */
8532 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8533 if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -1)
8535 nmlv.iItem = htInfo.iItem;
8536 nmlv.iSubItem = htInfo.iSubItem;
8543 nmlv.ptAction.x = wPosX;
8544 nmlv.ptAction.y = wPosY;
8545 listview_notify(hwnd, NM_DBLCLK, &nmlv);
8548 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8549 if(nmlv.iItem != -1)
8550 hdr_notify(hwnd, LVN_ITEMACTIVATE);
8557 * Processes mouse down messages (left mouse button).
8560 * [I] HWND : window handle
8561 * [I] WORD : key flag
8562 * [I] WORD : x coordinate
8563 * [I] WORD : y coordinate
8568 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8571 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8572 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8573 static BOOL bGroupSelect = TRUE;
8577 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8579 /* send NM_RELEASEDCAPTURE notification */
8580 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8582 if (infoPtr->bFocus == FALSE)
8585 /* set left button down flag */
8586 infoPtr->bLButtonDown = TRUE;
8588 ptPosition.x = wPosX;
8589 ptPosition.y = wPosY;
8590 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8591 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8593 if (lStyle & LVS_SINGLESEL)
8595 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8596 && infoPtr->nEditLabelItem == -1)
8597 infoPtr->nEditLabelItem = nItem;
8599 LISTVIEW_SetSelection(hwnd, nItem);
8603 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8606 LISTVIEW_AddGroupSelection(hwnd, nItem);
8608 LISTVIEW_AddSelection(hwnd, nItem);
8610 else if (wKey & MK_CONTROL)
8612 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8614 else if (wKey & MK_SHIFT)
8616 LISTVIEW_SetGroupSelection(hwnd, nItem);
8621 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8623 /* set selection (clears other pre-existing selections) */
8624 LISTVIEW_SetSelection(hwnd, nItem);
8626 if (was_selected && infoPtr->nEditLabelItem == -1)
8627 infoPtr->nEditLabelItem = nItem;
8633 /* remove all selections */
8634 LISTVIEW_RemoveAllSelections(hwnd);
8637 /* redraw if we could have possibly selected something */
8638 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8645 * Processes mouse up messages (left mouse button).
8648 * [I] HWND : window handle
8649 * [I] WORD : key flag
8650 * [I] WORD : x coordinate
8651 * [I] WORD : y coordinate
8656 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8659 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8661 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8663 if (infoPtr->bLButtonDown != FALSE)
8665 LVHITTESTINFO lvHitTestInfo;
8668 lvHitTestInfo.pt.x = wPosX;
8669 lvHitTestInfo.pt.y = wPosY;
8671 /* send NM_CLICK notification */
8672 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8673 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8675 nmlv.iItem = lvHitTestInfo.iItem;
8676 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8683 nmlv.ptAction.x = wPosX;
8684 nmlv.ptAction.y = wPosY;
8685 listview_notify(hwnd, NM_CLICK, &nmlv);
8687 /* set left button flag */
8688 infoPtr->bLButtonDown = FALSE;
8690 if(infoPtr->nEditLabelItem != -1)
8692 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL)
8693 LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
8694 infoPtr->nEditLabelItem = -1;
8703 * Creates the listview control (called before WM_CREATE).
8706 * [I] HWND : window handle
8707 * [I] WPARAM : unhandled
8708 * [I] LPARAM : widow creation info
8713 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8715 LISTVIEW_INFO *infoPtr;
8717 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8719 /* allocate memory for info structure */
8720 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8721 if (infoPtr == NULL)
8723 ERR("could not allocate info memory!\n");
8727 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
8728 if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr)
8730 ERR("pointer assignment error!\n");
8734 return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam);
8739 * Destroys the listview control (called after WM_DESTROY).
8742 * [I] HWND : window handle
8747 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8749 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8751 TRACE("(hwnd=%x)\n", hwnd);
8753 /* delete all items */
8754 LISTVIEW_DeleteAllItems(hwnd);
8756 /* destroy data structure */
8757 DPA_Destroy(infoPtr->hdpaItems);
8758 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8761 infoPtr->hFont = (HFONT)0;
8762 if (infoPtr->hDefaultFont)
8764 DeleteObject(infoPtr->hDefaultFont);
8767 /* free listview info pointer*/
8768 COMCTL32_Free(infoPtr);
8770 SetWindowLongW(hwnd, 0, 0);
8776 * Handles notifications from children.
8779 * [I] HWND : window handle
8780 * [I] INT : control identifier
8781 * [I] LPNMHDR : notification information
8786 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8788 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8790 TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
8792 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8794 /* handle notification from header control */
8795 if (lpnmh->code == HDN_ENDTRACKW)
8797 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8798 InvalidateRect(hwnd, NULL, TRUE);
8800 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
8802 /* Handle sorting by Header Column */
8805 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8807 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
8808 listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv);
8810 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8812 /* Idealy this should be done in HDN_ENDTRACKA
8813 * but since SetItemBounds in Header.c is called after
8814 * the notification is sent, it is neccessary to handle the
8815 * update of the scroll bar here (Header.c works fine as it is,
8816 * no need to disturb it)
8818 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8819 LISTVIEW_UpdateScroll(hwnd);
8820 InvalidateRect(hwnd, NULL, TRUE);
8830 * Determines the type of structure to use.
8833 * [I] HWND : window handle of the sender
8834 * [I] HWND : listview window handle
8835 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8840 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8842 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8844 TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
8846 if (nCommand == NF_REQUERY)
8847 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
8848 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8854 * Paints/Repaints the listview control.
8857 * [I] HWND : window handle
8858 * [I] HDC : device context handle
8863 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8867 TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc);
8871 hdc = BeginPaint(hwnd, &ps);
8872 LISTVIEW_Refresh(hwnd, hdc);
8873 EndPaint(hwnd, &ps);
8877 LISTVIEW_Refresh(hwnd, hdc);
8885 * Processes double click messages (right mouse button).
8888 * [I] HWND : window handle
8889 * [I] WORD : key flag
8890 * [I] WORD : x coordinate
8891 * [I] WORD : y coordinate
8896 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8899 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8901 /* send NM_RELEASEDCAPTURE notification */
8902 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8904 /* send NM_RDBLCLK notification */
8905 hdr_notify(hwnd, NM_RDBLCLK);
8912 * Processes mouse down messages (right mouse button).
8915 * [I] HWND : window handle
8916 * [I] WORD : key flag
8917 * [I] WORD : x coordinate
8918 * [I] WORD : y coordinate
8923 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8926 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8930 LVHITTESTINFO lvHitTestInfo;
8932 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8934 /* send NM_RELEASEDCAPTURE notification */
8935 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8937 /* make sure the listview control window has the focus */
8938 if (infoPtr->bFocus == FALSE)
8941 /* set right button down flag */
8942 infoPtr->bRButtonDown = TRUE;
8944 /* determine the index of the selected item */
8945 ptPosition.x = wPosX;
8946 ptPosition.y = wPosY;
8947 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8948 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8950 LISTVIEW_SetItemFocus(hwnd,nItem);
8951 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8952 !LISTVIEW_IsSelected(hwnd,nItem))
8953 LISTVIEW_SetSelection(hwnd, nItem);
8957 LISTVIEW_RemoveAllSelections(hwnd);
8960 lvHitTestInfo.pt.x = wPosX;
8961 lvHitTestInfo.pt.y = wPosY;
8963 /* Send NM_RClICK notification */
8964 ZeroMemory(&nmlv, sizeof(nmlv));
8965 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8967 nmlv.iItem = lvHitTestInfo.iItem;
8968 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8975 nmlv.ptAction.x = wPosX;
8976 nmlv.ptAction.y = wPosY;
8977 listview_notify(hwnd, NM_RCLICK, &nmlv);
8984 * Processes mouse up messages (right mouse button).
8987 * [I] HWND : window handle
8988 * [I] WORD : key flag
8989 * [I] WORD : x coordinate
8990 * [I] WORD : y coordinate
8995 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8998 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9000 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9002 if (infoPtr->bRButtonDown)
9009 /* set button flag */
9010 infoPtr->bRButtonDown = FALSE;
9012 /* Change to screen coordinate for WM_CONTEXTMENU */
9013 ClientToScreen(hwnd, &pt);
9015 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9016 SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
9027 * [I] HWND : window handle
9028 * [I] HWND : window handle of previously focused window
9033 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
9035 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9037 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
9039 /* send NM_SETFOCUS notification */
9040 hdr_notify(hwnd, NM_SETFOCUS);
9042 /* set window focus flag */
9043 infoPtr->bFocus = TRUE;
9055 * [I] HWND : window handle
9056 * [I] HFONT : font handle
9057 * [I] WORD : redraw flag
9062 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
9064 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9065 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
9067 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
9069 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9070 LISTVIEW_SaveTextMetrics(hwnd);
9072 if (uView == LVS_REPORT)
9074 /* set header font */
9075 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
9076 MAKELPARAM(fRedraw, 0));
9079 /* invalidate listview control client area */
9080 InvalidateRect(hwnd, NULL, TRUE);
9082 if (fRedraw != FALSE)
9090 * Message handling for WM_SETREDRAW.
9091 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9094 * [I] HWND : window handle
9095 * [I] bRedraw: state of redraw flag
9098 * DefWinProc return value
9100 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
9102 LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0);
9104 RedrawWindow(hwnd, NULL, 0,
9105 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
9111 * Resizes the listview control. This function processes WM_SIZE
9112 * messages. At this time, the width and height are not used.
9115 * [I] HWND : window handle
9116 * [I] WORD : new width
9117 * [I] WORD : new height
9122 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
9124 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9125 UINT uView = lStyle & LVS_TYPEMASK;
9127 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
9129 LISTVIEW_UpdateSize(hwnd);
9131 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
9133 if (lStyle & LVS_ALIGNLEFT)
9134 LISTVIEW_AlignLeft(hwnd);
9136 LISTVIEW_AlignTop(hwnd);
9139 LISTVIEW_UpdateScroll(hwnd);
9141 /* invalidate client area + erase background */
9142 InvalidateRect(hwnd, NULL, TRUE);
9149 * Sets the size information.
9152 * [I] HWND : window handle
9157 static VOID LISTVIEW_UpdateSize(HWND hwnd)
9159 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9160 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9161 UINT uView = lStyle & LVS_TYPEMASK;
9164 TRACE("(hwnd=%x)\n", hwnd);
9166 GetClientRect(hwnd, &rcList);
9167 infoPtr->rcList.left = 0;
9168 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
9169 infoPtr->rcList.top = 0;
9170 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
9172 if (uView == LVS_LIST)
9174 if (lStyle & WS_HSCROLL)
9176 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
9177 if (infoPtr->rcList.bottom > nHScrollHeight)
9178 infoPtr->rcList.bottom -= nHScrollHeight;
9181 else if (uView == LVS_REPORT)
9188 Header_Layout(infoPtr->hwndHeader, &hl);
9190 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9192 if (!(LVS_NOCOLUMNHEADER & lStyle))
9193 infoPtr->rcList.top = max(wp.cy, 0);
9199 * Processes WM_STYLECHANGED messages.
9202 * [I] HWND : window handle
9203 * [I] WPARAM : window style type (normal or extended)
9204 * [I] LPSTYLESTRUCT : window style information
9209 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
9212 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9213 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9214 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9215 RECT rcList = infoPtr->rcList;
9217 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
9218 hwnd, wStyleType, lpss);
9220 if (wStyleType == GWL_STYLE)
9222 if (uOldView == LVS_REPORT)
9223 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9225 if ((lpss->styleOld & WS_HSCROLL) != 0)
9226 ShowScrollBar(hwnd, SB_HORZ, FALSE);
9228 if ((lpss->styleOld & WS_VSCROLL) != 0)
9229 ShowScrollBar(hwnd, SB_VERT, FALSE);
9231 if (uNewView == LVS_ICON)
9233 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9234 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9235 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9236 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9237 if (lpss->styleNew & LVS_ALIGNLEFT)
9238 LISTVIEW_AlignLeft(hwnd);
9240 LISTVIEW_AlignTop(hwnd);
9242 else if (uNewView == LVS_REPORT)
9249 Header_Layout(infoPtr->hwndHeader, &hl);
9250 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
9252 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9253 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9255 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9256 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9257 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9258 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9260 else if (uNewView == LVS_LIST)
9262 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9263 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9264 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9265 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9269 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9270 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9271 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9272 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9273 if (lpss->styleNew & LVS_ALIGNLEFT)
9274 LISTVIEW_AlignLeft(hwnd);
9276 LISTVIEW_AlignTop(hwnd);
9279 /* update the size of the client area */
9280 LISTVIEW_UpdateSize(hwnd);
9282 /* add scrollbars if needed */
9283 LISTVIEW_UpdateScroll(hwnd);
9285 /* invalidate client area + erase background */
9286 InvalidateRect(hwnd, NULL, TRUE);
9288 /* print the list of unsupported window styles */
9289 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9292 /* If they change the view and we have an active edit control
9293 we will need to kill the control since the redraw will
9294 misplace the edit control.
9296 if (infoPtr->hwndEdit &&
9297 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9298 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9300 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9308 * Window procedure of the listview control.
9311 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9314 TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
9315 if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
9316 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9319 case LVM_APPROXIMATEVIEWRECT:
9320 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9321 LOWORD(lParam), HIWORD(lParam));
9323 return LISTVIEW_Arrange(hwnd, (INT)wParam);
9325 /* case LVM_CREATEDRAGIMAGE: */
9327 case LVM_DELETEALLITEMS:
9328 return LISTVIEW_DeleteAllItems(hwnd);
9330 case LVM_DELETECOLUMN:
9331 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9333 case LVM_DELETEITEM:
9334 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9336 case LVM_EDITLABELW:
9337 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
9339 case LVM_EDITLABELA:
9340 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
9342 case LVM_ENSUREVISIBLE:
9343 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9346 return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
9349 return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
9351 case LVM_GETBKCOLOR:
9352 return LISTVIEW_GetBkColor(hwnd);
9354 /* case LVM_GETBKIMAGE: */
9356 case LVM_GETCALLBACKMASK:
9357 return LISTVIEW_GetCallbackMask(hwnd);
9359 case LVM_GETCOLUMNA:
9360 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9362 case LVM_GETCOLUMNW:
9363 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9365 case LVM_GETCOLUMNORDERARRAY:
9366 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9368 case LVM_GETCOLUMNWIDTH:
9369 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9371 case LVM_GETCOUNTPERPAGE:
9372 return LISTVIEW_GetCountPerPage(hwnd);
9374 case LVM_GETEDITCONTROL:
9375 return LISTVIEW_GetEditControl(hwnd);
9377 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9378 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9381 return LISTVIEW_GetHeader(hwnd);
9383 case LVM_GETHOTCURSOR:
9384 FIXME("LVM_GETHOTCURSOR: unimplemented\n");
9387 case LVM_GETHOTITEM:
9388 return LISTVIEW_GetHotItem(hwnd);
9390 case LVM_GETHOVERTIME:
9391 return LISTVIEW_GetHoverTime(hwnd);
9393 case LVM_GETIMAGELIST:
9394 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9396 case LVM_GETISEARCHSTRINGA:
9397 case LVM_GETISEARCHSTRINGW:
9398 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9402 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
9405 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
9407 case LVM_GETITEMCOUNT:
9408 return LISTVIEW_GetItemCount(hwnd);
9410 case LVM_GETITEMPOSITION:
9411 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9413 case LVM_GETITEMRECT:
9414 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9416 case LVM_GETITEMSPACING:
9417 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9419 case LVM_GETITEMSTATE:
9420 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9422 case LVM_GETITEMTEXTA:
9423 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9425 case LVM_GETITEMTEXTW:
9426 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9428 case LVM_GETNEXTITEM:
9429 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9431 case LVM_GETNUMBEROFWORKAREAS:
9432 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9436 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9438 case LVM_GETSELECTEDCOUNT:
9439 return LISTVIEW_GetSelectedCount(hwnd);
9441 case LVM_GETSELECTIONMARK:
9442 return LISTVIEW_GetSelectionMark(hwnd);
9444 case LVM_GETSTRINGWIDTHA:
9445 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
9447 case LVM_GETSTRINGWIDTHW:
9448 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
9450 case LVM_GETSUBITEMRECT:
9451 FIXME("LVM_GETSUBITEMRECT: unimplemented\n");
9454 case LVM_GETTEXTBKCOLOR:
9455 return LISTVIEW_GetTextBkColor(hwnd);
9457 case LVM_GETTEXTCOLOR:
9458 return LISTVIEW_GetTextColor(hwnd);
9460 case LVM_GETTOOLTIPS:
9461 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9464 case LVM_GETTOPINDEX:
9465 return LISTVIEW_GetTopIndex(hwnd);
9467 /*case LVM_GETUNICODEFORMAT:
9468 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9471 case LVM_GETVIEWRECT:
9472 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9474 case LVM_GETWORKAREAS:
9475 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9479 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9481 case LVM_INSERTCOLUMNA:
9482 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9484 case LVM_INSERTCOLUMNW:
9485 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9487 case LVM_INSERTITEMA:
9488 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9490 case LVM_INSERTITEMW:
9491 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9493 case LVM_REDRAWITEMS:
9494 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9496 /* case LVM_SCROLL: */
9497 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9499 case LVM_SETBKCOLOR:
9500 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9502 /* case LVM_SETBKIMAGE: */
9504 case LVM_SETCALLBACKMASK:
9505 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9507 case LVM_SETCOLUMNA:
9508 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9510 case LVM_SETCOLUMNW:
9511 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9513 case LVM_SETCOLUMNORDERARRAY:
9514 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9516 case LVM_SETCOLUMNWIDTH:
9517 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9519 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9520 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9522 /* case LVM_SETHOTCURSOR: */
9524 case LVM_SETHOTITEM:
9525 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9527 case LVM_SETHOVERTIME:
9528 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9530 case LVM_SETICONSPACING:
9531 return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam);
9533 case LVM_SETIMAGELIST:
9534 return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9537 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9540 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9542 case LVM_SETITEMCOUNT:
9543 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9545 case LVM_SETITEMPOSITION:
9546 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9547 (INT)HIWORD(lParam));
9549 case LVM_SETITEMPOSITION32:
9550 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9551 ((POINT*)lParam)->y);
9553 case LVM_SETITEMSTATE:
9554 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam);
9556 case LVM_SETITEMTEXTA:
9557 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9559 case LVM_SETITEMTEXTW:
9560 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9562 case LVM_SETSELECTIONMARK:
9563 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9565 case LVM_SETTEXTBKCOLOR:
9566 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9568 case LVM_SETTEXTCOLOR:
9569 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9571 /* case LVM_SETTOOLTIPS: */
9572 /* case LVM_SETUNICODEFORMAT: */
9573 /* case LVM_SETWORKAREAS: */
9576 return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9578 /* case LVM_SUBITEMHITTEST: */
9581 return LISTVIEW_Update(hwnd, (INT)wParam);
9584 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9587 return LISTVIEW_Command(hwnd, wParam, lParam);
9590 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9593 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9596 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9599 return LISTVIEW_GetFont(hwnd);
9602 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
9603 (INT)HIWORD(wParam), (HWND)lParam);
9606 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9609 return LISTVIEW_KillFocus(hwnd);
9611 case WM_LBUTTONDBLCLK:
9612 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9615 case WM_LBUTTONDOWN:
9616 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9619 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9622 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9625 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9628 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9631 return LISTVIEW_NCDestroy(hwnd);
9634 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9636 case WM_NOTIFYFORMAT:
9637 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9640 return LISTVIEW_Paint(hwnd, (HDC)wParam);
9642 case WM_RBUTTONDBLCLK:
9643 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9646 case WM_RBUTTONDOWN:
9647 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9651 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9655 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9658 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9661 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9664 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9666 case WM_STYLECHANGED:
9667 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9669 /* case WM_TIMER: */
9672 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
9673 (INT)HIWORD(wParam), (HWND)lParam);
9676 if (wParam & (MK_SHIFT | MK_CONTROL))
9677 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9678 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
9680 /* case WM_WININICHANGE: */
9683 if (uMsg >= WM_USER)
9685 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
9689 /* call default window procedure */
9690 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9698 * Registers the window class.
9706 VOID LISTVIEW_Register(void)
9710 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9711 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9712 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9713 wndClass.cbClsExtra = 0;
9714 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9715 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
9716 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9717 wndClass.lpszClassName = WC_LISTVIEWW;
9718 RegisterClassW(&wndClass);
9723 * Unregisters the window class.
9731 VOID LISTVIEW_Unregister(void)
9733 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
9738 * Handle any WM_COMMAND messages
9744 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9746 switch (HIWORD(wParam))
9751 * Adjust the edit window size
9754 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9755 HDC hdc = GetDC(infoPtr->hwndEdit);
9756 HFONT hFont, hOldFont = 0;
9761 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9762 GetWindowRect(infoPtr->hwndEdit, &rect);
9764 /* Select font to get the right dimension of the string */
9765 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9768 hOldFont = SelectObject(hdc, hFont);
9771 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9773 TEXTMETRICW textMetric;
9775 /* Add Extra spacing for the next character */
9776 GetTextMetricsW(hdc, &textMetric);
9777 sz.cx += (textMetric.tmMaxCharWidth * 2);
9785 rect.bottom - rect.top,
9786 SWP_DRAWFRAME|SWP_NOMOVE);
9789 SelectObject(hdc, hOldFont);
9791 ReleaseDC(hwnd, hdc);
9797 return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9806 * Subclassed edit control windproc function
9812 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
9813 WPARAM wParam, LPARAM lParam, BOOL isW)
9815 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9816 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9817 static BOOL bIgnoreKillFocus = FALSE;
9818 BOOL cancel = FALSE;
9820 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9821 hwnd, uMsg, wParam, lParam, isW);
9826 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9829 if(bIgnoreKillFocus) return TRUE;
9834 WNDPROC editProc = einfo->EditWndProc;
9835 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9836 COMCTL32_Free(einfo);
9837 infoPtr->pedititem = NULL;
9838 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9842 if (VK_ESCAPE == (INT)wParam)
9847 else if (VK_RETURN == (INT)wParam)
9851 return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9854 if (einfo->EditLblCb)
9856 LPWSTR buffer = NULL;
9860 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9864 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9866 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9867 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9871 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9872 /* eg. Using a messagebox */
9873 bIgnoreKillFocus = TRUE;
9874 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9876 if (buffer) COMCTL32_Free(buffer);
9878 einfo->EditLblCb = NULL;
9879 bIgnoreKillFocus = FALSE;
9882 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9888 * Subclassed edit control windproc function
9894 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9896 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9901 * Subclassed edit control windproc function
9907 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9909 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9914 * Creates a subclassed edit cotrol
9920 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
9921 INT width, INT height, HWND parent, HINSTANCE hinst,
9922 EditlblCallbackW EditLblCb, DWORD param, BOOL isW)
9924 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
9925 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9930 TEXTMETRICW textMetric;
9932 TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
9934 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9937 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9938 hdc = GetDC(parent);
9940 /* Select the font to get appropriate metric dimensions */
9941 if(infoPtr->hFont != 0)
9942 hOldFont = SelectObject(hdc, infoPtr->hFont);
9944 /*Get String Lenght in pixels */
9945 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9947 /*Add Extra spacing for the next character */
9948 GetTextMetricsW(hdc, &textMetric);
9949 sz.cx += (textMetric.tmMaxCharWidth * 2);
9951 if(infoPtr->hFont != 0)
9952 SelectObject(hdc, hOldFont);
9954 ReleaseDC(parent, hdc);
9956 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
9958 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
9962 COMCTL32_Free(infoPtr->pedititem);
9966 infoPtr->pedititem->param = param;
9967 infoPtr->pedititem->EditLblCb = EditLblCb;
9968 infoPtr->pedititem->EditWndProc = (WNDPROC)
9969 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9970 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9972 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);