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".
64 #include "wine/port.h"
75 #include "wine/debug.h"
77 WINE_DEFAULT_DEBUG_CHANNEL(listview);
79 /* Some definitions for inline edit control */
80 typedef BOOL (*EditlblCallbackW)(HWND, LPWSTR, DWORD);
81 typedef BOOL (*EditlblCallbackA)(HWND, LPWSTR, DWORD);
83 typedef struct tagLV_INTHIT
86 DWORD distance; /* distance to closest item */
87 INT iDistItem; /* item number that is closest */
88 } LV_INTHIT, *LPLV_INTHIT;
91 typedef struct tagEDITLABEL_ITEM
95 EditlblCallbackW EditLblCb;
98 typedef struct tagLISTVIEW_SUBITEM
105 typedef struct tagLISTVIEW_ITEM
116 typedef struct tagLISTVIEW_SELECTION
120 } LISTVIEW_SELECTION;
122 typedef struct tagLISTVIEW_INFO
128 HIMAGELIST himlNormal;
129 HIMAGELIST himlSmall;
130 HIMAGELIST himlState;
134 HDPA hdpaSelectionRanges;
148 INT ntmHeight; /* from GetTextMetrics from above font */
149 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
151 DWORD dwExStyle; /* extended listview style */
153 PFNLVCOMPARE pfnCompare;
157 EDITLABEL_ITEM *pedititem;
159 INT nColumnCount; /* the number of columns in this control */
161 DWORD lastKeyPressTimestamp; /* Added */
162 WPARAM charCode; /* Added */
163 INT nSearchParamLength; /* Added */
164 WCHAR szSearchParam[ MAX_PATH ]; /* Added */
172 /* maximum size of a label */
173 #define DISP_TEXT_SIZE 512
175 /* padding for items in list and small icon display modes */
176 #define WIDTH_PADDING 12
178 /* padding for items in list, report and small icon display modes */
179 #define HEIGHT_PADDING 1
181 /* offset of items in report display mode */
182 #define REPORT_MARGINX 2
184 /* padding for icon in large icon display mode
185 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
186 * that HITTEST will see.
187 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
188 * ICON_TOP_PADDING - sum of the two above.
189 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
190 * LABEL_VERT_OFFSET - between bottom of text and end of box
192 #define ICON_TOP_PADDING_NOTHITABLE 2
193 #define ICON_TOP_PADDING_HITABLE 2
194 #define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE
195 #define ICON_BOTTOM_PADDING 4
196 #define LABEL_VERT_OFFSET 10
198 /* default label width for items in list and small icon display modes */
199 #define DEFAULT_LABEL_WIDTH 40
201 /* default column width for items in list display mode */
202 #define DEFAULT_COLUMN_WIDTH 96
204 /* Increment size of the horizontal scroll bar */
205 #define LISTVIEW_SCROLL_DIV_SIZE 10
207 /* Padding betwen image and label */
208 #define IMAGE_PADDING 2
210 /* Padding behind the label */
211 #define TRAILING_PADDING 5
213 /* Border for the icon caption */
214 #define CAPTION_BORDER 2
218 /* retrieve the number of items in the listview */
219 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
220 #define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA )
222 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
223 INT width, INT height, HWND parent, HINSTANCE hinst,
224 EditlblCallbackW EditLblCb, DWORD param, BOOL isW);
227 * forward declarations
229 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW);
230 static INT LISTVIEW_SuperHitTestItem(HWND, LPLV_INTHIT, BOOL);
231 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL);
232 static INT LISTVIEW_GetCountPerRow(HWND);
233 static INT LISTVIEW_GetCountPerColumn(HWND);
234 static VOID LISTVIEW_AlignLeft(HWND);
235 static VOID LISTVIEW_AlignTop(HWND);
236 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
237 static VOID LISTVIEW_AddSelection(HWND, INT);
238 static BOOL LISTVIEW_AddSubItemT(HWND, LPLVITEMW, BOOL);
239 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
240 static INT LISTVIEW_GetItemHeight(HWND);
241 static BOOL LISTVIEW_GetItemBoundBox(HWND, INT, LPRECT);
242 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
243 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
244 static LRESULT LISTVIEW_GetSubItemRect(HWND, INT, INT, INT, LPRECT);
245 static INT LISTVIEW_GetItemWidth(HWND);
246 static INT LISTVIEW_GetLabelWidth(HWND, INT);
247 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
248 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
249 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
250 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
251 static BOOL LISTVIEW_InitItemT(HWND, LISTVIEW_ITEM *, LPLVITEMW, BOOL);
252 static BOOL LISTVIEW_InitSubItemT(HWND, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL);
253 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
254 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
255 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
256 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
257 static BOOL LISTVIEW_SetItemT(HWND, LPLVITEMW, BOOL);
258 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
259 static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG);
260 static VOID LISTVIEW_UpdateScroll(HWND);
261 static VOID LISTVIEW_SetSelection(HWND, INT);
262 static BOOL LISTVIEW_UpdateSize(HWND);
263 static BOOL LISTVIEW_SetSubItemT(HWND, LPLVITEMW, BOOL);
264 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
265 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
266 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
267 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW);
268 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem);
269 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem);
270 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
271 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
272 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW);
273 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
274 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
275 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
276 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem);
277 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem);
278 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
279 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
280 static void ListView_UpdateLargeItemLabelRect (HWND hwnd, const LISTVIEW_INFO* infoPtr, int nItem, RECT *rect);
281 static LRESULT LISTVIEW_GetColumnT(HWND, INT, LPLVCOLUMNW, BOOL);
283 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
284 #define KEY_DELAY 450
286 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
288 static inline BOOL is_textW(LPCWSTR text)
290 return text != NULL && text != LPSTR_TEXTCALLBACKW;
293 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
295 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
296 return is_textW(text);
299 static inline int textlenT(LPCWSTR text, BOOL isW)
301 return !is_textT(text, isW) ? 0 :
302 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
305 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
308 if (isSrcW) lstrcpynW(dest, src, max);
309 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
311 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
312 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
315 static inline LPCSTR debugstr_t(LPCWSTR text, BOOL isW)
317 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
320 static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n)
322 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
325 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
327 LPWSTR wstr = (LPWSTR)text;
329 TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW);
332 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
333 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
334 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
336 TRACE(" wstr=%s\n", debugstr_w(wstr));
340 static inline void textfreeT(LPWSTR wstr, BOOL isW)
342 if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
346 * dest is a pointer to a Unicode string
347 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
349 static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
351 LPWSTR pszText = textdupTtoW(src, isW);
353 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
354 bResult = Str_SetPtrW(dest, pszText);
355 textfreeT(pszText, isW);
359 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
360 WPARAM wParam, LPARAM lParam, BOOL isW)
363 return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
365 return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
368 static inline BOOL notify(HWND self, INT code, LPNMHDR pnmh)
370 pnmh->hwndFrom = self;
371 pnmh->idFrom = GetWindowLongW(self, GWL_ID);
373 return (BOOL)SendMessageW(GetParent(self), WM_NOTIFY,
374 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
377 static inline BOOL hdr_notify(HWND self, INT code)
380 return notify(self, code, &nmh);
383 static inline BOOL listview_notify(HWND self, INT code, LPNMLISTVIEW plvnm)
385 return notify(self, code, (LPNMHDR)plvnm);
388 static int tabNotification[] = {
389 LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
390 LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
391 LVN_GETDISPINFOW, LVN_GETDISPINFOA,
392 LVN_SETDISPINFOW, LVN_SETDISPINFOA,
393 LVN_ODFINDITEMW, LVN_ODFINDITEMA,
394 LVN_GETINFOTIPW, LVN_GETINFOTIPA,
398 static int get_ansi_notification(INT unicodeNotificationCode)
400 int *pTabNotif = tabNotification;
401 while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
402 if (*pTabNotif) return *pTabNotif;
403 ERR("unknown notification %x\n", unicodeNotificationCode);
404 return unicodeNotificationCode;
408 Send notification. depends on dispinfoW having same
409 structure as dispinfoA.
410 self : listview handle
411 notificationCode : *Unicode* notification code
412 pdi : dispinfo structure (can be unicode or ansi)
413 isW : TRUE if dispinfo is Unicode
415 static BOOL dispinfo_notifyT(HWND self, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
417 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(self, 0);
418 BOOL bResult = FALSE;
419 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
421 INT cchTempBufMax = 0, savCchTextMax = 0;
422 LPWSTR pszTempBuf = NULL, savPszText = NULL;
424 TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self, notificationCode, pdi, isW);
425 TRACE(" notifyFormat=%s\n",
426 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
427 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
428 if (infoPtr->notifyFormat == NFR_ANSI)
429 realNotifCode = get_ansi_notification(notificationCode);
431 realNotifCode = notificationCode;
433 if (is_textT(pdi->item.pszText, isW))
435 if (isW && infoPtr->notifyFormat == NFR_ANSI)
436 convertToAnsi = TRUE;
437 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
438 convertToUnicode = TRUE;
441 if (convertToAnsi || convertToUnicode)
443 TRACE(" we have to convert the text to the correct format\n");
444 if (notificationCode != LVN_GETDISPINFOW)
445 { /* length of existing text */
446 cchTempBufMax = convertToUnicode ?
447 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
448 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
451 cchTempBufMax = pdi->item.cchTextMax;
453 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
454 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
455 if (!pszTempBuf) return FALSE;
456 if (convertToUnicode)
457 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
458 pszTempBuf, cchTempBufMax);
460 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
461 cchTempBufMax, NULL, NULL);
462 TRACE(" text=%s\n", debugstr_t(pszTempBuf, convertToUnicode));
463 savCchTextMax = pdi->item.cchTextMax;
464 savPszText = pdi->item.pszText;
465 pdi->item.pszText = pszTempBuf;
466 pdi->item.cchTextMax = cchTempBufMax;
469 bResult = notify(self, realNotifCode, (LPNMHDR)pdi);
471 if (convertToUnicode || convertToAnsi)
472 { /* convert back result */
473 TRACE(" returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode));
474 if (convertToUnicode) /* note : pointer can be changed by app ! */
475 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
476 savCchTextMax, NULL, NULL);
478 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
479 savPszText, savCchTextMax);
480 pdi->item.pszText = savPszText; /* restores our buffer */
481 pdi->item.cchTextMax = savCchTextMax;
482 HeapFree(GetProcessHeap(), 0, pszTempBuf);
487 static inline LRESULT LISTVIEW_GetItemW(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal)
489 return LISTVIEW_GetItemT(hwnd, lpLVItem, internal, TRUE);
492 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
496 n = min(min(n, strlenW(s1)), strlenW(s2));
497 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
498 return res ? res - 2 : res;
501 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
503 static int index = 0;
504 static char buffers[20][256];
505 char* buf = buffers[index++ % 20];
506 if (lpLVItem == NULL) return "(null)";
507 snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
508 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
509 lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
510 lpLVItem->state, lpLVItem->stateMask,
511 lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
512 debugstr_tn(lpLVItem->pszText, isW, 80),
513 lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
518 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
520 static int index = 0;
521 static char buffers[20][256];
522 char* buf = buffers[index++ % 20];
523 if (lpColumn == NULL) return "(null)";
524 snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
525 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
526 lpColumn->mask, lpColumn->fmt, lpColumn->cx,
527 lpColumn->mask & LVCF_TEXT ? lpColumn->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
528 debugstr_tn(lpColumn->pszText, isW, 80): "",
529 lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
533 static void LISTVIEW_DumpListview(LISTVIEW_INFO *iP, INT line)
535 DWORD dwStyle = GetWindowLongW (iP->hwndSelf, GWL_STYLE);
536 TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n",
537 iP->hwndSelf, line, iP->clrBk, iP->clrText, iP->clrTextBk,
538 iP->nItemHeight, iP->nItemWidth, dwStyle);
539 TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n",
540 iP->hwndSelf, line, iP->himlNormal, iP->himlSmall, iP->himlState,
541 iP->nFocusedItem, iP->nHotItem, iP->dwExStyle);
545 LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
548 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
549 NMLVCUSTOMDRAW nmcdhdr;
552 TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd, dwDrawStage, hdc);
554 nmcd= & nmcdhdr.nmcd;
555 nmcd->hdr.hwndFrom = hwnd;
556 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
557 nmcd->hdr.code = NM_CUSTOMDRAW;
558 nmcd->dwDrawStage= dwDrawStage;
560 nmcd->rc.left = rc.left;
561 nmcd->rc.right = rc.right;
562 nmcd->rc.bottom = rc.bottom;
563 nmcd->rc.top = rc.top;
564 nmcd->dwItemSpec = 0;
565 nmcd->uItemState = 0;
566 nmcd->lItemlParam= 0;
567 nmcdhdr.clrText = infoPtr->clrText;
568 nmcdhdr.clrTextBk= infoPtr->clrBk;
570 return (BOOL)SendMessageW (GetParent (hwnd), WM_NOTIFY,
571 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
575 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
576 UINT iItem, UINT iSubItem,
579 LISTVIEW_INFO *infoPtr;
580 NMLVCUSTOMDRAW nmcdhdr;
582 DWORD dwDrawStage,dwItemSpec;
588 infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
590 ZeroMemory(&item,sizeof(item));
592 item.mask = LVIF_PARAM;
593 ListView_GetItemW(hwnd,&item);
595 dwDrawStage=CDDS_ITEM | uItemDrawState;
599 if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
600 if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
601 if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
603 itemRect.left = LVIR_BOUNDS;
604 LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
606 nmcd= & nmcdhdr.nmcd;
607 nmcd->hdr.hwndFrom = hwnd;
608 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
609 nmcd->hdr.code = NM_CUSTOMDRAW;
610 nmcd->dwDrawStage= dwDrawStage;
612 nmcd->rc.left = itemRect.left;
613 nmcd->rc.right = itemRect.right;
614 nmcd->rc.bottom = itemRect.bottom;
615 nmcd->rc.top = itemRect.top;
616 nmcd->dwItemSpec = dwItemSpec;
617 nmcd->uItemState = uItemState;
618 nmcd->lItemlParam= item.lParam;
619 nmcdhdr.clrText = infoPtr->clrText;
620 nmcdhdr.clrTextBk= infoPtr->clrBk;
621 nmcdhdr.iSubItem =iSubItem;
623 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
624 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
625 nmcd->uItemState, nmcd->lItemlParam);
627 retval=SendMessageW (GetParent (hwnd), WM_NOTIFY,
628 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
630 infoPtr->clrText=nmcdhdr.clrText;
631 infoPtr->clrBk =nmcdhdr.clrTextBk;
632 return (BOOL) retval;
636 /*************************************************************************
637 * LISTVIEW_ProcessLetterKeys
639 * Processes keyboard messages generated by pressing the letter keys
641 * What this does is perform a case insensitive search from the
642 * current position with the following quirks:
643 * - If two chars or more are pressed in quick succession we search
644 * for the corresponding string (e.g. 'abc').
645 * - If there is a delay we wipe away the current search string and
646 * restart with just that char.
647 * - If the user keeps pressing the same character, whether slowly or
648 * fast, so that the search string is entirely composed of this
649 * character ('aaaaa' for instance), then we search for first item
650 * that starting with that character.
651 * - If the user types the above character in quick succession, then
652 * we must also search for the corresponding string ('aaaaa'), and
653 * go to that string if there is a match.
661 * - The current implementation has a list of characters it will
662 * accept and it ignores averything else. In particular it will
663 * ignore accentuated characters which seems to match what
664 * Windows does. But I'm not sure it makes sense to follow
666 * - We don't sound a beep when the search fails.
670 * TREEVIEW_ProcessLetterKeys
672 static INT LISTVIEW_ProcessLetterKeys(
673 HWND hwnd, /* handle to the window */
674 WPARAM charCode, /* the character code, the actual character */
675 LPARAM keyData /* key data */
678 LISTVIEW_INFO *infoPtr;
683 WCHAR buffer[MAX_PATH];
684 DWORD timestamp,elapsed;
686 /* simple parameter checking */
687 if (!hwnd || !charCode || !keyData)
690 infoPtr=(LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
694 /* only allow the valid WM_CHARs through */
695 if (!isalnum(charCode) &&
696 charCode != '.' && charCode != '`' && charCode != '!' &&
697 charCode != '@' && charCode != '#' && charCode != '$' &&
698 charCode != '%' && charCode != '^' && charCode != '&' &&
699 charCode != '*' && charCode != '(' && charCode != ')' &&
700 charCode != '-' && charCode != '_' && charCode != '+' &&
701 charCode != '=' && charCode != '\\'&& charCode != ']' &&
702 charCode != '}' && charCode != '[' && charCode != '{' &&
703 charCode != '/' && charCode != '?' && charCode != '>' &&
704 charCode != '<' && charCode != ',' && charCode != '~')
707 nSize=GETITEMCOUNT(infoPtr);
708 /* if there's one item or less, there is no where to go */
712 /* compute how much time elapsed since last keypress */
713 timestamp=GetTickCount();
714 if (timestamp > infoPtr->lastKeyPressTimestamp) {
715 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
717 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
720 /* update the search parameters */
721 infoPtr->lastKeyPressTimestamp=timestamp;
722 if (elapsed < KEY_DELAY) {
723 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
724 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
726 if (infoPtr->charCode != charCode) {
727 infoPtr->charCode=charCode=0;
730 infoPtr->charCode=charCode;
731 infoPtr->szSearchParam[0]=charCode;
732 infoPtr->nSearchParamLength=1;
733 /* Redundant with the 1 char string */
737 /* and search from the current position */
739 if (infoPtr->nFocusedItem >= 0) {
740 endidx=infoPtr->nFocusedItem;
742 /* if looking for single character match,
743 * then we must always move forward
745 if (infoPtr->nSearchParamLength == 1)
759 ZeroMemory(&item, sizeof(item));
760 item.mask = LVIF_TEXT;
763 item.pszText = buffer;
764 item.cchTextMax = COUNTOF(buffer);
765 ListView_GetItemW( hwnd, &item );
767 /* check for a match */
768 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
771 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
772 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
773 /* This would work but we must keep looking for a longer match */
777 } while (idx != endidx);
780 if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
781 /* refresh client area */
782 InvalidateRect(hwnd, NULL, TRUE);
790 /*************************************************************************
791 * LISTVIEW_UpdateHeaderSize [Internal]
793 * Function to resize the header control
796 * hwnd [I] handle to a window
797 * nNewScrollPos [I] Scroll Pos to Set
804 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
806 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
810 GetWindowRect(infoPtr->hwndHeader, &winRect);
811 point[0].x = winRect.left;
812 point[0].y = winRect.top;
813 point[1].x = winRect.right;
814 point[1].y = winRect.bottom;
816 MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
817 point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
818 point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
820 SetWindowPos(infoPtr->hwndHeader,0,
821 point[0].x,point[0].y,point[1].x,point[1].y,
822 SWP_NOZORDER | SWP_NOACTIVATE);
827 * Update the scrollbars. This functions should be called whenever
828 * the content, size or view changes.
831 * [I] HWND : window handle
836 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
838 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
839 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
840 UINT uView = lStyle & LVS_TYPEMASK;
841 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
842 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
843 SCROLLINFO scrollInfo;
845 if (lStyle & LVS_NOSCROLL) return;
847 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
848 scrollInfo.cbSize = sizeof(SCROLLINFO);
850 if (uView == LVS_LIST)
852 /* update horizontal scrollbar */
854 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
855 INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
856 INT nNumOfItems = GETITEMCOUNT(infoPtr);
858 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
859 if((nNumOfItems % nCountPerColumn) == 0)
867 scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
868 scrollInfo.nPage = nCountPerRow;
869 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
870 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
871 ShowScrollBar(hwnd, SB_VERT, FALSE);
873 else if (uView == LVS_REPORT)
875 /* update vertical scrollbar */
877 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
878 scrollInfo.nPos = ListView_GetTopIndex(hwnd);
879 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
880 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
881 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
883 /* update horizontal scrollbar */
884 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
885 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
886 || GETITEMCOUNT(infoPtr) == 0)
891 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
892 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
893 scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
894 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
896 /* Update the Header Control */
897 scrollInfo.fMask = SIF_POS;
898 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
899 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
906 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
908 INT nViewWidth = rcView.right - rcView.left;
909 INT nViewHeight = rcView.bottom - rcView.top;
911 /* Update Horizontal Scrollbar */
912 scrollInfo.fMask = SIF_POS;
913 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
914 || GETITEMCOUNT(infoPtr) == 0)
918 scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
920 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
921 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
922 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
924 /* Update Vertical Scrollbar */
925 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
926 scrollInfo.fMask = SIF_POS;
927 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
928 || GETITEMCOUNT(infoPtr) == 0)
932 scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
934 scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
935 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
936 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
943 * Prints a message for unsupported window styles.
944 * A kind of TODO list for window styles.
947 * [I] LONG : window style
952 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
954 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
955 FIXME(" LVS_NOSCROLL\n");
957 if (lStyle & LVS_EDITLABELS)
958 FIXME(" LVS_EDITLABELS\n");
960 if (lStyle & LVS_NOLABELWRAP)
961 FIXME(" LVS_NOLABELWRAP\n");
963 if (lStyle & LVS_SHAREIMAGELISTS)
964 FIXME(" LVS_SHAREIMAGELISTS\n");
966 if (lStyle & LVS_SORTASCENDING)
967 FIXME(" LVS_SORTASCENDING\n");
969 if (lStyle & LVS_SORTDESCENDING)
970 FIXME(" LVS_SORTDESCENDING\n");
975 * Aligns the items with the top edge of the window.
978 * [I] HWND : window handle
983 static VOID LISTVIEW_AlignTop(HWND hwnd)
985 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
986 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
987 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
990 INT i, off_x=0, off_y=0;
992 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
994 /* Since SetItemPosition uses upper-left of icon, and for
995 style=LVS_ICON the icon is not left adjusted, get the offset */
996 if (uView == LVS_ICON)
998 off_y = ICON_TOP_PADDING;
999 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1003 ZeroMemory(&rcView, sizeof(RECT));
1005 if (nListWidth > infoPtr->nItemWidth)
1007 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1009 if (ptItem.x + infoPtr->nItemWidth > nListWidth)
1012 ptItem.y += infoPtr->nItemHeight;
1015 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1016 ptItem.x += infoPtr->nItemWidth;
1017 rcView.right = max(rcView.right, ptItem.x);
1020 rcView.bottom = ptItem.y + infoPtr->nItemHeight;
1024 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1026 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1027 ptItem.y += infoPtr->nItemHeight;
1030 rcView.right = infoPtr->nItemWidth;
1031 rcView.bottom = ptItem.y;
1034 LISTVIEW_SetViewRect(hwnd, &rcView);
1040 * Aligns the items with the left edge of the window.
1043 * [I] HWND : window handle
1048 static VOID LISTVIEW_AlignLeft(HWND hwnd)
1050 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1051 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1052 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1055 INT i, off_x=0, off_y=0;
1057 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1059 /* Since SetItemPosition uses upper-left of icon, and for
1060 style=LVS_ICON the icon is not left adjusted, get the offset */
1061 if (uView == LVS_ICON)
1063 off_y = ICON_TOP_PADDING;
1064 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1068 ZeroMemory(&rcView, sizeof(RECT));
1070 if (nListHeight > infoPtr->nItemHeight)
1072 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1074 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1077 ptItem.x += infoPtr->nItemWidth;
1080 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1081 ptItem.y += infoPtr->nItemHeight;
1082 rcView.bottom = max(rcView.bottom, ptItem.y);
1085 rcView.right = ptItem.x + infoPtr->nItemWidth;
1089 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1091 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1092 ptItem.x += infoPtr->nItemWidth;
1095 rcView.bottom = infoPtr->nItemHeight;
1096 rcView.right = ptItem.x;
1099 LISTVIEW_SetViewRect(hwnd, &rcView);
1105 * Set the bounding rectangle of all the items.
1108 * [I] HWND : window handle
1109 * [I] LPRECT : bounding rectangle
1115 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
1117 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1118 BOOL bResult = FALSE;
1120 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
1121 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1123 if (lprcView != NULL)
1126 infoPtr->rcView.left = lprcView->left;
1127 infoPtr->rcView.top = lprcView->top;
1128 infoPtr->rcView.right = lprcView->right;
1129 infoPtr->rcView.bottom = lprcView->bottom;
1137 * Retrieves the bounding rectangle of all the items.
1140 * [I] HWND : window handle
1141 * [O] LPRECT : bounding rectangle
1147 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
1149 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1150 BOOL bResult = FALSE;
1153 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
1155 if (lprcView != NULL)
1157 bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
1158 if (bResult != FALSE)
1160 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1161 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1162 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1163 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1166 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1167 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1175 * Retrieves the subitem pointer associated with the subitem index.
1178 * [I] HDPA : DPA handle for a specific item
1179 * [I] INT : index of subitem
1182 * SUCCESS : subitem pointer
1185 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1188 LISTVIEW_SUBITEM *lpSubItem;
1191 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1193 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1194 if (lpSubItem != NULL)
1196 if (lpSubItem->iSubItem == nSubItem)
1208 * Calculates the width of an item.
1211 * [I] HWND : window handle
1212 * [I] LONG : window style
1215 * Returns item width.
1217 static INT LISTVIEW_GetItemWidth(HWND hwnd)
1219 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1220 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
1221 UINT uView = style & LVS_TYPEMASK;
1222 INT nHeaderItemCount;
1228 TRACE("(hwnd=%x)\n", hwnd);
1230 if (uView == LVS_ICON)
1232 nItemWidth = infoPtr->iconSpacing.cx;
1234 else if (uView == LVS_REPORT)
1236 /* calculate width of header */
1237 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1238 for (i = 0; i < nHeaderItemCount; i++)
1240 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1242 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1248 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1250 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
1251 nItemWidth = max(nItemWidth, nLabelWidth);
1254 /* default label size */
1255 if (GETITEMCOUNT(infoPtr) == 0)
1257 nItemWidth = DEFAULT_COLUMN_WIDTH;
1261 if (nItemWidth == 0)
1263 nItemWidth = DEFAULT_LABEL_WIDTH;
1268 nItemWidth += WIDTH_PADDING;
1270 if (infoPtr->himlSmall != NULL)
1272 nItemWidth += infoPtr->iconSize.cx;
1275 if (infoPtr->himlState != NULL)
1277 nItemWidth += infoPtr->iconSize.cx;
1284 /* nItemWidth Cannot be Zero */
1292 * Calculates the width of a specific item.
1295 * [I] HWND : window handle
1296 * [I] LPSTR : string
1299 * Returns the width of an item width a specified string.
1301 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1303 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1304 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1305 INT nHeaderItemCount;
1310 TRACE("(hwnd=%x)\n", hwnd);
1312 if (uView == LVS_ICON)
1314 nItemWidth = infoPtr->iconSpacing.cx;
1316 else if (uView == LVS_REPORT)
1318 /* calculate width of header */
1319 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1320 for (i = 0; i < nHeaderItemCount; i++)
1322 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1324 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1330 /* get width of string */
1331 nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1333 /* default label size */
1334 if (GETITEMCOUNT(infoPtr) == 0)
1336 nItemWidth = DEFAULT_COLUMN_WIDTH;
1340 if (nItemWidth == 0)
1342 nItemWidth = DEFAULT_LABEL_WIDTH;
1347 nItemWidth += WIDTH_PADDING;
1349 if (infoPtr->himlSmall != NULL)
1351 nItemWidth += infoPtr->iconSize.cx;
1354 if (infoPtr->himlState != NULL)
1356 nItemWidth += infoPtr->iconSize.cx;
1367 * Retrieves and saves important text metrics info for the current
1371 * [I] HWND : window handle
1374 static VOID LISTVIEW_SaveTextMetrics(HWND hwnd)
1376 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1378 HDC hdc = GetDC(hwnd);
1379 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1380 INT oldHeight, oldACW;
1382 GetTextMetricsW(hdc, &tm);
1384 oldHeight = infoPtr->ntmHeight;
1385 oldACW = infoPtr->ntmAveCharWidth;
1386 infoPtr->ntmHeight = tm.tmHeight;
1387 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1389 SelectObject(hdc, hOldFont);
1390 ReleaseDC(hwnd, hdc);
1391 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1392 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1398 * Calculates the height of an item.
1401 * [I] HWND : window handle
1404 * Returns item height.
1406 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1408 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1409 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1410 INT nItemHeight = 0;
1412 if (uView == LVS_ICON)
1414 nItemHeight = infoPtr->iconSpacing.cy;
1418 if(infoPtr->himlState || infoPtr->himlSmall)
1419 nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1421 nItemHeight = infoPtr->ntmHeight;
1428 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1430 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1431 LISTVIEW_SELECTION *selection;
1432 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1435 TRACE("Selections are:\n");
1436 for (i = 0; i < topSelection; i++)
1438 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1439 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1445 * A compare function for selection ranges
1448 * [I] LPVOID : Item 1;
1449 * [I] LPVOID : Item 2;
1450 * [I] LPARAM : flags
1453 * >0 : if Item 1 > Item 2
1454 * <0 : if Item 2 > Item 1
1455 * 0 : if Item 1 == Item 2
1457 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1460 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1461 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1462 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1463 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1477 * Adds a selection range.
1480 * [I] HWND : window handle
1481 * [I] INT : lower item index
1482 * [I] INT : upper item index
1487 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1489 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1490 LISTVIEW_SELECTION *selection;
1491 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1492 BOOL lowerzero=FALSE;
1494 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1495 selection->lower = lItem;
1496 selection->upper = uItem;
1498 TRACE("Add range %i - %i\n", lItem, uItem);
1501 LISTVIEW_SELECTION *checkselection,*checkselection2;
1502 INT index,mergeindex;
1504 /* find overlapping selections */
1505 /* we want to catch adjacent ranges so expand our range by 1 */
1508 if (selection->lower == 0)
1513 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1514 LISTVIEW_CompareSelectionRanges,
1516 selection->upper --;
1520 selection->lower ++;
1524 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1525 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1526 checkselection->upper);
1528 checkselection->lower = min(selection->lower,checkselection->lower);
1529 checkselection->upper = max(selection->upper,checkselection->upper);
1531 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1532 checkselection->upper);
1534 COMCTL32_Free(selection);
1536 /* merge now common selection ranges in the lower group*/
1539 checkselection->upper ++;
1540 if (checkselection->lower == 0)
1543 checkselection->lower --;
1545 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1546 checkselection->upper);
1548 /* not sorted yet */
1549 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1550 LISTVIEW_CompareSelectionRanges, 0,
1553 checkselection->upper --;
1557 checkselection->lower ++;
1559 if (mergeindex >=0 && mergeindex != index)
1561 TRACE("Merge with index %i\n",mergeindex);
1562 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1564 checkselection->lower = min(checkselection->lower,
1565 checkselection2->lower);
1566 checkselection->upper = max(checkselection->upper,
1567 checkselection2->upper);
1568 COMCTL32_Free(checkselection2);
1569 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1573 while (mergeindex > -1 && mergeindex <index);
1575 /* merge now common selection ranges in the upper group*/
1578 checkselection->upper ++;
1579 if (checkselection->lower == 0)
1582 checkselection->lower --;
1584 TRACE("search upper range %i (%lu - %lu)\n",index,
1585 checkselection->lower, checkselection->upper);
1587 /* not sorted yet */
1588 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1590 LISTVIEW_CompareSelectionRanges, 0,
1593 checkselection->upper --;
1597 checkselection->lower ++;
1599 if (mergeindex >=0 && mergeindex !=index)
1601 TRACE("Merge with index %i\n",mergeindex);
1602 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1604 checkselection->lower = min(checkselection->lower,
1605 checkselection2->lower);
1606 checkselection->upper = max(checkselection->upper,
1607 checkselection2->upper);
1608 COMCTL32_Free(checkselection2);
1609 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1612 while (mergeindex > -1);
1617 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1618 LISTVIEW_CompareSelectionRanges, 0,
1621 TRACE("Insert before index %i\n",index);
1624 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1629 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1634 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1635 LISTVIEW_PrintSelectionRanges(hwnd);
1640 * check if a specified index is selected.
1643 * [I] HWND : window handle
1644 * [I] INT : item index
1649 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1651 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1652 LISTVIEW_SELECTION selection;
1655 selection.upper = nItem;
1656 selection.lower = nItem;
1658 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1659 LISTVIEW_CompareSelectionRanges,
1669 * Removes all selection ranges
1672 * HWND: window handle
1678 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1680 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1681 LISTVIEW_SELECTION *selection;
1685 TRACE("(0x%x)\n",hwnd);
1687 ZeroMemory(&item,sizeof(item));
1688 item.stateMask = LVIS_SELECTED;
1692 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1695 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1696 for (i = selection->lower; i<=selection->upper; i++)
1697 LISTVIEW_SetItemState(hwnd,i,&item);
1698 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1701 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1709 * Removes a range selections.
1712 * [I] HWND : window handle
1713 * [I] INT : lower item index
1714 * [I] INT : upper item index
1719 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1721 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1722 LISTVIEW_SELECTION removeselection,*checkselection;
1725 removeselection.lower = lItem;
1726 removeselection.upper = uItem;
1728 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1729 LISTVIEW_PrintSelectionRanges(hwnd);
1731 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1732 LISTVIEW_CompareSelectionRanges,
1739 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1742 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1743 checkselection->upper);
1746 if ((checkselection->upper == removeselection.upper) &&
1747 (checkselection->lower == removeselection.lower))
1749 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1752 /* case 2: engulf */
1753 else if (((checkselection->upper < removeselection.upper) &&
1754 (checkselection->lower > removeselection.lower))||
1755 ((checkselection->upper <= removeselection.upper) &&
1756 (checkselection->lower > removeselection.lower)) ||
1757 ((checkselection->upper < removeselection.upper) &&
1758 (checkselection->lower >= removeselection.lower)))
1761 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1762 /* do it again because others may also get caught */
1764 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1766 /* case 3: overlap upper */
1767 else if ((checkselection->upper < removeselection.upper) &&
1768 (checkselection->lower < removeselection.lower))
1770 checkselection->upper = removeselection.lower - 1;
1772 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1774 /* case 4: overlap lower */
1775 else if ((checkselection->upper > removeselection.upper) &&
1776 (checkselection->lower > removeselection.lower))
1778 checkselection->lower = removeselection.upper + 1;
1780 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1782 /* case 5: fully internal */
1783 else if (checkselection->upper == removeselection.upper)
1784 checkselection->upper = removeselection.lower - 1;
1785 else if (checkselection->lower == removeselection.lower)
1786 checkselection->lower = removeselection.upper + 1;
1789 /* bisect the range */
1790 LISTVIEW_SELECTION *newselection;
1792 newselection = (LISTVIEW_SELECTION *)
1793 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1794 newselection -> lower = checkselection->lower;
1795 newselection -> upper = removeselection.lower - 1;
1796 checkselection -> lower = removeselection.upper + 1;
1797 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1799 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1801 LISTVIEW_PrintSelectionRanges(hwnd);
1806 * Updates the various indices after an item has been inserted or deleted.
1809 * [I] HWND : window handle
1810 * [I] INT : item index
1811 * [I] INT : Direction of shift, +1 or -1.
1816 static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
1818 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1819 LISTVIEW_SELECTION selection,*checkselection;
1822 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1824 selection.upper = nItem;
1825 selection.lower = nItem;
1827 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1828 LISTVIEW_CompareSelectionRanges,
1829 0,DPAS_SORTED|DPAS_INSERTAFTER);
1831 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1833 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1834 if ((checkselection->lower >= nItem)&&
1835 (checkselection->lower + direction >= 0))
1836 checkselection->lower += direction;
1837 if ((checkselection->upper >= nItem)&&
1838 (checkselection->upper + direction >=0))
1839 checkselection->upper += direction;
1843 /* Note that the following will fail if direction != +1 and -1 */
1844 if (infoPtr->nSelectionMark > nItem)
1845 infoPtr->nSelectionMark += direction;
1846 else if (infoPtr->nSelectionMark == nItem)
1849 infoPtr->nSelectionMark += direction;
1850 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1851 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1854 if (infoPtr->nFocusedItem > nItem)
1855 infoPtr->nFocusedItem += direction;
1856 else if (infoPtr->nFocusedItem == nItem)
1859 infoPtr->nFocusedItem += direction;
1862 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1863 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1864 if (infoPtr->nFocusedItem >= 0)
1865 LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
1868 /* But we are not supposed to modify nHotItem! */
1874 * Adds a block of selections.
1877 * [I] HWND : window handle
1878 * [I] INT : item index
1883 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1885 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1886 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1887 INT nLast = max(infoPtr->nSelectionMark, nItem);
1894 ZeroMemory(&item,sizeof(item));
1895 item.stateMask = LVIS_SELECTED;
1896 item.state = LVIS_SELECTED;
1898 for (i = nFirst; i <= nLast; i++)
1899 LISTVIEW_SetItemState(hwnd,i,&item);
1901 LISTVIEW_SetItemFocus(hwnd, nItem);
1902 infoPtr->nSelectionMark = nItem;
1908 * Adds a single selection.
1911 * [I] HWND : window handle
1912 * [I] INT : item index
1917 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1919 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1922 ZeroMemory(&item,sizeof(item));
1923 item.state = LVIS_SELECTED;
1924 item.stateMask = LVIS_SELECTED;
1926 LISTVIEW_SetItemState(hwnd,nItem,&item);
1928 LISTVIEW_SetItemFocus(hwnd, nItem);
1929 infoPtr->nSelectionMark = nItem;
1934 * Selects or unselects an item.
1937 * [I] HWND : window handle
1938 * [I] INT : item index
1944 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1946 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1950 ZeroMemory(&item,sizeof(item));
1951 item.stateMask = LVIS_SELECTED;
1953 if (LISTVIEW_IsSelected(hwnd,nItem))
1955 LISTVIEW_SetItemState(hwnd,nItem,&item);
1960 item.state = LVIS_SELECTED;
1961 LISTVIEW_SetItemState(hwnd,nItem,&item);
1965 LISTVIEW_SetItemFocus(hwnd, nItem);
1966 infoPtr->nSelectionMark = nItem;
1973 * Selects items based on view coordinates.
1976 * [I] HWND : window handle
1977 * [I] RECT : selection rectangle
1982 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
1984 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1989 ZeroMemory(&item,sizeof(item));
1990 item.stateMask = LVIS_SELECTED;
1992 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1994 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
1996 if (PtInRect(&rcSelRect, ptItem) != FALSE)
1997 item.state = LVIS_SELECTED;
2000 LISTVIEW_SetItemState(hwnd,i,&item);
2006 * Sets a single group selection.
2009 * [I] HWND : window handle
2010 * [I] INT : item index
2015 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
2017 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2018 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2021 ZeroMemory(&item,sizeof(item));
2022 item.stateMask = LVIS_SELECTED;
2024 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2029 if (infoPtr->nSelectionMark == -1)
2031 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2035 nFirst = min(infoPtr->nSelectionMark, nItem);
2036 nLast = max(infoPtr->nSelectionMark, nItem);
2039 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2041 if ((i < nFirst) || (i > nLast))
2044 item.state = LVIS_SELECTED;
2045 LISTVIEW_SetItemState(hwnd,i,&item);
2053 LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcItem);
2054 LISTVIEW_GetItemBoundBox(hwnd, infoPtr->nSelectionMark, &rcSelMark);
2055 rcSel.left = min(rcSelMark.left, rcItem.left);
2056 rcSel.top = min(rcSelMark.top, rcItem.top);
2057 rcSel.right = max(rcSelMark.right, rcItem.right);
2058 rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
2059 LISTVIEW_SetSelectionRect(hwnd, rcSel);
2060 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2061 nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
2062 infoPtr->nSelectionMark,
2063 rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
2064 rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2068 LISTVIEW_SetItemFocus(hwnd, nItem);
2073 * Manages the item focus.
2076 * [I] HWND : window handle
2077 * [I] INT : item index
2080 * TRUE : focused item changed
2081 * FALSE : focused item has NOT changed
2083 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
2085 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2086 BOOL bResult = FALSE;
2089 if (infoPtr->nFocusedItem != nItem)
2091 if (infoPtr->nFocusedItem >= 0)
2093 INT oldFocus = infoPtr->nFocusedItem;
2095 infoPtr->nFocusedItem = -1;
2096 ZeroMemory(&lvItem, sizeof(lvItem));
2097 lvItem.stateMask = LVIS_FOCUSED;
2098 ListView_SetItemState(hwnd, oldFocus, &lvItem);
2102 lvItem.state = LVIS_FOCUSED;
2103 lvItem.stateMask = LVIS_FOCUSED;
2104 ListView_SetItemState(hwnd, nItem, &lvItem);
2106 infoPtr->nFocusedItem = nItem;
2107 ListView_EnsureVisible(hwnd, nItem, FALSE);
2115 * Sets a single selection.
2118 * [I] HWND : window handle
2119 * [I] INT : item index
2124 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
2126 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2129 ZeroMemory(&lvItem, sizeof(lvItem));
2130 lvItem.stateMask = LVIS_FOCUSED;
2131 ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
2133 LISTVIEW_RemoveAllSelections(hwnd);
2135 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
2136 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2137 ListView_SetItemState(hwnd, nItem, &lvItem);
2139 infoPtr->nFocusedItem = nItem;
2140 infoPtr->nSelectionMark = nItem;
2145 * Set selection(s) with keyboard.
2148 * [I] HWND : window handle
2149 * [I] INT : item index
2152 * SUCCESS : TRUE (needs to be repainted)
2153 * FAILURE : FALSE (nothing has changed)
2155 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
2157 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2158 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2159 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2160 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2161 BOOL bResult = FALSE;
2163 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2165 if (lStyle & LVS_SINGLESEL)
2168 LISTVIEW_SetSelection(hwnd, nItem);
2169 ListView_EnsureVisible(hwnd, nItem, FALSE);
2176 LISTVIEW_SetGroupSelection(hwnd, nItem);
2180 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
2185 LISTVIEW_SetSelection(hwnd, nItem);
2186 ListView_EnsureVisible(hwnd, nItem, FALSE);
2196 * Called when the mouse is being actively tracked and has hovered for a specified
2200 * [I] HWND : window handle
2201 * [I] wParam : key indicator
2202 * [I] lParam : mouse position
2205 * 0 if the message was processed, non-zero if there was an error
2208 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2209 * over the item for a certain period of time.
2212 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
2214 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2217 pt.x = (INT)LOWORD(lParam);
2218 pt.y = (INT)HIWORD(lParam);
2220 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2221 /* select the item under the cursor */
2222 LISTVIEW_MouseSelection(hwnd, pt);
2230 * Called whenever WM_MOUSEMOVE is received.
2233 * [I] HWND : window handle
2234 * [I] wParam : key indicators
2235 * [I] lParam : cursor position
2238 * 0 if the message is processed, non-zero if there was an error
2240 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
2242 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2243 TRACKMOUSEEVENT trackinfo;
2245 /* see if we are supposed to be tracking mouse hovering */
2246 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2247 /* fill in the trackinfo struct */
2248 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2249 trackinfo.dwFlags = TME_QUERY;
2250 trackinfo.hwndTrack = hwnd;
2251 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2253 /* see if we are already tracking this hwnd */
2254 _TrackMouseEvent(&trackinfo);
2256 if(!(trackinfo.dwFlags & TME_HOVER)) {
2257 trackinfo.dwFlags = TME_HOVER;
2259 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2260 _TrackMouseEvent(&trackinfo);
2269 * Selects an item based on coordinates.
2272 * [I] HWND : window handle
2273 * [I] POINT : mouse click ccordinates
2276 * SUCCESS : item index
2279 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
2281 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2283 INT i,topindex,bottomindex;
2284 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2285 UINT uView = lStyle & LVS_TYPEMASK;
2287 topindex = ListView_GetTopIndex(hwnd);
2288 if (uView == LVS_REPORT)
2290 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
2291 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
2295 bottomindex = GETITEMCOUNT(infoPtr);
2298 for (i = topindex; i < bottomindex; i++)
2300 rcItem.left = LVIR_SELECTBOUNDS;
2301 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
2303 if (PtInRect(&rcItem, pt) != FALSE)
2318 * [IO] HDPA : dynamic pointer array handle
2319 * [I] INT : column index (subitem index)
2325 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2327 BOOL bResult = TRUE;
2331 for (i = 0; i < hdpaItems->nItemCount; i++)
2333 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2334 if (hdpaSubItems != NULL)
2336 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2348 * Removes a subitem at a given position.
2351 * [IO] HDPA : dynamic pointer array handle
2352 * [I] INT : subitem index
2358 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2360 LISTVIEW_SUBITEM *lpSubItem;
2363 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2365 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2366 if (lpSubItem != NULL)
2368 if (lpSubItem->iSubItem == nSubItem)
2371 if (is_textW(lpSubItem->pszText))
2372 COMCTL32_Free(lpSubItem->pszText);
2375 COMCTL32_Free(lpSubItem);
2377 /* free dpa memory */
2378 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2381 else if (lpSubItem->iSubItem > nSubItem)
2391 * Compares the item information.
2394 * [I] LISTVIEW_ITEM *: destination item
2395 * [I] LPLVITEM : source item
2396 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2399 * SUCCCESS : TRUE (EQUAL)
2400 * FAILURE : FALSE (NOT EQUAL)
2402 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2406 if ((lpItem != NULL) && (lpLVItem != NULL))
2408 if (lpLVItem->mask & LVIF_STATE)
2410 if ((lpItem->state & lpLVItem->stateMask) !=
2411 (lpLVItem->state & lpLVItem->stateMask))
2412 uChanged |= LVIF_STATE;
2415 if (lpLVItem->mask & LVIF_IMAGE)
2417 if (lpItem->iImage != lpLVItem->iImage)
2418 uChanged |= LVIF_IMAGE;
2421 if (lpLVItem->mask & LVIF_PARAM)
2423 if (lpItem->lParam != lpLVItem->lParam)
2424 uChanged |= LVIF_PARAM;
2427 if (lpLVItem->mask & LVIF_INDENT)
2429 if (lpItem->iIndent != lpLVItem->iIndent)
2430 uChanged |= LVIF_INDENT;
2433 if (lpLVItem->mask & LVIF_TEXT)
2435 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2437 if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2438 uChanged |= LVIF_TEXT;
2442 if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2444 uChanged |= LVIF_TEXT;
2448 if (lpLVItem->pszText)
2450 if (lpItem->pszText)
2452 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2453 if (pszText && strcmpW(pszText, lpItem->pszText))
2454 uChanged |= LVIF_TEXT;
2455 textfreeT(pszText, isW);
2459 uChanged |= LVIF_TEXT;
2464 if (lpItem->pszText)
2465 uChanged |= LVIF_TEXT;
2476 * Initializes item attributes.
2479 * [I] HWND : window handle
2480 * [O] LISTVIEW_ITEM *: destination item
2481 * [I] LPLVITEM : source item
2482 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2488 static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem,
2489 LPLVITEMW lpLVItem, BOOL isW)
2491 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2492 BOOL bResult = FALSE;
2494 if ((lpItem != NULL) && (lpLVItem != NULL))
2498 if (lpLVItem->mask & LVIF_STATE)
2500 lpItem->state &= ~lpLVItem->stateMask;
2501 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2504 if (lpLVItem->mask & LVIF_IMAGE)
2505 lpItem->iImage = lpLVItem->iImage;
2507 if (lpLVItem->mask & LVIF_PARAM)
2508 lpItem->lParam = lpLVItem->lParam;
2510 if (lpLVItem->mask & LVIF_INDENT)
2511 lpItem->iIndent = lpLVItem->iIndent;
2513 if (lpLVItem->mask & LVIF_TEXT)
2515 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2517 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2520 if (is_textW(lpItem->pszText))
2521 COMCTL32_Free(lpItem->pszText);
2523 lpItem->pszText = LPSTR_TEXTCALLBACKW;
2526 bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
2535 * Initializes subitem attributes.
2537 * NOTE: The documentation specifies that the operation fails if the user
2538 * tries to set the indent of a subitem.
2541 * [I] HWND : window handle
2542 * [O] LISTVIEW_SUBITEM *: destination subitem
2543 * [I] LPLVITEM : source subitem
2544 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2550 static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2551 LPLVITEMW lpLVItem, BOOL isW)
2553 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2554 BOOL bResult = FALSE;
2556 TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
2557 hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW);
2559 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2561 if (!(lpLVItem->mask & LVIF_INDENT))
2565 lpSubItem->iSubItem = lpLVItem->iSubItem;
2567 if (lpLVItem->mask & LVIF_IMAGE)
2568 lpSubItem->iImage = lpLVItem->iImage;
2570 if (lpLVItem->mask & LVIF_TEXT)
2572 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2574 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2577 if (is_textW(lpSubItem->pszText))
2578 COMCTL32_Free(lpSubItem->pszText);
2580 lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2583 bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
2593 * Adds a subitem at a given position (column index).
2596 * [I] HWND : window handle
2597 * [I] LPLVITEM : new subitem atttributes
2598 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2604 static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2606 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2607 LISTVIEW_SUBITEM *lpSubItem = NULL;
2608 BOOL bResult = FALSE;
2610 INT nPosition, nItem;
2611 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2613 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2615 if (lStyle & LVS_OWNERDATA)
2618 if (lpLVItem != NULL)
2620 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2621 if (hdpaSubItems != NULL)
2623 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2624 if (lpSubItem != NULL)
2626 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2627 if (LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW))
2629 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2630 lpSubItem->iSubItem);
2631 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2632 if (nItem != -1) bResult = TRUE;
2638 /* cleanup if unsuccessful */
2639 if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2646 * Finds the dpa insert position (array index).
2649 * [I] HWND : window handle
2650 * [I] INT : subitem index
2656 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2658 LISTVIEW_SUBITEM *lpSubItem;
2661 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2663 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2664 if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2668 return hdpaSubItems->nItemCount;
2673 * Retrieves a listview subitem at a given position (column index).
2676 * [I] HWND : window handle
2677 * [I] INT : subitem index
2683 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2685 LISTVIEW_SUBITEM *lpSubItem;
2688 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2690 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2691 if (lpSubItem != NULL)
2693 if (lpSubItem->iSubItem == nSubItem)
2695 else if (lpSubItem->iSubItem > nSubItem)
2705 * Sets item attributes.
2708 * [I] HWND : window handle
2709 * [I] LPLVITEM : new item atttributes
2710 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2716 static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2718 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2719 BOOL bResult = FALSE;
2721 LISTVIEW_ITEM *lpItem;
2723 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2725 UINT uView = lStyle & LVS_TYPEMASK;
2729 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2731 if (lStyle & LVS_OWNERDATA)
2733 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2737 ZeroMemory(&itm, sizeof(itm));
2738 itm.mask = LVIF_STATE | LVIF_PARAM;
2739 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2740 itm.iItem = lpLVItem->iItem;
2742 ListView_GetItemW(hwnd, &itm);
2745 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2746 nmlv.uNewState = lpLVItem->state;
2747 nmlv.uOldState = itm.state;
2748 nmlv.uChanged = LVIF_STATE;
2749 nmlv.lParam = itm.lParam;
2750 nmlv.iItem = lpLVItem->iItem;
2752 if ((itm.state & lpLVItem->stateMask) !=
2753 (lpLVItem->state & lpLVItem->stateMask))
2756 * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent
2757 * by LVS_OWERNDATA list controls
2759 if (lpLVItem->stateMask & LVIS_FOCUSED)
2761 if (lpLVItem->state & LVIS_FOCUSED)
2762 infoPtr->nFocusedItem = lpLVItem->iItem;
2763 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2764 infoPtr->nFocusedItem = -1;
2766 if (lpLVItem->stateMask & LVIS_SELECTED)
2768 if (lpLVItem->state & LVIS_SELECTED)
2770 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd);
2771 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2774 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2778 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2780 rcItem.left = LVIR_BOUNDS;
2781 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2782 if (!infoPtr->bIsDrawing)
2783 InvalidateRect(hwnd, &rcItem, TRUE);
2790 if (lpLVItem != NULL)
2792 if (lpLVItem->iSubItem == 0)
2794 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2795 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2797 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2800 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2801 nmlv.lParam = lpItem->lParam;
2802 uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2805 if (uChanged & LVIF_STATE)
2807 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2808 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2810 if (nmlv.uNewState & LVIS_SELECTED)
2813 * This is redundant if called through SetSelection
2815 * however is required if the used directly calls SetItem
2816 * to set the selection.
2818 if (lStyle & LVS_SINGLESEL)
2819 LISTVIEW_RemoveAllSelections(hwnd);
2821 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2824 else if (lpLVItem->stateMask & LVIS_SELECTED)
2826 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2829 if (nmlv.uNewState & LVIS_FOCUSED)
2832 * This is a fun hoop to jump to try to catch if
2833 * the user is calling us directly to call focus or if
2834 * this function is being called as a result of a
2835 * SetItemFocus call.
2837 if (infoPtr->nFocusedItem >= 0)
2838 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2842 nmlv.uChanged = uChanged;
2843 nmlv.iItem = lpLVItem->iItem;
2844 nmlv.lParam = lpItem->lParam;
2845 /* send LVN_ITEMCHANGING notification */
2846 listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv);
2848 /* copy information */
2849 bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW);
2851 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2852 based on the width of the items text */
2853 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2855 item_width = LISTVIEW_GetStringWidthT(hwnd, lpItem->pszText, TRUE);
2857 if(item_width > infoPtr->nItemWidth)
2858 infoPtr->nItemWidth = item_width;
2861 /* send LVN_ITEMCHANGED notification */
2862 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2871 rcItem.left = LVIR_BOUNDS;
2872 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2873 if (!infoPtr->bIsDrawing)
2874 InvalidateRect(hwnd, &rcItem, TRUE);
2886 * Sets subitem attributes.
2889 * [I] HWND : window handle
2890 * [I] LPLVITEM : new subitem atttributes
2891 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2897 static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2899 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2900 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2901 BOOL bResult = FALSE;
2903 LISTVIEW_SUBITEM *lpSubItem;
2906 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2908 if (lStyle & LVS_OWNERDATA)
2911 if (lpLVItem != NULL)
2913 if (lpLVItem->iSubItem > 0)
2915 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2916 if (hdpaSubItems != NULL)
2918 /* set subitem only if column is present */
2919 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2921 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2922 if (lpSubItem != NULL)
2923 bResult = LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW);
2925 bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW);
2927 rcItem.left = LVIR_BOUNDS;
2928 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2929 InvalidateRect(hwnd, &rcItem, FALSE);
2940 * Sets item attributes.
2943 * [I] HWND : window handle
2944 * [I] LPLVITEM : new item atttributes
2945 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2951 static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2953 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2955 if (!lpLVItem || lpLVItem->iItem < 0 ||
2956 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2958 if (lpLVItem->iSubItem == 0)
2959 return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW);
2961 return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW);
2966 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2969 * [I] HWND : window handle
2974 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2976 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2977 UINT uView = lStyle & LVS_TYPEMASK;
2979 SCROLLINFO scrollInfo;
2981 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2982 scrollInfo.cbSize = sizeof(SCROLLINFO);
2983 scrollInfo.fMask = SIF_POS;
2985 if (uView == LVS_LIST)
2987 if ((lStyle & WS_HSCROLL) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo))
2988 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2990 else if (uView == LVS_REPORT)
2992 if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo))
2993 nItem = scrollInfo.nPos;
3004 * [I] HWND : window handle
3005 * [I] HDC : device context handle
3006 * [I] INT : item index
3007 * [I] INT : subitem index
3008 * [I] RECT * : clipping rectangle
3013 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
3014 RECT rcItem, BOOL Selected)
3016 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3017 WCHAR szDispText[DISP_TEXT_SIZE];
3020 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
3023 INT nLabelWidth = 0;
3025 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
3028 /* get information needed for drawing the item */
3029 ZeroMemory(&lvItem, sizeof(lvItem));
3030 lvItem.mask = LVIF_TEXT;
3031 lvItem.iItem = nItem;
3032 lvItem.iSubItem = nSubItem;
3033 lvItem.cchTextMax = DISP_TEXT_SIZE;
3034 lvItem.pszText = szDispText;
3035 *lvItem.pszText = '\0';
3036 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3037 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3039 ZeroMemory(&lvColumn, sizeof(lvColumn));
3040 lvColumn.mask = LVCF_FMT;
3041 LISTVIEW_GetColumnT(hwnd, nSubItem, &lvColumn, TRUE);
3042 textLeft = rcItem.left;
3043 if (lvColumn.fmt != LVCFMT_LEFT)
3045 if ((nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE)))
3047 if (lvColumn.fmt == LVCFMT_RIGHT)
3048 textLeft = rcItem.right - nLabelWidth;
3050 textLeft = rcItem.left + (rcItem.right-rcItem.left-nLabelWidth)/2;
3055 /* redraw the background of the item */
3057 if(infoPtr->nColumnCount == (nSubItem + 1))
3058 rcTemp.right = infoPtr->rcList.right;
3060 rcTemp.right += WIDTH_PADDING;
3062 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3064 /* set item colors */
3065 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
3067 if (infoPtr->bFocus)
3069 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3070 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3074 SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3075 SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3080 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3082 SetBkMode(hdc, TRANSPARENT);
3083 textoutOptions &= ~ETO_OPAQUE;
3087 SetBkMode(hdc, OPAQUE);
3088 SetBkColor(hdc, infoPtr->clrTextBk);
3091 SetTextColor(hdc, infoPtr->clrText);
3094 ExtTextOutW(hdc, textLeft, rcItem.top, textoutOptions,
3095 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3099 /* fill in the gap */
3101 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
3103 CopyRect(&rec,&rcItem);
3104 rec.left = rec.right;
3105 rec.right = rec.left+REPORT_MARGINX;
3106 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3107 &rec, NULL, 0, NULL);
3109 CopyRect(&rec,&rcItem);
3110 rec.right = rec.left;
3111 rec.left = rec.left - REPORT_MARGINX;
3112 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3113 &rec, NULL, 0, NULL);
3123 * [I] HWND : window handle
3124 * [I] HDC : device context handle
3125 * [I] INT : item index
3126 * [I] RECT * : clipping rectangle
3131 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
3133 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3134 WCHAR szDispText[DISP_TEXT_SIZE];
3139 DWORD dwTextColor,dwTextX;
3140 BOOL bImage = FALSE;
3142 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
3145 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
3148 /* get information needed for drawing the item */
3149 ZeroMemory(&lvItem, sizeof(lvItem));
3150 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3151 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
3152 lvItem.iItem = nItem;
3153 lvItem.iSubItem = 0;
3154 lvItem.cchTextMax = DISP_TEXT_SIZE;
3155 lvItem.pszText = szDispText;
3156 *lvItem.pszText = '\0';
3157 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3158 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3160 /* redraw the background of the item */
3162 if(infoPtr->nColumnCount == (nItem + 1))
3163 rcTemp.right = infoPtr->rcList.right;
3165 rcTemp.right+=WIDTH_PADDING;
3167 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3170 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3172 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3175 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3179 if (infoPtr->himlState != NULL)
3181 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3182 if (uStateImage > 0)
3184 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3185 rcItem.top, ILD_NORMAL);
3188 rcItem.left += infoPtr->iconSize.cx;
3190 SuggestedFocus->left += infoPtr->iconSize.cx;
3195 if (infoPtr->himlSmall != NULL)
3197 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
3200 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3201 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3202 rcItem.top, ILD_SELECTED);
3204 else if (lvItem.iImage>=0)
3206 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3207 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3208 rcItem.top, ILD_NORMAL);
3211 rcItem.left += infoPtr->iconSize.cx;
3214 SuggestedFocus->left += infoPtr->iconSize.cx;
3218 /* Don't bother painting item being edited */
3219 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
3222 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
3224 /* set item colors */
3225 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3226 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3227 /* set raster mode */
3228 nMixMode = SetROP2(hdc, R2_XORPEN);
3230 else if ((GetWindowLongW(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3231 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
3233 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3234 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3235 /* set raster mode */
3236 nMixMode = SetROP2(hdc, R2_COPYPEN);
3240 /* set item colors */
3241 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3243 dwBkColor = GetBkColor(hdc);
3244 iBkMode = SetBkMode(hdc, TRANSPARENT);
3245 textoutOptions &= ~ETO_OPAQUE;
3249 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3250 iBkMode = SetBkMode(hdc, OPAQUE);
3253 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3254 /* set raster mode */
3255 nMixMode = SetROP2(hdc, R2_COPYPEN);
3258 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
3259 if (rcItem.left + nLabelWidth < rcItem.right)
3262 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3264 rcItem.right += IMAGE_PADDING;
3268 dwTextX = rcItem.left + 1;
3270 dwTextX += IMAGE_PADDING;
3273 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3274 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3276 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3278 /* fill in the gap */
3280 CopyRect(&rec,&rcItem);
3281 rec.left = rec.right;
3282 rec.right = rec.left+REPORT_MARGINX;
3283 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3284 &rec, NULL, 0, NULL);
3288 CopyRect(SuggestedFocus,&rcItem);
3292 SetROP2(hdc, R2_COPYPEN);
3293 SetBkColor(hdc, dwBkColor);
3294 SetTextColor(hdc, dwTextColor);
3296 SetBkMode(hdc, iBkMode);
3302 * Draws an item when in large icon display mode.
3305 * [I] HWND : window handle
3306 * [I] HDC : device context handle
3307 * [I] INT : item index
3308 * [I] RECT : clipping rectangle
3309 * [O] RECT * : The text rectangle about which to draw the focus
3314 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3315 RECT *SuggestedFocus)
3317 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3318 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3320 UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
3322 /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
3325 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3326 hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3328 /* get information needed for drawing the item */
3329 ZeroMemory(&lvItem, sizeof(lvItem));
3330 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3331 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3332 lvItem.iItem = nItem;
3333 lvItem.iSubItem = 0;
3334 lvItem.cchTextMax = DISP_TEXT_SIZE;
3335 lvItem.pszText = szDispText;
3336 *lvItem.pszText = '\0';
3337 LISTVIEW_GetItemW(hwnd, &lvItem, FALSE);
3338 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3340 /* redraw the background of the item */
3342 if(infoPtr->nColumnCount == (nItem + 1))
3343 rcTemp.right = infoPtr->rcList.right;
3345 rcTemp.right+=WIDTH_PADDING;
3346 /* The comment doesn't say WIDTH_PADDING applies to large icons */
3348 TRACE("background rect (%d,%d)-(%d,%d)\n",
3349 rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
3351 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3354 /* Figure out text colours etc. depending on state
3355 * At least the following states exist; there may be more.
3356 * Many items may be selected
3357 * At most one item may have the focus
3358 * The application may not actually be active currently
3359 * 1. The item is not selected in any way
3360 * 2. The cursor is flying over the icon or text and the text is being
3361 * expanded because it is not fully displayed currently.
3362 * 3. The item is selected and is focussed, i.e. the user has not clicked
3363 * in the blank area of the window, and the window (or application?)
3364 * still has the focus.
3365 * 4. As 3 except that a different window has the focus
3366 * 5. The item is the selected item of all the items, but the user has
3367 * clicked somewhere else on the window.
3368 * Only a few of these are handled currently. In particular 2 is not yet
3369 * handled since we do not support the functionality currently (or at least
3370 * we didn't when I wrote this)
3373 if (lvItem.state & LVIS_SELECTED)
3375 /* set item colors */
3376 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3377 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3378 SetBkMode (hdc, OPAQUE);
3379 /* set raster mode */
3380 SetROP2(hdc, R2_XORPEN);
3381 /* When exactly is it in XOR? while being dragged? */
3385 /* set item colors */
3386 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3388 SetBkMode(hdc, TRANSPARENT);
3392 SetBkMode(hdc, OPAQUE);
3393 SetBkColor(hdc, infoPtr->clrTextBk);
3396 SetTextColor(hdc, infoPtr->clrText);
3397 /* set raster mode */
3398 SetROP2(hdc, R2_COPYPEN);
3401 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3402 * wrapping and long words split.
3403 * In cases 1 and 4 only a portion of the text is displayed with word
3404 * wrapping and both word and end ellipsis. (I don't yet know about path
3407 uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
3410 DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3413 if (infoPtr->himlNormal != NULL)
3415 if (lvItem.iImage >= 0)
3417 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3419 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3423 /* Draw the text below the icon */
3425 /* Don't bother painting item being edited */
3426 if ((infoPtr->hwndEdit && (lvItem.state & LVIS_FOCUSED)) ||
3427 !lstrlenW(lvItem.pszText))
3429 SetRectEmpty(SuggestedFocus);
3433 /* Since rcItem.left is left point of icon, compute left point of item box */
3434 rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3435 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3436 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3437 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3438 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3439 infoPtr->iconSize.cx, infoPtr->nItemWidth);
3440 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3441 infoPtr->rcList.left, infoPtr->rcList.top,
3442 infoPtr->rcList.right, infoPtr->rcList.bottom,
3443 infoPtr->rcView.left, infoPtr->rcView.top,
3444 infoPtr->rcView.right, infoPtr->rcView.bottom);
3446 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3447 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3452 /* I am sure of most of the uFormat values. However I am not sure about
3453 * whether we need or do not need the following:
3454 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3455 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3456 * We certainly do not need
3457 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3458 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3461 /* If the text is being drawn without clipping (i.e. the full text) then we
3462 * need to jump through a few hoops to ensure that it all gets displayed and
3463 * that the background is complete
3465 if (uFormat & DT_NOCLIP)
3468 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3469 int dx, dy, old_wid, new_wid;
3470 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3471 /* Microsoft, in their great wisdom, have decided that the rectangle
3472 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3473 * not the location. So we have to do the centring ourselves (and take
3474 * responsibility for agreeing off-by-one consistency with them).
3476 old_wid = rcItem.right-rcItem.left;
3477 new_wid = rcBack.right - rcBack.left;
3478 dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
3479 dy = rcBack.top - rcItem.top;
3480 OffsetRect (&rcItem, dx, dy);
3481 FillRect(hdc, &rcItem, hBrush);
3482 DeleteObject(hBrush);
3484 /* else ? What if we are losing the focus? will we not get a complete
3487 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3489 CopyRect(SuggestedFocus, &rcItem);
3494 * Draws listview items when in report display mode.
3497 * [I] HWND : window handle
3498 * [I] HDC : device context handle
3503 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3505 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3506 SCROLLINFO scrollInfo;
3507 INT nDrawPosY = infoPtr->rcList.top;
3509 RECT rcItem, rcTemp;
3514 DWORD cditemmode = CDRF_DODEFAULT;
3515 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3518 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3519 scrollInfo.cbSize = sizeof(SCROLLINFO);
3520 scrollInfo.fMask = SIF_POS;
3522 nItem = ListView_GetTopIndex(hwnd);
3524 /* add 1 for displaying a partial item at the bottom */
3525 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3526 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3528 /* send cache hint notification */
3529 if (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3533 nmlv.hdr.hwndFrom = hwnd;
3534 nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID);
3535 nmlv.hdr.code = LVN_ODCACHEHINT;
3539 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3543 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3544 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3545 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3547 /* clear the background of any part of the control that doesn't contain items */
3548 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3549 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3551 /* nothing to draw */
3552 if(GETITEMCOUNT(infoPtr) == 0)
3555 /* Get scroll bar info once before loop */
3556 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3557 scrollOffset = scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
3559 for (; nItem < nLast; nItem++)
3561 RECT SuggestedFocusRect;
3564 if (lStyle & LVS_OWNERDRAWFIXED)
3566 UINT uID = GetWindowLongW( hwnd, GWL_ID);
3571 TRACE("Owner Drawn\n");
3572 dis.CtlType = ODT_LISTVIEW;
3575 dis.itemAction = ODA_DRAWENTIRE;
3578 if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3579 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3581 dis.hwndItem = hwnd;
3584 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3586 dis.rcItem.left = -scrollOffset;
3587 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3588 dis.rcItem.top = nDrawPosY;
3589 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3591 ZeroMemory(&item,sizeof(item));
3593 item.mask = LVIF_PARAM;
3594 ListView_GetItemW(hwnd, &item);
3596 dis.itemData = item.lParam;
3598 if (SendMessageW(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3600 nDrawPosY += infoPtr->nItemHeight;
3609 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3610 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3612 ir.left += REPORT_MARGINX;
3613 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3615 ir.bottom = ir.top + infoPtr->nItemHeight;
3617 CopyRect(&SuggestedFocusRect,&ir);
3620 for (j = 0; j < nColumnCount; j++)
3622 if (cdmode & CDRF_NOTIFYITEMDRAW)
3623 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3625 if (cditemmode & CDRF_SKIPDEFAULT)
3628 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3630 rcItem.left += REPORT_MARGINX;
3631 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3632 rcItem.top = nDrawPosY;
3633 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3635 /* Offset the Scroll Bar Pos */
3636 rcItem.left -= scrollOffset;
3637 rcItem.right -= scrollOffset;
3641 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3642 &SuggestedFocusRect);
3646 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, FullSelected);
3649 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3650 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3651 CDDS_ITEMPOSTPAINT);
3656 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3659 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3660 rop = SetROP2(hdc, R2_XORPEN);
3662 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3663 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3666 SetROP2(hdc, R2_COPYPEN);
3668 nDrawPosY += infoPtr->nItemHeight;
3674 * Retrieves the number of items that can fit vertically in the client area.
3677 * [I] HWND : window handle
3680 * Number of items per row.
3682 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3684 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3685 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3686 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3687 INT nCountPerRow = 1;
3691 if (uView != LVS_REPORT)
3693 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3694 if (nCountPerRow == 0) nCountPerRow = 1;
3698 return nCountPerRow;
3703 * Retrieves the number of items that can fit horizontally in the client
3707 * [I] HWND : window handle
3710 * Number of items per column.
3712 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3714 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3715 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3716 INT nCountPerColumn = 1;
3718 if (nListHeight > 0)
3720 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3721 if (nCountPerColumn == 0) nCountPerColumn = 1;
3724 return nCountPerColumn;
3729 * Retrieves the number of columns needed to display all the items when in
3730 * list display mode.
3733 * [I] HWND : window handle
3736 * Number of columns.
3738 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3740 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3741 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3742 INT nColumnCount = 0;
3744 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3746 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3747 if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3750 return nColumnCount;
3756 * Draws listview items when in list display mode.
3759 * [I] HWND : window handle
3760 * [I] HDC : device context handle
3765 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3767 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3768 RECT rcItem, FocusRect, rcTemp;
3772 INT nCountPerColumn;
3773 INT nItemWidth = infoPtr->nItemWidth;
3774 INT nItemHeight = infoPtr->nItemHeight;
3775 DWORD cditemmode = CDRF_DODEFAULT;
3777 /* get number of fully visible columns */
3778 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3779 infoPtr->nColumnCount = nColumnCount;
3780 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3781 nItem = ListView_GetTopIndex(hwnd);
3783 /* paint the background of the control that doesn't contain any items */
3784 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3785 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3787 /* nothing to draw, return here */
3788 if(GETITEMCOUNT(infoPtr) == 0)
3791 for (i = 0; i < nColumnCount; i++)
3793 for (j = 0; j < nCountPerColumn; j++, nItem++)
3795 if (nItem >= GETITEMCOUNT(infoPtr))
3798 if (cdmode & CDRF_NOTIFYITEMDRAW)
3799 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3801 if (cditemmode & CDRF_SKIPDEFAULT)
3804 rcItem.top = j * nItemHeight;
3805 rcItem.left = i * nItemWidth;
3806 rcItem.bottom = rcItem.top + nItemHeight;
3807 rcItem.right = rcItem.left + nItemWidth;
3808 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3812 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3813 Rectangle(hdc, FocusRect.left, FocusRect.top,
3814 FocusRect.right,FocusRect.bottom);
3816 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3817 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3818 CDDS_ITEMPOSTPAINT);
3826 * Draws listview items when in icon or small icon display mode.
3829 * [I] HWND : window handle
3830 * [I] HDC : device context handle
3835 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3837 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3840 RECT rcItem, SuggestedFocus, rcTemp;
3842 DWORD cditemmode = CDRF_DODEFAULT;
3844 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3845 /* DrawItem from erasing the incorrect background area */
3847 /* paint the background of the control that doesn't contain any items */
3848 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3849 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3851 /* nothing to draw, return here */
3852 if(GETITEMCOUNT(infoPtr) == 0)
3855 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3856 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3858 if (cdmode & CDRF_NOTIFYITEMDRAW)
3859 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3861 if (cditemmode & CDRF_SKIPDEFAULT)
3864 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3865 ptPosition.x += ptOrigin.x;
3866 ptPosition.y += ptOrigin.y;
3868 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3870 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3872 if (ptPosition.y < infoPtr->rcList.bottom)
3874 if (ptPosition.x < infoPtr->rcList.right)
3876 rcItem.top = ptPosition.y;
3877 rcItem.left = ptPosition.x;
3878 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3879 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3881 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3883 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3887 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3888 infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
3889 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3890 SuggestedFocus.right,SuggestedFocus.bottom);
3895 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3896 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3897 CDDS_ITEMPOSTPAINT);
3903 * Draws listview items.
3906 * [I] HWND : window handle
3907 * [I] HDC : device context handle
3912 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3914 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3915 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3921 infoPtr->bIsDrawing = TRUE;
3922 LISTVIEW_DumpListview (infoPtr, __LINE__);
3924 GetClientRect(hwnd, &rect);
3925 cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3927 if (cdmode == CDRF_SKIPDEFAULT) return;
3930 hOldFont = SelectObject(hdc, infoPtr->hFont);
3932 /* select the dotted pen (for drawing the focus box) */
3933 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3934 hOldPen = SelectObject(hdc, hPen);
3936 /* select transparent brush (for drawing the focus box) */
3937 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3939 if (uView == LVS_LIST)
3940 LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3941 else if (uView == LVS_REPORT)
3942 LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3943 else if (uView == LVS_SMALLICON)
3944 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3945 else if (uView == LVS_ICON)
3946 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3948 /* unselect objects */
3949 SelectObject(hdc, hOldFont);
3950 SelectObject(hdc, hOldPen);
3955 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3956 LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
3958 infoPtr->bIsDrawing = FALSE;
3964 * Calculates the approximate width and height of a given number of items.
3967 * [I] HWND : window handle
3968 * [I] INT : number of items
3973 * Returns a DWORD. The width in the low word and the height in high word.
3975 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
3976 WORD wWidth, WORD wHeight)
3978 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3979 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3980 INT nItemCountPerColumn = 1;
3981 INT nColumnCount = 0;
3982 DWORD dwViewRect = 0;
3984 if (nItemCount == -1)
3985 nItemCount = GETITEMCOUNT(infoPtr);
3987 if (uView == LVS_LIST)
3989 if (wHeight == 0xFFFF)
3991 /* use current height */
3992 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3995 if (wHeight < infoPtr->nItemHeight)
3996 wHeight = infoPtr->nItemHeight;
4000 if (infoPtr->nItemHeight > 0)
4002 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4003 if (nItemCountPerColumn == 0)
4004 nItemCountPerColumn = 1;
4006 if (nItemCount % nItemCountPerColumn != 0)
4007 nColumnCount = nItemCount / nItemCountPerColumn;
4009 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4013 /* Microsoft padding magic */
4014 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4015 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4017 dwViewRect = MAKELONG(wWidth, wHeight);
4019 else if (uView == LVS_REPORT)
4020 FIXME("uView == LVS_REPORT: not implemented\n");
4021 else if (uView == LVS_SMALLICON)
4022 FIXME("uView == LVS_SMALLICON: not implemented\n");
4023 else if (uView == LVS_ICON)
4024 FIXME("uView == LVS_ICON: not implemented\n");
4031 * Arranges listview items in icon display mode.
4034 * [I] HWND : window handle
4035 * [I] INT : alignment code
4041 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
4043 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4044 BOOL bResult = FALSE;
4046 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4051 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
4054 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
4057 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4059 case LVA_SNAPTOGRID:
4060 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4068 /* << LISTVIEW_CreateDragImage >> */
4073 * Removes all listview items and subitems.
4076 * [I] HWND : window handle
4082 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
4084 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4085 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4086 UINT uView = lStyle & LVS_TYPEMASK;
4087 LISTVIEW_ITEM *lpItem;
4088 LISTVIEW_SUBITEM *lpSubItem;
4091 BOOL bResult = FALSE;
4094 TRACE("(hwnd=%x,)\n", hwnd);
4096 LISTVIEW_RemoveAllSelections(hwnd);
4097 infoPtr->nSelectionMark=-1;
4098 infoPtr->nFocusedItem=-1;
4099 /* But we are supposed to leave nHotItem as is! */
4101 if (lStyle & LVS_OWNERDATA)
4103 infoPtr->hdpaItems->nItemCount = 0;
4104 InvalidateRect(hwnd, NULL, TRUE);
4108 if (GETITEMCOUNT(infoPtr) > 0)
4112 /* send LVN_DELETEALLITEMS notification */
4113 /* verify if subsequent LVN_DELETEITEM notifications should be
4115 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4117 bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &nmlv);
4119 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4121 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4122 if (hdpaSubItems != NULL)
4124 for (j = 1; j < hdpaSubItems->nItemCount; j++)
4126 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
4127 if (lpSubItem != NULL)
4129 /* free subitem string */
4130 if (is_textW(lpSubItem->pszText))
4131 COMCTL32_Free(lpSubItem->pszText);
4134 COMCTL32_Free(lpSubItem);
4138 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4143 /* send LVN_DELETEITEM notification */
4145 nmlv.lParam = lpItem->lParam;
4146 listview_notify(hwnd, LVN_DELETEITEM, &nmlv);
4149 /* free item string */
4150 if (is_textW(lpItem->pszText))
4151 COMCTL32_Free(lpItem->pszText);
4154 COMCTL32_Free(lpItem);
4157 DPA_Destroy(hdpaSubItems);
4161 /* reinitialize listview memory */
4162 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
4164 /* align items (set position of each item) */
4165 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4167 if (lStyle & LVS_ALIGNLEFT)
4169 LISTVIEW_AlignLeft(hwnd);
4173 LISTVIEW_AlignTop(hwnd);
4177 LISTVIEW_UpdateScroll(hwnd);
4179 /* invalidate client area (optimization needed) */
4180 InvalidateRect(hwnd, NULL, TRUE);
4188 * Removes a column from the listview control.
4191 * [I] HWND : window handle
4192 * [I] INT : column index
4198 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
4200 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4201 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4202 UINT uOwnerData = GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA;
4203 BOOL bResult = FALSE;
4205 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
4208 bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
4210 /* Need to reset the item width when deleting a column */
4211 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
4213 /* reset scroll parameters */
4214 if (uView == LVS_REPORT)
4216 /* update scrollbar(s) */
4217 LISTVIEW_UpdateScroll(hwnd);
4219 /* refresh client area */
4220 InvalidateRect(hwnd, NULL, FALSE);
4229 * Removes an item from the listview control.
4232 * [I] HWND : window handle
4233 * [I] INT : item index
4239 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
4241 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4242 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4243 UINT uView = lStyle & LVS_TYPEMASK;
4244 LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID);
4246 BOOL bResult = FALSE;
4248 LISTVIEW_ITEM *lpItem;
4249 LISTVIEW_SUBITEM *lpSubItem;
4253 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
4256 /* First, send LVN_DELETEITEM notification. */
4257 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4258 nmlv.hdr.hwndFrom = hwnd;
4259 nmlv.hdr.idFrom = lCtrlId;
4260 nmlv.hdr.code = LVN_DELETEITEM;
4262 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
4266 /* remove it from the selection range */
4267 ZeroMemory(&item,sizeof(item));
4268 item.stateMask = LVIS_SELECTED;
4269 LISTVIEW_SetItemState(hwnd,nItem,&item);
4271 if (lStyle & LVS_OWNERDATA)
4273 infoPtr->hdpaItems->nItemCount --;
4274 InvalidateRect(hwnd, NULL, TRUE);
4278 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4280 /* initialize memory */
4281 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4283 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4284 if (hdpaSubItems != NULL)
4286 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4288 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4289 if (lpSubItem != NULL)
4291 /* free item string */
4292 if (is_textW(lpSubItem->pszText))
4293 COMCTL32_Free(lpSubItem->pszText);
4296 COMCTL32_Free(lpSubItem);
4300 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4303 /* free item string */
4304 if (is_textW(lpItem->pszText))
4305 COMCTL32_Free(lpItem->pszText);
4308 COMCTL32_Free(lpItem);
4311 bResult = DPA_Destroy(hdpaSubItems);
4314 LISTVIEW_ShiftIndices(hwnd,nItem,-1);
4316 /* align items (set position of each item) */
4317 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4319 if (lStyle & LVS_ALIGNLEFT)
4320 LISTVIEW_AlignLeft(hwnd);
4322 LISTVIEW_AlignTop(hwnd);
4325 LISTVIEW_UpdateScroll(hwnd);
4327 /* refresh client area */
4328 InvalidateRect(hwnd, NULL, TRUE);
4337 * Return edit control handle of current edit label
4340 * [I] HWND : window handle
4346 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4348 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4349 return infoPtr->hwndEdit;
4355 * Callback implementation for editlabel control
4358 * [I] HWND : window handle
4359 * [I] LPSTR : modified text
4360 * [I] DWORD : item index
4361 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4367 static BOOL LISTVIEW_EndEditLabelT(HWND hwnd, LPWSTR pszText, DWORD nItem, BOOL isW)
4369 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4370 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4371 NMLVDISPINFOW dispInfo;
4372 LISTVIEW_ITEM *lpItem;
4374 LISTVIEW_ITEM lvItemRef;
4376 BOOL bResult = TRUE;
4378 TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd, debugstr_t(pszText, isW), nItem, isW);
4380 if (!(lStyle & LVS_OWNERDATA))
4382 if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4385 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4390 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4391 ZeroMemory(&item,sizeof(item));
4394 item.mask = LVIF_PARAM | LVIF_STATE;
4395 ListView_GetItemW(hwnd, &item);
4396 lvItemRef.state = item.state;
4397 lvItemRef.iImage = item.iImage;
4398 lvItemRef.lParam = item.lParam;
4399 lpItem = &lvItemRef;
4402 ZeroMemory(&dispInfo, sizeof(dispInfo));
4403 dispInfo.item.mask = 0;
4404 dispInfo.item.iItem = nItem;
4405 dispInfo.item.state = lpItem->state;
4406 dispInfo.item.stateMask = 0;
4407 dispInfo.item.pszText = pszText;
4408 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4409 dispInfo.item.iImage = lpItem->iImage;
4410 dispInfo.item.lParam = lpItem->lParam;
4411 infoPtr->hwndEdit = 0;
4413 /* Do we need to update the Item Text */
4414 if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, isW))
4415 if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
4416 bResult = textsetptrT(&lpItem->pszText, pszText, isW);
4423 * Callback implementation for editlabel control
4426 * [I] HWND : window handle
4427 * [I] LPSTR : modified text
4428 * [I] DWORD : item index
4434 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem)
4436 return LISTVIEW_EndEditLabelT(hwnd, pszText, nItem, TRUE);
4441 * Callback implementation for editlabel control
4444 * [I] HWND : window handle
4445 * [I] LPSTR : modified text
4446 * [I] DWORD : item index
4452 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem)
4454 return LISTVIEW_EndEditLabelT(hwnd, (LPWSTR)pszText, nItem, FALSE);
4459 * Begin in place editing of specified list view item
4462 * [I] HWND : window handle
4463 * [I] INT : item index
4464 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4470 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW)
4472 NMLVDISPINFOW dispInfo;
4474 LISTVIEW_ITEM *lpItem;
4476 HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE);
4477 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4479 WCHAR szDispText[DISP_TEXT_SIZE];
4481 LISTVIEW_ITEM lvItemRef;
4482 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4484 if (~GetWindowLongW(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4487 /* Is the EditBox still there, if so remove it */
4488 if(infoPtr->hwndEdit != 0)
4491 LISTVIEW_SetSelection(hwnd, nItem);
4492 LISTVIEW_SetItemFocus(hwnd, nItem);
4494 if (!(lStyle & LVS_OWNERDATA))
4496 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4499 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4505 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4506 ZeroMemory(&item, sizeof(item));
4509 item.mask = LVIF_PARAM | LVIF_STATE;
4510 ListView_GetItemW(hwnd, &item);
4511 lvItemRef.iImage = item.iImage;
4512 lvItemRef.state = item.state;
4513 lvItemRef.lParam = item.lParam;
4514 lpItem = &lvItemRef;
4517 /* get information needed for drawing the item */
4518 ZeroMemory(&lvItem, sizeof(lvItem));
4519 lvItem.mask = LVIF_TEXT;
4520 lvItem.iItem = nItem;
4521 lvItem.iSubItem = 0;
4522 lvItem.cchTextMax = DISP_TEXT_SIZE;
4523 lvItem.pszText = szDispText;
4524 *lvItem.pszText = '\0';
4525 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW);
4527 ZeroMemory(&dispInfo, sizeof(dispInfo));
4528 dispInfo.item.mask = 0;
4529 dispInfo.item.iItem = nItem;
4530 dispInfo.item.state = lpItem->state;
4531 dispInfo.item.stateMask = 0;
4532 dispInfo.item.pszText = lvItem.pszText;
4533 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4534 dispInfo.item.iImage = lpItem->iImage;
4535 dispInfo.item.lParam = lpItem->lParam;
4537 if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW))
4540 rect.left = LVIR_LABEL;
4541 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4544 if (!(hedit = CreateEditLabelT(szDispText , WS_VISIBLE,
4545 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, hwnd, hinst,
4546 isW ? LISTVIEW_EndEditLabelW : (EditlblCallbackW)LISTVIEW_EndEditLabelA,
4550 infoPtr->hwndEdit = hedit;
4552 SendMessageW(hedit, EM_SETSEL, 0, -1);
4560 * Ensures the specified item is visible, scrolling into view if necessary.
4563 * [I] HWND : window handle
4564 * [I] INT : item index
4565 * [I] BOOL : partially or entirely visible
4571 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4573 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4574 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4575 INT nScrollPosHeight = 0;
4576 INT nScrollPosWidth = 0;
4577 SCROLLINFO scrollInfo;
4579 BOOL bRedraw = FALSE;
4581 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4582 scrollInfo.cbSize = sizeof(SCROLLINFO);
4583 scrollInfo.fMask = SIF_POS;
4585 /* ALWAYS bPartial == FALSE, FOR NOW! */
4587 rcItem.left = LVIR_BOUNDS;
4588 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4590 if (rcItem.left < infoPtr->rcList.left)
4592 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4596 if (uView == LVS_LIST)
4598 nScrollPosWidth = infoPtr->nItemWidth;
4599 rcItem.left += infoPtr->rcList.left;
4601 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4603 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4604 rcItem.left += infoPtr->rcList.left;
4607 /* When in LVS_REPORT view, the scroll position should
4609 if (nScrollPosWidth != 0)
4611 if (rcItem.left % nScrollPosWidth == 0)
4612 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4614 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4616 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4620 else if (rcItem.right > infoPtr->rcList.right)
4622 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4626 if (uView == LVS_LIST)
4628 rcItem.right -= infoPtr->rcList.right;
4629 nScrollPosWidth = infoPtr->nItemWidth;
4631 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4633 rcItem.right -= infoPtr->rcList.right;
4634 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4637 /* When in LVS_REPORT view, the scroll position should
4639 if (nScrollPosWidth != 0)
4641 if (rcItem.right % nScrollPosWidth == 0)
4642 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4644 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4646 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4651 if (rcItem.top < infoPtr->rcList.top)
4655 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4657 if (uView == LVS_REPORT)
4659 rcItem.top -= infoPtr->rcList.top;
4660 nScrollPosHeight = infoPtr->nItemHeight;
4662 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4664 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4665 rcItem.top += infoPtr->rcList.top;
4668 if (rcItem.top % nScrollPosHeight == 0)
4669 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4671 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4673 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4676 else if (rcItem.bottom > infoPtr->rcList.bottom)
4680 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4682 if (uView == LVS_REPORT)
4684 rcItem.bottom -= infoPtr->rcList.bottom;
4685 nScrollPosHeight = infoPtr->nItemHeight;
4687 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4689 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4690 rcItem.bottom -= infoPtr->rcList.bottom;
4693 if (rcItem.bottom % nScrollPosHeight == 0)
4694 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4696 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4698 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4704 InvalidateRect(hwnd,NULL,TRUE);
4710 * Retrieves the nearest item, given a position and a direction.
4713 * [I] HWND : window handle
4714 * [I] POINT : start position
4715 * [I] UINT : direction
4718 * Item index if successdful, -1 otherwise.
4720 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4722 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4727 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4728 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4729 ((vkDirection == VK_UP) ? "VK_UP" :
4730 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4732 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4734 ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4735 LISTVIEW_GetOrigin(hwnd, &lvIntHit.ht.pt);
4736 lvIntHit.ht.pt.x += pt.x;
4737 lvIntHit.ht.pt.y += pt.y;
4739 if (vkDirection == VK_DOWN)
4740 lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4741 else if (vkDirection == VK_UP)
4742 lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4743 else if (vkDirection == VK_LEFT)
4744 lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4745 else if (vkDirection == VK_RIGHT)
4746 lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4748 if (PtInRect(&rcView, lvIntHit.ht.pt) == FALSE)
4752 nItem = LISTVIEW_SuperHitTestItem(hwnd, &lvIntHit, TRUE);
4753 return nItem == -1 ? lvIntHit.iDistItem : nItem;
4762 * Searches for an item with specific characteristics.
4765 * [I] hwnd : window handle
4766 * [I] nStart : base item index
4767 * [I] lpFindInfo : item information to look for
4770 * SUCCESS : index of item
4773 static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart,
4774 LPLVFINDINFOW lpFindInfo)
4776 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4778 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4782 INT nLast = GETITEMCOUNT(infoPtr);
4784 if ((nItem >= -1) && (lpFindInfo != NULL))
4786 ZeroMemory(&lvItem, sizeof(lvItem));
4788 if (lpFindInfo->flags & LVFI_PARAM)
4790 lvItem.mask |= LVIF_PARAM;
4793 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4795 lvItem.mask |= LVIF_TEXT;
4796 lvItem.pszText = szDispText;
4797 lvItem.cchTextMax = DISP_TEXT_SIZE;
4800 if (lpFindInfo->flags & LVFI_WRAP)
4803 if (lpFindInfo->flags & LVFI_NEARESTXY)
4805 ptItem.x = lpFindInfo->pt.x;
4806 ptItem.y = lpFindInfo->pt.y;
4811 while (nItem < nLast)
4813 if (lpFindInfo->flags & LVFI_NEARESTXY)
4815 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4816 lpFindInfo->vkDirection);
4819 /* get position of the new item index */
4820 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4831 lvItem.iItem = nItem;
4832 lvItem.iSubItem = 0;
4833 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
4835 if (lvItem.mask & LVIF_TEXT)
4837 if (lpFindInfo->flags & LVFI_PARTIAL)
4839 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4844 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4849 if (lvItem.mask & LVIF_PARAM)
4851 if (lpFindInfo->lParam != lvItem.lParam)
4877 * Searches for an item with specific characteristics.
4880 * [I] hwnd : window handle
4881 * [I] nStart : base item index
4882 * [I] lpFindInfo : item information to look for
4885 * SUCCESS : index of item
4888 static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart,
4889 LPLVFINDINFOA lpFindInfo)
4891 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4895 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4896 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4897 res = LISTVIEW_FindItemW(hwnd, nStart, &fiw);
4898 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4904 * Retrieves the background color of the listview control.
4907 * [I] HWND : window handle
4910 * COLORREF associated with the background.
4912 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4914 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4916 return infoPtr->clrBk;
4921 * Retrieves the background image of the listview control.
4924 * [I] HWND : window handle
4925 * [O] LPLVMKBIMAGE : background image attributes
4931 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4933 /* FIXME (listview, "empty stub!\n"); */
4939 * Retrieves the callback mask.
4942 * [I] HWND : window handle
4947 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4949 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4951 return infoPtr->uCallbackMask;
4956 * Retrieves column attributes.
4959 * [I] HWND : window handle
4960 * [I] INT : column index
4961 * [IO] LPLVCOLUMNW : column information
4962 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4963 * otherwise it is in fact a LPLVCOLUMNA
4969 static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4971 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4973 BOOL bResult = FALSE;
4975 if (lpColumn != NULL)
4978 TRACE("(hwnd=%x, col=%d, lpColumn=%s, isW=%d)\n",
4979 hwnd, nItem, debuglvcolumn_t(lpColumn, isW), isW);
4981 /* initialize memory */
4982 ZeroMemory(&hdi, sizeof(hdi));
4984 if (lpColumn->mask & LVCF_FMT)
4985 hdi.mask |= HDI_FORMAT;
4987 if (lpColumn->mask & LVCF_WIDTH)
4988 hdi.mask |= HDI_WIDTH;
4990 if (lpColumn->mask & LVCF_TEXT)
4992 hdi.mask |= HDI_TEXT;
4993 hdi.cchTextMax = lpColumn->cchTextMax;
4994 hdi.pszText = lpColumn->pszText;
4997 if (lpColumn->mask & LVCF_IMAGE)
4998 hdi.mask |= HDI_IMAGE;
5000 if (lpColumn->mask & LVCF_ORDER)
5001 hdi.mask |= HDI_ORDER;
5004 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
5006 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
5008 if (bResult != FALSE)
5010 if (lpColumn->mask & LVCF_FMT)
5014 if (hdi.fmt & HDF_LEFT)
5015 lpColumn->fmt |= LVCFMT_LEFT;
5016 else if (hdi.fmt & HDF_RIGHT)
5017 lpColumn->fmt |= LVCFMT_RIGHT;
5018 else if (hdi.fmt & HDF_CENTER)
5019 lpColumn->fmt |= LVCFMT_CENTER;
5021 if (hdi.fmt & HDF_IMAGE)
5022 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
5024 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
5025 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
5028 if (lpColumn->mask & LVCF_WIDTH)
5029 lpColumn->cx = hdi.cxy;
5031 if (lpColumn->mask & LVCF_IMAGE)
5032 lpColumn->iImage = hdi.iImage;
5034 if (lpColumn->mask & LVCF_ORDER)
5035 lpColumn->iOrder = hdi.iOrder;
5043 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
5045 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
5052 for (i = 0; i < iCount; i++)
5060 * Retrieves the column width.
5063 * [I] HWND : window handle
5064 * [I] int : column index
5067 * SUCCESS : column width
5070 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
5072 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5073 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5074 INT nColumnWidth = 0;
5077 if (uView == LVS_LIST)
5079 nColumnWidth = infoPtr->nItemWidth;
5081 else if (uView == LVS_REPORT)
5083 /* get column width from header */
5084 ZeroMemory(&hdi, sizeof(hdi));
5085 hdi.mask = HDI_WIDTH;
5086 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
5087 nColumnWidth = hdi.cxy;
5090 return nColumnWidth;
5095 * In list or report display mode, retrieves the number of items that can fit
5096 * vertically in the visible area. In icon or small icon display mode,
5097 * retrieves the total number of visible items.
5100 * [I] HWND : window handle
5103 * Number of fully visible items.
5105 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
5107 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5108 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5111 if (uView == LVS_LIST)
5113 if (infoPtr->rcList.right > infoPtr->nItemWidth)
5115 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
5116 LISTVIEW_GetCountPerColumn(hwnd);
5119 else if (uView == LVS_REPORT)
5121 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
5125 nItemCount = GETITEMCOUNT(infoPtr);
5131 /* LISTVIEW_GetEditControl */
5135 * Retrieves the extended listview style.
5138 * [I] HWND : window handle
5141 * SUCCESS : previous style
5144 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
5146 LISTVIEW_INFO *infoPtr;
5148 /* make sure we can get the listview info */
5149 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
5152 return (infoPtr->dwExStyle);
5157 * Retrieves the handle to the header control.
5160 * [I] HWND : window handle
5165 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
5167 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5169 return infoPtr->hwndHeader;
5172 /* LISTVIEW_GetHotCursor */
5176 * Returns the time that the mouse cursor must hover over an item
5177 * before it is selected.
5180 * [I] HWND : window handle
5183 * Returns the previously set hover time or (DWORD)-1 to indicate that the
5184 * hover time is set to the default hover time.
5186 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
5188 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5190 return infoPtr->dwHoverTime;
5195 * Retrieves an image list handle.
5198 * [I] HWND : window handle
5199 * [I] INT : image list identifier
5202 * SUCCESS : image list handle
5205 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
5207 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5208 HIMAGELIST himl = NULL;
5213 himl = infoPtr->himlNormal;
5216 himl = infoPtr->himlSmall;
5219 himl = infoPtr->himlState;
5223 return (LRESULT)himl;
5226 /* LISTVIEW_GetISearchString */
5230 * Retrieves item attributes.
5233 * [I] hwnd : window handle
5234 * [IO] lpLVItem : item info
5235 * [I] internal : if true then we will use tricks that avoid copies
5236 * but are not compatible with the regular interface
5237 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5238 * if FALSE, the lpLVItem is a LPLVITEMA.
5244 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
5246 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5247 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5248 NMLVDISPINFOW dispInfo;
5249 LISTVIEW_SUBITEM *lpSubItem;
5250 LISTVIEW_ITEM *lpItem;
5253 INT* piImage = (INT*)&null;
5254 LPWSTR* ppszText= (LPWSTR*)&null;
5255 LPARAM* plParam = (LPARAM*)&null;
5257 if (internal && !isW)
5259 ERR("We can't have internal non-Unicode GetItem!\n");
5263 /* In the following:
5264 * lpLVItem describes the information requested by the user
5265 * lpItem/lpSubItem is what we have
5266 * dispInfo is a structure we use to request the missing
5267 * information from the application
5270 TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n",
5271 hwnd, debuglvitem_t(lpLVItem, isW), internal, isW);
5273 if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5274 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5277 ZeroMemory(&dispInfo, sizeof(dispInfo));
5279 if (lStyle & LVS_OWNERDATA)
5281 if (lpLVItem->mask & ~LVIF_STATE)
5283 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5284 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5285 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5286 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5289 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5291 lpLVItem->state = 0;
5292 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5293 lpLVItem->state |= LVIS_FOCUSED;
5294 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5295 lpLVItem->state |= LVIS_SELECTED;
5301 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5302 if (hdpaSubItems == NULL) return FALSE;
5304 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5307 ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5308 if (lpLVItem->iSubItem == 0)
5310 piImage=&lpItem->iImage;
5311 ppszText=&lpItem->pszText;
5312 plParam=&lpItem->lParam;
5313 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5315 dispInfo.item.mask |= LVIF_STATE;
5316 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5321 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5322 if (lpSubItem != NULL)
5324 piImage=&lpSubItem->iImage;
5325 ppszText=&lpSubItem->pszText;
5329 if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
5331 dispInfo.item.mask |= LVIF_IMAGE;
5334 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5336 dispInfo.item.mask |= LVIF_TEXT;
5337 dispInfo.item.pszText = lpLVItem->pszText;
5338 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5339 if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5340 *dispInfo.item.pszText = '\0';
5341 if (dispInfo.item.pszText && (*ppszText == NULL))
5342 *dispInfo.item.pszText = '\0';
5345 if (dispInfo.item.mask != 0)
5347 /* We don't have all the requested info, query the application */
5348 dispInfo.item.iItem = lpLVItem->iItem;
5349 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5350 dispInfo.item.lParam = lpItem->lParam;
5351 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5352 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5355 if (dispInfo.item.mask & LVIF_IMAGE)
5357 lpLVItem->iImage = dispInfo.item.iImage;
5358 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
5359 *piImage = dispInfo.item.iImage;
5361 else if (lpLVItem->mask & LVIF_IMAGE)
5363 lpLVItem->iImage = *piImage;
5366 if (dispInfo.item.mask & LVIF_PARAM)
5368 lpLVItem->lParam = dispInfo.item.lParam;
5369 if (dispInfo.item.mask & LVIF_DI_SETITEM)
5370 *plParam = dispInfo.item.lParam;
5372 else if (lpLVItem->mask & LVIF_PARAM)
5373 lpLVItem->lParam = lpItem->lParam;
5375 if (dispInfo.item.mask & LVIF_TEXT)
5377 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5378 textsetptrT(ppszText, dispInfo.item.pszText, isW);
5380 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5381 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5382 if (lpLVItem->pszText != dispInfo.item.pszText)
5383 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5386 else if (lpLVItem->mask & LVIF_TEXT)
5388 if (internal) lpLVItem->pszText = *ppszText;
5389 else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5392 if (lpLVItem->iSubItem == 0)
5394 if (dispInfo.item.mask & LVIF_STATE)
5396 lpLVItem->state = lpItem->state;
5397 lpLVItem->state &= ~dispInfo.item.stateMask;
5398 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5400 lpLVItem->state &= ~LVIS_SELECTED;
5401 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5402 LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem))
5403 lpLVItem->state |= LVIS_SELECTED;
5405 else if (lpLVItem->mask & LVIF_STATE)
5407 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5409 lpLVItem->state &= ~LVIS_SELECTED;
5410 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5411 LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5412 lpLVItem->state |= LVIS_SELECTED;
5415 if (lpLVItem->mask & LVIF_PARAM)
5416 lpLVItem->lParam = lpItem->lParam;
5418 if (lpLVItem->mask & LVIF_INDENT)
5419 lpLVItem->iIndent = lpItem->iIndent;
5425 /* LISTVIEW_GetHotCursor */
5429 * Retrieves the index of the hot item.
5432 * [I] HWND : window handle
5435 * SUCCESS : hot item index
5436 * FAILURE : -1 (no hot item)
5438 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5440 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5442 return infoPtr->nHotItem;
5445 /* LISTVIEW_GetHoverTime */
5449 * Retrieves the number of items in the listview control.
5452 * [I] HWND : window handle
5457 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5459 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5461 return GETITEMCOUNT(infoPtr);
5466 * Retrieves the rectangle enclosing the item icon and text.
5469 * [I] HWND : window handle
5470 * [I] INT : item index
5471 * [O] LPRECT : coordinate information
5477 static BOOL LISTVIEW_GetItemBoundBox(HWND hwnd, INT nItem, LPRECT lpRect)
5479 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5480 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5481 UINT uView = lStyle & LVS_TYPEMASK;
5482 BOOL bResult = FALSE;
5484 LISTVIEW_ITEM *lpItem;
5485 INT nCountPerColumn;
5488 TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd, nItem, lpRect);
5490 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5493 if (uView == LVS_LIST)
5496 nItem = nItem - ListView_GetTopIndex(hwnd);
5497 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5500 nRow = nItem % nCountPerColumn;
5503 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5508 lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5509 lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5514 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5515 lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
5518 else if (uView == LVS_REPORT)
5521 lpRect->left = REPORT_MARGINX;
5522 lpRect->top = ((nItem - ListView_GetTopIndex(hwnd)) *
5523 infoPtr->nItemHeight) + infoPtr->rcList.top;
5525 if (!(lStyle & LVS_NOSCROLL))
5527 SCROLLINFO scrollInfo;
5528 /* Adjust position by scrollbar offset */
5529 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5530 scrollInfo.cbSize = sizeof(SCROLLINFO);
5531 scrollInfo.fMask = SIF_POS;
5532 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5533 lpRect->left -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5536 else /* either LVS_ICON or LVS_SMALLICON */
5538 if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
5540 if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
5543 lpRect->left = lpItem->ptPosition.x;
5544 lpRect->top = lpItem->ptPosition.y;
5549 lpRect->right = lpRect->left + infoPtr->nItemWidth;
5550 lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
5551 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5552 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
5558 * Retrieves the position (upper-left) of the listview control item.
5559 * Note that for LVS_ICON style, the upper-left is that of the icon
5560 * and not the bounding box.
5563 * [I] HWND : window handle
5564 * [I] INT : item index
5565 * [O] LPPOINT : coordinate information
5571 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, LPPOINT lpptPosition)
5573 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5574 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5575 BOOL bResult = FALSE;
5578 TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd, nItem, lpptPosition);
5580 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5581 (lpptPosition != NULL))
5583 bResult = LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcBounding);
5584 lpptPosition->x = rcBounding.left;
5585 lpptPosition->y = rcBounding.top;
5586 if (uView == LVS_ICON)
5588 lpptPosition->y += ICON_TOP_PADDING;
5589 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5591 TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
5592 lpptPosition->x, lpptPosition->y);
5598 * Update the bounding rectangle around the text under a large icon.
5599 * This depends on whether it has the focus or not.
5600 * On entry the rectangle's top, left and right should be set.
5601 * On return the bottom will also be set and the width may have been
5604 * This appears to be weird, even in the Microsoft implementation.
5607 static void ListView_UpdateLargeItemLabelRect (
5608 HWND hwnd, /* The window of the listview */
5609 const LISTVIEW_INFO *infoPtr, /* The listview itself */
5610 int nItem, /* The item for which we are calculating this */
5611 RECT *rect) /* The rectangle to be updated */
5613 HDC hdc = GetDC (hwnd);
5614 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
5616 if (infoPtr->bFocus && infoPtr->nFocusedItem == nItem)
5618 /* We (aim to) display the full text. In Windows 95 it appears to
5619 * calculate the size assuming the specified font and then it draws
5620 * the text in that region with the specified font except scaled to
5621 * 10 point (or the height of the system font or ...). Thus if the
5622 * window has 24 point Helvetica the highlit rectangle will be
5623 * taller than the text and if it is 7 point Helvetica then the text
5625 * For now we will simply say that it is the correct size to display
5626 * the text in the specified font.
5629 lvItem.mask = LVIF_TEXT;
5630 lvItem.iItem = nItem;
5631 lvItem.iSubItem = 0;
5632 /* We will specify INTERNAL and so will receive back a const
5633 * pointer to the text, rather than specifying a buffer to which
5636 LISTVIEW_GetItemW (hwnd, &lvItem, TRUE);
5637 DrawTextW (hdc, lvItem.pszText, -1, rect, DT_CALCRECT |
5638 DT_NOCLIP | DT_EDITCONTROL | DT_TOP | DT_CENTER |
5639 DT_WORDBREAK | DT_NOPREFIX);
5640 /* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
5644 /* As far as I can see the text region seems to be trying to be
5645 * "tall enough for two lines of text". Once again (comctl32.dll ver
5646 * 5.81?) it measures this on the basis of the selected font and then
5647 * draws it with the same font except in 10 point size. This can lead
5648 * to more or less than the two rows appearing.
5649 * Question; are we supposed to be including DT_EXTERNALLEADING?
5650 * Question; should the width be shrunk to the space required to
5651 * display the two lines?
5653 rect->bottom = rect->top + 2 * infoPtr->ntmHeight;
5656 SelectObject (hdc, hOldFont);
5657 ReleaseDC (hwnd, hdc);
5662 * Retrieves the bounding rectangle for a listview control item.
5665 * [I] HWND : window handle
5666 * [I] INT : item index
5667 * [IO] LPRECT : bounding rectangle coordinates
5668 * lprc->left specifies the portion of the item for which the bounding
5669 * rectangle will be retrieved.
5671 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5672 * including the icon and label.
5673 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5674 * LVIR_LABEL Returns the bounding rectangle of the item text.
5675 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5676 * rectangles, but excludes columns in report view.
5683 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5684 * upon whether the window has the focus currently and on whether the item
5685 * is the one with the focus. Ensure that the control's record of which
5686 * item has the focus agrees with the items' records.
5688 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5690 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5691 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5692 BOOL bResult = FALSE;
5701 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5703 if (uView & LVS_REPORT)
5705 ZeroMemory(&lvItem, sizeof(lvItem));
5706 lvItem.mask = LVIF_INDENT;
5707 lvItem.iItem = nItem;
5708 lvItem.iSubItem = 0;
5709 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
5712 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5713 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5720 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5725 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5726 if (uView == LVS_ICON)
5728 if (infoPtr->himlNormal != NULL)
5730 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5733 lprc->left = ptItem.x + ptOrigin.x;
5734 lprc->top = ptItem.y + ptOrigin.y;
5735 lprc->right = lprc->left + infoPtr->iconSize.cx;
5736 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5737 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5741 else if (uView == LVS_SMALLICON)
5743 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5746 lprc->left = ptItem.x + ptOrigin.x;
5747 lprc->top = ptItem.y + ptOrigin.y;
5748 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5750 if (infoPtr->himlState != NULL)
5751 lprc->left += infoPtr->iconSize.cx;
5753 if (infoPtr->himlSmall != NULL)
5754 lprc->right = lprc->left + infoPtr->iconSize.cx;
5756 lprc->right = lprc->left;
5762 lprc->left = ptItem.x;
5763 if (uView & LVS_REPORT)
5764 lprc->left += nIndent;
5765 lprc->top = ptItem.y;
5766 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5768 if (infoPtr->himlState != NULL)
5769 lprc->left += infoPtr->iconSize.cx;
5771 if (infoPtr->himlSmall != NULL)
5772 lprc->right = lprc->left + infoPtr->iconSize.cx;
5774 lprc->right = lprc->left;
5779 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5780 if (uView == LVS_ICON)
5782 if (infoPtr->himlNormal != NULL)
5784 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5787 lprc->left = ptItem.x + ptOrigin.x;
5788 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5789 ICON_BOTTOM_PADDING);
5790 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5791 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5793 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5794 lprc->right = lprc->left + nLabelWidth;
5799 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5800 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, lprc);
5805 else if (uView == LVS_SMALLICON)
5807 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5810 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5811 lprc->top = ptItem.y + ptOrigin.y;
5812 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5814 if (infoPtr->himlState != NULL)
5815 lprc->left += infoPtr->iconSize.cx;
5817 if (infoPtr->himlSmall != NULL)
5818 lprc->left += infoPtr->iconSize.cx;
5820 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5821 nLabelWidth += TRAILING_PADDING;
5822 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5823 lprc->right = lprc->left + nLabelWidth;
5825 lprc->right = nLeftPos + infoPtr->nItemWidth;
5831 if (uView == LVS_REPORT)
5832 nLeftPos = lprc->left = ptItem.x + nIndent;
5834 nLeftPos = lprc->left = ptItem.x;
5835 lprc->top = ptItem.y;
5836 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5838 if (infoPtr->himlState != NULL)
5839 lprc->left += infoPtr->iconSize.cx;
5841 if (infoPtr->himlSmall != NULL)
5842 lprc->left += infoPtr->iconSize.cx;
5844 if (uView != LVS_REPORT)
5846 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5847 nLabelWidth += TRAILING_PADDING;
5848 if (infoPtr->himlSmall)
5849 nLabelWidth += IMAGE_PADDING;
5852 nLabelWidth = LISTVIEW_GetColumnWidth(hwnd, 0)-lprc->left;
5853 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5854 lprc->right = lprc->left + nLabelWidth;
5856 lprc->right = nLeftPos + infoPtr->nItemWidth;
5861 if (!LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcInternal)) break;
5862 ptItem.x = rcInternal.left;
5863 ptItem.y = rcInternal.top;
5864 if (uView == LVS_ICON)
5866 if (infoPtr->himlNormal != NULL)
5868 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5871 INT text_left, text_right, icon_left, text_pos_x;
5872 /* for style LVS_ICON bounds
5873 * left = min(icon.left, text.left)
5874 * right = max(icon.right, text.right)
5875 * top = boundbox.top + NOTHITABLE
5876 * bottom = text.bottom + 1
5879 icon_left = text_left = ptItem.x;
5881 /* Correct ptItem to icon upper-left */
5882 icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5883 ptItem.y += ICON_TOP_PADDING;
5885 /* Compute the label left and right */
5886 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5887 text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
5890 text_left += text_pos_x / 2;
5891 text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
5896 text_right = text_left + infoPtr->iconSpacing.cx - 1;
5899 /* Compute rectangle w/o the text height */
5900 lprc->left = min(icon_left, text_left) + ptOrigin.x;
5901 lprc->right = max(icon_left + infoPtr->iconSize.cx,
5902 text_right) + ptOrigin.x;
5903 lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
5904 lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
5905 + infoPtr->iconSize.cy + 1
5906 + ICON_BOTTOM_PADDING;
5908 CopyRect (&label_rect, lprc);
5909 label_rect.top = lprc->bottom;
5910 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, &label_rect);
5911 UnionRect (lprc, lprc, &label_rect);
5915 else if (uView == LVS_SMALLICON)
5917 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5920 lprc->left = ptItem.x + ptOrigin.x;
5921 lprc->right = lprc->left;
5922 lprc->top = ptItem.y + ptOrigin.y;
5923 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5924 if (infoPtr->himlState != NULL)
5925 lprc->right += infoPtr->iconSize.cx;
5926 if (infoPtr->himlSmall != NULL)
5927 lprc->right += infoPtr->iconSize.cx;
5929 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5930 nLabelWidth += TRAILING_PADDING;
5931 if (infoPtr->himlSmall)
5932 nLabelWidth += IMAGE_PADDING;
5933 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5934 lprc->right += nLabelWidth;
5936 lprc->right = lprc->left + infoPtr->nItemWidth;
5942 lprc->left = ptItem.x;
5943 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5944 lprc->left += nIndent;
5945 lprc->right = lprc->left;
5946 lprc->top = ptItem.y;
5947 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5949 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
5952 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5953 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5955 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5959 if (infoPtr->himlState != NULL)
5960 lprc->right += infoPtr->iconSize.cx;
5962 if (infoPtr->himlSmall != NULL)
5963 lprc->right += infoPtr->iconSize.cx;
5965 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5966 nLabelWidth += TRAILING_PADDING;
5967 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5968 lprc->right += nLabelWidth;
5970 lprc->right = lprc->left + infoPtr->nItemWidth;
5975 case LVIR_SELECTBOUNDS:
5976 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5977 if (uView == LVS_ICON)
5979 if (infoPtr->himlNormal != NULL)
5981 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5984 lprc->left = ptItem.x + ptOrigin.x;
5985 lprc->top = ptItem.y + ptOrigin.y;
5986 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5987 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5991 else if (uView == LVS_SMALLICON)
5993 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5996 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5997 lprc->top = ptItem.y + ptOrigin.y;
5998 lprc->bottom = lprc->top + infoPtr->nItemHeight;
6000 if (infoPtr->himlState != NULL)
6001 lprc->left += infoPtr->iconSize.cx;
6003 lprc->right = lprc->left;
6005 if (infoPtr->himlSmall != NULL)
6006 lprc->right += infoPtr->iconSize.cx;
6008 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6009 nLabelWidth += TRAILING_PADDING;
6010 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6011 lprc->right += nLabelWidth;
6013 lprc->right = nLeftPos + infoPtr->nItemWidth;
6019 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
6020 nLeftPos = lprc->left = ptItem.x + nIndent;
6022 nLeftPos = lprc->left = ptItem.x;
6023 lprc->top = ptItem.y;
6024 lprc->bottom = lprc->top + infoPtr->nItemHeight;
6026 if (infoPtr->himlState != NULL)
6027 lprc->left += infoPtr->iconSize.cx;
6029 lprc->right = lprc->left;
6031 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
6034 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
6035 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
6037 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
6041 if (infoPtr->himlSmall != NULL)
6042 lprc->right += infoPtr->iconSize.cx;
6044 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6045 nLabelWidth += TRAILING_PADDING;
6046 if (infoPtr->himlSmall)
6047 nLabelWidth += IMAGE_PADDING;
6048 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6049 lprc->right += nLabelWidth;
6051 lprc->right = nLeftPos + infoPtr->nItemWidth;
6058 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
6059 lprc->left, lprc->top, lprc->right, lprc->bottom);
6065 static LRESULT LISTVIEW_GetSubItemRect(HWND hwnd, INT nItem, INT nSubItem, INT
6068 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6071 TRACE("(hwnd=%x, nItem=%d, nSubItem=%d lprc=%p)\n", hwnd, nItem, nSubItem,
6074 if (!(uView & LVS_REPORT))
6077 if (flags & LVIR_ICON)
6079 FIXME("Unimplemented LVIR_ICON\n");
6084 int top = min(((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0))->nColumnCount,
6087 LISTVIEW_GetItemRect(hwnd,nItem,lprc);
6088 for (count = 0; count < top; count++)
6089 lprc->left += LISTVIEW_GetColumnWidth(hwnd,count);
6091 lprc->right = LISTVIEW_GetColumnWidth(hwnd,(nSubItem-1)) +
6100 * Retrieves the width of a label.
6103 * [I] HWND : window handle
6106 * SUCCESS : string width (in pixels)
6109 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
6111 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6112 INT nLabelWidth = 0;
6115 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
6117 ZeroMemory(&lvItem, sizeof(lvItem));
6118 lvItem.mask = LVIF_TEXT;
6119 lvItem.iItem = nItem;
6120 lvItem.cchTextMax = DISP_TEXT_SIZE;
6121 lvItem.pszText = szDispText;
6122 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6123 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
6130 * Retrieves the spacing between listview control items.
6133 * [I] HWND : window handle
6134 * [I] BOOL : flag for small or large icon
6137 * Horizontal + vertical spacing
6139 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
6141 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6144 if (bSmall == FALSE)
6146 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6150 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
6151 if ((style & LVS_TYPEMASK) == LVS_ICON)
6152 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6154 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6161 * Retrieves the state of a listview control item.
6164 * [I] HWND : window handle
6165 * [I] INT : item index
6166 * [I] UINT : state mask
6169 * State specified by the mask.
6171 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
6173 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6177 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6179 ZeroMemory(&lvItem, sizeof(lvItem));
6180 lvItem.iItem = nItem;
6181 lvItem.stateMask = uMask;
6182 lvItem.mask = LVIF_STATE;
6183 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6184 uState = lvItem.state;
6192 * Retrieves the text of a listview control item or subitem.
6195 * [I] hwnd : window handle
6196 * [I] nItem : item index
6197 * [IO] lpLVItem : item information
6198 * [I] isW : TRUE if lpLVItem is Unicode
6201 * SUCCESS : string length
6204 static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6206 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6209 if (lpLVItem != NULL)
6211 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6213 lpLVItem->mask = LVIF_TEXT;
6214 lpLVItem->iItem = nItem;
6215 if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
6216 nLength = textlenT(lpLVItem->pszText, isW);
6225 * Searches for an item based on properties + relationships.
6228 * [I] HWND : window handle
6229 * [I] INT : item index
6230 * [I] INT : relationship flag
6233 * SUCCESS : item index
6236 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
6238 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6239 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6241 LVFINDINFOW lvFindInfo;
6242 INT nCountPerColumn;
6245 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6247 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6249 if (uFlags & LVNI_CUT)
6252 if (uFlags & LVNI_DROPHILITED)
6253 uMask |= LVIS_DROPHILITED;
6255 if (uFlags & LVNI_FOCUSED)
6256 uMask |= LVIS_FOCUSED;
6258 if (uFlags & LVNI_SELECTED)
6259 uMask |= LVIS_SELECTED;
6261 if (uFlags & LVNI_ABOVE)
6263 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6268 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6274 lvFindInfo.flags = LVFI_NEARESTXY;
6275 lvFindInfo.vkDirection = VK_UP;
6276 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6277 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6279 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6284 else if (uFlags & LVNI_BELOW)
6286 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6288 while (nItem < GETITEMCOUNT(infoPtr))
6291 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6297 lvFindInfo.flags = LVFI_NEARESTXY;
6298 lvFindInfo.vkDirection = VK_DOWN;
6299 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6300 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6302 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6307 else if (uFlags & LVNI_TOLEFT)
6309 if (uView == LVS_LIST)
6311 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6312 while (nItem - nCountPerColumn >= 0)
6314 nItem -= nCountPerColumn;
6315 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6319 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6321 lvFindInfo.flags = LVFI_NEARESTXY;
6322 lvFindInfo.vkDirection = VK_LEFT;
6323 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6324 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6326 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6331 else if (uFlags & LVNI_TORIGHT)
6333 if (uView == LVS_LIST)
6335 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6336 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6338 nItem += nCountPerColumn;
6339 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6343 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6345 lvFindInfo.flags = LVFI_NEARESTXY;
6346 lvFindInfo.vkDirection = VK_RIGHT;
6347 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6348 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6350 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6359 /* search by index */
6360 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6362 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
6371 /* LISTVIEW_GetNumberOfWorkAreas */
6375 * Retrieves the origin coordinates when in icon or small icon display mode.
6378 * [I] HWND : window handle
6379 * [O] LPPOINT : coordinate information
6385 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6387 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6388 UINT uView = lStyle & LVS_TYPEMASK;
6389 BOOL bResult = FALSE;
6391 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6393 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6395 SCROLLINFO scrollInfo;
6396 ZeroMemory(lpptOrigin, sizeof(POINT));
6397 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6398 scrollInfo.cbSize = sizeof(SCROLLINFO);
6400 if (lStyle & WS_HSCROLL)
6402 scrollInfo.fMask = SIF_POS;
6403 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6404 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6407 if (lStyle & WS_VSCROLL)
6409 scrollInfo.fMask = SIF_POS;
6410 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6411 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6422 * Retrieves the number of items that are marked as selected.
6425 * [I] HWND : window handle
6428 * Number of items selected.
6430 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6433 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6434 INT nSelectedCount = 0;
6437 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6439 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6443 return nSelectedCount;
6448 * Retrieves item index that marks the start of a multiple selection.
6451 * [I] HWND : window handle
6454 * Index number or -1 if there is no selection mark.
6456 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6458 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6460 return infoPtr->nSelectionMark;
6466 * Retrieves the width of a string.
6469 * [I] hwnd : window handle
6470 * [I] lpszText : text string to process
6471 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6474 * SUCCESS : string width (in pixels)
6477 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
6479 if (is_textT(lpszText, isW))
6481 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6482 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6483 HDC hdc = GetDC(hwnd);
6484 HFONT hOldFont = SelectObject(hdc, hFont);
6486 ZeroMemory(&stringSize, sizeof(SIZE));
6488 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6490 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6491 SelectObject(hdc, hOldFont);
6492 ReleaseDC(hwnd, hdc);
6493 return stringSize.cx;
6500 * Retrieves the text backgound color.
6503 * [I] HWND : window handle
6506 * COLORREF associated with the the background.
6508 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6510 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6512 return infoPtr->clrTextBk;
6517 * Retrieves the text color.
6520 * [I] HWND : window handle
6523 * COLORREF associated with the text.
6525 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6527 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6529 return infoPtr->clrText;
6534 * Determines item if a hit or closest if not
6537 * [I] HWND : window handle
6538 * [IO] LPLV_INTHIT : hit test information
6539 * [I] subitem : fill out iSubItem.
6542 * SUCCESS : item index of hit
6545 static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem)
6547 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6548 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6549 UINT uView = lStyle & LVS_TYPEMASK;
6550 INT i,j,topindex,bottomindex;
6551 RECT rcItem,rcSubItem;
6552 DWORD xterm, yterm, dist;
6554 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.pt.y);
6556 topindex = ListView_GetTopIndex(hwnd);
6557 if (uView == LVS_REPORT)
6559 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6560 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6564 bottomindex = GETITEMCOUNT(infoPtr);
6567 lpInt->distance = 0x7fffffff;
6568 lpInt->iDistItem = -1;
6570 for (i = topindex; i < bottomindex; i++)
6572 rcItem.left = LVIR_BOUNDS;
6573 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6575 if (PtInRect(&rcItem, lpInt->ht.pt))
6578 rcItem.left = LVIR_ICON;
6579 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6581 if (PtInRect(&rcItem, lpInt->ht.pt))
6583 lpInt->ht.flags = LVHT_ONITEMICON;
6584 lpInt->ht.iItem = i;
6589 rcItem.left = LVIR_LABEL;
6590 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6592 if (PtInRect(&rcItem, lpInt->ht.pt))
6594 lpInt->ht.flags = LVHT_ONITEMLABEL;
6595 lpInt->ht.iItem = i;
6600 lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6601 lpInt->ht.iItem = i;
6605 lpInt->ht.iSubItem = 0;
6606 rcSubItem.right = rcSubItem.left;
6607 for (j = 0; j < infoPtr->nColumnCount; j++)
6609 rcSubItem.left = rcSubItem.right;
6610 rcSubItem.right += LISTVIEW_GetColumnWidth(hwnd, j);
6611 if (PtInRect(&rcSubItem, lpInt->ht.pt))
6613 lpInt->ht.iSubItem = j;
6623 * Now compute distance from point to center of boundary
6624 * box. Since we are only interested in the relative
6625 * distance, we can skip the nasty square root operation
6627 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6628 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6629 dist = xterm * xterm + yterm * yterm;
6630 if (dist < lpInt->distance)
6632 lpInt->distance = dist;
6633 lpInt->iDistItem = i;
6639 lpInt->ht.flags = LVHT_NOWHERE;
6640 TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6647 * Determines which section of the item was selected (if any).
6650 * [I] HWND : window handle
6651 * [IO] LPLVHITTESTINFO : hit test information
6652 * [I] subitem : fill out iSubItem.
6655 * SUCCESS : item index
6658 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6661 LV_INTHIT lv_inthit;
6663 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6664 lpHitTestInfo->pt.y);
6666 memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6667 ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem);
6668 memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6674 * Determines which listview item is located at the specified position.
6677 * [I] HWND : window handle
6678 * [IO} LPLVHITTESTINFO : hit test information
6681 * SUCCESS : item index
6684 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6686 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6689 lpHitTestInfo->flags = 0;
6691 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6692 lpHitTestInfo->flags = LVHT_TOLEFT;
6693 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6694 lpHitTestInfo->flags = LVHT_TORIGHT;
6695 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6696 lpHitTestInfo->flags |= LVHT_ABOVE;
6697 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6698 lpHitTestInfo->flags |= LVHT_BELOW;
6700 if (lpHitTestInfo->flags == 0)
6702 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6703 * an app might pass only a structure with space up to iItem!
6704 * (MS Office 97 does that for instance in the file open dialog)
6706 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6714 * Determines which listview subitem is located at the specified position.
6717 * [I] HWND : window handle
6718 * [IO} LPLVHITTESTINFO : hit test information
6721 * SUCCESS : item index
6724 static LRESULT LISTVIEW_SubItemHitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6726 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6729 lpHitTestInfo->flags = 0;
6731 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6732 lpHitTestInfo->flags = LVHT_TOLEFT;
6733 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6734 lpHitTestInfo->flags = LVHT_TORIGHT;
6735 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6736 lpHitTestInfo->flags |= LVHT_ABOVE;
6737 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6738 lpHitTestInfo->flags |= LVHT_BELOW;
6740 if (lpHitTestInfo->flags == 0)
6741 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, TRUE);
6748 * Inserts a new column.
6751 * [I] HWND : window handle
6752 * [I] INT : column index
6753 * [I] LPLVCOLUMNW : column information
6756 * SUCCESS : new column index
6759 static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn,
6760 LPLVCOLUMNW lpColumn, BOOL isW)
6762 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6763 INT nNewColumn = -1;
6766 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
6768 if (lpColumn != NULL)
6770 /* initialize memory */
6771 ZeroMemory(&hdi, sizeof(hdi));
6773 if (lpColumn->mask & LVCF_FMT)
6775 /* format member is valid */
6776 hdi.mask |= HDI_FORMAT;
6778 /* set text alignment (leftmost column must be left-aligned) */
6781 hdi.fmt |= HDF_LEFT;
6785 if (lpColumn->fmt & LVCFMT_LEFT)
6787 hdi.fmt |= HDF_LEFT;
6789 else if (lpColumn->fmt & LVCFMT_RIGHT)
6791 hdi.fmt |= HDF_RIGHT;
6793 else if (lpColumn->fmt & LVCFMT_CENTER)
6795 hdi.fmt |= HDF_CENTER;
6799 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6801 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6805 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6810 if (lpColumn->fmt & LVCFMT_IMAGE)
6812 hdi.fmt |= HDF_IMAGE;
6813 hdi.iImage = I_IMAGECALLBACK;
6817 if (lpColumn->mask & LVCF_WIDTH)
6819 hdi.mask |= HDI_WIDTH;
6820 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6822 /* make it fill the remainder of the controls width */
6827 ZeroMemory(&hdit, sizeof(hdit));
6829 /* get the width of every item except the current one */
6830 hdit.mask = HDI_WIDTH;
6833 for(item_index = 0; item_index < (nColumn - 1); item_index++) {
6834 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
6838 /* retrieve the layout of the header */
6839 GetClientRect(hwnd, &rcHeader);
6840 /* GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6841 TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
6843 hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
6846 hdi.cxy = lpColumn->cx;
6849 if (lpColumn->mask & LVCF_TEXT)
6851 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6852 hdi.pszText = lpColumn->pszText;
6853 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6854 hdi.fmt |= HDF_STRING;
6857 if (lpColumn->mask & LVCF_IMAGE)
6859 hdi.mask |= HDI_IMAGE;
6860 hdi.iImage = lpColumn->iImage;
6863 if (lpColumn->mask & LVCF_ORDER)
6865 hdi.mask |= HDI_ORDER;
6866 hdi.iOrder = lpColumn->iOrder;
6869 /* insert item in header control */
6870 nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6871 (WPARAM)nColumn, (LPARAM)&hdi);
6873 /* Need to reset the item width when inserting a new column */
6874 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6876 LISTVIEW_UpdateScroll(hwnd);
6877 InvalidateRect(hwnd, NULL, FALSE);
6883 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6884 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6885 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6886 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6887 their own sort proc. when sending LVM_SORTITEMS.
6890 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6892 LVS_SORTXXX must be specified,
6893 LVS_OWNERDRAW is not set,
6894 <item>.pszText is not LPSTR_TEXTCALLBACK.
6896 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6897 are sorted based on item text..."
6899 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6901 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6902 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6903 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6904 INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6905 /* if we're sorting descending, negate the return value */
6906 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6911 * Inserts a new item in the listview control.
6914 * [I] HWND : window handle
6915 * [I] LPLVITEMW : item information
6916 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6919 * SUCCESS : new item index
6922 static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
6924 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6925 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6926 UINT uView = lStyle & LVS_TYPEMASK;
6930 LISTVIEW_ITEM *lpItem = NULL;
6932 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
6933 hwnd, debuglvitem_t(lpLVItem, isW), isW);
6935 if (lStyle & LVS_OWNERDATA)
6937 nItem = infoPtr->hdpaItems->nItemCount;
6938 infoPtr->hdpaItems->nItemCount ++;
6942 if (lpLVItem != NULL)
6944 /* make sure it's not a subitem; cannot insert a subitem */
6945 if (lpLVItem->iSubItem == 0)
6947 if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6949 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6950 if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
6952 /* insert item in listview control data structure */
6953 if ( (hdpaSubItems = DPA_Create(8)) )
6955 if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
6957 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6958 && !(lStyle & LVS_OWNERDRAWFIXED)
6959 && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
6961 /* Insert the item in the proper sort order based on the pszText
6962 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6963 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6964 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6965 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6966 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6970 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6977 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6979 /* manage item focus */
6980 if (lpLVItem->mask & LVIF_STATE)
6982 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6983 if (lpLVItem->stateMask & LVIS_SELECTED)
6984 LISTVIEW_SetSelection(hwnd, nItem);
6985 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6986 LISTVIEW_SetItemFocus(hwnd, nItem);
6989 /* send LVN_INSERTITEM notification */
6990 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6992 nmlv.lParam = lpItem->lParam;
6993 listview_notify(hwnd, LVN_INSERTITEM, &nmlv);
6995 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6997 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6998 if (nItemWidth > infoPtr->nItemWidth)
6999 infoPtr->nItemWidth = nItemWidth;
7002 /* align items (set position of each item) */
7003 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
7005 if (lStyle & LVS_ALIGNLEFT)
7006 LISTVIEW_AlignLeft(hwnd);
7008 LISTVIEW_AlignTop(hwnd);
7011 LISTVIEW_UpdateScroll(hwnd);
7012 /* refresh client area */
7013 InvalidateRect(hwnd, NULL, FALSE);
7022 /* free memory if unsuccessful */
7023 if ((nItem == -1) && (lpItem != NULL))
7024 COMCTL32_Free(lpItem);
7031 * Redraws a range of items.
7034 * [I] HWND : window handle
7035 * [I] INT : first item
7036 * [I] INT : last item
7042 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
7044 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7045 BOOL bResult = FALSE;
7049 if (nFirst <= nLast)
7051 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
7053 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
7055 for (i = nFirst; i <= nLast; i++)
7057 rcItem.left = LVIR_BOUNDS;
7058 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
7059 InvalidateRect(hwnd, &rcItem, TRUE);
7068 /* LISTVIEW_Scroll */
7072 * Sets the background color.
7075 * [I] HWND : window handle
7076 * [I] COLORREF : background color
7082 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
7084 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7086 if(infoPtr->clrBk!=clrBk){
7087 infoPtr->clrBk = clrBk;
7088 InvalidateRect(hwnd, NULL, TRUE);
7094 /* LISTVIEW_SetBkImage */
7098 * Sets the callback mask. This mask will be used when the parent
7099 * window stores state information (some or all).
7102 * [I] HWND : window handle
7103 * [I] UINT : state mask
7109 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
7111 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7113 infoPtr->uCallbackMask = uMask;
7120 * Sets the attributes of a header item.
7123 * [I] HWND : window handle
7124 * [I] INT : column index
7125 * [I] LPLVCOLUMNW : column attributes
7126 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
7127 * otherwise it is in fact a LPLVCOLUMNA
7133 static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn,
7134 LPLVCOLUMNW lpColumn, BOOL isW)
7136 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7137 BOOL bResult = FALSE;
7138 HDITEMW hdi, hdiget;
7140 if ((lpColumn != NULL) && (nColumn >= 0) &&
7141 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
7143 /* initialize memory */
7144 ZeroMemory(&hdi, sizeof(hdi));
7146 if (lpColumn->mask & LVCF_FMT)
7148 /* format member is valid */
7149 hdi.mask |= HDI_FORMAT;
7151 /* get current format first */
7152 hdiget.mask = HDI_FORMAT;
7153 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7154 /* preserve HDF_STRING if present */
7155 hdi.fmt = hdiget.fmt & HDF_STRING;
7157 /* set text alignment (leftmost column must be left-aligned) */
7160 hdi.fmt |= HDF_LEFT;
7164 if (lpColumn->fmt & LVCFMT_LEFT)
7165 hdi.fmt |= HDF_LEFT;
7166 else if (lpColumn->fmt & LVCFMT_RIGHT)
7167 hdi.fmt |= HDF_RIGHT;
7168 else if (lpColumn->fmt & LVCFMT_CENTER)
7169 hdi.fmt |= HDF_CENTER;
7172 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7173 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
7175 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7176 hdi.fmt |= HDF_IMAGE;
7178 if (lpColumn->fmt & LVCFMT_IMAGE)
7180 hdi.fmt |= HDF_IMAGE;
7181 hdi.iImage = I_IMAGECALLBACK;
7185 if (lpColumn->mask & LVCF_WIDTH)
7187 hdi.mask |= HDI_WIDTH;
7188 hdi.cxy = lpColumn->cx;
7191 if (lpColumn->mask & LVCF_TEXT)
7193 hdi.mask |= HDI_TEXT | HDI_FORMAT;
7194 hdi.pszText = lpColumn->pszText;
7195 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
7196 hdi.fmt |= HDF_STRING;
7199 if (lpColumn->mask & LVCF_IMAGE)
7201 hdi.mask |= HDI_IMAGE;
7202 hdi.iImage = lpColumn->iImage;
7205 if (lpColumn->mask & LVCF_ORDER)
7207 hdi.mask |= HDI_ORDER;
7208 hdi.iOrder = lpColumn->iOrder;
7211 /* set header item attributes */
7213 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7215 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
7223 * Sets the column order array
7226 * [I] HWND : window handle
7227 * [I] INT : number of elements in column order array
7228 * [I] INT : pointer to column order array
7234 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
7236 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
7238 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7249 * Sets the width of a column
7252 * [I] HWND : window handle
7253 * [I] INT : column index
7254 * [I] INT : column width
7260 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
7262 LISTVIEW_INFO *infoPtr;
7265 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7266 UINT uView = lStyle & LVS_TYPEMASK;
7271 WCHAR text_buffer[DISP_TEXT_SIZE];
7272 INT header_item_count;
7277 WCHAR szDispText[DISP_TEXT_SIZE];
7279 /* make sure we can get the listview info */
7280 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
7283 if (!infoPtr->hwndHeader) /* make sure we have a header */
7286 /* set column width only if in report or list mode */
7287 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7290 TRACE("(hwnd=%x, iCol=%d, cx=%d\n", hwnd, iCol, cx);
7292 /* take care of invalid cx values */
7293 if((uView == LVS_REPORT) && (cx < -2))
7294 cx = LVSCW_AUTOSIZE;
7295 else if (uView == LVS_LIST && (cx < 1))
7298 /* resize all columns if in LVS_LIST mode */
7299 if(uView == LVS_LIST) {
7300 infoPtr->nItemWidth = cx;
7301 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7305 /* autosize based on listview items width */
7306 if(cx == LVSCW_AUTOSIZE)
7308 /* set the width of the column to the width of the widest item */
7309 if (iCol == 0 || uView == LVS_LIST)
7312 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7314 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, item_index);
7315 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7317 if (infoPtr->himlSmall)
7318 cx += infoPtr->iconSize.cx + IMAGE_PADDING;
7322 ZeroMemory(&lvItem, sizeof(lvItem));
7323 lvItem.iSubItem = iCol;
7324 lvItem.mask = LVIF_TEXT;
7325 lvItem.cchTextMax = DISP_TEXT_SIZE;
7326 lvItem.pszText = szDispText;
7327 *lvItem.pszText = '\0';
7329 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7331 lvItem.iItem = item_index;
7332 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7333 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7334 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7337 cx += TRAILING_PADDING;
7338 } /* autosize based on listview header width */
7339 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7341 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7343 /* if iCol is the last column make it fill the remainder of the controls width */
7344 if(iCol == (header_item_count - 1)) {
7345 /* get the width of every item except the current one */
7346 hdi.mask = HDI_WIDTH;
7349 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7350 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7354 /* retrieve the layout of the header */
7355 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7357 cx = (rcHeader.right - rcHeader.left) - cx;
7361 /* Despite what the MS docs say, if this is not the last
7362 column, then MS resizes the column to the width of the
7363 largest text string in the column, including headers
7364 and items. This is different from LVSCW_AUTOSIZE in that
7365 LVSCW_AUTOSIZE ignores the header string length.
7368 /* retrieve header font */
7369 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7371 /* retrieve header text */
7372 hdi.mask = HDI_TEXT;
7373 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7374 hdi.pszText = text_buffer;
7376 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7378 /* determine the width of the text in the header */
7380 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7382 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7384 SelectObject(hdc, old_font); /* restore the old font */
7385 ReleaseDC(hwnd, hdc);
7387 ZeroMemory(&lvItem, sizeof(lvItem));
7388 lvItem.iSubItem = iCol;
7389 lvItem.mask = LVIF_TEXT;
7390 lvItem.cchTextMax = DISP_TEXT_SIZE;
7391 lvItem.pszText = szDispText;
7392 *lvItem.pszText = '\0';
7394 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7396 lvItem.iItem = item_index;
7397 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7398 nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7399 nLabelWidth += TRAILING_PADDING;
7400 /* While it is possible for subitems to have icons, even MS messes
7401 up the positioning, so I suspect no applications actually use
7403 if (item_index == 0 && infoPtr->himlSmall)
7404 nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
7405 cx = (nLabelWidth>cx)?nLabelWidth:cx;
7410 /* call header to update the column change */
7411 hdi.mask = HDI_WIDTH;
7414 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7416 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7423 * Sets the extended listview style.
7426 * [I] HWND : window handle
7431 * SUCCESS : previous style
7434 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
7436 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7437 DWORD dwOldStyle = infoPtr->dwExStyle;
7441 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7443 infoPtr->dwExStyle = dwStyle;
7448 /* LISTVIEW_SetHotCursor */
7452 * Sets the hot item index.
7455 * [I] HWND : window handle
7459 * SUCCESS : previous hot item index
7460 * FAILURE : -1 (no hot item)
7462 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7464 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7465 INT iOldIndex = infoPtr->nHotItem;
7468 infoPtr->nHotItem = iIndex;
7475 * Sets the amount of time the cursor must hover over an item before it is selected.
7478 * [I] HWND : window handle
7479 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7482 * Returns the previous hover time
7484 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7486 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7487 DWORD oldHoverTime = infoPtr->dwHoverTime;
7489 infoPtr->dwHoverTime = dwHoverTime;
7491 return oldHoverTime;
7496 * Sets spacing for icons of LVS_ICON style.
7499 * [I] HWND : window handle
7500 * [I] DWORD : MAKELONG(cx, cy)
7503 * MAKELONG(oldcx, oldcy)
7505 static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing)
7507 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7508 INT cy = HIWORD(spacing);
7509 INT cx = LOWORD(spacing);
7511 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7512 UINT uView = lStyle & LVS_TYPEMASK;
7514 oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7515 if (cx == -1) /* set to default */
7516 cx = GetSystemMetrics(SM_CXICONSPACING);
7517 if (cy == -1) /* set to default */
7518 cy = GetSystemMetrics(SM_CYICONSPACING);
7521 infoPtr->iconSpacing.cx = cx;
7523 { /* if 0 then compute width */
7524 if (uView == LVS_ICON)
7525 FIXME("width computation not yet done\n");
7527 * Should scan each item and determine max width of
7528 * icon or label, then make that the width
7530 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7531 infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd);
7534 infoPtr->iconSpacing.cy = cy;
7536 { /* if 0 then compute height */
7537 if (uView == LVS_ICON)
7538 infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + infoPtr->ntmHeight
7539 + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7540 /* FIXME. I don't think so; I think it is based on twice the ntmHeight */
7541 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7542 infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd);
7545 TRACE("old=(%d,%d), new=(%ld,%ld)\n", LOWORD(oldspacing), HIWORD(oldspacing),
7546 infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7548 /* these depend on the iconSpacing */
7549 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7550 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7560 * [I] HWND : window handle
7561 * [I] INT : image list type
7562 * [I] HIMAGELIST : image list handle
7565 * SUCCESS : old image list
7568 static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7570 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7571 HIMAGELIST himlOld = 0;
7577 himlOld = infoPtr->himlNormal;
7578 infoPtr->himlNormal = himl;
7582 himlOld = infoPtr->himlSmall;
7583 infoPtr->himlSmall = himl;
7587 himlOld = infoPtr->himlState;
7588 infoPtr->himlState = himl;
7589 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7593 oldHeight = infoPtr->nItemHeight;
7594 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7595 if (infoPtr->nItemHeight != oldHeight)
7596 LISTVIEW_UpdateScroll(hwnd);
7603 * Preallocates memory (does *not* set the actual count of items !)
7606 * [I] HWND : window handle
7607 * [I] INT : item count (projected number of items to allocate)
7608 * [I] DWORD : update flags
7614 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7616 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7618 TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
7620 if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7622 int precount,topvisible;
7624 TRACE("LVS_OWNERDATA is set!\n");
7625 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7626 FIXME("flags %s %s not implemented\n",
7627 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7629 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7632 * Internally remove all the selections.
7636 LISTVIEW_SELECTION *selection;
7637 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7639 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7642 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7644 precount = infoPtr->hdpaItems->nItemCount;
7645 topvisible = ListView_GetTopIndex(hwnd) +
7646 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7648 infoPtr->hdpaItems->nItemCount = nItems;
7650 infoPtr->nItemWidth = max(LISTVIEW_GetItemWidth(hwnd),
7651 DEFAULT_COLUMN_WIDTH);
7653 LISTVIEW_UpdateSize(hwnd);
7654 LISTVIEW_UpdateScroll(hwnd);
7656 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7657 InvalidateRect(hwnd, NULL, TRUE);
7661 /* According to MSDN for non-LVS_OWNERDATA this is just
7662 * a performance issue. The control allocates its internal
7663 * data structures for the number of items specified. It
7664 * cuts down on the number of memory allocations. Therefore
7665 * we will just issue a WARN here
7667 WARN("for non-ownerdata performance option not implemented.\n");
7675 * Sets the position of an item.
7678 * [I] HWND : window handle
7679 * [I] INT : item index
7680 * [I] LONG : x coordinate
7681 * [I] LONG : y coordinate
7687 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7688 LONG nPosX, LONG nPosY)
7690 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7691 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7692 UINT uView = lStyle & LVS_TYPEMASK;
7693 LISTVIEW_ITEM *lpItem;
7695 BOOL bResult = FALSE;
7697 TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7699 if (lStyle & LVS_OWNERDATA)
7702 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7704 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7706 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7708 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7712 orig = lpItem->ptPosition;
7713 if ((nPosX == -1) && (nPosY == -1))
7715 /* This point value seems to be an undocumented feature. The
7716 * best guess is that it means either at the origin, or at
7717 * the true beginning of the list. I will assume the origin.
7720 if (!LISTVIEW_GetOrigin(hwnd, &pt1))
7727 if (uView == LVS_ICON)
7729 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7730 nPosY += ICON_TOP_PADDING;
7732 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7736 lpItem->ptPosition.x = nPosX;
7737 lpItem->ptPosition.y = nPosY;
7738 if (uView == LVS_ICON)
7740 lpItem->ptPosition.y -= ICON_TOP_PADDING;
7741 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7742 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7744 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7745 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7748 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7749 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7754 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7755 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7768 * Sets the state of one or many items.
7771 * [I] HWND : window handle
7772 * [I]INT : item index
7773 * [I] LPLVITEM : item or subitem info
7779 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem)
7781 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7782 BOOL bResult = TRUE;
7785 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
7786 hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
7788 ZeroMemory(&lvItem, sizeof(lvItem));
7789 lvItem.mask = LVIF_STATE;
7790 lvItem.state = lpLVItem->state;
7791 lvItem.stateMask = lpLVItem->stateMask ;
7792 lvItem.iItem = nItem;
7796 /* apply to all items */
7797 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7798 if (!ListView_SetItemW(hwnd, &lvItem)) bResult = FALSE;
7801 bResult = ListView_SetItemW(hwnd, &lvItem);
7808 * Sets the text of an item or subitem.
7811 * [I] hwnd : window handle
7812 * [I] nItem : item index
7813 * [I] lpLVItem : item or subitem info
7814 * [I] isW : TRUE if input is Unicode
7820 static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7822 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7823 BOOL bResult = FALSE;
7826 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
7827 hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
7829 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7831 ZeroMemory(&lvItem, sizeof(LVITEMW));
7832 lvItem.mask = LVIF_TEXT;
7833 lvItem.pszText = lpLVItem->pszText;
7834 lvItem.iItem = nItem;
7835 lvItem.iSubItem = lpLVItem->iSubItem;
7836 if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
7837 else bResult = ListView_SetItemA(hwnd, &lvItem);
7845 * Set item index that marks the start of a multiple selection.
7848 * [I] HWND : window handle
7852 * Index number or -1 if there is no selection mark.
7854 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7856 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7857 INT nOldIndex = infoPtr->nSelectionMark;
7859 TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
7861 infoPtr->nSelectionMark = nIndex;
7868 * Sets the text background color.
7871 * [I] HWND : window handle
7872 * [I] COLORREF : text background color
7878 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7880 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7882 TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
7884 infoPtr->clrTextBk = clrTextBk;
7885 InvalidateRect(hwnd, NULL, TRUE);
7892 * Sets the text foreground color.
7895 * [I] HWND : window handle
7896 * [I] COLORREF : text color
7902 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7904 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7906 TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
7908 infoPtr->clrText = clrText;
7909 InvalidateRect(hwnd, NULL, TRUE);
7914 /* LISTVIEW_SetToolTips */
7915 /* LISTVIEW_SetUnicodeFormat */
7916 /* LISTVIEW_SetWorkAreas */
7920 * Callback internally used by LISTVIEW_SortItems()
7923 * [I] LPVOID : first LISTVIEW_ITEM to compare
7924 * [I] LPVOID : second LISTVIEW_ITEM to compare
7925 * [I] LPARAM : HWND of control
7928 * if first comes before second : negative
7929 * if first comes after second : positive
7930 * if first and second are equivalent : zero
7932 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7934 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7935 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
7936 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
7938 /* Forward the call to the client defined callback */
7939 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7944 * Sorts the listview items.
7947 * [I] HWND : window handle
7948 * [I] WPARAM : application-defined value
7949 * [I] LPARAM : pointer to comparision callback
7955 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7957 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7958 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7959 HDPA hdpaSubItems=NULL;
7960 LISTVIEW_ITEM *pLVItem=NULL;
7961 LPVOID selectionMarkItem;
7964 TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
7966 if (lStyle & LVS_OWNERDATA) return FALSE;
7968 if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
7970 nCount = GETITEMCOUNT(infoPtr);
7971 /* if there are 0 or 1 items, there is no need to sort */
7975 infoPtr->pfnCompare = pfnCompare;
7976 infoPtr->lParamSort = lParamSort;
7977 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7979 /* Adjust selections and indices so that they are the way they should
7980 * be after the sort (otherwise, the list items move around, but
7981 * whatever is at the item's previous original position will be
7984 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7985 for (i=0; i < nCount; i++)
7987 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7988 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7990 if (pLVItem->state & LVIS_SELECTED)
7991 LISTVIEW_AddSelectionRange(hwnd, i, i);
7993 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7994 if (pLVItem->state & LVIS_FOCUSED)
7995 infoPtr->nFocusedItem=i;
7997 if (selectionMarkItem != NULL)
7998 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7999 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8001 /* align the items */
8002 LISTVIEW_AlignTop(hwnd);
8004 /* refresh the display */
8005 InvalidateRect(hwnd, NULL, TRUE);
8010 /* LISTVIEW_SubItemHitTest */
8014 * Updates an items or rearranges the listview control.
8017 * [I] HWND : window handle
8018 * [I] INT : item index
8024 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
8026 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8027 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8028 BOOL bResult = FALSE;
8031 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
8033 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8037 /* rearrange with default alignment style */
8038 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
8039 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
8041 ListView_Arrange(hwnd, 0);
8045 /* get item bounding rectangle */
8046 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
8047 InvalidateRect(hwnd, &rc, TRUE);
8056 * Creates the listview control.
8059 * [I] HWND : window handle
8064 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
8066 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8067 UINT uView = lpcs->style & LVS_TYPEMASK;
8070 TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
8072 /* initialize info pointer */
8073 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
8075 /* determine the type of structures to use */
8076 infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT,
8077 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8079 /* initialize color information */
8080 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
8081 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
8082 infoPtr->clrTextBk = CLR_DEFAULT;
8084 /* set default values */
8085 infoPtr->hwndSelf = hwnd;
8086 infoPtr->uCallbackMask = 0;
8087 infoPtr->nFocusedItem = -1;
8088 infoPtr->nSelectionMark = -1;
8089 infoPtr->nHotItem = -1;
8090 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8091 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8092 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
8093 infoPtr->hwndEdit = 0;
8094 infoPtr->pedititem = NULL;
8095 infoPtr->nEditLabelItem = -1;
8096 infoPtr->bIsDrawing = FALSE;
8098 /* get default font (icon title) */
8099 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8100 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8101 infoPtr->hFont = infoPtr->hDefaultFont;
8102 LISTVIEW_SaveTextMetrics(hwnd);
8105 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
8106 WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
8107 0, 0, 0, 0, hwnd, (HMENU)0,
8108 lpcs->hInstance, NULL);
8110 /* set header unicode format */
8111 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
8113 /* set header font */
8114 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
8117 if (uView == LVS_ICON)
8119 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8120 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8122 else if (uView == LVS_REPORT)
8124 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8126 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8130 /* set HDS_HIDDEN flag to hide the header bar */
8131 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
8132 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
8136 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8137 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8141 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8142 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8145 /* display unsupported listview window styles */
8146 LISTVIEW_UnsupportedStyles(lpcs->style);
8148 /* allocate memory for the data structure */
8149 infoPtr->hdpaItems = DPA_Create(10);
8151 /* allocate memory for the selection ranges */
8152 infoPtr->hdpaSelectionRanges = DPA_Create(10);
8154 /* initialize size of items */
8155 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8156 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8158 /* initialize the hover time to -1(indicating the default system hover time) */
8159 infoPtr->dwHoverTime = -1;
8166 * Erases the background of the listview control.
8169 * [I] HWND : window handle
8170 * [I] WPARAM : device context handle
8171 * [I] LPARAM : not used
8177 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
8180 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8183 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8185 if (infoPtr->clrBk == CLR_NONE)
8187 bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
8192 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8193 GetClientRect(hwnd, &rc);
8194 FillRect((HDC)wParam, &rc, hBrush);
8195 DeleteObject(hBrush);
8203 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
8205 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8207 TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
8209 if (infoPtr->clrBk != CLR_NONE)
8211 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8212 FillRect(hdc, rc, hBrush);
8213 DeleteObject(hBrush);
8219 * Retrieves the listview control font.
8222 * [I] HWND : window handle
8227 static LRESULT LISTVIEW_GetFont(HWND hwnd)
8229 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8231 TRACE("(hwnd=%x)\n", hwnd);
8233 return infoPtr->hFont;
8238 * Performs vertical scrolling.
8241 * [I] HWND : window handle
8242 * [I] INT : scroll code
8243 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8245 * [I] HWND : scrollbar control window handle
8250 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8253 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8254 SCROLLINFO scrollInfo;
8256 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8257 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8259 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8261 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8262 scrollInfo.cbSize = sizeof(SCROLLINFO);
8263 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8265 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8267 INT nOldScrollPos = scrollInfo.nPos;
8268 switch (nScrollCode)
8271 if (scrollInfo.nPos > scrollInfo.nMin)
8276 if (scrollInfo.nPos < scrollInfo.nMax)
8281 if (scrollInfo.nPos > scrollInfo.nMin)
8283 if (scrollInfo.nPos >= scrollInfo.nPage)
8284 scrollInfo.nPos -= scrollInfo.nPage;
8286 scrollInfo.nPos = scrollInfo.nMin;
8291 if (scrollInfo.nPos < scrollInfo.nMax)
8293 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8294 scrollInfo.nPos += scrollInfo.nPage;
8296 scrollInfo.nPos = scrollInfo.nMax;
8300 case SB_THUMBPOSITION:
8302 scrollInfo.nPos = nCurrentPos;
8303 if (scrollInfo.nPos > scrollInfo.nMax)
8304 scrollInfo.nPos=scrollInfo.nMax;
8306 if (scrollInfo.nPos < scrollInfo.nMin)
8307 scrollInfo.nPos=scrollInfo.nMin;
8312 if (nOldScrollPos != scrollInfo.nPos)
8314 scrollInfo.fMask = SIF_POS;
8315 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
8316 if (IsWindowVisible(infoPtr->hwndHeader))
8318 RECT rListview, rcHeader, rDest;
8319 GetClientRect(hwnd, &rListview);
8320 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
8321 MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2);
8322 SubtractRect(&rDest, &rListview, &rcHeader);
8323 InvalidateRect(hwnd, &rDest, TRUE);
8326 InvalidateRect(hwnd, NULL, TRUE);
8335 * Performs horizontal scrolling.
8338 * [I] HWND : window handle
8339 * [I] INT : scroll code
8340 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8342 * [I] HWND : scrollbar control window handle
8347 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8350 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8351 SCROLLINFO scrollInfo;
8353 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8354 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8356 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8358 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8359 scrollInfo.cbSize = sizeof(SCROLLINFO);
8360 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8362 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
8364 INT nOldScrollPos = scrollInfo.nPos;
8366 switch (nScrollCode)
8369 if (scrollInfo.nPos > scrollInfo.nMin)
8374 if (scrollInfo.nPos < scrollInfo.nMax)
8379 if (scrollInfo.nPos > scrollInfo.nMin)
8381 if (scrollInfo.nPos >= scrollInfo.nPage)
8382 scrollInfo.nPos -= scrollInfo.nPage;
8384 scrollInfo.nPos = scrollInfo.nMin;
8389 if (scrollInfo.nPos < scrollInfo.nMax)
8391 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8392 scrollInfo.nPos += scrollInfo.nPage;
8394 scrollInfo.nPos = scrollInfo.nMax;
8398 case SB_THUMBPOSITION:
8400 scrollInfo.nPos = nCurrentPos;
8402 if (scrollInfo.nPos > scrollInfo.nMax)
8403 scrollInfo.nPos=scrollInfo.nMax;
8405 if (scrollInfo.nPos < scrollInfo.nMin)
8406 scrollInfo.nPos=scrollInfo.nMin;
8410 if (nOldScrollPos != scrollInfo.nPos)
8412 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8413 scrollInfo.fMask = SIF_POS;
8414 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
8415 if(uView == LVS_REPORT)
8417 scrollInfo.fMask = SIF_POS;
8418 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
8419 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
8421 InvalidateRect(hwnd, NULL, TRUE);
8428 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
8430 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8431 INT gcWheelDelta = 0;
8432 UINT pulScrollLines = 3;
8433 SCROLLINFO scrollInfo;
8435 TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
8437 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8438 gcWheelDelta -= wheelDelta;
8440 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8441 scrollInfo.cbSize = sizeof(SCROLLINFO);
8442 scrollInfo.fMask = SIF_POS | SIF_RANGE;
8449 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8450 * should be fixed in the future.
8452 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8453 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
8457 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8459 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8461 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
8462 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8463 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8469 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8480 * [I] HWND : window handle
8481 * [I] INT : virtual key
8482 * [I] LONG : key data
8487 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
8489 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8490 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8492 NMLVKEYDOWN nmKeyDown;
8494 TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
8496 /* send LVN_KEYDOWN notification */
8497 nmKeyDown.wVKey = nVirtualKey;
8498 nmKeyDown.flags = 0;
8499 notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
8501 switch (nVirtualKey)
8504 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8506 hdr_notify(hwnd, NM_RETURN); /* NM_RETURN notification */
8507 hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
8512 if (GETITEMCOUNT(infoPtr) > 0)
8517 if (GETITEMCOUNT(infoPtr) > 0)
8518 nItem = GETITEMCOUNT(infoPtr) - 1;
8522 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8526 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8530 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8534 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8538 if (uView == LVS_REPORT)
8539 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8541 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8542 * LISTVIEW_GetCountPerRow(hwnd);
8543 if(nItem < 0) nItem = 0;
8547 if (uView == LVS_REPORT)
8548 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8550 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8551 * LISTVIEW_GetCountPerRow(hwnd);
8552 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8556 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8558 if (LISTVIEW_KeySelection(hwnd, nItem))
8559 UpdateWindow(hwnd); /* update client area */
8570 * [I] HWND : window handle
8575 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8577 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
8578 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8581 TRACE("(hwnd=%x)\n", hwnd);
8583 /* send NM_KILLFOCUS notification */
8584 hdr_notify(hwnd, NM_KILLFOCUS);
8586 /* set window focus flag */
8587 infoPtr->bFocus = FALSE;
8589 /* NEED drawing optimization ; redraw the selected items */
8590 if (uView & LVS_REPORT)
8592 nTop = LISTVIEW_GetTopIndex(hwnd);
8594 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8599 nBottom = GETITEMCOUNT(infoPtr);
8601 for (i = nTop; i<nBottom; i++)
8603 if (LISTVIEW_IsSelected(hwnd,i))
8606 rcItem.left = LVIR_BOUNDS;
8607 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8608 InvalidateRect(hwnd, &rcItem, FALSE);
8617 * Processes double click messages (left mouse button).
8620 * [I] HWND : window handle
8621 * [I] WORD : key flag
8622 * [I] WORD : x coordinate
8623 * [I] WORD : y coordinate
8628 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8631 LVHITTESTINFO htInfo;
8634 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8636 htInfo.pt.x = wPosX;
8637 htInfo.pt.y = wPosY;
8639 /* send NM_DBLCLK notification */
8640 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8641 if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -1)
8643 nmlv.iItem = htInfo.iItem;
8644 nmlv.iSubItem = htInfo.iSubItem;
8651 nmlv.ptAction.x = wPosX;
8652 nmlv.ptAction.y = wPosY;
8653 listview_notify(hwnd, NM_DBLCLK, &nmlv);
8656 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8657 if(nmlv.iItem != -1)
8658 hdr_notify(hwnd, LVN_ITEMACTIVATE);
8665 * Processes mouse down messages (left mouse button).
8668 * [I] HWND : window handle
8669 * [I] WORD : key flag
8670 * [I] WORD : x coordinate
8671 * [I] WORD : y coordinate
8676 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8679 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8680 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8681 static BOOL bGroupSelect = TRUE;
8685 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8687 /* send NM_RELEASEDCAPTURE notification */
8688 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8690 if (infoPtr->bFocus == FALSE)
8693 /* set left button down flag */
8694 infoPtr->bLButtonDown = TRUE;
8696 ptPosition.x = wPosX;
8697 ptPosition.y = wPosY;
8698 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8699 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8701 if (lStyle & LVS_SINGLESEL)
8703 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8704 && infoPtr->nEditLabelItem == -1)
8705 infoPtr->nEditLabelItem = nItem;
8707 LISTVIEW_SetSelection(hwnd, nItem);
8711 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8714 LISTVIEW_AddGroupSelection(hwnd, nItem);
8716 LISTVIEW_AddSelection(hwnd, nItem);
8718 else if (wKey & MK_CONTROL)
8720 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8722 else if (wKey & MK_SHIFT)
8724 LISTVIEW_SetGroupSelection(hwnd, nItem);
8729 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8731 /* set selection (clears other pre-existing selections) */
8732 LISTVIEW_SetSelection(hwnd, nItem);
8734 if (was_selected && infoPtr->nEditLabelItem == -1)
8735 infoPtr->nEditLabelItem = nItem;
8741 /* remove all selections */
8742 LISTVIEW_RemoveAllSelections(hwnd);
8745 /* redraw if we could have possibly selected something */
8746 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8753 * Processes mouse up messages (left mouse button).
8756 * [I] HWND : window handle
8757 * [I] WORD : key flag
8758 * [I] WORD : x coordinate
8759 * [I] WORD : y coordinate
8764 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8767 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8769 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8771 if (infoPtr->bLButtonDown != FALSE)
8773 LVHITTESTINFO lvHitTestInfo;
8776 lvHitTestInfo.pt.x = wPosX;
8777 lvHitTestInfo.pt.y = wPosY;
8779 /* send NM_CLICK notification */
8780 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8781 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8783 nmlv.iItem = lvHitTestInfo.iItem;
8784 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8791 nmlv.ptAction.x = wPosX;
8792 nmlv.ptAction.y = wPosY;
8793 listview_notify(hwnd, NM_CLICK, &nmlv);
8795 /* set left button flag */
8796 infoPtr->bLButtonDown = FALSE;
8798 if(infoPtr->nEditLabelItem != -1)
8800 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL)
8801 LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
8802 infoPtr->nEditLabelItem = -1;
8811 * Creates the listview control (called before WM_CREATE).
8814 * [I] HWND : window handle
8815 * [I] WPARAM : unhandled
8816 * [I] LPARAM : widow creation info
8821 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8823 LISTVIEW_INFO *infoPtr;
8825 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8827 /* allocate memory for info structure */
8828 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8829 if (infoPtr == NULL)
8831 ERR("could not allocate info memory!\n");
8835 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
8836 if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr)
8838 ERR("pointer assignment error!\n");
8842 return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam);
8847 * Destroys the listview control (called after WM_DESTROY).
8850 * [I] HWND : window handle
8855 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8857 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8859 TRACE("(hwnd=%x)\n", hwnd);
8861 /* delete all items */
8862 LISTVIEW_DeleteAllItems(hwnd);
8864 /* destroy data structure */
8865 DPA_Destroy(infoPtr->hdpaItems);
8866 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8869 infoPtr->hFont = (HFONT)0;
8870 if (infoPtr->hDefaultFont)
8872 DeleteObject(infoPtr->hDefaultFont);
8875 /* free listview info pointer*/
8876 COMCTL32_Free(infoPtr);
8878 SetWindowLongW(hwnd, 0, 0);
8884 * Handles notifications from children.
8887 * [I] HWND : window handle
8888 * [I] INT : control identifier
8889 * [I] LPNMHDR : notification information
8894 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8896 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8898 TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
8900 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8902 /* handle notification from header control */
8903 if (lpnmh->code == HDN_ENDTRACKW)
8905 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8906 InvalidateRect(hwnd, NULL, TRUE);
8908 else if(lpnmh->code == HDN_ITEMCLICKW || lpnmh->code == HDN_ITEMCLICKA)
8910 /* Handle sorting by Header Column */
8913 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8915 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
8916 listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv);
8918 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8920 /* Idealy this should be done in HDN_ENDTRACKA
8921 * but since SetItemBounds in Header.c is called after
8922 * the notification is sent, it is neccessary to handle the
8923 * update of the scroll bar here (Header.c works fine as it is,
8924 * no need to disturb it)
8926 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8927 LISTVIEW_UpdateScroll(hwnd);
8928 InvalidateRect(hwnd, NULL, TRUE);
8938 * Determines the type of structure to use.
8941 * [I] HWND : window handle of the sender
8942 * [I] HWND : listview window handle
8943 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8948 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8950 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8952 TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
8954 if (nCommand == NF_REQUERY)
8955 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
8956 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8962 * Paints/Repaints the listview control.
8965 * [I] HWND : window handle
8966 * [I] HDC : device context handle
8971 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8975 TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc);
8979 hdc = BeginPaint(hwnd, &ps);
8980 LISTVIEW_Refresh(hwnd, hdc);
8981 EndPaint(hwnd, &ps);
8985 LISTVIEW_Refresh(hwnd, hdc);
8993 * Processes double click messages (right mouse button).
8996 * [I] HWND : window handle
8997 * [I] WORD : key flag
8998 * [I] WORD : x coordinate
8999 * [I] WORD : y coordinate
9004 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
9007 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9009 /* send NM_RELEASEDCAPTURE notification */
9010 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
9012 /* send NM_RDBLCLK notification */
9013 hdr_notify(hwnd, NM_RDBLCLK);
9020 * Processes mouse down messages (right mouse button).
9023 * [I] HWND : window handle
9024 * [I] WORD : key flag
9025 * [I] WORD : x coordinate
9026 * [I] WORD : y coordinate
9031 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
9034 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9038 LVHITTESTINFO lvHitTestInfo;
9040 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9042 /* send NM_RELEASEDCAPTURE notification */
9043 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
9045 /* make sure the listview control window has the focus */
9046 if (infoPtr->bFocus == FALSE)
9049 /* set right button down flag */
9050 infoPtr->bRButtonDown = TRUE;
9052 /* determine the index of the selected item */
9053 ptPosition.x = wPosX;
9054 ptPosition.y = wPosY;
9055 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
9056 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
9058 LISTVIEW_SetItemFocus(hwnd,nItem);
9059 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9060 !LISTVIEW_IsSelected(hwnd,nItem))
9061 LISTVIEW_SetSelection(hwnd, nItem);
9065 LISTVIEW_RemoveAllSelections(hwnd);
9068 lvHitTestInfo.pt.x = wPosX;
9069 lvHitTestInfo.pt.y = wPosY;
9071 /* Send NM_RClICK notification */
9072 ZeroMemory(&nmlv, sizeof(nmlv));
9073 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
9075 nmlv.iItem = lvHitTestInfo.iItem;
9076 nmlv.iSubItem = lvHitTestInfo.iSubItem;
9083 nmlv.ptAction.x = wPosX;
9084 nmlv.ptAction.y = wPosY;
9085 listview_notify(hwnd, NM_RCLICK, &nmlv);
9092 * Processes mouse up messages (right mouse button).
9095 * [I] HWND : window handle
9096 * [I] WORD : key flag
9097 * [I] WORD : x coordinate
9098 * [I] WORD : y coordinate
9103 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
9106 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9108 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9110 if (infoPtr->bRButtonDown)
9117 /* set button flag */
9118 infoPtr->bRButtonDown = FALSE;
9120 /* Change to screen coordinate for WM_CONTEXTMENU */
9121 ClientToScreen(hwnd, &pt);
9123 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9124 SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
9135 * [I] HWND : window handle
9136 * [I] HWND : window handle of previously focused window
9141 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
9143 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9145 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
9147 /* send NM_SETFOCUS notification */
9148 hdr_notify(hwnd, NM_SETFOCUS);
9150 /* set window focus flag */
9151 infoPtr->bFocus = TRUE;
9163 * [I] HWND : window handle
9164 * [I] HFONT : font handle
9165 * [I] WORD : redraw flag
9170 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
9172 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9173 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
9175 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
9177 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9178 LISTVIEW_SaveTextMetrics(hwnd);
9180 if (uView == LVS_REPORT)
9182 /* set header font */
9183 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
9184 MAKELPARAM(fRedraw, 0));
9187 /* invalidate listview control client area */
9188 InvalidateRect(hwnd, NULL, TRUE);
9190 if (fRedraw != FALSE)
9198 * Message handling for WM_SETREDRAW.
9199 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9202 * [I] HWND : window handle
9203 * [I] bRedraw: state of redraw flag
9206 * DefWinProc return value
9208 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
9210 LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0);
9212 RedrawWindow(hwnd, NULL, 0,
9213 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
9219 * Resizes the listview control. This function processes WM_SIZE
9220 * messages. At this time, the width and height are not used.
9223 * [I] HWND : window handle
9224 * [I] WORD : new width
9225 * [I] WORD : new height
9230 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
9232 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9233 UINT uView = lStyle & LVS_TYPEMASK;
9235 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
9237 if (LISTVIEW_UpdateSize(hwnd))
9239 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
9241 if (lStyle & LVS_ALIGNLEFT)
9242 LISTVIEW_AlignLeft(hwnd);
9244 LISTVIEW_AlignTop(hwnd);
9247 LISTVIEW_UpdateScroll(hwnd);
9249 /* invalidate client area + erase background */
9250 InvalidateRect(hwnd, NULL, TRUE);
9258 * Sets the size information.
9261 * [I] HWND : window handle
9264 * Zero if no size change
9267 static BOOL LISTVIEW_UpdateSize(HWND hwnd)
9269 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9270 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9271 UINT uView = lStyle & LVS_TYPEMASK;
9275 TRACE("(hwnd=%x)\n", hwnd);
9277 GetClientRect(hwnd, &rcList);
9278 CopyRect(&rcOld,&(infoPtr->rcList));
9279 infoPtr->rcList.left = 0;
9280 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
9281 infoPtr->rcList.top = 0;
9282 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
9284 if (uView == LVS_LIST)
9286 if (lStyle & WS_HSCROLL)
9288 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
9289 if (infoPtr->rcList.bottom > nHScrollHeight)
9290 infoPtr->rcList.bottom -= nHScrollHeight;
9293 else if (uView == LVS_REPORT)
9300 Header_Layout(infoPtr->hwndHeader, &hl);
9302 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9304 if (!(LVS_NOCOLUMNHEADER & lStyle))
9305 infoPtr->rcList.top = max(wp.cy, 0);
9307 return (EqualRect(&rcOld,&(infoPtr->rcList)));
9312 * Processes WM_STYLECHANGED messages.
9315 * [I] HWND : window handle
9316 * [I] WPARAM : window style type (normal or extended)
9317 * [I] LPSTYLESTRUCT : window style information
9322 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
9325 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9326 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9327 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9328 RECT rcList = infoPtr->rcList;
9330 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
9331 hwnd, wStyleType, lpss);
9333 if (wStyleType == GWL_STYLE)
9335 if (uOldView == LVS_REPORT)
9336 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9338 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9339 ((lpss->styleNew & WS_HSCROLL) == 0))
9340 ShowScrollBar(hwnd, SB_HORZ, FALSE);
9342 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9343 ((lpss->styleNew & WS_VSCROLL) == 0))
9344 ShowScrollBar(hwnd, SB_VERT, FALSE);
9346 if (uNewView == LVS_ICON)
9348 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9349 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9350 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9351 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9352 if (lpss->styleNew & LVS_ALIGNLEFT)
9353 LISTVIEW_AlignLeft(hwnd);
9355 LISTVIEW_AlignTop(hwnd);
9357 else if (uNewView == LVS_REPORT)
9364 Header_Layout(infoPtr->hwndHeader, &hl);
9365 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
9367 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9368 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9370 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9371 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9372 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9373 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9375 else if (uNewView == LVS_LIST)
9377 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9378 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9379 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9380 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9384 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9385 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9386 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9387 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9388 if (lpss->styleNew & LVS_ALIGNLEFT)
9389 LISTVIEW_AlignLeft(hwnd);
9391 LISTVIEW_AlignTop(hwnd);
9394 /* update the size of the client area */
9395 LISTVIEW_UpdateSize(hwnd);
9397 /* add scrollbars if needed */
9398 LISTVIEW_UpdateScroll(hwnd);
9400 /* invalidate client area + erase background */
9401 InvalidateRect(hwnd, NULL, TRUE);
9403 /* print the list of unsupported window styles */
9404 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9407 /* If they change the view and we have an active edit control
9408 we will need to kill the control since the redraw will
9409 misplace the edit control.
9411 if (infoPtr->hwndEdit &&
9412 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9413 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9415 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9423 * Window procedure of the listview control.
9426 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9429 TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
9430 if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
9431 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9434 case LVM_APPROXIMATEVIEWRECT:
9435 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9436 LOWORD(lParam), HIWORD(lParam));
9438 return LISTVIEW_Arrange(hwnd, (INT)wParam);
9440 /* case LVM_CREATEDRAGIMAGE: */
9442 case LVM_DELETEALLITEMS:
9443 return LISTVIEW_DeleteAllItems(hwnd);
9445 case LVM_DELETECOLUMN:
9446 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9448 case LVM_DELETEITEM:
9449 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9451 case LVM_EDITLABELW:
9452 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
9454 case LVM_EDITLABELA:
9455 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
9457 case LVM_ENSUREVISIBLE:
9458 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9461 return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
9464 return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
9466 case LVM_GETBKCOLOR:
9467 return LISTVIEW_GetBkColor(hwnd);
9469 /* case LVM_GETBKIMAGE: */
9471 case LVM_GETCALLBACKMASK:
9472 return LISTVIEW_GetCallbackMask(hwnd);
9474 case LVM_GETCOLUMNA:
9475 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9477 case LVM_GETCOLUMNW:
9478 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9480 case LVM_GETCOLUMNORDERARRAY:
9481 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9483 case LVM_GETCOLUMNWIDTH:
9484 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9486 case LVM_GETCOUNTPERPAGE:
9487 return LISTVIEW_GetCountPerPage(hwnd);
9489 case LVM_GETEDITCONTROL:
9490 return LISTVIEW_GetEditControl(hwnd);
9492 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9493 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9496 return LISTVIEW_GetHeader(hwnd);
9498 case LVM_GETHOTCURSOR:
9499 FIXME("LVM_GETHOTCURSOR: unimplemented\n");
9502 case LVM_GETHOTITEM:
9503 return LISTVIEW_GetHotItem(hwnd);
9505 case LVM_GETHOVERTIME:
9506 return LISTVIEW_GetHoverTime(hwnd);
9508 case LVM_GETIMAGELIST:
9509 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9511 case LVM_GETISEARCHSTRINGA:
9512 case LVM_GETISEARCHSTRINGW:
9513 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9517 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
9520 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
9522 case LVM_GETITEMCOUNT:
9523 return LISTVIEW_GetItemCount(hwnd);
9525 case LVM_GETITEMPOSITION:
9526 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9528 case LVM_GETITEMRECT:
9529 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9531 case LVM_GETITEMSPACING:
9532 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9534 case LVM_GETITEMSTATE:
9535 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9537 case LVM_GETITEMTEXTA:
9538 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9540 case LVM_GETITEMTEXTW:
9541 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9543 case LVM_GETNEXTITEM:
9544 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9546 case LVM_GETNUMBEROFWORKAREAS:
9547 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9551 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9553 case LVM_GETSELECTEDCOUNT:
9554 return LISTVIEW_GetSelectedCount(hwnd);
9556 case LVM_GETSELECTIONMARK:
9557 return LISTVIEW_GetSelectionMark(hwnd);
9559 case LVM_GETSTRINGWIDTHA:
9560 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
9562 case LVM_GETSTRINGWIDTHW:
9563 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
9565 case LVM_GETSUBITEMRECT:
9566 return LISTVIEW_GetSubItemRect(hwnd, (UINT)wParam, ((LPRECT)lParam)->top,
9567 ((LPRECT)lParam)->left, (LPRECT)lParam);
9569 case LVM_GETTEXTBKCOLOR:
9570 return LISTVIEW_GetTextBkColor(hwnd);
9572 case LVM_GETTEXTCOLOR:
9573 return LISTVIEW_GetTextColor(hwnd);
9575 case LVM_GETTOOLTIPS:
9576 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9579 case LVM_GETTOPINDEX:
9580 return LISTVIEW_GetTopIndex(hwnd);
9582 /*case LVM_GETUNICODEFORMAT:
9583 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9586 case LVM_GETVIEWRECT:
9587 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9589 case LVM_GETWORKAREAS:
9590 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9594 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9596 case LVM_INSERTCOLUMNA:
9597 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9599 case LVM_INSERTCOLUMNW:
9600 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9602 case LVM_INSERTITEMA:
9603 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9605 case LVM_INSERTITEMW:
9606 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9608 case LVM_REDRAWITEMS:
9609 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9611 /* case LVM_SCROLL: */
9612 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9614 case LVM_SETBKCOLOR:
9615 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9617 /* case LVM_SETBKIMAGE: */
9619 case LVM_SETCALLBACKMASK:
9620 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9622 case LVM_SETCOLUMNA:
9623 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9625 case LVM_SETCOLUMNW:
9626 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9628 case LVM_SETCOLUMNORDERARRAY:
9629 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9631 case LVM_SETCOLUMNWIDTH:
9632 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9634 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9635 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9637 /* case LVM_SETHOTCURSOR: */
9639 case LVM_SETHOTITEM:
9640 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9642 case LVM_SETHOVERTIME:
9643 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9645 case LVM_SETICONSPACING:
9646 return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam);
9648 case LVM_SETIMAGELIST:
9649 return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9652 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9655 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9657 case LVM_SETITEMCOUNT:
9658 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9660 case LVM_SETITEMPOSITION:
9661 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9662 (INT)HIWORD(lParam));
9664 case LVM_SETITEMPOSITION32:
9665 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9666 ((POINT*)lParam)->y);
9668 case LVM_SETITEMSTATE:
9669 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam);
9671 case LVM_SETITEMTEXTA:
9672 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9674 case LVM_SETITEMTEXTW:
9675 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9677 case LVM_SETSELECTIONMARK:
9678 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9680 case LVM_SETTEXTBKCOLOR:
9681 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9683 case LVM_SETTEXTCOLOR:
9684 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9686 /* case LVM_SETTOOLTIPS: */
9687 /* case LVM_SETUNICODEFORMAT: */
9688 /* case LVM_SETWORKAREAS: */
9691 return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9693 case LVM_SUBITEMHITTEST:
9694 return LISTVIEW_SubItemHitTest(hwnd, (LPLVHITTESTINFO)lParam);
9697 return LISTVIEW_Update(hwnd, (INT)wParam);
9700 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9703 return LISTVIEW_Command(hwnd, wParam, lParam);
9706 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9709 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9712 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9715 return LISTVIEW_GetFont(hwnd);
9718 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
9719 (INT)HIWORD(wParam), (HWND)lParam);
9722 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9725 return LISTVIEW_KillFocus(hwnd);
9727 case WM_LBUTTONDBLCLK:
9728 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9731 case WM_LBUTTONDOWN:
9732 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9735 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9738 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9741 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9744 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9747 return LISTVIEW_NCDestroy(hwnd);
9750 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9752 case WM_NOTIFYFORMAT:
9753 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9756 return LISTVIEW_Paint(hwnd, (HDC)wParam);
9758 case WM_RBUTTONDBLCLK:
9759 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9762 case WM_RBUTTONDOWN:
9763 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9767 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9771 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9774 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9777 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9780 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9782 case WM_STYLECHANGED:
9783 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9785 /* case WM_TIMER: */
9788 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
9789 (INT)HIWORD(wParam), (HWND)lParam);
9792 if (wParam & (MK_SHIFT | MK_CONTROL))
9793 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9794 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
9796 /* case WM_WININICHANGE: */
9799 if (uMsg >= WM_USER)
9801 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
9805 /* call default window procedure */
9806 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9814 * Registers the window class.
9822 VOID LISTVIEW_Register(void)
9826 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9827 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9828 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9829 wndClass.cbClsExtra = 0;
9830 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9831 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
9832 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9833 wndClass.lpszClassName = WC_LISTVIEWW;
9834 RegisterClassW(&wndClass);
9839 * Unregisters the window class.
9847 VOID LISTVIEW_Unregister(void)
9849 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
9854 * Handle any WM_COMMAND messages
9860 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9862 switch (HIWORD(wParam))
9867 * Adjust the edit window size
9870 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9871 HDC hdc = GetDC(infoPtr->hwndEdit);
9872 HFONT hFont, hOldFont = 0;
9877 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9878 GetWindowRect(infoPtr->hwndEdit, &rect);
9880 /* Select font to get the right dimension of the string */
9881 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9884 hOldFont = SelectObject(hdc, hFont);
9887 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9889 TEXTMETRICW textMetric;
9891 /* Add Extra spacing for the next character */
9892 GetTextMetricsW(hdc, &textMetric);
9893 sz.cx += (textMetric.tmMaxCharWidth * 2);
9901 rect.bottom - rect.top,
9902 SWP_DRAWFRAME|SWP_NOMOVE);
9905 SelectObject(hdc, hOldFont);
9907 ReleaseDC(hwnd, hdc);
9913 return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9922 * Subclassed edit control windproc function
9928 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
9929 WPARAM wParam, LPARAM lParam, BOOL isW)
9931 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9932 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9933 static BOOL bIgnoreKillFocus = FALSE;
9934 BOOL cancel = FALSE;
9936 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9937 hwnd, uMsg, wParam, lParam, isW);
9942 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9945 if(bIgnoreKillFocus) return TRUE;
9950 WNDPROC editProc = einfo->EditWndProc;
9951 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9952 COMCTL32_Free(einfo);
9953 infoPtr->pedititem = NULL;
9954 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9958 if (VK_ESCAPE == (INT)wParam)
9963 else if (VK_RETURN == (INT)wParam)
9967 return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9970 if (einfo->EditLblCb)
9972 LPWSTR buffer = NULL;
9976 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9980 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9982 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9983 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9987 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9988 /* eg. Using a messagebox */
9989 bIgnoreKillFocus = TRUE;
9990 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9992 if (buffer) COMCTL32_Free(buffer);
9994 einfo->EditLblCb = NULL;
9995 bIgnoreKillFocus = FALSE;
9998 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10004 * Subclassed edit control windproc function
10010 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10012 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10017 * Subclassed edit control windproc function
10023 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10025 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10030 * Creates a subclassed edit cotrol
10036 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
10037 INT width, INT height, HWND parent, HINSTANCE hinst,
10038 EditlblCallbackW EditLblCb, DWORD param, BOOL isW)
10040 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
10041 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10046 TEXTMETRICW textMetric;
10048 TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
10050 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
10053 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
10054 hdc = GetDC(parent);
10056 /* Select the font to get appropriate metric dimensions */
10057 if(infoPtr->hFont != 0)
10058 hOldFont = SelectObject(hdc, infoPtr->hFont);
10060 /*Get String Lenght in pixels */
10061 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10063 /*Add Extra spacing for the next character */
10064 GetTextMetricsW(hdc, &textMetric);
10065 sz.cx += (textMetric.tmMaxCharWidth * 2);
10067 if(infoPtr->hFont != 0)
10068 SelectObject(hdc, hOldFont);
10070 ReleaseDC(parent, hdc);
10072 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
10074 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
10078 COMCTL32_Free(infoPtr->pedititem);
10082 infoPtr->pedititem->param = param;
10083 infoPtr->pedititem->EditLblCb = EditLblCb;
10084 infoPtr->pedititem->EditWndProc = (WNDPROC)
10085 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
10086 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
10088 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);