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 BOOL 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)
861 scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
862 scrollInfo.nPage = nCountPerRow;
863 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
864 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
865 ShowScrollBar(hwnd, SB_VERT, FALSE);
867 else if (uView == LVS_REPORT)
869 /* update vertical scrollbar */
871 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
872 scrollInfo.nPos = ListView_GetTopIndex(hwnd);
873 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
874 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
875 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
877 /* update horizontal scrollbar */
878 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
879 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
880 || GETITEMCOUNT(infoPtr) == 0)
885 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
886 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
887 scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
888 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
890 /* Update the Header Control */
891 scrollInfo.fMask = SIF_POS;
892 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
893 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
900 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
902 INT nViewWidth = rcView.right - rcView.left;
903 INT nViewHeight = rcView.bottom - rcView.top;
905 /* Update Horizontal Scrollbar */
906 scrollInfo.fMask = SIF_POS;
907 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
908 || GETITEMCOUNT(infoPtr) == 0)
912 scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
914 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
915 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
916 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
918 /* Update Vertical Scrollbar */
919 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
920 scrollInfo.fMask = SIF_POS;
921 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
922 || GETITEMCOUNT(infoPtr) == 0)
926 scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
928 scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
929 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
930 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
937 * Prints a message for unsupported window styles.
938 * A kind of TODO list for window styles.
941 * [I] LONG : window style
946 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
948 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
949 FIXME(" LVS_NOSCROLL\n");
951 if (lStyle & LVS_EDITLABELS)
952 FIXME(" LVS_EDITLABELS\n");
954 if (lStyle & LVS_NOLABELWRAP)
955 FIXME(" LVS_NOLABELWRAP\n");
957 if (lStyle & LVS_SHAREIMAGELISTS)
958 FIXME(" LVS_SHAREIMAGELISTS\n");
960 if (lStyle & LVS_SORTASCENDING)
961 FIXME(" LVS_SORTASCENDING\n");
963 if (lStyle & LVS_SORTDESCENDING)
964 FIXME(" LVS_SORTDESCENDING\n");
969 * Aligns the items with the top edge of the window.
972 * [I] HWND : window handle
977 static VOID LISTVIEW_AlignTop(HWND hwnd)
979 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
980 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
981 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
984 INT i, off_x=0, off_y=0;
986 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
988 /* Since SetItemPosition uses upper-left of icon, and for
989 style=LVS_ICON the icon is not left adjusted, get the offset */
990 if (uView == LVS_ICON)
992 off_y = ICON_TOP_PADDING;
993 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
997 ZeroMemory(&rcView, sizeof(RECT));
999 if (nListWidth > infoPtr->nItemWidth)
1001 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1003 if (ptItem.x + infoPtr->nItemWidth > nListWidth)
1006 ptItem.y += infoPtr->nItemHeight;
1009 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1010 ptItem.x += infoPtr->nItemWidth;
1011 rcView.right = max(rcView.right, ptItem.x);
1014 rcView.bottom = ptItem.y + infoPtr->nItemHeight;
1018 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1020 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1021 ptItem.y += infoPtr->nItemHeight;
1024 rcView.right = infoPtr->nItemWidth;
1025 rcView.bottom = ptItem.y;
1028 LISTVIEW_SetViewRect(hwnd, &rcView);
1034 * Aligns the items with the left edge of the window.
1037 * [I] HWND : window handle
1042 static VOID LISTVIEW_AlignLeft(HWND hwnd)
1044 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1045 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1046 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1049 INT i, off_x=0, off_y=0;
1051 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1053 /* Since SetItemPosition uses upper-left of icon, and for
1054 style=LVS_ICON the icon is not left adjusted, get the offset */
1055 if (uView == LVS_ICON)
1057 off_y = ICON_TOP_PADDING;
1058 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1062 ZeroMemory(&rcView, sizeof(RECT));
1064 if (nListHeight > infoPtr->nItemHeight)
1066 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1068 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1071 ptItem.x += infoPtr->nItemWidth;
1074 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1075 ptItem.y += infoPtr->nItemHeight;
1076 rcView.bottom = max(rcView.bottom, ptItem.y);
1079 rcView.right = ptItem.x + infoPtr->nItemWidth;
1083 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1085 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1086 ptItem.x += infoPtr->nItemWidth;
1089 rcView.bottom = infoPtr->nItemHeight;
1090 rcView.right = ptItem.x;
1093 LISTVIEW_SetViewRect(hwnd, &rcView);
1099 * Set the bounding rectangle of all the items.
1102 * [I] HWND : window handle
1103 * [I] LPRECT : bounding rectangle
1109 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
1111 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1112 BOOL bResult = FALSE;
1114 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
1115 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1117 if (lprcView != NULL)
1120 infoPtr->rcView.left = lprcView->left;
1121 infoPtr->rcView.top = lprcView->top;
1122 infoPtr->rcView.right = lprcView->right;
1123 infoPtr->rcView.bottom = lprcView->bottom;
1131 * Retrieves the bounding rectangle of all the items.
1134 * [I] HWND : window handle
1135 * [O] LPRECT : bounding rectangle
1141 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
1143 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1144 BOOL bResult = FALSE;
1147 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
1149 if (lprcView != NULL)
1151 bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
1152 if (bResult != FALSE)
1154 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1155 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1156 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1157 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1160 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1161 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1169 * Retrieves the subitem pointer associated with the subitem index.
1172 * [I] HDPA : DPA handle for a specific item
1173 * [I] INT : index of subitem
1176 * SUCCESS : subitem pointer
1179 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1182 LISTVIEW_SUBITEM *lpSubItem;
1185 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1187 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1188 if (lpSubItem != NULL)
1190 if (lpSubItem->iSubItem == nSubItem)
1202 * Calculates the width of an item.
1205 * [I] HWND : window handle
1206 * [I] LONG : window style
1209 * Returns item width.
1211 static INT LISTVIEW_GetItemWidth(HWND hwnd)
1213 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1214 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
1215 UINT uView = style & LVS_TYPEMASK;
1216 INT nHeaderItemCount;
1222 TRACE("(hwnd=%x)\n", hwnd);
1224 if (uView == LVS_ICON)
1226 nItemWidth = infoPtr->iconSpacing.cx;
1228 else if (uView == LVS_REPORT)
1230 /* calculate width of header */
1231 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1232 for (i = 0; i < nHeaderItemCount; i++)
1234 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1236 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1242 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1244 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
1245 nItemWidth = max(nItemWidth, nLabelWidth);
1248 /* default label size */
1249 if (GETITEMCOUNT(infoPtr) == 0)
1251 nItemWidth = DEFAULT_COLUMN_WIDTH;
1255 if (nItemWidth == 0)
1257 nItemWidth = DEFAULT_LABEL_WIDTH;
1262 nItemWidth += WIDTH_PADDING;
1264 if (infoPtr->himlSmall != NULL)
1266 nItemWidth += infoPtr->iconSize.cx;
1269 if (infoPtr->himlState != NULL)
1271 nItemWidth += infoPtr->iconSize.cx;
1278 /* nItemWidth Cannot be Zero */
1286 * Calculates the width of a specific item.
1289 * [I] HWND : window handle
1290 * [I] LPSTR : string
1293 * Returns the width of an item width a specified string.
1295 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1297 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1298 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1299 INT nHeaderItemCount;
1304 TRACE("(hwnd=%x)\n", hwnd);
1306 if (uView == LVS_ICON)
1308 nItemWidth = infoPtr->iconSpacing.cx;
1310 else if (uView == LVS_REPORT)
1312 /* calculate width of header */
1313 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1314 for (i = 0; i < nHeaderItemCount; i++)
1316 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1318 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1324 /* get width of string */
1325 nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1327 /* default label size */
1328 if (GETITEMCOUNT(infoPtr) == 0)
1330 nItemWidth = DEFAULT_COLUMN_WIDTH;
1334 if (nItemWidth == 0)
1336 nItemWidth = DEFAULT_LABEL_WIDTH;
1341 nItemWidth += WIDTH_PADDING;
1343 if (infoPtr->himlSmall != NULL)
1345 nItemWidth += infoPtr->iconSize.cx;
1348 if (infoPtr->himlState != NULL)
1350 nItemWidth += infoPtr->iconSize.cx;
1361 * Retrieves and saves important text metrics info for the current
1365 * [I] HWND : window handle
1368 static VOID LISTVIEW_SaveTextMetrics(HWND hwnd)
1370 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1372 HDC hdc = GetDC(hwnd);
1373 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1374 INT oldHeight, oldACW;
1376 GetTextMetricsW(hdc, &tm);
1378 oldHeight = infoPtr->ntmHeight;
1379 oldACW = infoPtr->ntmAveCharWidth;
1380 infoPtr->ntmHeight = tm.tmHeight;
1381 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1383 SelectObject(hdc, hOldFont);
1384 ReleaseDC(hwnd, hdc);
1385 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1386 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1392 * Calculates the height of an item.
1395 * [I] HWND : window handle
1398 * Returns item height.
1400 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1402 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1403 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1404 INT nItemHeight = 0;
1406 if (uView == LVS_ICON)
1408 nItemHeight = infoPtr->iconSpacing.cy;
1412 if(infoPtr->himlState || infoPtr->himlSmall)
1413 nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1415 nItemHeight = infoPtr->ntmHeight;
1422 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1424 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1425 LISTVIEW_SELECTION *selection;
1426 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1429 TRACE("Selections are:\n");
1430 for (i = 0; i < topSelection; i++)
1432 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1433 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1439 * A compare function for selection ranges
1442 * [I] LPVOID : Item 1;
1443 * [I] LPVOID : Item 2;
1444 * [I] LPARAM : flags
1447 * >0 : if Item 1 > Item 2
1448 * <0 : if Item 2 > Item 1
1449 * 0 : if Item 1 == Item 2
1451 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1454 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1455 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1456 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1457 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1471 * Adds a selection range.
1474 * [I] HWND : window handle
1475 * [I] INT : lower item index
1476 * [I] INT : upper item index
1481 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1483 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1484 LISTVIEW_SELECTION *selection;
1485 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1486 BOOL lowerzero=FALSE;
1488 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1489 selection->lower = lItem;
1490 selection->upper = uItem;
1492 TRACE("Add range %i - %i\n", lItem, uItem);
1495 LISTVIEW_SELECTION *checkselection,*checkselection2;
1496 INT index,mergeindex;
1498 /* find overlapping selections */
1499 /* we want to catch adjacent ranges so expand our range by 1 */
1502 if (selection->lower == 0)
1507 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1508 LISTVIEW_CompareSelectionRanges,
1510 selection->upper --;
1514 selection->lower ++;
1518 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1519 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1520 checkselection->upper);
1522 checkselection->lower = min(selection->lower,checkselection->lower);
1523 checkselection->upper = max(selection->upper,checkselection->upper);
1525 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1526 checkselection->upper);
1528 COMCTL32_Free(selection);
1530 /* merge now common selection ranges in the lower group*/
1533 checkselection->upper ++;
1534 if (checkselection->lower == 0)
1537 checkselection->lower --;
1539 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1540 checkselection->upper);
1542 /* not sorted yet */
1543 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1544 LISTVIEW_CompareSelectionRanges, 0,
1547 checkselection->upper --;
1551 checkselection->lower ++;
1553 if (mergeindex >=0 && mergeindex != index)
1555 TRACE("Merge with index %i\n",mergeindex);
1556 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1558 checkselection->lower = min(checkselection->lower,
1559 checkselection2->lower);
1560 checkselection->upper = max(checkselection->upper,
1561 checkselection2->upper);
1562 COMCTL32_Free(checkselection2);
1563 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1567 while (mergeindex > -1 && mergeindex <index);
1569 /* merge now common selection ranges in the upper group*/
1572 checkselection->upper ++;
1573 if (checkselection->lower == 0)
1576 checkselection->lower --;
1578 TRACE("search upper range %i (%lu - %lu)\n",index,
1579 checkselection->lower, checkselection->upper);
1581 /* not sorted yet */
1582 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1584 LISTVIEW_CompareSelectionRanges, 0,
1587 checkselection->upper --;
1591 checkselection->lower ++;
1593 if (mergeindex >=0 && mergeindex !=index)
1595 TRACE("Merge with index %i\n",mergeindex);
1596 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1598 checkselection->lower = min(checkselection->lower,
1599 checkselection2->lower);
1600 checkselection->upper = max(checkselection->upper,
1601 checkselection2->upper);
1602 COMCTL32_Free(checkselection2);
1603 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1606 while (mergeindex > -1);
1611 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1612 LISTVIEW_CompareSelectionRanges, 0,
1615 TRACE("Insert before index %i\n",index);
1618 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1623 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1628 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1629 LISTVIEW_PrintSelectionRanges(hwnd);
1634 * check if a specified index is selected.
1637 * [I] HWND : window handle
1638 * [I] INT : item index
1643 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1645 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1646 LISTVIEW_SELECTION selection;
1649 selection.upper = nItem;
1650 selection.lower = nItem;
1652 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1653 LISTVIEW_CompareSelectionRanges,
1663 * Removes all selection ranges
1666 * HWND: window handle
1672 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1674 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1675 LISTVIEW_SELECTION *selection;
1679 TRACE("(0x%x)\n",hwnd);
1681 ZeroMemory(&item,sizeof(item));
1682 item.stateMask = LVIS_SELECTED;
1686 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1689 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1690 for (i = selection->lower; i<=selection->upper; i++)
1691 LISTVIEW_SetItemState(hwnd,i,&item);
1692 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1695 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1703 * Removes a range selections.
1706 * [I] HWND : window handle
1707 * [I] INT : lower item index
1708 * [I] INT : upper item index
1713 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1715 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1716 LISTVIEW_SELECTION removeselection,*checkselection;
1719 removeselection.lower = lItem;
1720 removeselection.upper = uItem;
1722 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1723 LISTVIEW_PrintSelectionRanges(hwnd);
1725 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1726 LISTVIEW_CompareSelectionRanges,
1733 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1736 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1737 checkselection->upper);
1740 if ((checkselection->upper == removeselection.upper) &&
1741 (checkselection->lower == removeselection.lower))
1743 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1746 /* case 2: engulf */
1747 else if (((checkselection->upper < removeselection.upper) &&
1748 (checkselection->lower > removeselection.lower))||
1749 ((checkselection->upper <= removeselection.upper) &&
1750 (checkselection->lower > removeselection.lower)) ||
1751 ((checkselection->upper < removeselection.upper) &&
1752 (checkselection->lower >= removeselection.lower)))
1755 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1756 /* do it again because others may also get caught */
1758 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1760 /* case 3: overlap upper */
1761 else if ((checkselection->upper < removeselection.upper) &&
1762 (checkselection->lower < removeselection.lower))
1764 checkselection->upper = removeselection.lower - 1;
1766 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1768 /* case 4: overlap lower */
1769 else if ((checkselection->upper > removeselection.upper) &&
1770 (checkselection->lower > removeselection.lower))
1772 checkselection->lower = removeselection.upper + 1;
1774 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1776 /* case 5: fully internal */
1777 else if (checkselection->upper == removeselection.upper)
1778 checkselection->upper = removeselection.lower - 1;
1779 else if (checkselection->lower == removeselection.lower)
1780 checkselection->lower = removeselection.upper + 1;
1783 /* bisect the range */
1784 LISTVIEW_SELECTION *newselection;
1786 newselection = (LISTVIEW_SELECTION *)
1787 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1788 newselection -> lower = checkselection->lower;
1789 newselection -> upper = removeselection.lower - 1;
1790 checkselection -> lower = removeselection.upper + 1;
1791 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1793 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1795 LISTVIEW_PrintSelectionRanges(hwnd);
1800 * Updates the various indices after an item has been inserted or deleted.
1803 * [I] HWND : window handle
1804 * [I] INT : item index
1805 * [I] INT : Direction of shift, +1 or -1.
1810 static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
1812 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1813 LISTVIEW_SELECTION selection,*checkselection;
1816 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1818 selection.upper = nItem;
1819 selection.lower = nItem;
1821 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1822 LISTVIEW_CompareSelectionRanges,
1823 0,DPAS_SORTED|DPAS_INSERTAFTER);
1825 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1827 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1828 if ((checkselection->lower >= nItem)&&
1829 (checkselection->lower + direction >= 0))
1830 checkselection->lower += direction;
1831 if ((checkselection->upper >= nItem)&&
1832 (checkselection->upper + direction >=0))
1833 checkselection->upper += direction;
1837 /* Note that the following will fail if direction != +1 and -1 */
1838 if (infoPtr->nSelectionMark > nItem)
1839 infoPtr->nSelectionMark += direction;
1840 else if (infoPtr->nSelectionMark == nItem)
1843 infoPtr->nSelectionMark += direction;
1844 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1845 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1848 if (infoPtr->nFocusedItem > nItem)
1849 infoPtr->nFocusedItem += direction;
1850 else if (infoPtr->nFocusedItem == nItem)
1853 infoPtr->nFocusedItem += direction;
1856 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1857 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1858 if (infoPtr->nFocusedItem >= 0)
1859 LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
1862 /* But we are not supposed to modify nHotItem! */
1868 * Adds a block of selections.
1871 * [I] HWND : window handle
1872 * [I] INT : item index
1877 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1879 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1880 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1881 INT nLast = max(infoPtr->nSelectionMark, nItem);
1888 ZeroMemory(&item,sizeof(item));
1889 item.stateMask = LVIS_SELECTED;
1890 item.state = LVIS_SELECTED;
1892 for (i = nFirst; i <= nLast; i++)
1893 LISTVIEW_SetItemState(hwnd,i,&item);
1895 LISTVIEW_SetItemFocus(hwnd, nItem);
1896 infoPtr->nSelectionMark = nItem;
1902 * Adds a single selection.
1905 * [I] HWND : window handle
1906 * [I] INT : item index
1911 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1913 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1916 ZeroMemory(&item,sizeof(item));
1917 item.state = LVIS_SELECTED;
1918 item.stateMask = LVIS_SELECTED;
1920 LISTVIEW_SetItemState(hwnd,nItem,&item);
1922 LISTVIEW_SetItemFocus(hwnd, nItem);
1923 infoPtr->nSelectionMark = nItem;
1928 * Selects or unselects an item.
1931 * [I] HWND : window handle
1932 * [I] INT : item index
1938 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1940 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1944 ZeroMemory(&item,sizeof(item));
1945 item.stateMask = LVIS_SELECTED;
1947 if (LISTVIEW_IsSelected(hwnd,nItem))
1949 LISTVIEW_SetItemState(hwnd,nItem,&item);
1954 item.state = LVIS_SELECTED;
1955 LISTVIEW_SetItemState(hwnd,nItem,&item);
1959 LISTVIEW_SetItemFocus(hwnd, nItem);
1960 infoPtr->nSelectionMark = nItem;
1967 * Selects items based on view coordinates.
1970 * [I] HWND : window handle
1971 * [I] RECT : selection rectangle
1976 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
1978 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1983 ZeroMemory(&item,sizeof(item));
1984 item.stateMask = LVIS_SELECTED;
1986 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1988 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
1990 if (PtInRect(&rcSelRect, ptItem) != FALSE)
1991 item.state = LVIS_SELECTED;
1994 LISTVIEW_SetItemState(hwnd,i,&item);
2000 * Sets a single group selection.
2003 * [I] HWND : window handle
2004 * [I] INT : item index
2009 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
2011 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2012 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2015 ZeroMemory(&item,sizeof(item));
2016 item.stateMask = LVIS_SELECTED;
2018 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2023 if (infoPtr->nSelectionMark == -1)
2025 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2029 nFirst = min(infoPtr->nSelectionMark, nItem);
2030 nLast = max(infoPtr->nSelectionMark, nItem);
2033 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2035 if ((i < nFirst) || (i > nLast))
2038 item.state = LVIS_SELECTED;
2039 LISTVIEW_SetItemState(hwnd,i,&item);
2047 LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcItem);
2048 LISTVIEW_GetItemBoundBox(hwnd, infoPtr->nSelectionMark, &rcSelMark);
2049 rcSel.left = min(rcSelMark.left, rcItem.left);
2050 rcSel.top = min(rcSelMark.top, rcItem.top);
2051 rcSel.right = max(rcSelMark.right, rcItem.right);
2052 rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
2053 LISTVIEW_SetSelectionRect(hwnd, rcSel);
2054 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2055 nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
2056 infoPtr->nSelectionMark,
2057 rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
2058 rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2062 LISTVIEW_SetItemFocus(hwnd, nItem);
2067 * Manages the item focus.
2070 * [I] HWND : window handle
2071 * [I] INT : item index
2074 * TRUE : focused item changed
2075 * FALSE : focused item has NOT changed
2077 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
2079 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2080 BOOL bResult = FALSE;
2083 if (infoPtr->nFocusedItem != nItem)
2085 if (infoPtr->nFocusedItem >= 0)
2087 INT oldFocus = infoPtr->nFocusedItem;
2089 infoPtr->nFocusedItem = -1;
2090 ZeroMemory(&lvItem, sizeof(lvItem));
2091 lvItem.stateMask = LVIS_FOCUSED;
2092 ListView_SetItemState(hwnd, oldFocus, &lvItem);
2096 lvItem.state = LVIS_FOCUSED;
2097 lvItem.stateMask = LVIS_FOCUSED;
2098 ListView_SetItemState(hwnd, nItem, &lvItem);
2100 infoPtr->nFocusedItem = nItem;
2101 ListView_EnsureVisible(hwnd, nItem, FALSE);
2109 * Sets a single selection.
2112 * [I] HWND : window handle
2113 * [I] INT : item index
2118 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
2120 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2123 ZeroMemory(&lvItem, sizeof(lvItem));
2124 lvItem.stateMask = LVIS_FOCUSED;
2125 ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
2127 LISTVIEW_RemoveAllSelections(hwnd);
2129 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
2130 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2131 ListView_SetItemState(hwnd, nItem, &lvItem);
2133 infoPtr->nFocusedItem = nItem;
2134 infoPtr->nSelectionMark = nItem;
2139 * Set selection(s) with keyboard.
2142 * [I] HWND : window handle
2143 * [I] INT : item index
2146 * SUCCESS : TRUE (needs to be repainted)
2147 * FAILURE : FALSE (nothing has changed)
2149 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
2151 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2152 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2153 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2154 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2155 BOOL bResult = FALSE;
2157 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2159 if (lStyle & LVS_SINGLESEL)
2162 LISTVIEW_SetSelection(hwnd, nItem);
2163 ListView_EnsureVisible(hwnd, nItem, FALSE);
2170 LISTVIEW_SetGroupSelection(hwnd, nItem);
2174 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
2179 LISTVIEW_SetSelection(hwnd, nItem);
2180 ListView_EnsureVisible(hwnd, nItem, FALSE);
2190 * Called when the mouse is being actively tracked and has hovered for a specified
2194 * [I] HWND : window handle
2195 * [I] wParam : key indicator
2196 * [I] lParam : mouse position
2199 * 0 if the message was processed, non-zero if there was an error
2202 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2203 * over the item for a certain period of time.
2206 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
2208 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2211 pt.x = (INT)LOWORD(lParam);
2212 pt.y = (INT)HIWORD(lParam);
2214 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2215 /* select the item under the cursor */
2216 LISTVIEW_MouseSelection(hwnd, pt);
2224 * Called whenever WM_MOUSEMOVE is received.
2227 * [I] HWND : window handle
2228 * [I] wParam : key indicators
2229 * [I] lParam : cursor position
2232 * 0 if the message is processed, non-zero if there was an error
2234 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
2236 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2237 TRACKMOUSEEVENT trackinfo;
2239 /* see if we are supposed to be tracking mouse hovering */
2240 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2241 /* fill in the trackinfo struct */
2242 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2243 trackinfo.dwFlags = TME_QUERY;
2244 trackinfo.hwndTrack = hwnd;
2245 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2247 /* see if we are already tracking this hwnd */
2248 _TrackMouseEvent(&trackinfo);
2250 if(!(trackinfo.dwFlags & TME_HOVER)) {
2251 trackinfo.dwFlags = TME_HOVER;
2253 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2254 _TrackMouseEvent(&trackinfo);
2263 * Selects an item based on coordinates.
2266 * [I] HWND : window handle
2267 * [I] POINT : mouse click ccordinates
2270 * SUCCESS : item index
2273 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
2275 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2277 INT i,topindex,bottomindex;
2278 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2279 UINT uView = lStyle & LVS_TYPEMASK;
2281 topindex = ListView_GetTopIndex(hwnd);
2282 if (uView == LVS_REPORT)
2284 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
2285 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
2289 bottomindex = GETITEMCOUNT(infoPtr);
2292 for (i = topindex; i < bottomindex; i++)
2294 rcItem.left = LVIR_SELECTBOUNDS;
2295 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
2297 if (PtInRect(&rcItem, pt) != FALSE)
2312 * [IO] HDPA : dynamic pointer array handle
2313 * [I] INT : column index (subitem index)
2319 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2321 BOOL bResult = TRUE;
2325 for (i = 0; i < hdpaItems->nItemCount; i++)
2327 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2328 if (hdpaSubItems != NULL)
2330 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2342 * Removes a subitem at a given position.
2345 * [IO] HDPA : dynamic pointer array handle
2346 * [I] INT : subitem index
2352 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2354 LISTVIEW_SUBITEM *lpSubItem;
2357 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2359 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2360 if (lpSubItem != NULL)
2362 if (lpSubItem->iSubItem == nSubItem)
2365 if (is_textW(lpSubItem->pszText))
2366 COMCTL32_Free(lpSubItem->pszText);
2369 COMCTL32_Free(lpSubItem);
2371 /* free dpa memory */
2372 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2375 else if (lpSubItem->iSubItem > nSubItem)
2385 * Compares the item information.
2388 * [I] LISTVIEW_ITEM *: destination item
2389 * [I] LPLVITEM : source item
2390 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2393 * SUCCCESS : TRUE (EQUAL)
2394 * FAILURE : FALSE (NOT EQUAL)
2396 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2400 if ((lpItem != NULL) && (lpLVItem != NULL))
2402 if (lpLVItem->mask & LVIF_STATE)
2404 if ((lpItem->state & lpLVItem->stateMask) !=
2405 (lpLVItem->state & lpLVItem->stateMask))
2406 uChanged |= LVIF_STATE;
2409 if (lpLVItem->mask & LVIF_IMAGE)
2411 if (lpItem->iImage != lpLVItem->iImage)
2412 uChanged |= LVIF_IMAGE;
2415 if (lpLVItem->mask & LVIF_PARAM)
2417 if (lpItem->lParam != lpLVItem->lParam)
2418 uChanged |= LVIF_PARAM;
2421 if (lpLVItem->mask & LVIF_INDENT)
2423 if (lpItem->iIndent != lpLVItem->iIndent)
2424 uChanged |= LVIF_INDENT;
2427 if (lpLVItem->mask & LVIF_TEXT)
2429 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2431 if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2432 uChanged |= LVIF_TEXT;
2436 if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2438 uChanged |= LVIF_TEXT;
2442 if (lpLVItem->pszText)
2444 if (lpItem->pszText)
2446 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2447 if (pszText && strcmpW(pszText, lpItem->pszText))
2448 uChanged |= LVIF_TEXT;
2449 textfreeT(pszText, isW);
2453 uChanged |= LVIF_TEXT;
2458 if (lpItem->pszText)
2459 uChanged |= LVIF_TEXT;
2470 * Initializes item attributes.
2473 * [I] HWND : window handle
2474 * [O] LISTVIEW_ITEM *: destination item
2475 * [I] LPLVITEM : source item
2476 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2482 static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem,
2483 LPLVITEMW lpLVItem, BOOL isW)
2485 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2486 BOOL bResult = FALSE;
2488 if ((lpItem != NULL) && (lpLVItem != NULL))
2492 if (lpLVItem->mask & LVIF_STATE)
2494 lpItem->state &= ~lpLVItem->stateMask;
2495 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2498 if (lpLVItem->mask & LVIF_IMAGE)
2499 lpItem->iImage = lpLVItem->iImage;
2501 if (lpLVItem->mask & LVIF_PARAM)
2502 lpItem->lParam = lpLVItem->lParam;
2504 if (lpLVItem->mask & LVIF_INDENT)
2505 lpItem->iIndent = lpLVItem->iIndent;
2507 if (lpLVItem->mask & LVIF_TEXT)
2509 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2511 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2514 if (is_textW(lpItem->pszText))
2515 COMCTL32_Free(lpItem->pszText);
2517 lpItem->pszText = LPSTR_TEXTCALLBACKW;
2520 bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
2529 * Initializes subitem attributes.
2531 * NOTE: The documentation specifies that the operation fails if the user
2532 * tries to set the indent of a subitem.
2535 * [I] HWND : window handle
2536 * [O] LISTVIEW_SUBITEM *: destination subitem
2537 * [I] LPLVITEM : source subitem
2538 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2544 static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2545 LPLVITEMW lpLVItem, BOOL isW)
2547 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2548 BOOL bResult = FALSE;
2550 TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
2551 hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW);
2553 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2555 if (!(lpLVItem->mask & LVIF_INDENT))
2559 lpSubItem->iSubItem = lpLVItem->iSubItem;
2561 if (lpLVItem->mask & LVIF_IMAGE)
2562 lpSubItem->iImage = lpLVItem->iImage;
2564 if (lpLVItem->mask & LVIF_TEXT)
2566 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2568 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2571 if (is_textW(lpSubItem->pszText))
2572 COMCTL32_Free(lpSubItem->pszText);
2574 lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2577 bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
2587 * Adds a subitem at a given position (column index).
2590 * [I] HWND : window handle
2591 * [I] LPLVITEM : new subitem atttributes
2592 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2598 static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2600 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2601 LISTVIEW_SUBITEM *lpSubItem = NULL;
2602 BOOL bResult = FALSE;
2604 INT nPosition, nItem;
2605 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2607 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2609 if (lStyle & LVS_OWNERDATA)
2612 if (lpLVItem != NULL)
2614 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2615 if (hdpaSubItems != NULL)
2617 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2618 if (lpSubItem != NULL)
2620 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2621 if (LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW))
2623 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2624 lpSubItem->iSubItem);
2625 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2626 if (nItem != -1) bResult = TRUE;
2632 /* cleanup if unsuccessful */
2633 if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2640 * Finds the dpa insert position (array index).
2643 * [I] HWND : window handle
2644 * [I] INT : subitem index
2650 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2652 LISTVIEW_SUBITEM *lpSubItem;
2655 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2657 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2658 if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2662 return hdpaSubItems->nItemCount;
2667 * Retrieves a listview subitem at a given position (column index).
2670 * [I] HWND : window handle
2671 * [I] INT : subitem index
2677 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2679 LISTVIEW_SUBITEM *lpSubItem;
2682 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2684 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2685 if (lpSubItem != NULL)
2687 if (lpSubItem->iSubItem == nSubItem)
2689 else if (lpSubItem->iSubItem > nSubItem)
2699 * Sets item attributes.
2702 * [I] HWND : window handle
2703 * [I] LPLVITEM : new item atttributes
2704 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2710 static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2712 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2713 BOOL bResult = FALSE;
2715 LISTVIEW_ITEM *lpItem;
2717 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2719 UINT uView = lStyle & LVS_TYPEMASK;
2723 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2725 if (lStyle & LVS_OWNERDATA)
2727 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2731 ZeroMemory(&itm, sizeof(itm));
2732 itm.mask = LVIF_STATE | LVIF_PARAM;
2733 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2734 itm.iItem = lpLVItem->iItem;
2736 ListView_GetItemW(hwnd, &itm);
2739 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2740 nmlv.uNewState = lpLVItem->state;
2741 nmlv.uOldState = itm.state;
2742 nmlv.uChanged = LVIF_STATE;
2743 nmlv.lParam = itm.lParam;
2744 nmlv.iItem = lpLVItem->iItem;
2746 if ((itm.state & lpLVItem->stateMask) !=
2747 (lpLVItem->state & lpLVItem->stateMask))
2749 /* send LVN_ITEMCHANGING notification */
2750 if (!listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv))
2752 if (lpLVItem->stateMask & LVIS_FOCUSED)
2754 if (lpLVItem->state & LVIS_FOCUSED)
2755 infoPtr->nFocusedItem = lpLVItem->iItem;
2756 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2757 infoPtr->nFocusedItem = -1;
2759 if (lpLVItem->stateMask & LVIS_SELECTED)
2761 if (lpLVItem->state & LVIS_SELECTED)
2763 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd);
2764 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2767 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2771 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2773 rcItem.left = LVIR_BOUNDS;
2774 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2775 InvalidateRect(hwnd, &rcItem, TRUE);
2783 if (lpLVItem != NULL)
2785 if (lpLVItem->iSubItem == 0)
2787 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2788 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2790 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2793 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2794 nmlv.lParam = lpItem->lParam;
2795 uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2798 if (uChanged & LVIF_STATE)
2800 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2801 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2803 if (nmlv.uNewState & LVIS_SELECTED)
2806 * This is redundant if called through SetSelection
2808 * however is required if the used directly calls SetItem
2809 * to set the selection.
2811 if (lStyle & LVS_SINGLESEL)
2812 LISTVIEW_RemoveAllSelections(hwnd);
2814 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2817 else if (lpLVItem->stateMask & LVIS_SELECTED)
2819 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2822 if (nmlv.uNewState & LVIS_FOCUSED)
2825 * This is a fun hoop to jump to try to catch if
2826 * the user is calling us directly to call focus or if
2827 * this function is being called as a result of a
2828 * SetItemFocus call.
2830 if (infoPtr->nFocusedItem >= 0)
2831 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2835 nmlv.uChanged = uChanged;
2836 nmlv.iItem = lpLVItem->iItem;
2837 nmlv.lParam = lpItem->lParam;
2838 /* send LVN_ITEMCHANGING notification */
2839 listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv);
2841 /* copy information */
2842 bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW);
2844 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2845 based on the width of the items text */
2846 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2848 item_width = LISTVIEW_GetStringWidthT(hwnd, lpItem->pszText, TRUE);
2850 if(item_width > infoPtr->nItemWidth)
2851 infoPtr->nItemWidth = item_width;
2854 /* send LVN_ITEMCHANGED notification */
2855 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2864 rcItem.left = LVIR_BOUNDS;
2865 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2866 InvalidateRect(hwnd, &rcItem, TRUE);
2878 * Sets subitem attributes.
2881 * [I] HWND : window handle
2882 * [I] LPLVITEM : new subitem atttributes
2883 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2889 static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2891 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2892 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2893 BOOL bResult = FALSE;
2895 LISTVIEW_SUBITEM *lpSubItem;
2898 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2900 if (lStyle & LVS_OWNERDATA)
2903 if (lpLVItem != NULL)
2905 if (lpLVItem->iSubItem > 0)
2907 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2908 if (hdpaSubItems != NULL)
2910 /* set subitem only if column is present */
2911 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2913 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2914 if (lpSubItem != NULL)
2915 bResult = LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW);
2917 bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW);
2919 rcItem.left = LVIR_BOUNDS;
2920 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2921 InvalidateRect(hwnd, &rcItem, FALSE);
2932 * Sets item attributes.
2935 * [I] HWND : window handle
2936 * [I] LPLVITEM : new item atttributes
2937 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2943 static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2945 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2947 if (!lpLVItem || lpLVItem->iItem < 0 ||
2948 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2950 if (lpLVItem->iSubItem == 0)
2951 return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW);
2953 return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW);
2958 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2961 * [I] HWND : window handle
2966 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2968 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2969 UINT uView = lStyle & LVS_TYPEMASK;
2971 SCROLLINFO scrollInfo;
2973 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2974 scrollInfo.cbSize = sizeof(SCROLLINFO);
2975 scrollInfo.fMask = SIF_POS;
2977 if (uView == LVS_LIST)
2979 if ((lStyle & WS_HSCROLL) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo))
2980 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2982 else if (uView == LVS_REPORT)
2984 if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo))
2985 nItem = scrollInfo.nPos;
2996 * [I] HWND : window handle
2997 * [I] HDC : device context handle
2998 * [I] INT : item index
2999 * [I] INT : subitem index
3000 * [I] RECT * : clipping rectangle
3005 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
3006 RECT rcItem, BOOL Selected)
3008 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3009 WCHAR szDispText[DISP_TEXT_SIZE];
3012 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
3015 INT nLabelWidth = 0;
3017 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
3020 /* get information needed for drawing the item */
3021 ZeroMemory(&lvItem, sizeof(lvItem));
3022 lvItem.mask = LVIF_TEXT;
3023 lvItem.iItem = nItem;
3024 lvItem.iSubItem = nSubItem;
3025 lvItem.cchTextMax = DISP_TEXT_SIZE;
3026 lvItem.pszText = szDispText;
3027 *lvItem.pszText = '\0';
3028 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3029 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3031 ZeroMemory(&lvColumn, sizeof(lvColumn));
3032 lvColumn.mask = LVCF_FMT;
3033 LISTVIEW_GetColumnT(hwnd, nSubItem, &lvColumn, TRUE);
3034 textLeft = rcItem.left;
3035 if (lvColumn.fmt != LVCFMT_LEFT)
3037 if ((nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE)))
3039 if (lvColumn.fmt == LVCFMT_RIGHT)
3040 textLeft = rcItem.right - nLabelWidth;
3042 textLeft = rcItem.left + (rcItem.right-rcItem.left-nLabelWidth)/2;
3047 /* redraw the background of the item */
3049 if(infoPtr->nColumnCount == (nSubItem + 1))
3050 rcTemp.right = infoPtr->rcList.right;
3052 rcTemp.right += WIDTH_PADDING;
3054 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3056 /* set item colors */
3057 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
3059 if (infoPtr->bFocus)
3061 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3062 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3066 SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3067 SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3072 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3074 SetBkMode(hdc, TRANSPARENT);
3075 textoutOptions &= ~ETO_OPAQUE;
3079 SetBkMode(hdc, OPAQUE);
3080 SetBkColor(hdc, infoPtr->clrTextBk);
3083 SetTextColor(hdc, infoPtr->clrText);
3086 ExtTextOutW(hdc, textLeft, rcItem.top, textoutOptions,
3087 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3091 /* fill in the gap */
3093 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
3095 CopyRect(&rec,&rcItem);
3096 rec.left = rec.right;
3097 rec.right = rec.left+REPORT_MARGINX;
3098 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3099 &rec, NULL, 0, NULL);
3101 CopyRect(&rec,&rcItem);
3102 rec.right = rec.left;
3103 rec.left = rec.left - REPORT_MARGINX;
3104 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3105 &rec, NULL, 0, NULL);
3115 * [I] HWND : window handle
3116 * [I] HDC : device context handle
3117 * [I] INT : item index
3118 * [I] RECT * : clipping rectangle
3123 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
3125 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3126 WCHAR szDispText[DISP_TEXT_SIZE];
3131 DWORD dwTextColor,dwTextX;
3132 BOOL bImage = FALSE;
3134 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
3137 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
3140 /* get information needed for drawing the item */
3141 ZeroMemory(&lvItem, sizeof(lvItem));
3142 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3143 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
3144 lvItem.iItem = nItem;
3145 lvItem.iSubItem = 0;
3146 lvItem.cchTextMax = DISP_TEXT_SIZE;
3147 lvItem.pszText = szDispText;
3148 *lvItem.pszText = '\0';
3149 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3150 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3152 /* redraw the background of the item */
3154 if(infoPtr->nColumnCount == (nItem + 1))
3155 rcTemp.right = infoPtr->rcList.right;
3157 rcTemp.right+=WIDTH_PADDING;
3159 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3162 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3164 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3167 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3171 if (infoPtr->himlState != NULL)
3173 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3174 if (uStateImage > 0)
3176 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3177 rcItem.top, ILD_NORMAL);
3180 rcItem.left += infoPtr->iconSize.cx;
3182 SuggestedFocus->left += infoPtr->iconSize.cx;
3187 if (infoPtr->himlSmall != NULL)
3189 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
3192 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3193 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3194 rcItem.top, ILD_SELECTED);
3196 else if (lvItem.iImage>=0)
3198 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3199 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3200 rcItem.top, ILD_NORMAL);
3203 rcItem.left += infoPtr->iconSize.cx;
3206 SuggestedFocus->left += infoPtr->iconSize.cx;
3210 /* Don't bother painting item being edited */
3211 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
3214 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
3216 /* set item colors */
3217 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3218 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3219 /* set raster mode */
3220 nMixMode = SetROP2(hdc, R2_XORPEN);
3222 else if ((GetWindowLongW(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3223 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
3225 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3226 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3227 /* set raster mode */
3228 nMixMode = SetROP2(hdc, R2_COPYPEN);
3232 /* set item colors */
3233 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3235 dwBkColor = GetBkColor(hdc);
3236 iBkMode = SetBkMode(hdc, TRANSPARENT);
3237 textoutOptions &= ~ETO_OPAQUE;
3241 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3242 iBkMode = SetBkMode(hdc, OPAQUE);
3245 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3246 /* set raster mode */
3247 nMixMode = SetROP2(hdc, R2_COPYPEN);
3250 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
3251 if (rcItem.left + nLabelWidth < rcItem.right)
3254 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3256 rcItem.right += IMAGE_PADDING;
3260 dwTextX = rcItem.left + 1;
3262 dwTextX += IMAGE_PADDING;
3265 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3266 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3268 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3270 /* fill in the gap */
3272 CopyRect(&rec,&rcItem);
3273 rec.left = rec.right;
3274 rec.right = rec.left+REPORT_MARGINX;
3275 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3276 &rec, NULL, 0, NULL);
3280 CopyRect(SuggestedFocus,&rcItem);
3284 SetROP2(hdc, R2_COPYPEN);
3285 SetBkColor(hdc, dwBkColor);
3286 SetTextColor(hdc, dwTextColor);
3288 SetBkMode(hdc, iBkMode);
3294 * Draws an item when in large icon display mode.
3297 * [I] HWND : window handle
3298 * [I] HDC : device context handle
3299 * [I] INT : item index
3300 * [I] RECT : clipping rectangle
3301 * [O] RECT * : The text rectangle about which to draw the focus
3306 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3307 RECT *SuggestedFocus)
3309 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3310 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3312 UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
3314 /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
3317 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3318 hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3320 /* get information needed for drawing the item */
3321 ZeroMemory(&lvItem, sizeof(lvItem));
3322 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3323 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3324 lvItem.iItem = nItem;
3325 lvItem.iSubItem = 0;
3326 lvItem.cchTextMax = DISP_TEXT_SIZE;
3327 lvItem.pszText = szDispText;
3328 *lvItem.pszText = '\0';
3329 LISTVIEW_GetItemW(hwnd, &lvItem, FALSE);
3330 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3332 /* redraw the background of the item */
3334 if(infoPtr->nColumnCount == (nItem + 1))
3335 rcTemp.right = infoPtr->rcList.right;
3337 rcTemp.right+=WIDTH_PADDING;
3338 /* The comment doesn't say WIDTH_PADDING applies to large icons */
3340 TRACE("background rect (%d,%d)-(%d,%d)\n",
3341 rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
3343 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3346 /* Figure out text colours etc. depending on state
3347 * At least the following states exist; there may be more.
3348 * Many items may be selected
3349 * At most one item may have the focus
3350 * The application may not actually be active currently
3351 * 1. The item is not selected in any way
3352 * 2. The cursor is flying over the icon or text and the text is being
3353 * expanded because it is not fully displayed currently.
3354 * 3. The item is selected and is focussed, i.e. the user has not clicked
3355 * in the blank area of the window, and the window (or application?)
3356 * still has the focus.
3357 * 4. As 3 except that a different window has the focus
3358 * 5. The item is the selected item of all the items, but the user has
3359 * clicked somewhere else on the window.
3360 * Only a few of these are handled currently. In particular 2 is not yet
3361 * handled since we do not support the functionality currently (or at least
3362 * we didn't when I wrote this)
3365 if (lvItem.state & LVIS_SELECTED)
3367 /* set item colors */
3368 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3369 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3370 SetBkMode (hdc, OPAQUE);
3371 /* set raster mode */
3372 SetROP2(hdc, R2_XORPEN);
3373 /* When exactly is it in XOR? while being dragged? */
3377 /* set item colors */
3378 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3380 SetBkMode(hdc, TRANSPARENT);
3384 SetBkMode(hdc, OPAQUE);
3385 SetBkColor(hdc, infoPtr->clrTextBk);
3388 SetTextColor(hdc, infoPtr->clrText);
3389 /* set raster mode */
3390 SetROP2(hdc, R2_COPYPEN);
3393 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3394 * wrapping and long words split.
3395 * In cases 1 and 4 only a portion of the text is displayed with word
3396 * wrapping and both word and end ellipsis. (I don't yet know about path
3399 uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
3402 DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3405 if (infoPtr->himlNormal != NULL)
3407 if (lvItem.iImage >= 0)
3409 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3411 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3415 /* Draw the text below the icon */
3417 /* Don't bother painting item being edited */
3418 if ((infoPtr->hwndEdit && (lvItem.state & LVIS_FOCUSED)) ||
3419 !lstrlenW(lvItem.pszText))
3421 SetRectEmpty(SuggestedFocus);
3425 /* Since rcItem.left is left point of icon, compute left point of item box */
3426 rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3427 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3428 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3429 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3430 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3431 infoPtr->iconSize.cx, infoPtr->nItemWidth);
3432 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3433 infoPtr->rcList.left, infoPtr->rcList.top,
3434 infoPtr->rcList.right, infoPtr->rcList.bottom,
3435 infoPtr->rcView.left, infoPtr->rcView.top,
3436 infoPtr->rcView.right, infoPtr->rcView.bottom);
3438 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3439 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3444 /* I am sure of most of the uFormat values. However I am not sure about
3445 * whether we need or do not need the following:
3446 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3447 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3448 * We certainly do not need
3449 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3450 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3453 /* If the text is being drawn without clipping (i.e. the full text) then we
3454 * need to jump through a few hoops to ensure that it all gets displayed and
3455 * that the background is complete
3457 if (uFormat & DT_NOCLIP)
3460 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3461 int dx, dy, old_wid, new_wid;
3462 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3463 /* Microsoft, in their great wisdom, have decided that the rectangle
3464 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3465 * not the location. So we have to do the centring ourselves (and take
3466 * responsibility for agreeing off-by-one consistency with them).
3468 old_wid = rcItem.right-rcItem.left;
3469 new_wid = rcBack.right - rcBack.left;
3470 dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
3471 dy = rcBack.top - rcItem.top;
3472 OffsetRect (&rcItem, dx, dy);
3473 FillRect(hdc, &rcItem, hBrush);
3474 DeleteObject(hBrush);
3476 /* else ? What if we are losing the focus? will we not get a complete
3479 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3481 CopyRect(SuggestedFocus, &rcItem);
3486 * Draws listview items when in report display mode.
3489 * [I] HWND : window handle
3490 * [I] HDC : device context handle
3495 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3497 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3498 SCROLLINFO scrollInfo;
3499 INT nDrawPosY = infoPtr->rcList.top;
3501 RECT rcItem, rcTemp;
3506 DWORD cditemmode = CDRF_DODEFAULT;
3507 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3510 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3511 scrollInfo.cbSize = sizeof(SCROLLINFO);
3512 scrollInfo.fMask = SIF_POS;
3514 nItem = ListView_GetTopIndex(hwnd);
3516 /* add 1 for displaying a partial item at the bottom */
3517 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3518 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3520 /* send cache hint notification */
3521 if (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3525 nmlv.hdr.hwndFrom = hwnd;
3526 nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID);
3527 nmlv.hdr.code = LVN_ODCACHEHINT;
3531 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3535 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3536 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3537 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3539 /* clear the background of any part of the control that doesn't contain items */
3540 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3541 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3543 /* nothing to draw */
3544 if(GETITEMCOUNT(infoPtr) == 0)
3547 /* Get scroll bar info once before loop */
3548 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3549 scrollOffset = scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
3551 for (; nItem < nLast; nItem++)
3553 RECT SuggestedFocusRect;
3556 if (lStyle & LVS_OWNERDRAWFIXED)
3558 UINT uID = GetWindowLongW( hwnd, GWL_ID);
3563 TRACE("Owner Drawn\n");
3564 dis.CtlType = ODT_LISTVIEW;
3567 dis.itemAction = ODA_DRAWENTIRE;
3570 if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3571 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3573 dis.hwndItem = hwnd;
3576 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3578 dis.rcItem.left = -scrollOffset;
3579 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3580 dis.rcItem.top = nDrawPosY;
3581 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3583 ZeroMemory(&item,sizeof(item));
3585 item.mask = LVIF_PARAM;
3586 ListView_GetItemW(hwnd, &item);
3588 dis.itemData = item.lParam;
3590 if (SendMessageW(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3592 nDrawPosY += infoPtr->nItemHeight;
3601 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3602 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3604 ir.left += REPORT_MARGINX;
3605 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3607 ir.bottom = ir.top + infoPtr->nItemHeight;
3609 CopyRect(&SuggestedFocusRect,&ir);
3612 for (j = 0; j < nColumnCount; j++)
3614 if (cdmode & CDRF_NOTIFYITEMDRAW)
3615 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3617 if (cditemmode & CDRF_SKIPDEFAULT)
3620 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3622 rcItem.left += REPORT_MARGINX;
3623 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3624 rcItem.top = nDrawPosY;
3625 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3627 /* Offset the Scroll Bar Pos */
3628 rcItem.left -= scrollOffset;
3629 rcItem.right -= scrollOffset;
3633 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3634 &SuggestedFocusRect);
3638 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, FullSelected);
3641 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3642 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3643 CDDS_ITEMPOSTPAINT);
3648 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3651 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3652 rop = SetROP2(hdc, R2_XORPEN);
3654 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3655 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3658 SetROP2(hdc, R2_COPYPEN);
3660 nDrawPosY += infoPtr->nItemHeight;
3666 * Retrieves the number of items that can fit vertically in the client area.
3669 * [I] HWND : window handle
3672 * Number of items per row.
3674 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3676 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3677 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3678 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3679 INT nCountPerRow = 1;
3683 if (uView != LVS_REPORT)
3685 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3686 if (nCountPerRow == 0) nCountPerRow = 1;
3690 return nCountPerRow;
3695 * Retrieves the number of items that can fit horizontally in the client
3699 * [I] HWND : window handle
3702 * Number of items per column.
3704 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3706 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3707 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3708 INT nCountPerColumn = 1;
3710 if (nListHeight > 0)
3712 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3713 if (nCountPerColumn == 0) nCountPerColumn = 1;
3716 return nCountPerColumn;
3721 * Retrieves the number of columns needed to display all the items when in
3722 * list display mode.
3725 * [I] HWND : window handle
3728 * Number of columns.
3730 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3732 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3733 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3734 INT nColumnCount = 0;
3736 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3738 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3739 if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3742 return nColumnCount;
3748 * Draws listview items when in list display mode.
3751 * [I] HWND : window handle
3752 * [I] HDC : device context handle
3757 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3759 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3760 RECT rcItem, FocusRect, rcTemp;
3764 INT nCountPerColumn;
3765 INT nItemWidth = infoPtr->nItemWidth;
3766 INT nItemHeight = infoPtr->nItemHeight;
3767 DWORD cditemmode = CDRF_DODEFAULT;
3769 /* get number of fully visible columns */
3770 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3771 infoPtr->nColumnCount = nColumnCount;
3772 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3773 nItem = ListView_GetTopIndex(hwnd);
3775 /* paint the background of the control that doesn't contain any items */
3776 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3777 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3779 /* nothing to draw, return here */
3780 if(GETITEMCOUNT(infoPtr) == 0)
3783 for (i = 0; i < nColumnCount; i++)
3785 for (j = 0; j < nCountPerColumn; j++, nItem++)
3787 if (nItem >= GETITEMCOUNT(infoPtr))
3790 if (cdmode & CDRF_NOTIFYITEMDRAW)
3791 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3793 if (cditemmode & CDRF_SKIPDEFAULT)
3796 rcItem.top = j * nItemHeight;
3797 rcItem.left = i * nItemWidth;
3798 rcItem.bottom = rcItem.top + nItemHeight;
3799 rcItem.right = rcItem.left + nItemWidth;
3800 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3804 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3805 Rectangle(hdc, FocusRect.left, FocusRect.top,
3806 FocusRect.right,FocusRect.bottom);
3808 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3809 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3810 CDDS_ITEMPOSTPAINT);
3818 * Draws listview items when in icon or small icon display mode.
3821 * [I] HWND : window handle
3822 * [I] HDC : device context handle
3827 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3829 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3832 RECT rcItem, SuggestedFocus, rcTemp;
3834 DWORD cditemmode = CDRF_DODEFAULT;
3836 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3837 /* DrawItem from erasing the incorrect background area */
3839 /* paint the background of the control that doesn't contain any items */
3840 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3841 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3843 /* nothing to draw, return here */
3844 if(GETITEMCOUNT(infoPtr) == 0)
3847 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3848 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3850 if (cdmode & CDRF_NOTIFYITEMDRAW)
3851 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3853 if (cditemmode & CDRF_SKIPDEFAULT)
3856 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3857 ptPosition.x += ptOrigin.x;
3858 ptPosition.y += ptOrigin.y;
3860 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3862 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3864 if (ptPosition.y < infoPtr->rcList.bottom)
3866 if (ptPosition.x < infoPtr->rcList.right)
3868 rcItem.top = ptPosition.y;
3869 rcItem.left = ptPosition.x;
3870 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3871 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3873 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3875 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3879 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3880 infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
3881 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3882 SuggestedFocus.right,SuggestedFocus.bottom);
3887 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3888 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3889 CDDS_ITEMPOSTPAINT);
3895 * Draws listview items.
3898 * [I] HWND : window handle
3899 * [I] HDC : device context handle
3904 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3906 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3907 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3913 LISTVIEW_DumpListview (infoPtr, __LINE__);
3915 GetClientRect(hwnd, &rect);
3916 cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3918 if (cdmode == CDRF_SKIPDEFAULT) return;
3921 hOldFont = SelectObject(hdc, infoPtr->hFont);
3923 /* select the dotted pen (for drawing the focus box) */
3924 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3925 hOldPen = SelectObject(hdc, hPen);
3927 /* select transparent brush (for drawing the focus box) */
3928 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3930 if (uView == LVS_LIST)
3931 LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3932 else if (uView == LVS_REPORT)
3933 LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3934 else if (uView == LVS_SMALLICON)
3935 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3936 else if (uView == LVS_ICON)
3937 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3939 /* unselect objects */
3940 SelectObject(hdc, hOldFont);
3941 SelectObject(hdc, hOldPen);
3946 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3947 LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
3953 * Calculates the approximate width and height of a given number of items.
3956 * [I] HWND : window handle
3957 * [I] INT : number of items
3962 * Returns a DWORD. The width in the low word and the height in high word.
3964 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
3965 WORD wWidth, WORD wHeight)
3967 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3968 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3969 INT nItemCountPerColumn = 1;
3970 INT nColumnCount = 0;
3971 DWORD dwViewRect = 0;
3973 if (nItemCount == -1)
3974 nItemCount = GETITEMCOUNT(infoPtr);
3976 if (uView == LVS_LIST)
3978 if (wHeight == 0xFFFF)
3980 /* use current height */
3981 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3984 if (wHeight < infoPtr->nItemHeight)
3985 wHeight = infoPtr->nItemHeight;
3989 if (infoPtr->nItemHeight > 0)
3991 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3992 if (nItemCountPerColumn == 0)
3993 nItemCountPerColumn = 1;
3995 if (nItemCount % nItemCountPerColumn != 0)
3996 nColumnCount = nItemCount / nItemCountPerColumn;
3998 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4002 /* Microsoft padding magic */
4003 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4004 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4006 dwViewRect = MAKELONG(wWidth, wHeight);
4008 else if (uView == LVS_REPORT)
4009 FIXME("uView == LVS_REPORT: not implemented\n");
4010 else if (uView == LVS_SMALLICON)
4011 FIXME("uView == LVS_SMALLICON: not implemented\n");
4012 else if (uView == LVS_ICON)
4013 FIXME("uView == LVS_ICON: not implemented\n");
4020 * Arranges listview items in icon display mode.
4023 * [I] HWND : window handle
4024 * [I] INT : alignment code
4030 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
4032 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4033 BOOL bResult = FALSE;
4035 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4040 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
4043 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
4046 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4048 case LVA_SNAPTOGRID:
4049 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4057 /* << LISTVIEW_CreateDragImage >> */
4062 * Removes all listview items and subitems.
4065 * [I] HWND : window handle
4071 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
4073 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4074 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4075 UINT uView = lStyle & LVS_TYPEMASK;
4076 LISTVIEW_ITEM *lpItem;
4077 LISTVIEW_SUBITEM *lpSubItem;
4080 BOOL bResult = FALSE;
4083 TRACE("(hwnd=%x,)\n", hwnd);
4085 LISTVIEW_RemoveAllSelections(hwnd);
4086 infoPtr->nSelectionMark=-1;
4087 infoPtr->nFocusedItem=-1;
4088 /* But we are supposed to leave nHotItem as is! */
4090 if (lStyle & LVS_OWNERDATA)
4092 infoPtr->hdpaItems->nItemCount = 0;
4093 InvalidateRect(hwnd, NULL, TRUE);
4097 if (GETITEMCOUNT(infoPtr) > 0)
4101 /* send LVN_DELETEALLITEMS notification */
4102 /* verify if subsequent LVN_DELETEITEM notifications should be
4104 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4106 bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &nmlv);
4108 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4110 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4111 if (hdpaSubItems != NULL)
4113 for (j = 1; j < hdpaSubItems->nItemCount; j++)
4115 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
4116 if (lpSubItem != NULL)
4118 /* free subitem string */
4119 if (is_textW(lpSubItem->pszText))
4120 COMCTL32_Free(lpSubItem->pszText);
4123 COMCTL32_Free(lpSubItem);
4127 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4132 /* send LVN_DELETEITEM notification */
4134 nmlv.lParam = lpItem->lParam;
4135 listview_notify(hwnd, LVN_DELETEITEM, &nmlv);
4138 /* free item string */
4139 if (is_textW(lpItem->pszText))
4140 COMCTL32_Free(lpItem->pszText);
4143 COMCTL32_Free(lpItem);
4146 DPA_Destroy(hdpaSubItems);
4150 /* reinitialize listview memory */
4151 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
4153 /* align items (set position of each item) */
4154 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4156 if (lStyle & LVS_ALIGNLEFT)
4158 LISTVIEW_AlignLeft(hwnd);
4162 LISTVIEW_AlignTop(hwnd);
4166 LISTVIEW_UpdateScroll(hwnd);
4168 /* invalidate client area (optimization needed) */
4169 InvalidateRect(hwnd, NULL, TRUE);
4177 * Removes a column from the listview control.
4180 * [I] HWND : window handle
4181 * [I] INT : column index
4187 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
4189 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4190 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4191 UINT uOwnerData = GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA;
4192 BOOL bResult = FALSE;
4194 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
4197 bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
4199 /* Need to reset the item width when deleting a column */
4200 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
4202 /* reset scroll parameters */
4203 if (uView == LVS_REPORT)
4205 /* update scrollbar(s) */
4206 LISTVIEW_UpdateScroll(hwnd);
4208 /* refresh client area */
4209 InvalidateRect(hwnd, NULL, FALSE);
4218 * Removes an item from the listview control.
4221 * [I] HWND : window handle
4222 * [I] INT : item index
4228 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
4230 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4231 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4232 UINT uView = lStyle & LVS_TYPEMASK;
4233 LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID);
4235 BOOL bResult = FALSE;
4237 LISTVIEW_ITEM *lpItem;
4238 LISTVIEW_SUBITEM *lpSubItem;
4242 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
4245 /* First, send LVN_DELETEITEM notification. */
4246 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4247 nmlv.hdr.hwndFrom = hwnd;
4248 nmlv.hdr.idFrom = lCtrlId;
4249 nmlv.hdr.code = LVN_DELETEITEM;
4251 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
4255 /* remove it from the selection range */
4256 ZeroMemory(&item,sizeof(item));
4257 item.stateMask = LVIS_SELECTED;
4258 LISTVIEW_SetItemState(hwnd,nItem,&item);
4260 if (lStyle & LVS_OWNERDATA)
4262 infoPtr->hdpaItems->nItemCount --;
4263 InvalidateRect(hwnd, NULL, TRUE);
4267 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4269 /* initialize memory */
4270 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4272 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4273 if (hdpaSubItems != NULL)
4275 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4277 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4278 if (lpSubItem != NULL)
4280 /* free item string */
4281 if (is_textW(lpSubItem->pszText))
4282 COMCTL32_Free(lpSubItem->pszText);
4285 COMCTL32_Free(lpSubItem);
4289 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4292 /* free item string */
4293 if (is_textW(lpItem->pszText))
4294 COMCTL32_Free(lpItem->pszText);
4297 COMCTL32_Free(lpItem);
4300 bResult = DPA_Destroy(hdpaSubItems);
4303 LISTVIEW_ShiftIndices(hwnd,nItem,-1);
4305 /* align items (set position of each item) */
4306 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4308 if (lStyle & LVS_ALIGNLEFT)
4309 LISTVIEW_AlignLeft(hwnd);
4311 LISTVIEW_AlignTop(hwnd);
4314 LISTVIEW_UpdateScroll(hwnd);
4316 /* refresh client area */
4317 InvalidateRect(hwnd, NULL, TRUE);
4326 * Return edit control handle of current edit label
4329 * [I] HWND : window handle
4335 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4337 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4338 return infoPtr->hwndEdit;
4344 * Callback implementation for editlabel control
4347 * [I] HWND : window handle
4348 * [I] LPSTR : modified text
4349 * [I] DWORD : item index
4350 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4356 static BOOL LISTVIEW_EndEditLabelT(HWND hwnd, LPWSTR pszText, DWORD nItem, BOOL isW)
4358 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4359 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4360 NMLVDISPINFOW dispInfo;
4361 LISTVIEW_ITEM *lpItem;
4363 LISTVIEW_ITEM lvItemRef;
4365 BOOL bResult = TRUE;
4367 TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd, debugstr_t(pszText, isW), nItem, isW);
4369 if (!(lStyle & LVS_OWNERDATA))
4371 if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4374 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4379 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4380 ZeroMemory(&item,sizeof(item));
4383 item.mask = LVIF_PARAM | LVIF_STATE;
4384 ListView_GetItemW(hwnd, &item);
4385 lvItemRef.state = item.state;
4386 lvItemRef.iImage = item.iImage;
4387 lvItemRef.lParam = item.lParam;
4388 lpItem = &lvItemRef;
4391 ZeroMemory(&dispInfo, sizeof(dispInfo));
4392 dispInfo.item.mask = 0;
4393 dispInfo.item.iItem = nItem;
4394 dispInfo.item.state = lpItem->state;
4395 dispInfo.item.stateMask = 0;
4396 dispInfo.item.pszText = pszText;
4397 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4398 dispInfo.item.iImage = lpItem->iImage;
4399 dispInfo.item.lParam = lpItem->lParam;
4400 infoPtr->hwndEdit = 0;
4402 /* Do we need to update the Item Text */
4403 if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, isW))
4404 if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
4405 bResult = textsetptrT(&lpItem->pszText, pszText, isW);
4412 * Callback implementation for editlabel control
4415 * [I] HWND : window handle
4416 * [I] LPSTR : modified text
4417 * [I] DWORD : item index
4423 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem)
4425 return LISTVIEW_EndEditLabelT(hwnd, pszText, nItem, TRUE);
4430 * Callback implementation for editlabel control
4433 * [I] HWND : window handle
4434 * [I] LPSTR : modified text
4435 * [I] DWORD : item index
4441 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem)
4443 return LISTVIEW_EndEditLabelT(hwnd, (LPWSTR)pszText, nItem, FALSE);
4448 * Begin in place editing of specified list view item
4451 * [I] HWND : window handle
4452 * [I] INT : item index
4453 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4459 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW)
4461 NMLVDISPINFOW dispInfo;
4463 LISTVIEW_ITEM *lpItem;
4465 HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE);
4466 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4468 WCHAR szDispText[DISP_TEXT_SIZE];
4470 LISTVIEW_ITEM lvItemRef;
4471 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4473 if (~GetWindowLongW(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4476 /* Is the EditBox still there, if so remove it */
4477 if(infoPtr->hwndEdit != 0)
4480 LISTVIEW_SetSelection(hwnd, nItem);
4481 LISTVIEW_SetItemFocus(hwnd, nItem);
4483 if (!(lStyle & LVS_OWNERDATA))
4485 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4488 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4494 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4495 ZeroMemory(&item, sizeof(item));
4498 item.mask = LVIF_PARAM | LVIF_STATE;
4499 ListView_GetItemW(hwnd, &item);
4500 lvItemRef.iImage = item.iImage;
4501 lvItemRef.state = item.state;
4502 lvItemRef.lParam = item.lParam;
4503 lpItem = &lvItemRef;
4506 /* get information needed for drawing the item */
4507 ZeroMemory(&lvItem, sizeof(lvItem));
4508 lvItem.mask = LVIF_TEXT;
4509 lvItem.iItem = nItem;
4510 lvItem.iSubItem = 0;
4511 lvItem.cchTextMax = DISP_TEXT_SIZE;
4512 lvItem.pszText = szDispText;
4513 *lvItem.pszText = '\0';
4514 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW);
4516 ZeroMemory(&dispInfo, sizeof(dispInfo));
4517 dispInfo.item.mask = 0;
4518 dispInfo.item.iItem = nItem;
4519 dispInfo.item.state = lpItem->state;
4520 dispInfo.item.stateMask = 0;
4521 dispInfo.item.pszText = lvItem.pszText;
4522 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4523 dispInfo.item.iImage = lpItem->iImage;
4524 dispInfo.item.lParam = lpItem->lParam;
4526 if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW))
4529 rect.left = LVIR_LABEL;
4530 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4533 if (!(hedit = CreateEditLabelT(szDispText , WS_VISIBLE,
4534 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, hwnd, hinst,
4535 isW ? LISTVIEW_EndEditLabelW : (EditlblCallbackW)LISTVIEW_EndEditLabelA,
4539 infoPtr->hwndEdit = hedit;
4541 SendMessageW(hedit, EM_SETSEL, 0, -1);
4549 * Ensures the specified item is visible, scrolling into view if necessary.
4552 * [I] HWND : window handle
4553 * [I] INT : item index
4554 * [I] BOOL : partially or entirely visible
4560 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4562 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4563 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4564 INT nScrollPosHeight = 0;
4565 INT nScrollPosWidth = 0;
4566 SCROLLINFO scrollInfo;
4568 BOOL bRedraw = FALSE;
4570 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4571 scrollInfo.cbSize = sizeof(SCROLLINFO);
4572 scrollInfo.fMask = SIF_POS;
4574 /* ALWAYS bPartial == FALSE, FOR NOW! */
4576 rcItem.left = LVIR_BOUNDS;
4577 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4579 if (rcItem.left < infoPtr->rcList.left)
4581 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4585 if (uView == LVS_LIST)
4587 nScrollPosWidth = infoPtr->nItemWidth;
4588 rcItem.left += infoPtr->rcList.left;
4590 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4592 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4593 rcItem.left += infoPtr->rcList.left;
4596 /* When in LVS_REPORT view, the scroll position should
4598 if (nScrollPosWidth != 0)
4600 if (rcItem.left % nScrollPosWidth == 0)
4601 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4603 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4605 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4609 else if (rcItem.right > infoPtr->rcList.right)
4611 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4615 if (uView == LVS_LIST)
4617 rcItem.right -= infoPtr->rcList.right;
4618 nScrollPosWidth = infoPtr->nItemWidth;
4620 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4622 rcItem.right -= infoPtr->rcList.right;
4623 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4626 /* When in LVS_REPORT view, the scroll position should
4628 if (nScrollPosWidth != 0)
4630 if (rcItem.right % nScrollPosWidth == 0)
4631 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4633 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4635 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4640 if (rcItem.top < infoPtr->rcList.top)
4644 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4646 if (uView == LVS_REPORT)
4648 rcItem.top -= infoPtr->rcList.top;
4649 nScrollPosHeight = infoPtr->nItemHeight;
4651 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4653 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4654 rcItem.top += infoPtr->rcList.top;
4657 if (rcItem.top % nScrollPosHeight == 0)
4658 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4660 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4662 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4665 else if (rcItem.bottom > infoPtr->rcList.bottom)
4669 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4671 if (uView == LVS_REPORT)
4673 rcItem.bottom -= infoPtr->rcList.bottom;
4674 nScrollPosHeight = infoPtr->nItemHeight;
4676 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4678 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4679 rcItem.bottom -= infoPtr->rcList.bottom;
4682 if (rcItem.bottom % nScrollPosHeight == 0)
4683 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4685 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4687 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4693 InvalidateRect(hwnd,NULL,TRUE);
4699 * Retrieves the nearest item, given a position and a direction.
4702 * [I] HWND : window handle
4703 * [I] POINT : start position
4704 * [I] UINT : direction
4707 * Item index if successdful, -1 otherwise.
4709 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4711 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4716 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4717 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4718 ((vkDirection == VK_UP) ? "VK_UP" :
4719 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4721 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4723 ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4724 LISTVIEW_GetOrigin(hwnd, &lvIntHit.ht.pt);
4725 lvIntHit.ht.pt.x += pt.x;
4726 lvIntHit.ht.pt.y += pt.y;
4728 if (vkDirection == VK_DOWN)
4729 lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4730 else if (vkDirection == VK_UP)
4731 lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4732 else if (vkDirection == VK_LEFT)
4733 lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4734 else if (vkDirection == VK_RIGHT)
4735 lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4737 if (PtInRect(&rcView, lvIntHit.ht.pt) == FALSE)
4741 nItem = LISTVIEW_SuperHitTestItem(hwnd, &lvIntHit, TRUE);
4742 return nItem == -1 ? lvIntHit.iDistItem : nItem;
4751 * Searches for an item with specific characteristics.
4754 * [I] hwnd : window handle
4755 * [I] nStart : base item index
4756 * [I] lpFindInfo : item information to look for
4759 * SUCCESS : index of item
4762 static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart,
4763 LPLVFINDINFOW lpFindInfo)
4765 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4767 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4771 INT nLast = GETITEMCOUNT(infoPtr);
4773 if ((nItem >= -1) && (lpFindInfo != NULL))
4775 ZeroMemory(&lvItem, sizeof(lvItem));
4777 if (lpFindInfo->flags & LVFI_PARAM)
4779 lvItem.mask |= LVIF_PARAM;
4782 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4784 lvItem.mask |= LVIF_TEXT;
4785 lvItem.pszText = szDispText;
4786 lvItem.cchTextMax = DISP_TEXT_SIZE;
4789 if (lpFindInfo->flags & LVFI_WRAP)
4792 if (lpFindInfo->flags & LVFI_NEARESTXY)
4794 ptItem.x = lpFindInfo->pt.x;
4795 ptItem.y = lpFindInfo->pt.y;
4800 while (nItem < nLast)
4802 if (lpFindInfo->flags & LVFI_NEARESTXY)
4804 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4805 lpFindInfo->vkDirection);
4808 /* get position of the new item index */
4809 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4820 lvItem.iItem = nItem;
4821 lvItem.iSubItem = 0;
4822 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
4824 if (lvItem.mask & LVIF_TEXT)
4826 if (lpFindInfo->flags & LVFI_PARTIAL)
4828 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4833 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4838 if (lvItem.mask & LVIF_PARAM)
4840 if (lpFindInfo->lParam != lvItem.lParam)
4866 * Searches for an item with specific characteristics.
4869 * [I] hwnd : window handle
4870 * [I] nStart : base item index
4871 * [I] lpFindInfo : item information to look for
4874 * SUCCESS : index of item
4877 static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart,
4878 LPLVFINDINFOA lpFindInfo)
4880 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4884 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4885 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4886 res = LISTVIEW_FindItemW(hwnd, nStart, &fiw);
4887 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4893 * Retrieves the background color of the listview control.
4896 * [I] HWND : window handle
4899 * COLORREF associated with the background.
4901 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4903 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4905 return infoPtr->clrBk;
4910 * Retrieves the background image of the listview control.
4913 * [I] HWND : window handle
4914 * [O] LPLVMKBIMAGE : background image attributes
4920 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4922 /* FIXME (listview, "empty stub!\n"); */
4928 * Retrieves the callback mask.
4931 * [I] HWND : window handle
4936 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4938 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4940 return infoPtr->uCallbackMask;
4945 * Retrieves column attributes.
4948 * [I] HWND : window handle
4949 * [I] INT : column index
4950 * [IO] LPLVCOLUMNW : column information
4951 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4952 * otherwise it is in fact a LPLVCOLUMNA
4958 static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4960 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4962 BOOL bResult = FALSE;
4964 if (lpColumn != NULL)
4967 TRACE("(hwnd=%x, col=%d, lpColumn=%s, isW=%d)\n",
4968 hwnd, nItem, debuglvcolumn_t(lpColumn, isW), isW);
4970 /* initialize memory */
4971 ZeroMemory(&hdi, sizeof(hdi));
4973 if (lpColumn->mask & LVCF_FMT)
4974 hdi.mask |= HDI_FORMAT;
4976 if (lpColumn->mask & LVCF_WIDTH)
4977 hdi.mask |= HDI_WIDTH;
4979 if (lpColumn->mask & LVCF_TEXT)
4981 hdi.mask |= HDI_TEXT;
4982 hdi.cchTextMax = lpColumn->cchTextMax;
4983 hdi.pszText = lpColumn->pszText;
4986 if (lpColumn->mask & LVCF_IMAGE)
4987 hdi.mask |= HDI_IMAGE;
4989 if (lpColumn->mask & LVCF_ORDER)
4990 hdi.mask |= HDI_ORDER;
4993 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4995 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4997 if (bResult != FALSE)
4999 if (lpColumn->mask & LVCF_FMT)
5003 if (hdi.fmt & HDF_LEFT)
5004 lpColumn->fmt |= LVCFMT_LEFT;
5005 else if (hdi.fmt & HDF_RIGHT)
5006 lpColumn->fmt |= LVCFMT_RIGHT;
5007 else if (hdi.fmt & HDF_CENTER)
5008 lpColumn->fmt |= LVCFMT_CENTER;
5010 if (hdi.fmt & HDF_IMAGE)
5011 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
5013 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
5014 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
5017 if (lpColumn->mask & LVCF_WIDTH)
5018 lpColumn->cx = hdi.cxy;
5020 if (lpColumn->mask & LVCF_IMAGE)
5021 lpColumn->iImage = hdi.iImage;
5023 if (lpColumn->mask & LVCF_ORDER)
5024 lpColumn->iOrder = hdi.iOrder;
5032 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
5034 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
5041 for (i = 0; i < iCount; i++)
5049 * Retrieves the column width.
5052 * [I] HWND : window handle
5053 * [I] int : column index
5056 * SUCCESS : column width
5059 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
5061 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5062 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5063 INT nColumnWidth = 0;
5066 if (uView == LVS_LIST)
5068 nColumnWidth = infoPtr->nItemWidth;
5070 else if (uView == LVS_REPORT)
5072 /* get column width from header */
5073 ZeroMemory(&hdi, sizeof(hdi));
5074 hdi.mask = HDI_WIDTH;
5075 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
5076 nColumnWidth = hdi.cxy;
5079 return nColumnWidth;
5084 * In list or report display mode, retrieves the number of items that can fit
5085 * vertically in the visible area. In icon or small icon display mode,
5086 * retrieves the total number of visible items.
5089 * [I] HWND : window handle
5092 * Number of fully visible items.
5094 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
5096 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5097 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5100 if (uView == LVS_LIST)
5102 if (infoPtr->rcList.right > infoPtr->nItemWidth)
5104 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
5105 LISTVIEW_GetCountPerColumn(hwnd);
5108 else if (uView == LVS_REPORT)
5110 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
5114 nItemCount = GETITEMCOUNT(infoPtr);
5120 /* LISTVIEW_GetEditControl */
5124 * Retrieves the extended listview style.
5127 * [I] HWND : window handle
5130 * SUCCESS : previous style
5133 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
5135 LISTVIEW_INFO *infoPtr;
5137 /* make sure we can get the listview info */
5138 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
5141 return (infoPtr->dwExStyle);
5146 * Retrieves the handle to the header control.
5149 * [I] HWND : window handle
5154 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
5156 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5158 return infoPtr->hwndHeader;
5161 /* LISTVIEW_GetHotCursor */
5165 * Returns the time that the mouse cursor must hover over an item
5166 * before it is selected.
5169 * [I] HWND : window handle
5172 * Returns the previously set hover time or (DWORD)-1 to indicate that the
5173 * hover time is set to the default hover time.
5175 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
5177 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5179 return infoPtr->dwHoverTime;
5184 * Retrieves an image list handle.
5187 * [I] HWND : window handle
5188 * [I] INT : image list identifier
5191 * SUCCESS : image list handle
5194 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
5196 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5197 HIMAGELIST himl = NULL;
5202 himl = infoPtr->himlNormal;
5205 himl = infoPtr->himlSmall;
5208 himl = infoPtr->himlState;
5212 return (LRESULT)himl;
5215 /* LISTVIEW_GetISearchString */
5219 * Retrieves item attributes.
5222 * [I] hwnd : window handle
5223 * [IO] lpLVItem : item info
5224 * [I] internal : if true then we will use tricks that avoid copies
5225 * but are not compatible with the regular interface
5226 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5227 * if FALSE, the lpLVItem is a LPLVITEMA.
5233 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
5235 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5236 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5237 NMLVDISPINFOW dispInfo;
5238 LISTVIEW_SUBITEM *lpSubItem;
5239 LISTVIEW_ITEM *lpItem;
5242 INT* piImage = (INT*)&null;
5243 LPWSTR* ppszText= (LPWSTR*)&null;
5244 LPARAM* plParam = (LPARAM*)&null;
5246 if (internal && !isW)
5248 ERR("We can't have internal non-Unicode GetItem!\n");
5252 /* In the following:
5253 * lpLVItem describes the information requested by the user
5254 * lpItem/lpSubItem is what we have
5255 * dispInfo is a structure we use to request the missing
5256 * information from the application
5259 TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n",
5260 hwnd, debuglvitem_t(lpLVItem, isW), internal, isW);
5262 if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5263 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5266 ZeroMemory(&dispInfo, sizeof(dispInfo));
5268 if (lStyle & LVS_OWNERDATA)
5270 if (lpLVItem->mask & ~LVIF_STATE)
5272 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5273 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5274 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5275 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5278 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5280 lpLVItem->state = 0;
5281 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5282 lpLVItem->state |= LVIS_FOCUSED;
5283 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5284 lpLVItem->state |= LVIS_SELECTED;
5290 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5291 if (hdpaSubItems == NULL) return FALSE;
5293 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5296 ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5297 if (lpLVItem->iSubItem == 0)
5299 piImage=&lpItem->iImage;
5300 ppszText=&lpItem->pszText;
5301 plParam=&lpItem->lParam;
5302 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5304 dispInfo.item.mask |= LVIF_STATE;
5305 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5310 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5311 if (lpSubItem != NULL)
5313 piImage=&lpSubItem->iImage;
5314 ppszText=&lpSubItem->pszText;
5318 if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
5320 dispInfo.item.mask |= LVIF_IMAGE;
5323 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5325 dispInfo.item.mask |= LVIF_TEXT;
5326 dispInfo.item.pszText = lpLVItem->pszText;
5327 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5328 if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5329 *dispInfo.item.pszText = '\0';
5330 if (dispInfo.item.pszText && (*ppszText == NULL))
5331 *dispInfo.item.pszText = '\0';
5334 if (dispInfo.item.mask != 0)
5336 /* We don't have all the requested info, query the application */
5337 dispInfo.item.iItem = lpLVItem->iItem;
5338 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5339 dispInfo.item.lParam = lpItem->lParam;
5340 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5341 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5344 if (dispInfo.item.mask & LVIF_IMAGE)
5346 lpLVItem->iImage = dispInfo.item.iImage;
5347 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
5348 *piImage = dispInfo.item.iImage;
5350 else if (lpLVItem->mask & LVIF_IMAGE)
5352 lpLVItem->iImage = *piImage;
5355 if (dispInfo.item.mask & LVIF_PARAM)
5357 lpLVItem->lParam = dispInfo.item.lParam;
5358 if (dispInfo.item.mask & LVIF_DI_SETITEM)
5359 *plParam = dispInfo.item.lParam;
5361 else if (lpLVItem->mask & LVIF_PARAM)
5362 lpLVItem->lParam = lpItem->lParam;
5364 if (dispInfo.item.mask & LVIF_TEXT)
5366 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5367 textsetptrT(ppszText, dispInfo.item.pszText, isW);
5369 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5370 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5371 if (lpLVItem->pszText != dispInfo.item.pszText)
5372 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5375 else if (lpLVItem->mask & LVIF_TEXT)
5377 if (internal) lpLVItem->pszText = *ppszText;
5378 else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5381 if (lpLVItem->iSubItem == 0)
5383 if (dispInfo.item.mask & LVIF_STATE)
5385 lpLVItem->state = lpItem->state;
5386 lpLVItem->state &= ~dispInfo.item.stateMask;
5387 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5389 lpLVItem->state &= ~LVIS_SELECTED;
5390 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5391 LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem))
5392 lpLVItem->state |= LVIS_SELECTED;
5394 else if (lpLVItem->mask & LVIF_STATE)
5396 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5398 lpLVItem->state &= ~LVIS_SELECTED;
5399 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5400 LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5401 lpLVItem->state |= LVIS_SELECTED;
5404 if (lpLVItem->mask & LVIF_PARAM)
5405 lpLVItem->lParam = lpItem->lParam;
5407 if (lpLVItem->mask & LVIF_INDENT)
5408 lpLVItem->iIndent = lpItem->iIndent;
5414 /* LISTVIEW_GetHotCursor */
5418 * Retrieves the index of the hot item.
5421 * [I] HWND : window handle
5424 * SUCCESS : hot item index
5425 * FAILURE : -1 (no hot item)
5427 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5429 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5431 return infoPtr->nHotItem;
5434 /* LISTVIEW_GetHoverTime */
5438 * Retrieves the number of items in the listview control.
5441 * [I] HWND : window handle
5446 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5448 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5450 return GETITEMCOUNT(infoPtr);
5455 * Retrieves the rectangle enclosing the item icon and text.
5458 * [I] HWND : window handle
5459 * [I] INT : item index
5460 * [O] LPRECT : coordinate information
5466 static BOOL LISTVIEW_GetItemBoundBox(HWND hwnd, INT nItem, LPRECT lpRect)
5468 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5469 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5470 UINT uView = lStyle & LVS_TYPEMASK;
5471 BOOL bResult = FALSE;
5473 LISTVIEW_ITEM *lpItem;
5474 INT nCountPerColumn;
5477 TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd, nItem, lpRect);
5479 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5482 if (uView == LVS_LIST)
5485 nItem = nItem - ListView_GetTopIndex(hwnd);
5486 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5489 nRow = nItem % nCountPerColumn;
5492 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5497 lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5498 lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5503 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5504 lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
5507 else if (uView == LVS_REPORT)
5510 lpRect->left = REPORT_MARGINX;
5511 lpRect->top = ((nItem - ListView_GetTopIndex(hwnd)) *
5512 infoPtr->nItemHeight) + infoPtr->rcList.top;
5514 if (!(lStyle & LVS_NOSCROLL))
5516 SCROLLINFO scrollInfo;
5517 /* Adjust position by scrollbar offset */
5518 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5519 scrollInfo.cbSize = sizeof(SCROLLINFO);
5520 scrollInfo.fMask = SIF_POS;
5521 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5522 lpRect->left -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5525 else /* either LVS_ICON or LVS_SMALLICON */
5527 if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
5529 if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
5532 lpRect->left = lpItem->ptPosition.x;
5533 lpRect->top = lpItem->ptPosition.y;
5538 lpRect->right = lpRect->left + infoPtr->nItemWidth;
5539 lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
5540 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5541 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
5547 * Retrieves the position (upper-left) of the listview control item.
5548 * Note that for LVS_ICON style, the upper-left is that of the icon
5549 * and not the bounding box.
5552 * [I] HWND : window handle
5553 * [I] INT : item index
5554 * [O] LPPOINT : coordinate information
5560 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, LPPOINT lpptPosition)
5562 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5563 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5564 BOOL bResult = FALSE;
5567 TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd, nItem, lpptPosition);
5569 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5570 (lpptPosition != NULL))
5572 bResult = LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcBounding);
5573 lpptPosition->x = rcBounding.left;
5574 lpptPosition->y = rcBounding.top;
5575 if (uView == LVS_ICON)
5577 lpptPosition->y += ICON_TOP_PADDING;
5578 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5580 TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
5581 lpptPosition->x, lpptPosition->y);
5587 * Update the bounding rectangle around the text under a large icon.
5588 * This depends on whether it has the focus or not.
5589 * On entry the rectangle's top, left and right should be set.
5590 * On return the bottom will also be set and the width may have been
5593 * This appears to be weird, even in the Microsoft implementation.
5596 static void ListView_UpdateLargeItemLabelRect (
5597 HWND hwnd, /* The window of the listview */
5598 const LISTVIEW_INFO *infoPtr, /* The listview itself */
5599 int nItem, /* The item for which we are calculating this */
5600 RECT *rect) /* The rectangle to be updated */
5602 HDC hdc = GetDC (hwnd);
5603 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
5605 if (infoPtr->bFocus && infoPtr->nFocusedItem == nItem)
5607 /* We (aim to) display the full text. In Windows 95 it appears to
5608 * calculate the size assuming the specified font and then it draws
5609 * the text in that region with the specified font except scaled to
5610 * 10 point (or the height of the system font or ...). Thus if the
5611 * window has 24 point Helvetica the highlit rectangle will be
5612 * taller than the text and if it is 7 point Helvetica then the text
5614 * For now we will simply say that it is the correct size to display
5615 * the text in the specified font.
5618 lvItem.mask = LVIF_TEXT;
5619 lvItem.iItem = nItem;
5620 lvItem.iSubItem = 0;
5621 /* We will specify INTERNAL and so will receive back a const
5622 * pointer to the text, rather than specifying a buffer to which
5625 LISTVIEW_GetItemW (hwnd, &lvItem, TRUE);
5626 DrawTextW (hdc, lvItem.pszText, -1, rect, DT_CALCRECT |
5627 DT_NOCLIP | DT_EDITCONTROL | DT_TOP | DT_CENTER |
5628 DT_WORDBREAK | DT_NOPREFIX);
5629 /* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
5633 /* As far as I can see the text region seems to be trying to be
5634 * "tall enough for two lines of text". Once again (comctl32.dll ver
5635 * 5.81?) it measures this on the basis of the selected font and then
5636 * draws it with the same font except in 10 point size. This can lead
5637 * to more or less than the two rows appearing.
5638 * Question; are we supposed to be including DT_EXTERNALLEADING?
5639 * Question; should the width be shrunk to the space required to
5640 * display the two lines?
5642 rect->bottom = rect->top + 2 * infoPtr->ntmHeight;
5645 SelectObject (hdc, hOldFont);
5646 ReleaseDC (hwnd, hdc);
5651 * Retrieves the bounding rectangle for a listview control item.
5654 * [I] HWND : window handle
5655 * [I] INT : item index
5656 * [IO] LPRECT : bounding rectangle coordinates
5657 * lprc->left specifies the portion of the item for which the bounding
5658 * rectangle will be retrieved.
5660 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5661 * including the icon and label.
5662 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5663 * LVIR_LABEL Returns the bounding rectangle of the item text.
5664 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5665 * rectangles, but excludes columns in report view.
5672 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5673 * upon whether the window has the focus currently and on whether the item
5674 * is the one with the focus. Ensure that the control's record of which
5675 * item has the focus agrees with the items' records.
5677 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5679 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5680 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5681 BOOL bResult = FALSE;
5690 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5692 if (uView & LVS_REPORT)
5694 ZeroMemory(&lvItem, sizeof(lvItem));
5695 lvItem.mask = LVIF_INDENT;
5696 lvItem.iItem = nItem;
5697 lvItem.iSubItem = 0;
5698 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
5701 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5702 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5709 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5714 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5715 if (uView == LVS_ICON)
5717 if (infoPtr->himlNormal != NULL)
5719 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5722 lprc->left = ptItem.x + ptOrigin.x;
5723 lprc->top = ptItem.y + ptOrigin.y;
5724 lprc->right = lprc->left + infoPtr->iconSize.cx;
5725 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5726 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5730 else if (uView == LVS_SMALLICON)
5732 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5735 lprc->left = ptItem.x + ptOrigin.x;
5736 lprc->top = ptItem.y + ptOrigin.y;
5737 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5739 if (infoPtr->himlState != NULL)
5740 lprc->left += infoPtr->iconSize.cx;
5742 if (infoPtr->himlSmall != NULL)
5743 lprc->right = lprc->left + infoPtr->iconSize.cx;
5745 lprc->right = lprc->left;
5751 lprc->left = ptItem.x;
5752 if (uView & LVS_REPORT)
5753 lprc->left += nIndent;
5754 lprc->top = ptItem.y;
5755 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5757 if (infoPtr->himlState != NULL)
5758 lprc->left += infoPtr->iconSize.cx;
5760 if (infoPtr->himlSmall != NULL)
5761 lprc->right = lprc->left + infoPtr->iconSize.cx;
5763 lprc->right = lprc->left;
5768 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5769 if (uView == LVS_ICON)
5771 if (infoPtr->himlNormal != NULL)
5773 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5776 lprc->left = ptItem.x + ptOrigin.x;
5777 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5778 ICON_BOTTOM_PADDING);
5779 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5780 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5782 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5783 lprc->right = lprc->left + nLabelWidth;
5788 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5789 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, lprc);
5794 else if (uView == LVS_SMALLICON)
5796 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5799 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5800 lprc->top = ptItem.y + ptOrigin.y;
5801 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5803 if (infoPtr->himlState != NULL)
5804 lprc->left += infoPtr->iconSize.cx;
5806 if (infoPtr->himlSmall != NULL)
5807 lprc->left += infoPtr->iconSize.cx;
5809 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5810 nLabelWidth += TRAILING_PADDING;
5811 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5812 lprc->right = lprc->left + nLabelWidth;
5814 lprc->right = nLeftPos + infoPtr->nItemWidth;
5820 if (uView == LVS_REPORT)
5821 nLeftPos = lprc->left = ptItem.x + nIndent;
5823 nLeftPos = lprc->left = ptItem.x;
5824 lprc->top = ptItem.y;
5825 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5827 if (infoPtr->himlState != NULL)
5828 lprc->left += infoPtr->iconSize.cx;
5830 if (infoPtr->himlSmall != NULL)
5831 lprc->left += infoPtr->iconSize.cx;
5833 if (uView != LVS_REPORT)
5835 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5836 nLabelWidth += TRAILING_PADDING;
5837 if (infoPtr->himlSmall)
5838 nLabelWidth += IMAGE_PADDING;
5841 nLabelWidth = LISTVIEW_GetColumnWidth(hwnd, 0)-lprc->left;
5842 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5843 lprc->right = lprc->left + nLabelWidth;
5845 lprc->right = nLeftPos + infoPtr->nItemWidth;
5850 if (!LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcInternal)) break;
5851 ptItem.x = rcInternal.left;
5852 ptItem.y = rcInternal.top;
5853 if (uView == LVS_ICON)
5855 if (infoPtr->himlNormal != NULL)
5857 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5860 INT text_left, text_right, icon_left, text_pos_x;
5861 /* for style LVS_ICON bounds
5862 * left = min(icon.left, text.left)
5863 * right = max(icon.right, text.right)
5864 * top = boundbox.top + NOTHITABLE
5865 * bottom = text.bottom + 1
5868 icon_left = text_left = ptItem.x;
5870 /* Correct ptItem to icon upper-left */
5871 icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5872 ptItem.y += ICON_TOP_PADDING;
5874 /* Compute the label left and right */
5875 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5876 text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
5879 text_left += text_pos_x / 2;
5880 text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
5885 text_right = text_left + infoPtr->iconSpacing.cx - 1;
5888 /* Compute rectangle w/o the text height */
5889 lprc->left = min(icon_left, text_left) + ptOrigin.x;
5890 lprc->right = max(icon_left + infoPtr->iconSize.cx,
5891 text_right) + ptOrigin.x;
5892 lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
5893 lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
5894 + infoPtr->iconSize.cy + 1
5895 + ICON_BOTTOM_PADDING;
5897 CopyRect (&label_rect, lprc);
5898 label_rect.top = lprc->bottom;
5899 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, &label_rect);
5900 UnionRect (lprc, lprc, &label_rect);
5904 else if (uView == LVS_SMALLICON)
5906 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5909 lprc->left = ptItem.x + ptOrigin.x;
5910 lprc->right = lprc->left;
5911 lprc->top = ptItem.y + ptOrigin.y;
5912 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5913 if (infoPtr->himlState != NULL)
5914 lprc->right += infoPtr->iconSize.cx;
5915 if (infoPtr->himlSmall != NULL)
5916 lprc->right += infoPtr->iconSize.cx;
5918 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5919 nLabelWidth += TRAILING_PADDING;
5920 if (infoPtr->himlSmall)
5921 nLabelWidth += IMAGE_PADDING;
5922 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5923 lprc->right += nLabelWidth;
5925 lprc->right = lprc->left + infoPtr->nItemWidth;
5931 lprc->left = ptItem.x;
5932 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5933 lprc->left += nIndent;
5934 lprc->right = lprc->left;
5935 lprc->top = ptItem.y;
5936 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5938 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
5941 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5942 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5944 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5948 if (infoPtr->himlState != NULL)
5949 lprc->right += infoPtr->iconSize.cx;
5951 if (infoPtr->himlSmall != NULL)
5952 lprc->right += infoPtr->iconSize.cx;
5954 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5955 nLabelWidth += TRAILING_PADDING;
5956 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5957 lprc->right += nLabelWidth;
5959 lprc->right = lprc->left + infoPtr->nItemWidth;
5964 case LVIR_SELECTBOUNDS:
5965 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5966 if (uView == LVS_ICON)
5968 if (infoPtr->himlNormal != NULL)
5970 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5973 lprc->left = ptItem.x + ptOrigin.x;
5974 lprc->top = ptItem.y + ptOrigin.y;
5975 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5976 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5980 else if (uView == LVS_SMALLICON)
5982 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5985 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5986 lprc->top = ptItem.y + ptOrigin.y;
5987 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5989 if (infoPtr->himlState != NULL)
5990 lprc->left += infoPtr->iconSize.cx;
5992 lprc->right = lprc->left;
5994 if (infoPtr->himlSmall != NULL)
5995 lprc->right += infoPtr->iconSize.cx;
5997 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5998 nLabelWidth += TRAILING_PADDING;
5999 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6000 lprc->right += nLabelWidth;
6002 lprc->right = nLeftPos + infoPtr->nItemWidth;
6008 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
6009 nLeftPos = lprc->left = ptItem.x + nIndent;
6011 nLeftPos = lprc->left = ptItem.x;
6012 lprc->top = ptItem.y;
6013 lprc->bottom = lprc->top + infoPtr->nItemHeight;
6015 if (infoPtr->himlState != NULL)
6016 lprc->left += infoPtr->iconSize.cx;
6018 lprc->right = lprc->left;
6020 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
6023 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
6024 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
6026 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
6030 if (infoPtr->himlSmall != NULL)
6031 lprc->right += infoPtr->iconSize.cx;
6033 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6034 nLabelWidth += TRAILING_PADDING;
6035 if (infoPtr->himlSmall)
6036 nLabelWidth += IMAGE_PADDING;
6037 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6038 lprc->right += nLabelWidth;
6040 lprc->right = nLeftPos + infoPtr->nItemWidth;
6047 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
6048 lprc->left, lprc->top, lprc->right, lprc->bottom);
6055 * Retrieves the width of a label.
6058 * [I] HWND : window handle
6061 * SUCCESS : string width (in pixels)
6064 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
6066 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6067 INT nLabelWidth = 0;
6070 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
6072 ZeroMemory(&lvItem, sizeof(lvItem));
6073 lvItem.mask = LVIF_TEXT;
6074 lvItem.iItem = nItem;
6075 lvItem.cchTextMax = DISP_TEXT_SIZE;
6076 lvItem.pszText = szDispText;
6077 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6078 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
6085 * Retrieves the spacing between listview control items.
6088 * [I] HWND : window handle
6089 * [I] BOOL : flag for small or large icon
6092 * Horizontal + vertical spacing
6094 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
6096 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6099 if (bSmall == FALSE)
6101 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6105 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
6106 if ((style & LVS_TYPEMASK) == LVS_ICON)
6107 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6109 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6116 * Retrieves the state of a listview control item.
6119 * [I] HWND : window handle
6120 * [I] INT : item index
6121 * [I] UINT : state mask
6124 * State specified by the mask.
6126 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
6128 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6132 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6134 ZeroMemory(&lvItem, sizeof(lvItem));
6135 lvItem.iItem = nItem;
6136 lvItem.stateMask = uMask;
6137 lvItem.mask = LVIF_STATE;
6138 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6139 uState = lvItem.state;
6147 * Retrieves the text of a listview control item or subitem.
6150 * [I] hwnd : window handle
6151 * [I] nItem : item index
6152 * [IO] lpLVItem : item information
6153 * [I] isW : TRUE if lpLVItem is Unicode
6156 * SUCCESS : string length
6159 static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6161 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6164 if (lpLVItem != NULL)
6166 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6168 lpLVItem->mask = LVIF_TEXT;
6169 lpLVItem->iItem = nItem;
6170 if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
6171 nLength = textlenT(lpLVItem->pszText, isW);
6180 * Searches for an item based on properties + relationships.
6183 * [I] HWND : window handle
6184 * [I] INT : item index
6185 * [I] INT : relationship flag
6188 * SUCCESS : item index
6191 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
6193 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6194 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6196 LVFINDINFOW lvFindInfo;
6197 INT nCountPerColumn;
6200 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6202 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6204 if (uFlags & LVNI_CUT)
6207 if (uFlags & LVNI_DROPHILITED)
6208 uMask |= LVIS_DROPHILITED;
6210 if (uFlags & LVNI_FOCUSED)
6211 uMask |= LVIS_FOCUSED;
6213 if (uFlags & LVNI_SELECTED)
6214 uMask |= LVIS_SELECTED;
6216 if (uFlags & LVNI_ABOVE)
6218 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6223 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6229 lvFindInfo.flags = LVFI_NEARESTXY;
6230 lvFindInfo.vkDirection = VK_UP;
6231 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6232 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6234 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6239 else if (uFlags & LVNI_BELOW)
6241 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6243 while (nItem < GETITEMCOUNT(infoPtr))
6246 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6252 lvFindInfo.flags = LVFI_NEARESTXY;
6253 lvFindInfo.vkDirection = VK_DOWN;
6254 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6255 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6257 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6262 else if (uFlags & LVNI_TOLEFT)
6264 if (uView == LVS_LIST)
6266 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6267 while (nItem - nCountPerColumn >= 0)
6269 nItem -= nCountPerColumn;
6270 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6274 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6276 lvFindInfo.flags = LVFI_NEARESTXY;
6277 lvFindInfo.vkDirection = VK_LEFT;
6278 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6279 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6281 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6286 else if (uFlags & LVNI_TORIGHT)
6288 if (uView == LVS_LIST)
6290 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6291 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6293 nItem += nCountPerColumn;
6294 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6298 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6300 lvFindInfo.flags = LVFI_NEARESTXY;
6301 lvFindInfo.vkDirection = VK_RIGHT;
6302 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6303 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6305 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6314 /* search by index */
6315 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6317 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
6326 /* LISTVIEW_GetNumberOfWorkAreas */
6330 * Retrieves the origin coordinates when in icon or small icon display mode.
6333 * [I] HWND : window handle
6334 * [O] LPPOINT : coordinate information
6340 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6342 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6343 UINT uView = lStyle & LVS_TYPEMASK;
6344 BOOL bResult = FALSE;
6346 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6348 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6350 SCROLLINFO scrollInfo;
6351 ZeroMemory(lpptOrigin, sizeof(POINT));
6352 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6353 scrollInfo.cbSize = sizeof(SCROLLINFO);
6355 if (lStyle & WS_HSCROLL)
6357 scrollInfo.fMask = SIF_POS;
6358 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6359 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6362 if (lStyle & WS_VSCROLL)
6364 scrollInfo.fMask = SIF_POS;
6365 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6366 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6377 * Retrieves the number of items that are marked as selected.
6380 * [I] HWND : window handle
6383 * Number of items selected.
6385 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6388 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6389 INT nSelectedCount = 0;
6392 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6394 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6398 return nSelectedCount;
6403 * Retrieves item index that marks the start of a multiple selection.
6406 * [I] HWND : window handle
6409 * Index number or -1 if there is no selection mark.
6411 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6413 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6415 return infoPtr->nSelectionMark;
6421 * Retrieves the width of a string.
6424 * [I] hwnd : window handle
6425 * [I] lpszText : text string to process
6426 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6429 * SUCCESS : string width (in pixels)
6432 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
6434 if (is_textT(lpszText, isW))
6436 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6437 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6438 HDC hdc = GetDC(hwnd);
6439 HFONT hOldFont = SelectObject(hdc, hFont);
6441 ZeroMemory(&stringSize, sizeof(SIZE));
6443 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6445 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6446 SelectObject(hdc, hOldFont);
6447 ReleaseDC(hwnd, hdc);
6448 return stringSize.cx;
6455 * Retrieves the text backgound color.
6458 * [I] HWND : window handle
6461 * COLORREF associated with the the background.
6463 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6465 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6467 return infoPtr->clrTextBk;
6472 * Retrieves the text color.
6475 * [I] HWND : window handle
6478 * COLORREF associated with the text.
6480 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6482 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6484 return infoPtr->clrText;
6489 * Determines item if a hit or closest if not
6492 * [I] HWND : window handle
6493 * [IO] LPLV_INTHIT : hit test information
6494 * [I] subitem : fill out iSubItem.
6497 * SUCCESS : item index of hit
6500 static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem)
6502 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6503 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6504 UINT uView = lStyle & LVS_TYPEMASK;
6505 INT i,topindex,bottomindex;
6507 DWORD xterm, yterm, dist;
6509 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.pt.y);
6511 topindex = ListView_GetTopIndex(hwnd);
6512 if (uView == LVS_REPORT)
6514 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6515 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6519 bottomindex = GETITEMCOUNT(infoPtr);
6522 lpInt->distance = 0x7fffffff;
6523 lpInt->iDistItem = -1;
6525 for (i = topindex; i < bottomindex; i++)
6527 rcItem.left = LVIR_BOUNDS;
6528 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6530 if (PtInRect(&rcItem, lpInt->ht.pt))
6532 rcItem.left = LVIR_ICON;
6533 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6535 if (PtInRect(&rcItem, lpInt->ht.pt))
6537 lpInt->ht.flags = LVHT_ONITEMICON;
6538 lpInt->ht.iItem = i;
6539 if (subitem) lpInt->ht.iSubItem = 0;
6544 rcItem.left = LVIR_LABEL;
6545 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6547 if (PtInRect(&rcItem, lpInt->ht.pt))
6549 lpInt->ht.flags = LVHT_ONITEMLABEL;
6550 lpInt->ht.iItem = i;
6551 if (subitem) lpInt->ht.iSubItem = 0;
6556 lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6557 lpInt->ht.iItem = i;
6558 if (subitem) lpInt->ht.iSubItem = 0;
6564 * Now compute distance from point to center of boundary
6565 * box. Since we are only interested in the relative
6566 * distance, we can skip the nasty square root operation
6568 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6569 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6570 dist = xterm * xterm + yterm * yterm;
6571 if (dist < lpInt->distance)
6573 lpInt->distance = dist;
6574 lpInt->iDistItem = i;
6580 lpInt->ht.flags = LVHT_NOWHERE;
6581 TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6588 * Determines which section of the item was selected (if any).
6591 * [I] HWND : window handle
6592 * [IO] LPLVHITTESTINFO : hit test information
6593 * [I] subitem : fill out iSubItem.
6596 * SUCCESS : item index
6599 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6602 LV_INTHIT lv_inthit;
6604 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6605 lpHitTestInfo->pt.y);
6607 memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6608 ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem);
6609 memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6615 * Determines which listview item is located at the specified position.
6618 * [I] HWND : window handle
6619 * [IO} LPLVHITTESTINFO : hit test information
6622 * SUCCESS : item index
6625 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6627 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6630 lpHitTestInfo->flags = 0;
6632 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6633 lpHitTestInfo->flags = LVHT_TOLEFT;
6634 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6635 lpHitTestInfo->flags = LVHT_TORIGHT;
6636 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6637 lpHitTestInfo->flags |= LVHT_ABOVE;
6638 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6639 lpHitTestInfo->flags |= LVHT_BELOW;
6641 if (lpHitTestInfo->flags == 0)
6643 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6644 * an app might pass only a structure with space up to iItem!
6645 * (MS Office 97 does that for instance in the file open dialog)
6647 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6655 * Inserts a new column.
6658 * [I] HWND : window handle
6659 * [I] INT : column index
6660 * [I] LPLVCOLUMNW : column information
6663 * SUCCESS : new column index
6666 static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn,
6667 LPLVCOLUMNW lpColumn, BOOL isW)
6669 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6670 INT nNewColumn = -1;
6673 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
6675 if (lpColumn != NULL)
6677 /* initialize memory */
6678 ZeroMemory(&hdi, sizeof(hdi));
6680 if (lpColumn->mask & LVCF_FMT)
6682 /* format member is valid */
6683 hdi.mask |= HDI_FORMAT;
6685 /* set text alignment (leftmost column must be left-aligned) */
6688 hdi.fmt |= HDF_LEFT;
6692 if (lpColumn->fmt & LVCFMT_LEFT)
6694 hdi.fmt |= HDF_LEFT;
6696 else if (lpColumn->fmt & LVCFMT_RIGHT)
6698 hdi.fmt |= HDF_RIGHT;
6700 else if (lpColumn->fmt & LVCFMT_CENTER)
6702 hdi.fmt |= HDF_CENTER;
6706 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6708 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6712 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6717 if (lpColumn->fmt & LVCFMT_IMAGE)
6719 hdi.fmt |= HDF_IMAGE;
6720 hdi.iImage = I_IMAGECALLBACK;
6724 if (lpColumn->mask & LVCF_WIDTH)
6726 hdi.mask |= HDI_WIDTH;
6727 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6729 /* make it fill the remainder of the controls width */
6734 ZeroMemory(&hdit, sizeof(hdit));
6736 /* get the width of every item except the current one */
6737 hdit.mask = HDI_WIDTH;
6740 for(item_index = 0; item_index < (nColumn - 1); item_index++) {
6741 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
6745 /* retrieve the layout of the header */
6746 GetClientRect(hwnd, &rcHeader);
6747 /* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6748 TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
6750 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
6753 hdi.cxy = lpColumn->cx;
6756 if (lpColumn->mask & LVCF_TEXT)
6758 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6759 hdi.pszText = lpColumn->pszText;
6760 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6761 hdi.fmt |= HDF_STRING;
6764 if (lpColumn->mask & LVCF_IMAGE)
6766 hdi.mask |= HDI_IMAGE;
6767 hdi.iImage = lpColumn->iImage;
6770 if (lpColumn->mask & LVCF_ORDER)
6772 hdi.mask |= HDI_ORDER;
6773 hdi.iOrder = lpColumn->iOrder;
6776 /* insert item in header control */
6777 nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6778 (WPARAM)nColumn, (LPARAM)&hdi);
6780 /* Need to reset the item width when inserting a new column */
6781 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6783 LISTVIEW_UpdateScroll(hwnd);
6784 InvalidateRect(hwnd, NULL, FALSE);
6790 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6791 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6792 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6793 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6794 their own sort proc. when sending LVM_SORTITEMS.
6797 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6799 LVS_SORTXXX must be specified,
6800 LVS_OWNERDRAW is not set,
6801 <item>.pszText is not LPSTR_TEXTCALLBACK.
6803 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6804 are sorted based on item text..."
6806 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6808 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6809 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6810 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6811 INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6812 /* if we're sorting descending, negate the return value */
6813 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6818 * Inserts a new item in the listview control.
6821 * [I] HWND : window handle
6822 * [I] LPLVITEMW : item information
6823 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6826 * SUCCESS : new item index
6829 static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
6831 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6832 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6833 UINT uView = lStyle & LVS_TYPEMASK;
6837 LISTVIEW_ITEM *lpItem = NULL;
6839 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
6840 hwnd, debuglvitem_t(lpLVItem, isW), isW);
6842 if (lStyle & LVS_OWNERDATA)
6844 nItem = infoPtr->hdpaItems->nItemCount;
6845 infoPtr->hdpaItems->nItemCount ++;
6849 if (lpLVItem != NULL)
6851 /* make sure it's not a subitem; cannot insert a subitem */
6852 if (lpLVItem->iSubItem == 0)
6854 if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6856 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6857 if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
6859 /* insert item in listview control data structure */
6860 if ( (hdpaSubItems = DPA_Create(8)) )
6862 if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
6864 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6865 && !(lStyle & LVS_OWNERDRAWFIXED)
6866 && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
6868 /* Insert the item in the proper sort order based on the pszText
6869 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6870 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6871 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6872 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6873 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6877 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6884 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6886 /* manage item focus */
6887 if (lpLVItem->mask & LVIF_STATE)
6889 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6890 if (lpLVItem->stateMask & LVIS_SELECTED)
6891 LISTVIEW_SetSelection(hwnd, nItem);
6892 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6893 LISTVIEW_SetItemFocus(hwnd, nItem);
6896 /* send LVN_INSERTITEM notification */
6897 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6899 nmlv.lParam = lpItem->lParam;
6900 listview_notify(hwnd, LVN_INSERTITEM, &nmlv);
6902 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6904 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6905 if (nItemWidth > infoPtr->nItemWidth)
6906 infoPtr->nItemWidth = nItemWidth;
6909 /* align items (set position of each item) */
6910 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6912 if (lStyle & LVS_ALIGNLEFT)
6913 LISTVIEW_AlignLeft(hwnd);
6915 LISTVIEW_AlignTop(hwnd);
6918 LISTVIEW_UpdateScroll(hwnd);
6919 /* refresh client area */
6920 InvalidateRect(hwnd, NULL, FALSE);
6929 /* free memory if unsuccessful */
6930 if ((nItem == -1) && (lpItem != NULL))
6931 COMCTL32_Free(lpItem);
6938 * Redraws a range of items.
6941 * [I] HWND : window handle
6942 * [I] INT : first item
6943 * [I] INT : last item
6949 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6951 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6952 BOOL bResult = FALSE;
6956 if (nFirst <= nLast)
6958 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6960 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6962 for (i = nFirst; i <= nLast; i++)
6964 rcItem.left = LVIR_BOUNDS;
6965 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
6966 InvalidateRect(hwnd, &rcItem, TRUE);
6975 /* LISTVIEW_Scroll */
6979 * Sets the background color.
6982 * [I] HWND : window handle
6983 * [I] COLORREF : background color
6989 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6991 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6993 if(infoPtr->clrBk!=clrBk){
6994 infoPtr->clrBk = clrBk;
6995 InvalidateRect(hwnd, NULL, TRUE);
7001 /* LISTVIEW_SetBkImage */
7005 * Sets the callback mask. This mask will be used when the parent
7006 * window stores state information (some or all).
7009 * [I] HWND : window handle
7010 * [I] UINT : state mask
7016 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
7018 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7020 infoPtr->uCallbackMask = uMask;
7027 * Sets the attributes of a header item.
7030 * [I] HWND : window handle
7031 * [I] INT : column index
7032 * [I] LPLVCOLUMNW : column attributes
7033 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
7034 * otherwise it is in fact a LPLVCOLUMNA
7040 static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn,
7041 LPLVCOLUMNW lpColumn, BOOL isW)
7043 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7044 BOOL bResult = FALSE;
7045 HDITEMW hdi, hdiget;
7047 if ((lpColumn != NULL) && (nColumn >= 0) &&
7048 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
7050 /* initialize memory */
7051 ZeroMemory(&hdi, sizeof(hdi));
7053 if (lpColumn->mask & LVCF_FMT)
7055 /* format member is valid */
7056 hdi.mask |= HDI_FORMAT;
7058 /* get current format first */
7059 hdiget.mask = HDI_FORMAT;
7060 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7061 /* preserve HDF_STRING if present */
7062 hdi.fmt = hdiget.fmt & HDF_STRING;
7064 /* set text alignment (leftmost column must be left-aligned) */
7067 hdi.fmt |= HDF_LEFT;
7071 if (lpColumn->fmt & LVCFMT_LEFT)
7072 hdi.fmt |= HDF_LEFT;
7073 else if (lpColumn->fmt & LVCFMT_RIGHT)
7074 hdi.fmt |= HDF_RIGHT;
7075 else if (lpColumn->fmt & LVCFMT_CENTER)
7076 hdi.fmt |= HDF_CENTER;
7079 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7080 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
7082 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7083 hdi.fmt |= HDF_IMAGE;
7085 if (lpColumn->fmt & LVCFMT_IMAGE)
7087 hdi.fmt |= HDF_IMAGE;
7088 hdi.iImage = I_IMAGECALLBACK;
7092 if (lpColumn->mask & LVCF_WIDTH)
7094 hdi.mask |= HDI_WIDTH;
7095 hdi.cxy = lpColumn->cx;
7098 if (lpColumn->mask & LVCF_TEXT)
7100 hdi.mask |= HDI_TEXT | HDI_FORMAT;
7101 hdi.pszText = lpColumn->pszText;
7102 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
7103 hdi.fmt |= HDF_STRING;
7106 if (lpColumn->mask & LVCF_IMAGE)
7108 hdi.mask |= HDI_IMAGE;
7109 hdi.iImage = lpColumn->iImage;
7112 if (lpColumn->mask & LVCF_ORDER)
7114 hdi.mask |= HDI_ORDER;
7115 hdi.iOrder = lpColumn->iOrder;
7118 /* set header item attributes */
7120 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7122 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
7130 * Sets the column order array
7133 * [I] HWND : window handle
7134 * [I] INT : number of elements in column order array
7135 * [I] INT : pointer to column order array
7141 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
7143 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
7145 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7156 * Sets the width of a column
7159 * [I] HWND : window handle
7160 * [I] INT : column index
7161 * [I] INT : column width
7167 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
7169 LISTVIEW_INFO *infoPtr;
7172 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7173 UINT uView = lStyle & LVS_TYPEMASK;
7178 WCHAR text_buffer[DISP_TEXT_SIZE];
7179 INT header_item_count;
7184 WCHAR szDispText[DISP_TEXT_SIZE];
7186 /* make sure we can get the listview info */
7187 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
7190 if (!infoPtr->hwndHeader) /* make sure we have a header */
7193 /* set column width only if in report or list mode */
7194 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7197 TRACE("(hwnd=%x, iCol=%d, cx=%d\n", hwnd, iCol, cx);
7199 /* take care of invalid cx values */
7200 if((uView == LVS_REPORT) && (cx < -2))
7201 cx = LVSCW_AUTOSIZE;
7202 else if (uView == LVS_LIST && (cx < 1))
7205 /* resize all columns if in LVS_LIST mode */
7206 if(uView == LVS_LIST) {
7207 infoPtr->nItemWidth = cx;
7208 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7212 /* autosize based on listview items width */
7213 if(cx == LVSCW_AUTOSIZE)
7215 /* set the width of the column to the width of the widest item */
7216 if (iCol == 0 || uView == LVS_LIST)
7219 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7221 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, item_index);
7222 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7224 if (infoPtr->himlSmall)
7225 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
7229 ZeroMemory(&lvItem, sizeof(lvItem));
7230 lvItem.iSubItem = iCol;
7231 lvItem.mask = LVIF_TEXT;
7232 lvItem.cchTextMax = DISP_TEXT_SIZE;
7233 lvItem.pszText = szDispText;
7234 *lvItem.pszText = '\0';
7236 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7238 lvItem.iItem = item_index;
7239 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7240 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7241 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7244 cx += TRAILING_PADDING;
7245 } /* autosize based on listview header width */
7246 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7248 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7250 /* if iCol is the last column make it fill the remainder of the controls width */
7251 if(iCol == (header_item_count - 1)) {
7252 /* get the width of every item except the current one */
7253 hdi.mask = HDI_WIDTH;
7256 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7257 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7261 /* retrieve the layout of the header */
7262 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7264 cx = (rcHeader.right - rcHeader.left) - cx;
7268 /* Despite what the MS docs say, if this is not the last
7269 column, then MS resizes the column to the width of the
7270 largest text string in the column, including headers
7271 and items. This is different from LVSCW_AUTOSIZE in that
7272 LVSCW_AUTOSIZE ignores the header string length.
7275 /* retrieve header font */
7276 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7278 /* retrieve header text */
7279 hdi.mask = HDI_TEXT;
7280 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7281 hdi.pszText = text_buffer;
7283 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7285 /* determine the width of the text in the header */
7287 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7289 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7291 SelectObject(hdc, old_font); /* restore the old font */
7292 ReleaseDC(hwnd, hdc);
7294 ZeroMemory(&lvItem, sizeof(lvItem));
7295 lvItem.iSubItem = iCol;
7296 lvItem.mask = LVIF_TEXT;
7297 lvItem.cchTextMax = DISP_TEXT_SIZE;
7298 lvItem.pszText = szDispText;
7299 *lvItem.pszText = '\0';
7301 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7303 lvItem.iItem = item_index;
7304 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7305 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7306 nLabelWidth += TRAILING_PADDING;
7307 /* While it is possible for subitems to have icons, even MS messes
7308 up the positioning, so I suspect no applications actually use
7310 if (item_index == 0 && infoPtr->himlSmall)
7311 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
7312 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7317 /* call header to update the column change */
7318 hdi.mask = HDI_WIDTH;
7321 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7323 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7330 * Sets the extended listview style.
7333 * [I] HWND : window handle
7338 * SUCCESS : previous style
7341 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
7343 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7344 DWORD dwOldStyle = infoPtr->dwExStyle;
7348 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7350 infoPtr->dwExStyle = dwStyle;
7355 /* LISTVIEW_SetHotCursor */
7359 * Sets the hot item index.
7362 * [I] HWND : window handle
7366 * SUCCESS : previous hot item index
7367 * FAILURE : -1 (no hot item)
7369 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7371 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7372 INT iOldIndex = infoPtr->nHotItem;
7375 infoPtr->nHotItem = iIndex;
7382 * Sets the amount of time the cursor must hover over an item before it is selected.
7385 * [I] HWND : window handle
7386 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7389 * Returns the previous hover time
7391 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7393 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7394 DWORD oldHoverTime = infoPtr->dwHoverTime;
7396 infoPtr->dwHoverTime = dwHoverTime;
7398 return oldHoverTime;
7403 * Sets spacing for icons of LVS_ICON style.
7406 * [I] HWND : window handle
7407 * [I] DWORD : MAKELONG(cx, cy)
7410 * MAKELONG(oldcx, oldcy)
7412 static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing)
7414 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7415 INT cy = HIWORD(spacing);
7416 INT cx = LOWORD(spacing);
7418 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7419 UINT uView = lStyle & LVS_TYPEMASK;
7421 oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7422 if (cx == -1) /* set to default */
7423 cx = GetSystemMetrics(SM_CXICONSPACING);
7424 if (cy == -1) /* set to default */
7425 cy = GetSystemMetrics(SM_CYICONSPACING);
7428 infoPtr->iconSpacing.cx = cx;
7430 { /* if 0 then compute width */
7431 if (uView == LVS_ICON)
7432 FIXME("width computation not yet done\n");
7434 * Should scan each item and determine max width of
7435 * icon or label, then make that the width
7437 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7438 infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd);
7441 infoPtr->iconSpacing.cy = cy;
7443 { /* if 0 then compute height */
7444 if (uView == LVS_ICON)
7445 infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + infoPtr->ntmHeight
7446 + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7447 /* FIXME. I don't think so; I think it is based on twice the ntmHeight */
7448 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7449 infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd);
7452 TRACE("old=(%d,%d), new=(%ld,%ld)\n", LOWORD(oldspacing), HIWORD(oldspacing),
7453 infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7455 /* these depend on the iconSpacing */
7456 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7457 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7467 * [I] HWND : window handle
7468 * [I] INT : image list type
7469 * [I] HIMAGELIST : image list handle
7472 * SUCCESS : old image list
7475 static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7477 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7478 HIMAGELIST himlOld = 0;
7484 himlOld = infoPtr->himlNormal;
7485 infoPtr->himlNormal = himl;
7489 himlOld = infoPtr->himlSmall;
7490 infoPtr->himlSmall = himl;
7494 himlOld = infoPtr->himlState;
7495 infoPtr->himlState = himl;
7496 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7500 oldHeight = infoPtr->nItemHeight;
7501 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7502 if (infoPtr->nItemHeight != oldHeight)
7503 LISTVIEW_UpdateScroll(hwnd);
7510 * Preallocates memory (does *not* set the actual count of items !)
7513 * [I] HWND : window handle
7514 * [I] INT : item count (projected number of items to allocate)
7515 * [I] DWORD : update flags
7521 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7523 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7525 TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
7527 if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7529 int precount,topvisible;
7531 TRACE("LVS_OWNERDATA is set!\n");
7532 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7533 FIXME("flags %s %s not implemented\n",
7534 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7536 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7539 * Internally remove all the selections.
7543 LISTVIEW_SELECTION *selection;
7544 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7546 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7549 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7551 precount = infoPtr->hdpaItems->nItemCount;
7552 topvisible = ListView_GetTopIndex(hwnd) +
7553 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7555 infoPtr->hdpaItems->nItemCount = nItems;
7557 infoPtr->nItemWidth = max(LISTVIEW_GetItemWidth(hwnd),
7558 DEFAULT_COLUMN_WIDTH);
7560 LISTVIEW_UpdateSize(hwnd);
7561 LISTVIEW_UpdateScroll(hwnd);
7563 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7564 InvalidateRect(hwnd, NULL, TRUE);
7568 /* According to MSDN for non-LVS_OWNERDATA this is just
7569 * a performance issue. The control allocates its internal
7570 * data structures for the number of items specified. It
7571 * cuts down on the number of memory allocations. Therefore
7572 * we will just issue a WARN here
7574 WARN("for non-ownerdata performance option not implemented.\n");
7582 * Sets the position of an item.
7585 * [I] HWND : window handle
7586 * [I] INT : item index
7587 * [I] LONG : x coordinate
7588 * [I] LONG : y coordinate
7594 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7595 LONG nPosX, LONG nPosY)
7597 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7598 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7599 UINT uView = lStyle & LVS_TYPEMASK;
7600 LISTVIEW_ITEM *lpItem;
7602 BOOL bResult = FALSE;
7604 TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7606 if (lStyle & LVS_OWNERDATA)
7609 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7611 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7613 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7615 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7619 orig = lpItem->ptPosition;
7620 if ((nPosX == -1) && (nPosY == -1))
7622 /* This point value seems to be an undocumented feature. The
7623 * best guess is that it means either at the origin, or at
7624 * the true beginning of the list. I will assume the origin.
7627 if (!LISTVIEW_GetOrigin(hwnd, &pt1))
7634 if (uView == LVS_ICON)
7636 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7637 nPosY += ICON_TOP_PADDING;
7639 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7643 lpItem->ptPosition.x = nPosX;
7644 lpItem->ptPosition.y = nPosY;
7645 if (uView == LVS_ICON)
7647 lpItem->ptPosition.y -= ICON_TOP_PADDING;
7648 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7649 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7651 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7652 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7655 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7656 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7661 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7662 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7675 * Sets the state of one or many items.
7678 * [I] HWND : window handle
7679 * [I]INT : item index
7680 * [I] LPLVITEM : item or subitem info
7686 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem)
7688 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7689 BOOL bResult = TRUE;
7692 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
7693 hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
7695 ZeroMemory(&lvItem, sizeof(lvItem));
7696 lvItem.mask = LVIF_STATE;
7697 lvItem.state = lpLVItem->state;
7698 lvItem.stateMask = lpLVItem->stateMask ;
7699 lvItem.iItem = nItem;
7703 /* apply to all items */
7704 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7705 if (!ListView_SetItemW(hwnd, &lvItem)) bResult = FALSE;
7708 bResult = ListView_SetItemW(hwnd, &lvItem);
7715 * Sets the text of an item or subitem.
7718 * [I] hwnd : window handle
7719 * [I] nItem : item index
7720 * [I] lpLVItem : item or subitem info
7721 * [I] isW : TRUE if input is Unicode
7727 static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7729 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7730 BOOL bResult = FALSE;
7733 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
7734 hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
7736 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7738 ZeroMemory(&lvItem, sizeof(LVITEMW));
7739 lvItem.mask = LVIF_TEXT;
7740 lvItem.pszText = lpLVItem->pszText;
7741 lvItem.iItem = nItem;
7742 lvItem.iSubItem = lpLVItem->iSubItem;
7743 if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
7744 else bResult = ListView_SetItemA(hwnd, &lvItem);
7752 * Set item index that marks the start of a multiple selection.
7755 * [I] HWND : window handle
7759 * Index number or -1 if there is no selection mark.
7761 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7763 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7764 INT nOldIndex = infoPtr->nSelectionMark;
7766 TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
7768 infoPtr->nSelectionMark = nIndex;
7775 * Sets the text background color.
7778 * [I] HWND : window handle
7779 * [I] COLORREF : text background color
7785 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7787 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7789 TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
7791 infoPtr->clrTextBk = clrTextBk;
7792 InvalidateRect(hwnd, NULL, TRUE);
7799 * Sets the text foreground color.
7802 * [I] HWND : window handle
7803 * [I] COLORREF : text color
7809 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7811 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7813 TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
7815 infoPtr->clrText = clrText;
7816 InvalidateRect(hwnd, NULL, TRUE);
7821 /* LISTVIEW_SetToolTips */
7822 /* LISTVIEW_SetUnicodeFormat */
7823 /* LISTVIEW_SetWorkAreas */
7827 * Callback internally used by LISTVIEW_SortItems()
7830 * [I] LPVOID : first LISTVIEW_ITEM to compare
7831 * [I] LPVOID : second LISTVIEW_ITEM to compare
7832 * [I] LPARAM : HWND of control
7835 * if first comes before second : negative
7836 * if first comes after second : positive
7837 * if first and second are equivalent : zero
7839 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7841 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7842 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
7843 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
7845 /* Forward the call to the client defined callback */
7846 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7851 * Sorts the listview items.
7854 * [I] HWND : window handle
7855 * [I] WPARAM : application-defined value
7856 * [I] LPARAM : pointer to comparision callback
7862 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7864 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7865 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7866 HDPA hdpaSubItems=NULL;
7867 LISTVIEW_ITEM *pLVItem=NULL;
7868 LPVOID selectionMarkItem;
7871 TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
7873 if (lStyle & LVS_OWNERDATA) return FALSE;
7875 if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
7877 nCount = GETITEMCOUNT(infoPtr);
7878 /* if there are 0 or 1 items, there is no need to sort */
7882 infoPtr->pfnCompare = pfnCompare;
7883 infoPtr->lParamSort = lParamSort;
7884 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7886 /* Adjust selections and indices so that they are the way they should
7887 * be after the sort (otherwise, the list items move around, but
7888 * whatever is at the item's previous original position will be
7891 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7892 for (i=0; i < nCount; i++)
7894 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7895 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7897 if (pLVItem->state & LVIS_SELECTED)
7898 LISTVIEW_AddSelectionRange(hwnd, i, i);
7900 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7901 if (pLVItem->state & LVIS_FOCUSED)
7902 infoPtr->nFocusedItem=i;
7904 if (selectionMarkItem != NULL)
7905 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7906 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7908 /* align the items */
7909 LISTVIEW_AlignTop(hwnd);
7911 /* refresh the display */
7912 InvalidateRect(hwnd, NULL, TRUE);
7917 /* LISTVIEW_SubItemHitTest */
7921 * Updates an items or rearranges the listview control.
7924 * [I] HWND : window handle
7925 * [I] INT : item index
7931 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7933 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7934 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7935 BOOL bResult = FALSE;
7938 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
7940 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7944 /* rearrange with default alignment style */
7945 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7946 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7948 ListView_Arrange(hwnd, 0);
7952 /* get item bounding rectangle */
7953 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
7954 InvalidateRect(hwnd, &rc, TRUE);
7963 * Creates the listview control.
7966 * [I] HWND : window handle
7971 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7973 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7974 UINT uView = lpcs->style & LVS_TYPEMASK;
7977 TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
7979 /* initialize info pointer */
7980 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7982 /* determine the type of structures to use */
7983 infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT,
7984 (WPARAM)hwnd, (LPARAM)NF_QUERY);
7986 /* initialize color information */
7987 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7988 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7989 infoPtr->clrTextBk = CLR_DEFAULT;
7991 /* set default values */
7992 infoPtr->hwndSelf = hwnd;
7993 infoPtr->uCallbackMask = 0;
7994 infoPtr->nFocusedItem = -1;
7995 infoPtr->nSelectionMark = -1;
7996 infoPtr->nHotItem = -1;
7997 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7998 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7999 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
8000 infoPtr->hwndEdit = 0;
8001 infoPtr->pedititem = NULL;
8002 infoPtr->nEditLabelItem = -1;
8004 /* get default font (icon title) */
8005 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8006 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8007 infoPtr->hFont = infoPtr->hDefaultFont;
8008 LISTVIEW_SaveTextMetrics(hwnd);
8011 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
8012 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
8013 0, 0, 0, 0, hwnd, (HMENU)0,
8014 lpcs->hInstance, NULL);
8016 /* set header unicode format */
8017 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
8019 /* set header font */
8020 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
8023 if (uView == LVS_ICON)
8025 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8026 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8028 else if (uView == LVS_REPORT)
8030 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8032 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8036 /* set HDS_HIDDEN flag to hide the header bar */
8037 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
8038 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
8042 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8043 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8047 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8048 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8051 /* display unsupported listview window styles */
8052 LISTVIEW_UnsupportedStyles(lpcs->style);
8054 /* allocate memory for the data structure */
8055 infoPtr->hdpaItems = DPA_Create(10);
8057 /* allocate memory for the selection ranges */
8058 infoPtr->hdpaSelectionRanges = DPA_Create(10);
8060 /* initialize size of items */
8061 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8062 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8064 /* initialize the hover time to -1(indicating the default system hover time) */
8065 infoPtr->dwHoverTime = -1;
8072 * Erases the background of the listview control.
8075 * [I] HWND : window handle
8076 * [I] WPARAM : device context handle
8077 * [I] LPARAM : not used
8083 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
8086 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8089 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8091 if (infoPtr->clrBk == CLR_NONE)
8093 bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
8098 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8099 GetClientRect(hwnd, &rc);
8100 FillRect((HDC)wParam, &rc, hBrush);
8101 DeleteObject(hBrush);
8109 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
8111 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8113 TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
8115 if (infoPtr->clrBk != CLR_NONE)
8117 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8118 FillRect(hdc, rc, hBrush);
8119 DeleteObject(hBrush);
8125 * Retrieves the listview control font.
8128 * [I] HWND : window handle
8133 static LRESULT LISTVIEW_GetFont(HWND hwnd)
8135 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8137 TRACE("(hwnd=%x)\n", hwnd);
8139 return infoPtr->hFont;
8144 * Performs vertical scrolling.
8147 * [I] HWND : window handle
8148 * [I] INT : scroll code
8149 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8151 * [I] HWND : scrollbar control window handle
8156 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8159 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8160 SCROLLINFO scrollInfo;
8162 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8163 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8165 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8167 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8168 scrollInfo.cbSize = sizeof(SCROLLINFO);
8169 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8171 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8173 INT nOldScrollPos = scrollInfo.nPos;
8174 switch (nScrollCode)
8177 if (scrollInfo.nPos > scrollInfo.nMin)
8182 if (scrollInfo.nPos < scrollInfo.nMax)
8187 if (scrollInfo.nPos > scrollInfo.nMin)
8189 if (scrollInfo.nPos >= scrollInfo.nPage)
8190 scrollInfo.nPos -= scrollInfo.nPage;
8192 scrollInfo.nPos = scrollInfo.nMin;
8197 if (scrollInfo.nPos < scrollInfo.nMax)
8199 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8200 scrollInfo.nPos += scrollInfo.nPage;
8202 scrollInfo.nPos = scrollInfo.nMax;
8206 case SB_THUMBPOSITION:
8208 scrollInfo.nPos = nCurrentPos;
8209 if (scrollInfo.nPos > scrollInfo.nMax)
8210 scrollInfo.nPos=scrollInfo.nMax;
8212 if (scrollInfo.nPos < scrollInfo.nMin)
8213 scrollInfo.nPos=scrollInfo.nMin;
8218 if (nOldScrollPos != scrollInfo.nPos)
8220 scrollInfo.fMask = SIF_POS;
8221 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
8222 if (IsWindowVisible(infoPtr->hwndHeader))
8224 RECT rListview, rcHeader, rDest;
8225 GetClientRect(hwnd, &rListview);
8226 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
8227 MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2);
8228 SubtractRect(&rDest, &rListview, &rcHeader);
8229 InvalidateRect(hwnd, &rDest, TRUE);
8232 InvalidateRect(hwnd, NULL, TRUE);
8241 * Performs horizontal scrolling.
8244 * [I] HWND : window handle
8245 * [I] INT : scroll code
8246 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8248 * [I] HWND : scrollbar control window handle
8253 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8256 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8257 SCROLLINFO scrollInfo;
8259 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8260 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8262 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8264 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8265 scrollInfo.cbSize = sizeof(SCROLLINFO);
8266 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8268 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
8270 INT nOldScrollPos = scrollInfo.nPos;
8272 switch (nScrollCode)
8275 if (scrollInfo.nPos > scrollInfo.nMin)
8280 if (scrollInfo.nPos < scrollInfo.nMax)
8285 if (scrollInfo.nPos > scrollInfo.nMin)
8287 if (scrollInfo.nPos >= scrollInfo.nPage)
8288 scrollInfo.nPos -= scrollInfo.nPage;
8290 scrollInfo.nPos = scrollInfo.nMin;
8295 if (scrollInfo.nPos < scrollInfo.nMax)
8297 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8298 scrollInfo.nPos += scrollInfo.nPage;
8300 scrollInfo.nPos = scrollInfo.nMax;
8304 case SB_THUMBPOSITION:
8306 scrollInfo.nPos = nCurrentPos;
8308 if (scrollInfo.nPos > scrollInfo.nMax)
8309 scrollInfo.nPos=scrollInfo.nMax;
8311 if (scrollInfo.nPos < scrollInfo.nMin)
8312 scrollInfo.nPos=scrollInfo.nMin;
8316 if (nOldScrollPos != scrollInfo.nPos)
8318 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8319 scrollInfo.fMask = SIF_POS;
8320 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
8321 if(uView == LVS_REPORT)
8323 scrollInfo.fMask = SIF_POS;
8324 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
8325 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
8327 InvalidateRect(hwnd, NULL, TRUE);
8334 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
8336 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8337 INT gcWheelDelta = 0;
8338 UINT pulScrollLines = 3;
8339 SCROLLINFO scrollInfo;
8341 TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
8343 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8344 gcWheelDelta -= wheelDelta;
8346 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8347 scrollInfo.cbSize = sizeof(SCROLLINFO);
8348 scrollInfo.fMask = SIF_POS | SIF_RANGE;
8355 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8356 * should be fixed in the future.
8358 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8359 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
8363 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8365 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8367 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
8368 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8369 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8375 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8386 * [I] HWND : window handle
8387 * [I] INT : virtual key
8388 * [I] LONG : key data
8393 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
8395 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8396 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8398 NMLVKEYDOWN nmKeyDown;
8400 TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
8402 /* send LVN_KEYDOWN notification */
8403 nmKeyDown.wVKey = nVirtualKey;
8404 nmKeyDown.flags = 0;
8405 notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
8407 switch (nVirtualKey)
8410 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8412 hdr_notify(hwnd, NM_RETURN); /* NM_RETURN notification */
8413 hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
8418 if (GETITEMCOUNT(infoPtr) > 0)
8423 if (GETITEMCOUNT(infoPtr) > 0)
8424 nItem = GETITEMCOUNT(infoPtr) - 1;
8428 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8432 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8436 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8440 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8444 if (uView == LVS_REPORT)
8445 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8447 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8448 * LISTVIEW_GetCountPerRow(hwnd);
8449 if(nItem < 0) nItem = 0;
8453 if (uView == LVS_REPORT)
8454 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8456 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8457 * LISTVIEW_GetCountPerRow(hwnd);
8458 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8462 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8464 if (LISTVIEW_KeySelection(hwnd, nItem))
8465 UpdateWindow(hwnd); /* update client area */
8476 * [I] HWND : window handle
8481 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8483 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
8484 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8487 TRACE("(hwnd=%x)\n", hwnd);
8489 /* send NM_KILLFOCUS notification */
8490 hdr_notify(hwnd, NM_KILLFOCUS);
8492 /* set window focus flag */
8493 infoPtr->bFocus = FALSE;
8495 /* NEED drawing optimization ; redraw the selected items */
8496 if (uView & LVS_REPORT)
8498 nTop = LISTVIEW_GetTopIndex(hwnd);
8500 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8505 nBottom = GETITEMCOUNT(infoPtr);
8507 for (i = nTop; i<nBottom; i++)
8509 if (LISTVIEW_IsSelected(hwnd,i))
8512 rcItem.left = LVIR_BOUNDS;
8513 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8514 InvalidateRect(hwnd, &rcItem, FALSE);
8523 * Processes double click messages (left mouse button).
8526 * [I] HWND : window handle
8527 * [I] WORD : key flag
8528 * [I] WORD : x coordinate
8529 * [I] WORD : y coordinate
8534 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8537 LVHITTESTINFO htInfo;
8540 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8542 htInfo.pt.x = wPosX;
8543 htInfo.pt.y = wPosY;
8545 /* send NM_DBLCLK notification */
8546 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8547 if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -1)
8549 nmlv.iItem = htInfo.iItem;
8550 nmlv.iSubItem = htInfo.iSubItem;
8557 nmlv.ptAction.x = wPosX;
8558 nmlv.ptAction.y = wPosY;
8559 listview_notify(hwnd, NM_DBLCLK, &nmlv);
8562 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8563 if(nmlv.iItem != -1)
8564 hdr_notify(hwnd, LVN_ITEMACTIVATE);
8571 * Processes mouse down messages (left mouse button).
8574 * [I] HWND : window handle
8575 * [I] WORD : key flag
8576 * [I] WORD : x coordinate
8577 * [I] WORD : y coordinate
8582 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8585 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8586 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8587 static BOOL bGroupSelect = TRUE;
8591 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8593 /* send NM_RELEASEDCAPTURE notification */
8594 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8596 if (infoPtr->bFocus == FALSE)
8599 /* set left button down flag */
8600 infoPtr->bLButtonDown = TRUE;
8602 ptPosition.x = wPosX;
8603 ptPosition.y = wPosY;
8604 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8605 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8607 if (lStyle & LVS_SINGLESEL)
8609 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8610 && infoPtr->nEditLabelItem == -1)
8611 infoPtr->nEditLabelItem = nItem;
8613 LISTVIEW_SetSelection(hwnd, nItem);
8617 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8620 LISTVIEW_AddGroupSelection(hwnd, nItem);
8622 LISTVIEW_AddSelection(hwnd, nItem);
8624 else if (wKey & MK_CONTROL)
8626 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8628 else if (wKey & MK_SHIFT)
8630 LISTVIEW_SetGroupSelection(hwnd, nItem);
8635 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8637 /* set selection (clears other pre-existing selections) */
8638 LISTVIEW_SetSelection(hwnd, nItem);
8640 if (was_selected && infoPtr->nEditLabelItem == -1)
8641 infoPtr->nEditLabelItem = nItem;
8647 /* remove all selections */
8648 LISTVIEW_RemoveAllSelections(hwnd);
8651 /* redraw if we could have possibly selected something */
8652 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8659 * Processes mouse up messages (left mouse button).
8662 * [I] HWND : window handle
8663 * [I] WORD : key flag
8664 * [I] WORD : x coordinate
8665 * [I] WORD : y coordinate
8670 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8673 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8675 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8677 if (infoPtr->bLButtonDown != FALSE)
8679 LVHITTESTINFO lvHitTestInfo;
8682 lvHitTestInfo.pt.x = wPosX;
8683 lvHitTestInfo.pt.y = wPosY;
8685 /* send NM_CLICK notification */
8686 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8687 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8689 nmlv.iItem = lvHitTestInfo.iItem;
8690 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8697 nmlv.ptAction.x = wPosX;
8698 nmlv.ptAction.y = wPosY;
8699 listview_notify(hwnd, NM_CLICK, &nmlv);
8701 /* set left button flag */
8702 infoPtr->bLButtonDown = FALSE;
8704 if(infoPtr->nEditLabelItem != -1)
8706 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL)
8707 LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
8708 infoPtr->nEditLabelItem = -1;
8717 * Creates the listview control (called before WM_CREATE).
8720 * [I] HWND : window handle
8721 * [I] WPARAM : unhandled
8722 * [I] LPARAM : widow creation info
8727 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8729 LISTVIEW_INFO *infoPtr;
8731 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8733 /* allocate memory for info structure */
8734 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8735 if (infoPtr == NULL)
8737 ERR("could not allocate info memory!\n");
8741 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
8742 if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr)
8744 ERR("pointer assignment error!\n");
8748 return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam);
8753 * Destroys the listview control (called after WM_DESTROY).
8756 * [I] HWND : window handle
8761 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8763 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8765 TRACE("(hwnd=%x)\n", hwnd);
8767 /* delete all items */
8768 LISTVIEW_DeleteAllItems(hwnd);
8770 /* destroy data structure */
8771 DPA_Destroy(infoPtr->hdpaItems);
8772 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8775 infoPtr->hFont = (HFONT)0;
8776 if (infoPtr->hDefaultFont)
8778 DeleteObject(infoPtr->hDefaultFont);
8781 /* free listview info pointer*/
8782 COMCTL32_Free(infoPtr);
8784 SetWindowLongW(hwnd, 0, 0);
8790 * Handles notifications from children.
8793 * [I] HWND : window handle
8794 * [I] INT : control identifier
8795 * [I] LPNMHDR : notification information
8800 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8802 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8804 TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
8806 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8808 /* handle notification from header control */
8809 if (lpnmh->code == HDN_ENDTRACKW)
8811 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8812 InvalidateRect(hwnd, NULL, TRUE);
8814 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
8816 /* Handle sorting by Header Column */
8819 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8821 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
8822 listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv);
8824 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8826 /* Idealy this should be done in HDN_ENDTRACKA
8827 * but since SetItemBounds in Header.c is called after
8828 * the notification is sent, it is neccessary to handle the
8829 * update of the scroll bar here (Header.c works fine as it is,
8830 * no need to disturb it)
8832 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8833 LISTVIEW_UpdateScroll(hwnd);
8834 InvalidateRect(hwnd, NULL, TRUE);
8844 * Determines the type of structure to use.
8847 * [I] HWND : window handle of the sender
8848 * [I] HWND : listview window handle
8849 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8854 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8856 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8858 TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
8860 if (nCommand == NF_REQUERY)
8861 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
8862 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8868 * Paints/Repaints the listview control.
8871 * [I] HWND : window handle
8872 * [I] HDC : device context handle
8877 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8881 TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc);
8885 hdc = BeginPaint(hwnd, &ps);
8886 LISTVIEW_Refresh(hwnd, hdc);
8887 EndPaint(hwnd, &ps);
8891 LISTVIEW_Refresh(hwnd, hdc);
8899 * Processes double click messages (right mouse button).
8902 * [I] HWND : window handle
8903 * [I] WORD : key flag
8904 * [I] WORD : x coordinate
8905 * [I] WORD : y coordinate
8910 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8913 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8915 /* send NM_RELEASEDCAPTURE notification */
8916 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8918 /* send NM_RDBLCLK notification */
8919 hdr_notify(hwnd, NM_RDBLCLK);
8926 * Processes mouse down messages (right mouse button).
8929 * [I] HWND : window handle
8930 * [I] WORD : key flag
8931 * [I] WORD : x coordinate
8932 * [I] WORD : y coordinate
8937 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8940 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8944 LVHITTESTINFO lvHitTestInfo;
8946 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8948 /* send NM_RELEASEDCAPTURE notification */
8949 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8951 /* make sure the listview control window has the focus */
8952 if (infoPtr->bFocus == FALSE)
8955 /* set right button down flag */
8956 infoPtr->bRButtonDown = TRUE;
8958 /* determine the index of the selected item */
8959 ptPosition.x = wPosX;
8960 ptPosition.y = wPosY;
8961 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8962 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8964 LISTVIEW_SetItemFocus(hwnd,nItem);
8965 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8966 !LISTVIEW_IsSelected(hwnd,nItem))
8967 LISTVIEW_SetSelection(hwnd, nItem);
8971 LISTVIEW_RemoveAllSelections(hwnd);
8974 lvHitTestInfo.pt.x = wPosX;
8975 lvHitTestInfo.pt.y = wPosY;
8977 /* Send NM_RClICK notification */
8978 ZeroMemory(&nmlv, sizeof(nmlv));
8979 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8981 nmlv.iItem = lvHitTestInfo.iItem;
8982 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8989 nmlv.ptAction.x = wPosX;
8990 nmlv.ptAction.y = wPosY;
8991 listview_notify(hwnd, NM_RCLICK, &nmlv);
8998 * Processes mouse up messages (right mouse button).
9001 * [I] HWND : window handle
9002 * [I] WORD : key flag
9003 * [I] WORD : x coordinate
9004 * [I] WORD : y coordinate
9009 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
9012 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9014 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9016 if (infoPtr->bRButtonDown)
9023 /* set button flag */
9024 infoPtr->bRButtonDown = FALSE;
9026 /* Change to screen coordinate for WM_CONTEXTMENU */
9027 ClientToScreen(hwnd, &pt);
9029 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9030 SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
9041 * [I] HWND : window handle
9042 * [I] HWND : window handle of previously focused window
9047 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
9049 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9051 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
9053 /* send NM_SETFOCUS notification */
9054 hdr_notify(hwnd, NM_SETFOCUS);
9056 /* set window focus flag */
9057 infoPtr->bFocus = TRUE;
9069 * [I] HWND : window handle
9070 * [I] HFONT : font handle
9071 * [I] WORD : redraw flag
9076 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
9078 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9079 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
9081 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
9083 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9084 LISTVIEW_SaveTextMetrics(hwnd);
9086 if (uView == LVS_REPORT)
9088 /* set header font */
9089 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
9090 MAKELPARAM(fRedraw, 0));
9093 /* invalidate listview control client area */
9094 InvalidateRect(hwnd, NULL, TRUE);
9096 if (fRedraw != FALSE)
9104 * Message handling for WM_SETREDRAW.
9105 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9108 * [I] HWND : window handle
9109 * [I] bRedraw: state of redraw flag
9112 * DefWinProc return value
9114 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
9116 LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0);
9118 RedrawWindow(hwnd, NULL, 0,
9119 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
9125 * Resizes the listview control. This function processes WM_SIZE
9126 * messages. At this time, the width and height are not used.
9129 * [I] HWND : window handle
9130 * [I] WORD : new width
9131 * [I] WORD : new height
9136 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
9138 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9139 UINT uView = lStyle & LVS_TYPEMASK;
9141 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
9143 if (LISTVIEW_UpdateSize(hwnd))
9145 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
9147 if (lStyle & LVS_ALIGNLEFT)
9148 LISTVIEW_AlignLeft(hwnd);
9150 LISTVIEW_AlignTop(hwnd);
9153 LISTVIEW_UpdateScroll(hwnd);
9155 /* invalidate client area + erase background */
9156 InvalidateRect(hwnd, NULL, TRUE);
9164 * Sets the size information.
9167 * [I] HWND : window handle
9170 * Zero if no size change
9173 static BOOL LISTVIEW_UpdateSize(HWND hwnd)
9175 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9176 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9177 UINT uView = lStyle & LVS_TYPEMASK;
9181 TRACE("(hwnd=%x)\n", hwnd);
9183 GetClientRect(hwnd, &rcList);
9184 CopyRect(&rcOld,&(infoPtr->rcList));
9185 infoPtr->rcList.left = 0;
9186 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
9187 infoPtr->rcList.top = 0;
9188 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
9190 if (uView == LVS_LIST)
9192 if (lStyle & WS_HSCROLL)
9194 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
9195 if (infoPtr->rcList.bottom > nHScrollHeight)
9196 infoPtr->rcList.bottom -= nHScrollHeight;
9199 else if (uView == LVS_REPORT)
9206 Header_Layout(infoPtr->hwndHeader, &hl);
9208 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9210 if (!(LVS_NOCOLUMNHEADER & lStyle))
9211 infoPtr->rcList.top = max(wp.cy, 0);
9213 return (EqualRect(&rcOld,&(infoPtr->rcList)));
9218 * Processes WM_STYLECHANGED messages.
9221 * [I] HWND : window handle
9222 * [I] WPARAM : window style type (normal or extended)
9223 * [I] LPSTYLESTRUCT : window style information
9228 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
9231 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9232 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9233 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9234 RECT rcList = infoPtr->rcList;
9236 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
9237 hwnd, wStyleType, lpss);
9239 if (wStyleType == GWL_STYLE)
9241 if (uOldView == LVS_REPORT)
9242 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9244 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9245 ((lpss->styleNew & WS_HSCROLL) == 0))
9246 ShowScrollBar(hwnd, SB_HORZ, FALSE);
9248 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9249 ((lpss->styleNew & WS_VSCROLL) == 0))
9250 ShowScrollBar(hwnd, SB_VERT, FALSE);
9252 if (uNewView == LVS_ICON)
9254 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9255 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9256 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9257 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9258 if (lpss->styleNew & LVS_ALIGNLEFT)
9259 LISTVIEW_AlignLeft(hwnd);
9261 LISTVIEW_AlignTop(hwnd);
9263 else if (uNewView == LVS_REPORT)
9270 Header_Layout(infoPtr->hwndHeader, &hl);
9271 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
9273 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9274 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9276 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9277 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9278 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9279 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9281 else if (uNewView == LVS_LIST)
9283 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9284 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9285 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9286 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9290 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9291 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9292 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9293 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9294 if (lpss->styleNew & LVS_ALIGNLEFT)
9295 LISTVIEW_AlignLeft(hwnd);
9297 LISTVIEW_AlignTop(hwnd);
9300 /* update the size of the client area */
9301 LISTVIEW_UpdateSize(hwnd);
9303 /* add scrollbars if needed */
9304 LISTVIEW_UpdateScroll(hwnd);
9306 /* invalidate client area + erase background */
9307 InvalidateRect(hwnd, NULL, TRUE);
9309 /* print the list of unsupported window styles */
9310 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9313 /* If they change the view and we have an active edit control
9314 we will need to kill the control since the redraw will
9315 misplace the edit control.
9317 if (infoPtr->hwndEdit &&
9318 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9319 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9321 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9329 * Window procedure of the listview control.
9332 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9335 TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
9336 if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
9337 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9340 case LVM_APPROXIMATEVIEWRECT:
9341 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9342 LOWORD(lParam), HIWORD(lParam));
9344 return LISTVIEW_Arrange(hwnd, (INT)wParam);
9346 /* case LVM_CREATEDRAGIMAGE: */
9348 case LVM_DELETEALLITEMS:
9349 return LISTVIEW_DeleteAllItems(hwnd);
9351 case LVM_DELETECOLUMN:
9352 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9354 case LVM_DELETEITEM:
9355 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9357 case LVM_EDITLABELW:
9358 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
9360 case LVM_EDITLABELA:
9361 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
9363 case LVM_ENSUREVISIBLE:
9364 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9367 return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
9370 return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
9372 case LVM_GETBKCOLOR:
9373 return LISTVIEW_GetBkColor(hwnd);
9375 /* case LVM_GETBKIMAGE: */
9377 case LVM_GETCALLBACKMASK:
9378 return LISTVIEW_GetCallbackMask(hwnd);
9380 case LVM_GETCOLUMNA:
9381 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9383 case LVM_GETCOLUMNW:
9384 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9386 case LVM_GETCOLUMNORDERARRAY:
9387 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9389 case LVM_GETCOLUMNWIDTH:
9390 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9392 case LVM_GETCOUNTPERPAGE:
9393 return LISTVIEW_GetCountPerPage(hwnd);
9395 case LVM_GETEDITCONTROL:
9396 return LISTVIEW_GetEditControl(hwnd);
9398 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9399 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9402 return LISTVIEW_GetHeader(hwnd);
9404 case LVM_GETHOTCURSOR:
9405 FIXME("LVM_GETHOTCURSOR: unimplemented\n");
9408 case LVM_GETHOTITEM:
9409 return LISTVIEW_GetHotItem(hwnd);
9411 case LVM_GETHOVERTIME:
9412 return LISTVIEW_GetHoverTime(hwnd);
9414 case LVM_GETIMAGELIST:
9415 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9417 case LVM_GETISEARCHSTRINGA:
9418 case LVM_GETISEARCHSTRINGW:
9419 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9423 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
9426 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
9428 case LVM_GETITEMCOUNT:
9429 return LISTVIEW_GetItemCount(hwnd);
9431 case LVM_GETITEMPOSITION:
9432 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9434 case LVM_GETITEMRECT:
9435 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9437 case LVM_GETITEMSPACING:
9438 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9440 case LVM_GETITEMSTATE:
9441 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9443 case LVM_GETITEMTEXTA:
9444 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9446 case LVM_GETITEMTEXTW:
9447 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9449 case LVM_GETNEXTITEM:
9450 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9452 case LVM_GETNUMBEROFWORKAREAS:
9453 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9457 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9459 case LVM_GETSELECTEDCOUNT:
9460 return LISTVIEW_GetSelectedCount(hwnd);
9462 case LVM_GETSELECTIONMARK:
9463 return LISTVIEW_GetSelectionMark(hwnd);
9465 case LVM_GETSTRINGWIDTHA:
9466 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
9468 case LVM_GETSTRINGWIDTHW:
9469 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
9471 case LVM_GETSUBITEMRECT:
9472 FIXME("LVM_GETSUBITEMRECT: unimplemented\n");
9475 case LVM_GETTEXTBKCOLOR:
9476 return LISTVIEW_GetTextBkColor(hwnd);
9478 case LVM_GETTEXTCOLOR:
9479 return LISTVIEW_GetTextColor(hwnd);
9481 case LVM_GETTOOLTIPS:
9482 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9485 case LVM_GETTOPINDEX:
9486 return LISTVIEW_GetTopIndex(hwnd);
9488 /*case LVM_GETUNICODEFORMAT:
9489 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9492 case LVM_GETVIEWRECT:
9493 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9495 case LVM_GETWORKAREAS:
9496 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9500 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9502 case LVM_INSERTCOLUMNA:
9503 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9505 case LVM_INSERTCOLUMNW:
9506 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9508 case LVM_INSERTITEMA:
9509 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9511 case LVM_INSERTITEMW:
9512 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9514 case LVM_REDRAWITEMS:
9515 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9517 /* case LVM_SCROLL: */
9518 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9520 case LVM_SETBKCOLOR:
9521 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9523 /* case LVM_SETBKIMAGE: */
9525 case LVM_SETCALLBACKMASK:
9526 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9528 case LVM_SETCOLUMNA:
9529 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9531 case LVM_SETCOLUMNW:
9532 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9534 case LVM_SETCOLUMNORDERARRAY:
9535 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9537 case LVM_SETCOLUMNWIDTH:
9538 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9540 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9541 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9543 /* case LVM_SETHOTCURSOR: */
9545 case LVM_SETHOTITEM:
9546 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9548 case LVM_SETHOVERTIME:
9549 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9551 case LVM_SETICONSPACING:
9552 return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam);
9554 case LVM_SETIMAGELIST:
9555 return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9558 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9561 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9563 case LVM_SETITEMCOUNT:
9564 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9566 case LVM_SETITEMPOSITION:
9567 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9568 (INT)HIWORD(lParam));
9570 case LVM_SETITEMPOSITION32:
9571 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9572 ((POINT*)lParam)->y);
9574 case LVM_SETITEMSTATE:
9575 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam);
9577 case LVM_SETITEMTEXTA:
9578 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9580 case LVM_SETITEMTEXTW:
9581 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9583 case LVM_SETSELECTIONMARK:
9584 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9586 case LVM_SETTEXTBKCOLOR:
9587 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9589 case LVM_SETTEXTCOLOR:
9590 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9592 /* case LVM_SETTOOLTIPS: */
9593 /* case LVM_SETUNICODEFORMAT: */
9594 /* case LVM_SETWORKAREAS: */
9597 return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9599 /* case LVM_SUBITEMHITTEST: */
9602 return LISTVIEW_Update(hwnd, (INT)wParam);
9605 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9608 return LISTVIEW_Command(hwnd, wParam, lParam);
9611 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9614 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9617 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9620 return LISTVIEW_GetFont(hwnd);
9623 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
9624 (INT)HIWORD(wParam), (HWND)lParam);
9627 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9630 return LISTVIEW_KillFocus(hwnd);
9632 case WM_LBUTTONDBLCLK:
9633 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9636 case WM_LBUTTONDOWN:
9637 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9640 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9643 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9646 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9649 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9652 return LISTVIEW_NCDestroy(hwnd);
9655 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9657 case WM_NOTIFYFORMAT:
9658 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9661 return LISTVIEW_Paint(hwnd, (HDC)wParam);
9663 case WM_RBUTTONDBLCLK:
9664 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9667 case WM_RBUTTONDOWN:
9668 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9672 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9676 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9679 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9682 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9685 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9687 case WM_STYLECHANGED:
9688 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9690 /* case WM_TIMER: */
9693 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
9694 (INT)HIWORD(wParam), (HWND)lParam);
9697 if (wParam & (MK_SHIFT | MK_CONTROL))
9698 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9699 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
9701 /* case WM_WININICHANGE: */
9704 if (uMsg >= WM_USER)
9706 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
9710 /* call default window procedure */
9711 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9719 * Registers the window class.
9727 VOID LISTVIEW_Register(void)
9731 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9732 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9733 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9734 wndClass.cbClsExtra = 0;
9735 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9736 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
9737 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9738 wndClass.lpszClassName = WC_LISTVIEWW;
9739 RegisterClassW(&wndClass);
9744 * Unregisters the window class.
9752 VOID LISTVIEW_Unregister(void)
9754 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
9759 * Handle any WM_COMMAND messages
9765 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9767 switch (HIWORD(wParam))
9772 * Adjust the edit window size
9775 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9776 HDC hdc = GetDC(infoPtr->hwndEdit);
9777 HFONT hFont, hOldFont = 0;
9782 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9783 GetWindowRect(infoPtr->hwndEdit, &rect);
9785 /* Select font to get the right dimension of the string */
9786 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9789 hOldFont = SelectObject(hdc, hFont);
9792 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9794 TEXTMETRICW textMetric;
9796 /* Add Extra spacing for the next character */
9797 GetTextMetricsW(hdc, &textMetric);
9798 sz.cx += (textMetric.tmMaxCharWidth * 2);
9806 rect.bottom - rect.top,
9807 SWP_DRAWFRAME|SWP_NOMOVE);
9810 SelectObject(hdc, hOldFont);
9812 ReleaseDC(hwnd, hdc);
9818 return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9827 * Subclassed edit control windproc function
9833 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
9834 WPARAM wParam, LPARAM lParam, BOOL isW)
9836 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9837 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9838 static BOOL bIgnoreKillFocus = FALSE;
9839 BOOL cancel = FALSE;
9841 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9842 hwnd, uMsg, wParam, lParam, isW);
9847 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9850 if(bIgnoreKillFocus) return TRUE;
9855 WNDPROC editProc = einfo->EditWndProc;
9856 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9857 COMCTL32_Free(einfo);
9858 infoPtr->pedititem = NULL;
9859 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9863 if (VK_ESCAPE == (INT)wParam)
9868 else if (VK_RETURN == (INT)wParam)
9872 return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9875 if (einfo->EditLblCb)
9877 LPWSTR buffer = NULL;
9881 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9885 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9887 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9888 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9892 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9893 /* eg. Using a messagebox */
9894 bIgnoreKillFocus = TRUE;
9895 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9897 if (buffer) COMCTL32_Free(buffer);
9899 einfo->EditLblCb = NULL;
9900 bIgnoreKillFocus = FALSE;
9903 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9909 * Subclassed edit control windproc function
9915 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9917 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9922 * Subclassed edit control windproc function
9928 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9930 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9935 * Creates a subclassed edit cotrol
9941 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
9942 INT width, INT height, HWND parent, HINSTANCE hinst,
9943 EditlblCallbackW EditLblCb, DWORD param, BOOL isW)
9945 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
9946 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9951 TEXTMETRICW textMetric;
9953 TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
9955 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9958 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9959 hdc = GetDC(parent);
9961 /* Select the font to get appropriate metric dimensions */
9962 if(infoPtr->hFont != 0)
9963 hOldFont = SelectObject(hdc, infoPtr->hFont);
9965 /*Get String Lenght in pixels */
9966 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9968 /*Add Extra spacing for the next character */
9969 GetTextMetricsW(hdc, &textMetric);
9970 sz.cx += (textMetric.tmMaxCharWidth * 2);
9972 if(infoPtr->hFont != 0)
9973 SelectObject(hdc, hOldFont);
9975 ReleaseDC(parent, hdc);
9977 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
9979 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
9983 COMCTL32_Free(infoPtr->pedititem);
9987 infoPtr->pedititem->param = param;
9988 infoPtr->pedititem->EditLblCb = EditLblCb;
9989 infoPtr->pedititem->EditWndProc = (WNDPROC)
9990 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9991 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9993 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);