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
11 * Listview control implementation.
14 * 1. No horizontal scrolling when header is larger than the client area.
15 * 2. Drawing optimizations.
16 * 3. Hot item handling.
19 * LISTVIEW_Notify : most notifications from children (editbox and header)
22 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
24 * Advanced functionality:
25 * LISTVIEW_GetNumberOfWorkAreas : not implemented
26 * LISTVIEW_GetHotCursor : not implemented
27 * LISTVIEW_GetISearchString : not implemented
28 * LISTVIEW_GetBkImage : not implemented
29 * LISTVIEW_SetBkImage : not implemented
30 * LISTVIEW_GetColumnOrderArray : simple hack only
31 * LISTVIEW_SetColumnOrderArray : simple hack only
32 * LISTVIEW_Arrange : empty stub
33 * LISTVIEW_ApproximateViewRect : incomplete
34 * LISTVIEW_Scroll : not implemented
35 * LISTVIEW_Update : not completed
37 * Known differences in message stream from native control (not known if
38 * these differences cause problems):
39 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
40 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
41 * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
42 * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
43 * does *not* invoke DefWindowProc
44 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
45 * processing for "USEDOUBLECLICKTIME".
57 #include "debugtools.h"
59 DEFAULT_DEBUG_CHANNEL(listview);
61 /* Some definitions for inline edit control */
62 typedef BOOL (*EditlblCallbackW)(HWND, LPWSTR, DWORD);
63 typedef BOOL (*EditlblCallbackA)(HWND, LPWSTR, DWORD);
65 typedef struct tagLV_INTHIT
68 DWORD distance; /* distance to closest item */
69 INT iDistItem; /* item number that is closest */
70 } LV_INTHIT, *LPLV_INTHIT;
73 typedef struct tagEDITLABEL_ITEM
77 EditlblCallbackW EditLblCb;
80 typedef struct tagLISTVIEW_SUBITEM
87 typedef struct tagLISTVIEW_ITEM
98 typedef struct tagLISTVIEW_SELECTION
102 } LISTVIEW_SELECTION;
104 typedef struct tagLISTVIEW_INFO
110 HIMAGELIST himlNormal;
111 HIMAGELIST himlSmall;
112 HIMAGELIST himlState;
116 HDPA hdpaSelectionRanges;
130 INT ntmHeight; /* from GetTextMetrics from above font */
131 INT ntmAveCharWidth; /* from GetTextMetrics from above font */
133 DWORD dwExStyle; /* extended listview style */
135 PFNLVCOMPARE pfnCompare;
139 EDITLABEL_ITEM *pedititem;
141 INT nColumnCount; /* the number of columns in this control */
143 DWORD lastKeyPressTimestamp; /* Added */
144 WPARAM charCode; /* Added */
145 INT nSearchParamLength; /* Added */
146 WCHAR szSearchParam[ MAX_PATH ]; /* Added */
153 /* maximum size of a label */
154 #define DISP_TEXT_SIZE 512
156 /* padding for items in list and small icon display modes */
157 #define WIDTH_PADDING 12
159 /* padding for items in list, report and small icon display modes */
160 #define HEIGHT_PADDING 1
162 /* offset of items in report display mode */
163 #define REPORT_MARGINX 2
165 /* padding for icon in large icon display mode
166 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
167 * that HITTEST will see.
168 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
169 * ICON_TOP_PADDING - sum of the two above.
170 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
171 * LABEL_VERT_OFFSET - between bottom of text and end of box
173 #define ICON_TOP_PADDING_NOTHITABLE 2
174 #define ICON_TOP_PADDING_HITABLE 2
175 #define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE
176 #define ICON_BOTTOM_PADDING 4
177 #define LABEL_VERT_OFFSET 10
179 /* default label width for items in list and small icon display modes */
180 #define DEFAULT_LABEL_WIDTH 40
182 /* default column width for items in list display mode */
183 #define DEFAULT_COLUMN_WIDTH 96
185 /* Increment size of the horizontal scroll bar */
186 #define LISTVIEW_SCROLL_DIV_SIZE 10
188 /* Padding betwen image and label */
189 #define IMAGE_PADDING 2
191 /* Padding behind the label */
192 #define TRAILING_PADDING 5
194 /* Border for the icon caption */
195 #define CAPTION_BORDER 2
199 /* retrieve the number of items in the listview */
200 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
201 #define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA )
203 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
204 INT width, INT height, HWND parent, HINSTANCE hinst,
205 EditlblCallbackW EditLblCb, DWORD param, BOOL isW);
208 * forward declarations
210 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW);
211 static INT LISTVIEW_SuperHitTestItem(HWND, LPLV_INTHIT, BOOL);
212 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL);
213 static INT LISTVIEW_GetCountPerRow(HWND);
214 static INT LISTVIEW_GetCountPerColumn(HWND);
215 static VOID LISTVIEW_AlignLeft(HWND);
216 static VOID LISTVIEW_AlignTop(HWND);
217 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
218 static VOID LISTVIEW_AddSelection(HWND, INT);
219 static BOOL LISTVIEW_AddSubItemT(HWND, LPLVITEMW, BOOL);
220 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
221 static INT LISTVIEW_GetItemHeight(HWND);
222 static BOOL LISTVIEW_GetItemBoundBox(HWND, INT, LPRECT);
223 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
224 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
225 static INT LISTVIEW_GetItemWidth(HWND);
226 static INT LISTVIEW_GetLabelWidth(HWND, INT);
227 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
228 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
229 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
230 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
231 static BOOL LISTVIEW_InitItemT(HWND, LISTVIEW_ITEM *, LPLVITEMW, BOOL);
232 static BOOL LISTVIEW_InitSubItemT(HWND, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL);
233 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
234 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
235 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
236 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
237 static BOOL LISTVIEW_SetItemT(HWND, LPLVITEMW, BOOL);
238 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
239 static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG);
240 static VOID LISTVIEW_UpdateScroll(HWND);
241 static VOID LISTVIEW_SetSelection(HWND, INT);
242 static VOID LISTVIEW_UpdateSize(HWND);
243 static BOOL LISTVIEW_SetSubItemT(HWND, LPLVITEMW, BOOL);
244 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
245 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
246 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
247 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW);
248 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem);
249 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem);
250 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
251 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
252 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW);
253 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
254 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
255 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
256 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem);
257 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem);
258 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
259 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
260 static void ListView_UpdateLargeItemLabelRect (HWND hwnd, const LISTVIEW_INFO* infoPtr, int nItem, RECT *rect);
262 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
263 #define KEY_DELAY 450
265 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
267 static inline BOOL is_textW(LPCWSTR text)
269 return text != NULL && text != LPSTR_TEXTCALLBACKW;
272 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
274 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
275 return is_textW(text);
278 static inline int textlenT(LPCWSTR text, BOOL isW)
280 return !is_textT(text, isW) ? 0 :
281 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
284 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
287 if (isSrcW) lstrcpynW(dest, src, max);
288 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
290 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
291 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
294 static inline LPCSTR debugstr_t(LPCWSTR text, BOOL isW)
296 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
299 static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n)
301 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
304 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
306 LPWSTR wstr = (LPWSTR)text;
308 TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW);
311 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
312 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
313 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
315 TRACE(" wstr=%s\n", debugstr_w(wstr));
319 static inline void textfreeT(LPWSTR wstr, BOOL isW)
321 if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
325 * dest is a pointer to a Unicode string
326 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
328 static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
330 LPWSTR pszText = textdupTtoW(src, isW);
332 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
333 bResult = Str_SetPtrW(dest, pszText);
334 textfreeT(pszText, isW);
338 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
339 WPARAM wParam, LPARAM lParam, BOOL isW)
342 return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
344 return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
347 static inline BOOL notify(HWND self, INT code, LPNMHDR pnmh)
349 pnmh->hwndFrom = self;
350 pnmh->idFrom = GetWindowLongW(self, GWL_ID);
352 return (BOOL)SendMessageW(GetParent(self), WM_NOTIFY,
353 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
356 static inline BOOL hdr_notify(HWND self, INT code)
359 return notify(self, code, &nmh);
362 static inline BOOL listview_notify(HWND self, INT code, LPNMLISTVIEW plvnm)
364 return notify(self, code, (LPNMHDR)plvnm);
367 static int tabNotification[] = {
368 LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
369 LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
370 LVN_GETDISPINFOW, LVN_GETDISPINFOA,
371 LVN_SETDISPINFOW, LVN_SETDISPINFOA,
372 LVN_ODFINDITEMW, LVN_ODFINDITEMA,
373 LVN_GETINFOTIPW, LVN_GETINFOTIPA,
377 static int get_ansi_notification(INT unicodeNotificationCode)
379 int *pTabNotif = tabNotification;
380 while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
381 if (*pTabNotif) return *pTabNotif;
382 ERR("unknown notification %x\n", unicodeNotificationCode);
383 return unicodeNotificationCode;
387 Send notification. depends on dispinfoW having same
388 structure as dispinfoA.
389 self : listview handle
390 notificationCode : *Unicode* notification code
391 pdi : dispinfo structure (can be unicode or ansi)
392 isW : TRUE if dispinfo is Unicode
394 static BOOL dispinfo_notifyT(HWND self, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
396 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(self, 0);
397 BOOL bResult = FALSE;
398 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
400 INT cchTempBufMax = 0, savCchTextMax = 0;
401 LPWSTR pszTempBuf = NULL, savPszText = NULL;
403 TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self, notificationCode, pdi, isW);
404 TRACE(" notifyFormat=%s\n",
405 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
406 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
407 if (infoPtr->notifyFormat == NFR_ANSI)
408 realNotifCode = get_ansi_notification(notificationCode);
410 realNotifCode = notificationCode;
412 if (is_textT(pdi->item.pszText, isW))
414 if (isW && infoPtr->notifyFormat == NFR_ANSI)
415 convertToAnsi = TRUE;
416 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
417 convertToUnicode = TRUE;
420 if (convertToAnsi || convertToUnicode)
422 TRACE(" we have to convert the text to the correct format\n");
423 if (notificationCode != LVN_GETDISPINFOW)
424 { /* length of existing text */
425 cchTempBufMax = convertToUnicode ?
426 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
427 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
430 cchTempBufMax = pdi->item.cchTextMax;
432 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
433 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
434 if (!pszTempBuf) return FALSE;
435 if (convertToUnicode)
436 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
437 pszTempBuf, cchTempBufMax);
439 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
440 cchTempBufMax, NULL, NULL);
441 TRACE(" text=%s\n", debugstr_t(pszTempBuf, convertToUnicode));
442 savCchTextMax = pdi->item.cchTextMax;
443 savPszText = pdi->item.pszText;
444 pdi->item.pszText = pszTempBuf;
445 pdi->item.cchTextMax = cchTempBufMax;
448 bResult = notify(self, realNotifCode, (LPNMHDR)pdi);
450 if (convertToUnicode || convertToAnsi)
451 { /* convert back result */
452 TRACE(" returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode));
453 if (convertToUnicode) /* note : pointer can be changed by app ! */
454 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
455 savCchTextMax, NULL, NULL);
457 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
458 savPszText, savCchTextMax);
459 pdi->item.pszText = savPszText; /* restores our buffer */
460 pdi->item.cchTextMax = savCchTextMax;
461 HeapFree(GetProcessHeap(), 0, pszTempBuf);
466 static inline LRESULT LISTVIEW_GetItemW(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal)
468 return LISTVIEW_GetItemT(hwnd, lpLVItem, internal, TRUE);
471 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
475 n = min(min(n, strlenW(s1)), strlenW(s2));
476 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
477 return res ? res - 2 : res;
480 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
482 static int index = 0;
483 static char buffers[20][256];
484 char* buf = buffers[index++ % 20];
485 if (lpLVItem == NULL) return "(null)";
486 snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
487 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
488 lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
489 lpLVItem->state, lpLVItem->stateMask,
490 lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
491 debugstr_tn(lpLVItem->pszText, isW, 80),
492 lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
497 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
499 static int index = 0;
500 static char buffers[20][256];
501 char* buf = buffers[index++ % 20];
502 if (lpColumn == NULL) return "(null)";
503 snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
504 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
505 lpColumn->mask, lpColumn->fmt, lpColumn->cx,
506 lpColumn->mask & LVCF_TEXT ? lpColumn->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
507 debugstr_tn(lpColumn->pszText, isW, 80): "",
508 lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
512 static void LISTVIEW_DumpListview(LISTVIEW_INFO *iP, INT line)
514 DWORD dwStyle = GetWindowLongW (iP->hwndSelf, GWL_STYLE);
515 TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n",
516 iP->hwndSelf, line, iP->clrBk, iP->clrText, iP->clrTextBk,
517 iP->nItemHeight, iP->nItemWidth, dwStyle);
518 TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n",
519 iP->hwndSelf, line, iP->himlNormal, iP->himlSmall, iP->himlState,
520 iP->nFocusedItem, iP->nHotItem, iP->dwExStyle);
524 LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
527 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
528 NMLVCUSTOMDRAW nmcdhdr;
531 TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd, dwDrawStage, hdc);
533 nmcd= & nmcdhdr.nmcd;
534 nmcd->hdr.hwndFrom = hwnd;
535 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
536 nmcd->hdr.code = NM_CUSTOMDRAW;
537 nmcd->dwDrawStage= dwDrawStage;
539 nmcd->rc.left = rc.left;
540 nmcd->rc.right = rc.right;
541 nmcd->rc.bottom = rc.bottom;
542 nmcd->rc.top = rc.top;
543 nmcd->dwItemSpec = 0;
544 nmcd->uItemState = 0;
545 nmcd->lItemlParam= 0;
546 nmcdhdr.clrText = infoPtr->clrText;
547 nmcdhdr.clrTextBk= infoPtr->clrBk;
549 return (BOOL)SendMessageW (GetParent (hwnd), WM_NOTIFY,
550 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
554 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
555 UINT iItem, UINT iSubItem,
558 LISTVIEW_INFO *infoPtr;
559 NMLVCUSTOMDRAW nmcdhdr;
561 DWORD dwDrawStage,dwItemSpec;
567 infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
569 ZeroMemory(&item,sizeof(item));
571 item.mask = LVIF_PARAM;
572 ListView_GetItemW(hwnd,&item);
574 dwDrawStage=CDDS_ITEM | uItemDrawState;
578 if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
579 if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
580 if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
582 itemRect.left = LVIR_BOUNDS;
583 LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
585 nmcd= & nmcdhdr.nmcd;
586 nmcd->hdr.hwndFrom = hwnd;
587 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
588 nmcd->hdr.code = NM_CUSTOMDRAW;
589 nmcd->dwDrawStage= dwDrawStage;
591 nmcd->rc.left = itemRect.left;
592 nmcd->rc.right = itemRect.right;
593 nmcd->rc.bottom = itemRect.bottom;
594 nmcd->rc.top = itemRect.top;
595 nmcd->dwItemSpec = dwItemSpec;
596 nmcd->uItemState = uItemState;
597 nmcd->lItemlParam= item.lParam;
598 nmcdhdr.clrText = infoPtr->clrText;
599 nmcdhdr.clrTextBk= infoPtr->clrBk;
600 nmcdhdr.iSubItem =iSubItem;
602 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
603 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
604 nmcd->uItemState, nmcd->lItemlParam);
606 retval=SendMessageW (GetParent (hwnd), WM_NOTIFY,
607 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
609 infoPtr->clrText=nmcdhdr.clrText;
610 infoPtr->clrBk =nmcdhdr.clrTextBk;
611 return (BOOL) retval;
615 /*************************************************************************
616 * LISTVIEW_ProcessLetterKeys
618 * Processes keyboard messages generated by pressing the letter keys
620 * What this does is perform a case insensitive search from the
621 * current position with the following quirks:
622 * - If two chars or more are pressed in quick succession we search
623 * for the corresponding string (e.g. 'abc').
624 * - If there is a delay we wipe away the current search string and
625 * restart with just that char.
626 * - If the user keeps pressing the same character, whether slowly or
627 * fast, so that the search string is entirely composed of this
628 * character ('aaaaa' for instance), then we search for first item
629 * that starting with that character.
630 * - If the user types the above character in quick succession, then
631 * we must also search for the corresponding string ('aaaaa'), and
632 * go to that string if there is a match.
640 * - The current implementation has a list of characters it will
641 * accept and it ignores averything else. In particular it will
642 * ignore accentuated characters which seems to match what
643 * Windows does. But I'm not sure it makes sense to follow
645 * - We don't sound a beep when the search fails.
649 * TREEVIEW_ProcessLetterKeys
651 static INT LISTVIEW_ProcessLetterKeys(
652 HWND hwnd, /* handle to the window */
653 WPARAM charCode, /* the character code, the actual character */
654 LPARAM keyData /* key data */
657 LISTVIEW_INFO *infoPtr;
662 WCHAR buffer[MAX_PATH];
663 DWORD timestamp,elapsed;
665 /* simple parameter checking */
666 if (!hwnd || !charCode || !keyData)
669 infoPtr=(LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
673 /* only allow the valid WM_CHARs through */
674 if (!isalnum(charCode) &&
675 charCode != '.' && charCode != '`' && charCode != '!' &&
676 charCode != '@' && charCode != '#' && charCode != '$' &&
677 charCode != '%' && charCode != '^' && charCode != '&' &&
678 charCode != '*' && charCode != '(' && charCode != ')' &&
679 charCode != '-' && charCode != '_' && charCode != '+' &&
680 charCode != '=' && charCode != '\\'&& charCode != ']' &&
681 charCode != '}' && charCode != '[' && charCode != '{' &&
682 charCode != '/' && charCode != '?' && charCode != '>' &&
683 charCode != '<' && charCode != ',' && charCode != '~')
686 nSize=GETITEMCOUNT(infoPtr);
687 /* if there's one item or less, there is no where to go */
691 /* compute how much time elapsed since last keypress */
692 timestamp=GetTickCount();
693 if (timestamp > infoPtr->lastKeyPressTimestamp) {
694 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
696 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
699 /* update the search parameters */
700 infoPtr->lastKeyPressTimestamp=timestamp;
701 if (elapsed < KEY_DELAY) {
702 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
703 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
705 if (infoPtr->charCode != charCode) {
706 infoPtr->charCode=charCode=0;
709 infoPtr->charCode=charCode;
710 infoPtr->szSearchParam[0]=charCode;
711 infoPtr->nSearchParamLength=1;
712 /* Redundant with the 1 char string */
716 /* and search from the current position */
718 if (infoPtr->nFocusedItem >= 0) {
719 endidx=infoPtr->nFocusedItem;
721 /* if looking for single character match,
722 * then we must always move forward
724 if (infoPtr->nSearchParamLength == 1)
738 ZeroMemory(&item, sizeof(item));
739 item.mask = LVIF_TEXT;
742 item.pszText = buffer;
743 item.cchTextMax = COUNTOF(buffer);
744 ListView_GetItemW( hwnd, &item );
746 /* check for a match */
747 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
750 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
751 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
752 /* This would work but we must keep looking for a longer match */
756 } while (idx != endidx);
759 if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
760 /* refresh client area */
761 InvalidateRect(hwnd, NULL, TRUE);
769 /*************************************************************************
770 * LISTVIEW_UpdateHeaderSize [Internal]
772 * Function to resize the header control
775 * hwnd [I] handle to a window
776 * nNewScrollPos [I] Scroll Pos to Set
783 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
785 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
789 GetWindowRect(infoPtr->hwndHeader, &winRect);
790 point[0].x = winRect.left;
791 point[0].y = winRect.top;
792 point[1].x = winRect.right;
793 point[1].y = winRect.bottom;
795 MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
796 point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
797 point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
799 SetWindowPos(infoPtr->hwndHeader,0,
800 point[0].x,point[0].y,point[1].x,point[1].y,
801 SWP_NOZORDER | SWP_NOACTIVATE);
806 * Update the scrollbars. This functions should be called whenever
807 * the content, size or view changes.
810 * [I] HWND : window handle
815 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
817 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
818 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
819 UINT uView = lStyle & LVS_TYPEMASK;
820 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
821 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
822 SCROLLINFO scrollInfo;
824 if (lStyle & LVS_NOSCROLL) return;
826 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
827 scrollInfo.cbSize = sizeof(SCROLLINFO);
829 if (uView == LVS_LIST)
831 /* update horizontal scrollbar */
833 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
834 INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
835 INT nNumOfItems = GETITEMCOUNT(infoPtr);
837 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
838 if((nNumOfItems % nCountPerColumn) == 0)
842 scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
843 scrollInfo.nPage = nCountPerRow;
844 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
845 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
846 ShowScrollBar(hwnd, SB_VERT, FALSE);
848 else if (uView == LVS_REPORT)
850 /* update vertical scrollbar */
852 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
853 scrollInfo.nPos = ListView_GetTopIndex(hwnd);
854 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
855 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
856 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
858 /* update horizontal scrollbar */
859 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
860 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
861 || GETITEMCOUNT(infoPtr) == 0)
866 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
867 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
868 scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
869 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
871 /* Update the Header Control */
872 scrollInfo.fMask = SIF_POS;
873 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
874 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
881 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
883 INT nViewWidth = rcView.right - rcView.left;
884 INT nViewHeight = rcView.bottom - rcView.top;
886 /* Update Horizontal Scrollbar */
887 scrollInfo.fMask = SIF_POS;
888 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
889 || GETITEMCOUNT(infoPtr) == 0)
893 scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
895 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
896 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
897 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
899 /* Update Vertical Scrollbar */
900 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
901 scrollInfo.fMask = SIF_POS;
902 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
903 || GETITEMCOUNT(infoPtr) == 0)
907 scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
909 scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
910 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
911 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
918 * Prints a message for unsupported window styles.
919 * A kind of TODO list for window styles.
922 * [I] LONG : window style
927 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
929 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
930 FIXME(" LVS_NOSCROLL\n");
932 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSORTHEADER)
933 FIXME(" LVS_NOSORTHEADER\n");
935 if (lStyle & LVS_EDITLABELS)
936 FIXME(" LVS_EDITLABELS\n");
938 if (lStyle & LVS_NOLABELWRAP)
939 FIXME(" LVS_NOLABELWRAP\n");
941 if (lStyle & LVS_SHAREIMAGELISTS)
942 FIXME(" LVS_SHAREIMAGELISTS\n");
944 if (lStyle & LVS_SORTASCENDING)
945 FIXME(" LVS_SORTASCENDING\n");
947 if (lStyle & LVS_SORTDESCENDING)
948 FIXME(" LVS_SORTDESCENDING\n");
953 * Aligns the items with the top edge of the window.
956 * [I] HWND : window handle
961 static VOID LISTVIEW_AlignTop(HWND hwnd)
963 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
964 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
965 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
968 INT i, off_x=0, off_y=0;
970 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
972 /* Since SetItemPosition uses upper-left of icon, and for
973 style=LVS_ICON the icon is not left adjusted, get the offset */
974 if (uView == LVS_ICON)
976 off_y = ICON_TOP_PADDING;
977 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
981 ZeroMemory(&rcView, sizeof(RECT));
983 if (nListWidth > infoPtr->nItemWidth)
985 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
987 if (ptItem.x + infoPtr->nItemWidth > nListWidth)
990 ptItem.y += infoPtr->nItemHeight;
993 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
994 ptItem.x += infoPtr->nItemWidth;
995 rcView.right = max(rcView.right, ptItem.x);
998 rcView.bottom = ptItem.y + infoPtr->nItemHeight;
1002 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1004 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1005 ptItem.y += infoPtr->nItemHeight;
1008 rcView.right = infoPtr->nItemWidth;
1009 rcView.bottom = ptItem.y;
1012 LISTVIEW_SetViewRect(hwnd, &rcView);
1018 * Aligns the items with the left edge of the window.
1021 * [I] HWND : window handle
1026 static VOID LISTVIEW_AlignLeft(HWND hwnd)
1028 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1029 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1030 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1033 INT i, off_x=0, off_y=0;
1035 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1037 /* Since SetItemPosition uses upper-left of icon, and for
1038 style=LVS_ICON the icon is not left adjusted, get the offset */
1039 if (uView == LVS_ICON)
1041 off_y = ICON_TOP_PADDING;
1042 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1046 ZeroMemory(&rcView, sizeof(RECT));
1048 if (nListHeight > infoPtr->nItemHeight)
1050 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1052 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1055 ptItem.x += infoPtr->nItemWidth;
1058 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1059 ptItem.y += infoPtr->nItemHeight;
1060 rcView.bottom = max(rcView.bottom, ptItem.y);
1063 rcView.right = ptItem.x + infoPtr->nItemWidth;
1067 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1069 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1070 ptItem.x += infoPtr->nItemWidth;
1073 rcView.bottom = infoPtr->nItemHeight;
1074 rcView.right = ptItem.x;
1077 LISTVIEW_SetViewRect(hwnd, &rcView);
1083 * Set the bounding rectangle of all the items.
1086 * [I] HWND : window handle
1087 * [I] LPRECT : bounding rectangle
1093 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
1095 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1096 BOOL bResult = FALSE;
1098 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
1099 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1101 if (lprcView != NULL)
1104 infoPtr->rcView.left = lprcView->left;
1105 infoPtr->rcView.top = lprcView->top;
1106 infoPtr->rcView.right = lprcView->right;
1107 infoPtr->rcView.bottom = lprcView->bottom;
1115 * Retrieves the bounding rectangle of all the items.
1118 * [I] HWND : window handle
1119 * [O] LPRECT : bounding rectangle
1125 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
1127 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1128 BOOL bResult = FALSE;
1131 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
1133 if (lprcView != NULL)
1135 bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
1136 if (bResult != FALSE)
1138 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1139 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1140 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1141 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1144 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1145 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1153 * Retrieves the subitem pointer associated with the subitem index.
1156 * [I] HDPA : DPA handle for a specific item
1157 * [I] INT : index of subitem
1160 * SUCCESS : subitem pointer
1163 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1166 LISTVIEW_SUBITEM *lpSubItem;
1169 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1171 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1172 if (lpSubItem != NULL)
1174 if (lpSubItem->iSubItem == nSubItem)
1186 * Calculates the width of an item.
1189 * [I] HWND : window handle
1190 * [I] LONG : window style
1193 * Returns item width.
1195 static INT LISTVIEW_GetItemWidth(HWND hwnd)
1197 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1198 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
1199 UINT uView = style & LVS_TYPEMASK;
1200 INT nHeaderItemCount;
1206 TRACE("(hwnd=%x)\n", hwnd);
1208 if (uView == LVS_ICON)
1210 nItemWidth = infoPtr->iconSpacing.cx;
1212 else if (uView == LVS_REPORT)
1214 /* calculate width of header */
1215 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1216 for (i = 0; i < nHeaderItemCount; i++)
1218 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1220 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1226 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1228 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
1229 nItemWidth = max(nItemWidth, nLabelWidth);
1232 /* default label size */
1233 if (GETITEMCOUNT(infoPtr) == 0)
1235 nItemWidth = DEFAULT_COLUMN_WIDTH;
1239 if (nItemWidth == 0)
1241 nItemWidth = DEFAULT_LABEL_WIDTH;
1246 nItemWidth += WIDTH_PADDING;
1248 if (infoPtr->himlSmall != NULL)
1250 nItemWidth += infoPtr->iconSize.cx;
1253 if (infoPtr->himlState != NULL)
1255 nItemWidth += infoPtr->iconSize.cx;
1262 /* nItemWidth Cannot be Zero */
1270 * Calculates the width of a specific item.
1273 * [I] HWND : window handle
1274 * [I] LPSTR : string
1277 * Returns the width of an item width a specified string.
1279 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1281 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1282 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1283 INT nHeaderItemCount;
1288 TRACE("(hwnd=%x)\n", hwnd);
1290 if (uView == LVS_ICON)
1292 nItemWidth = infoPtr->iconSpacing.cx;
1294 else if (uView == LVS_REPORT)
1296 /* calculate width of header */
1297 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1298 for (i = 0; i < nHeaderItemCount; i++)
1300 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1302 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1308 /* get width of string */
1309 nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1311 /* default label size */
1312 if (GETITEMCOUNT(infoPtr) == 0)
1314 nItemWidth = DEFAULT_COLUMN_WIDTH;
1318 if (nItemWidth == 0)
1320 nItemWidth = DEFAULT_LABEL_WIDTH;
1325 nItemWidth += WIDTH_PADDING;
1327 if (infoPtr->himlSmall != NULL)
1329 nItemWidth += infoPtr->iconSize.cx;
1332 if (infoPtr->himlState != NULL)
1334 nItemWidth += infoPtr->iconSize.cx;
1345 * Retrieves and saves important text metrics info for the current
1349 * [I] HWND : window handle
1352 static VOID LISTVIEW_SaveTextMetrics(HWND hwnd)
1354 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1356 HDC hdc = GetDC(hwnd);
1357 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1358 INT oldHeight, oldACW;
1360 GetTextMetricsW(hdc, &tm);
1362 oldHeight = infoPtr->ntmHeight;
1363 oldACW = infoPtr->ntmAveCharWidth;
1364 infoPtr->ntmHeight = tm.tmHeight;
1365 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1367 SelectObject(hdc, hOldFont);
1368 ReleaseDC(hwnd, hdc);
1369 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1370 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1376 * Calculates the height of an item.
1379 * [I] HWND : window handle
1382 * Returns item height.
1384 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1386 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1387 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1388 INT nItemHeight = 0;
1390 if (uView == LVS_ICON)
1392 nItemHeight = infoPtr->iconSpacing.cy;
1396 if(infoPtr->himlState || infoPtr->himlSmall)
1397 nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1399 nItemHeight = infoPtr->ntmHeight;
1406 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1408 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1409 LISTVIEW_SELECTION *selection;
1410 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1413 TRACE("Selections are:\n");
1414 for (i = 0; i < topSelection; i++)
1416 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1417 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1423 * A compare function for selection ranges
1426 * [I] LPVOID : Item 1;
1427 * [I] LPVOID : Item 2;
1428 * [I] LPARAM : flags
1431 * >0 : if Item 1 > Item 2
1432 * <0 : if Item 2 > Item 1
1433 * 0 : if Item 1 == Item 2
1435 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1438 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1439 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1440 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1441 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1455 * Adds a selection range.
1458 * [I] HWND : window handle
1459 * [I] INT : lower item index
1460 * [I] INT : upper item index
1465 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1467 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1468 LISTVIEW_SELECTION *selection;
1469 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1470 BOOL lowerzero=FALSE;
1472 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1473 selection->lower = lItem;
1474 selection->upper = uItem;
1476 TRACE("Add range %i - %i\n", lItem, uItem);
1479 LISTVIEW_SELECTION *checkselection,*checkselection2;
1480 INT index,mergeindex;
1482 /* find overlapping selections */
1483 /* we want to catch adjacent ranges so expand our range by 1 */
1486 if (selection->lower == 0)
1491 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1492 LISTVIEW_CompareSelectionRanges,
1494 selection->upper --;
1498 selection->lower ++;
1502 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1503 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1504 checkselection->upper);
1506 checkselection->lower = min(selection->lower,checkselection->lower);
1507 checkselection->upper = max(selection->upper,checkselection->upper);
1509 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1510 checkselection->upper);
1512 COMCTL32_Free(selection);
1514 /* merge now common selection ranges in the lower group*/
1517 checkselection->upper ++;
1518 if (checkselection->lower == 0)
1521 checkselection->lower --;
1523 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1524 checkselection->upper);
1526 /* not sorted yet */
1527 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1528 LISTVIEW_CompareSelectionRanges, 0,
1531 checkselection->upper --;
1535 checkselection->lower ++;
1537 if (mergeindex >=0 && mergeindex != index)
1539 TRACE("Merge with index %i\n",mergeindex);
1540 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1542 checkselection->lower = min(checkselection->lower,
1543 checkselection2->lower);
1544 checkselection->upper = max(checkselection->upper,
1545 checkselection2->upper);
1546 COMCTL32_Free(checkselection2);
1547 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1551 while (mergeindex > -1 && mergeindex <index);
1553 /* merge now common selection ranges in the upper group*/
1556 checkselection->upper ++;
1557 if (checkselection->lower == 0)
1560 checkselection->lower --;
1562 TRACE("search upper range %i (%lu - %lu)\n",index,
1563 checkselection->lower, checkselection->upper);
1565 /* not sorted yet */
1566 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1568 LISTVIEW_CompareSelectionRanges, 0,
1571 checkselection->upper --;
1575 checkselection->lower ++;
1577 if (mergeindex >=0 && mergeindex !=index)
1579 TRACE("Merge with index %i\n",mergeindex);
1580 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1582 checkselection->lower = min(checkselection->lower,
1583 checkselection2->lower);
1584 checkselection->upper = max(checkselection->upper,
1585 checkselection2->upper);
1586 COMCTL32_Free(checkselection2);
1587 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1590 while (mergeindex > -1);
1595 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1596 LISTVIEW_CompareSelectionRanges, 0,
1599 TRACE("Insert before index %i\n",index);
1602 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1607 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1612 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1613 LISTVIEW_PrintSelectionRanges(hwnd);
1618 * check if a specified index is selected.
1621 * [I] HWND : window handle
1622 * [I] INT : item index
1627 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1629 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1630 LISTVIEW_SELECTION selection;
1633 selection.upper = nItem;
1634 selection.lower = nItem;
1636 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1637 LISTVIEW_CompareSelectionRanges,
1647 * Removes all selection ranges
1650 * HWND: window handle
1656 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1658 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1659 LISTVIEW_SELECTION *selection;
1663 TRACE("(0x%x)\n",hwnd);
1665 ZeroMemory(&item,sizeof(item));
1666 item.stateMask = LVIS_SELECTED;
1670 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1673 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1674 for (i = selection->lower; i<=selection->upper; i++)
1675 LISTVIEW_SetItemState(hwnd,i,&item);
1676 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1679 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1687 * Removes a range selections.
1690 * [I] HWND : window handle
1691 * [I] INT : lower item index
1692 * [I] INT : upper item index
1697 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1699 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1700 LISTVIEW_SELECTION removeselection,*checkselection;
1703 removeselection.lower = lItem;
1704 removeselection.upper = uItem;
1706 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1707 LISTVIEW_PrintSelectionRanges(hwnd);
1709 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1710 LISTVIEW_CompareSelectionRanges,
1717 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1720 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1721 checkselection->upper);
1724 if ((checkselection->upper == removeselection.upper) &&
1725 (checkselection->lower == removeselection.lower))
1727 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1730 /* case 2: engulf */
1731 else if (((checkselection->upper < removeselection.upper) &&
1732 (checkselection->lower > removeselection.lower))||
1733 ((checkselection->upper <= removeselection.upper) &&
1734 (checkselection->lower > removeselection.lower)) ||
1735 ((checkselection->upper < removeselection.upper) &&
1736 (checkselection->lower >= removeselection.lower)))
1739 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1740 /* do it again because others may also get caught */
1742 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1744 /* case 3: overlap upper */
1745 else if ((checkselection->upper < removeselection.upper) &&
1746 (checkselection->lower < removeselection.lower))
1748 checkselection->upper = removeselection.lower - 1;
1750 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1752 /* case 4: overlap lower */
1753 else if ((checkselection->upper > removeselection.upper) &&
1754 (checkselection->lower > removeselection.lower))
1756 checkselection->lower = removeselection.upper + 1;
1758 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1760 /* case 5: fully internal */
1761 else if (checkselection->upper == removeselection.upper)
1762 checkselection->upper = removeselection.lower - 1;
1763 else if (checkselection->lower == removeselection.lower)
1764 checkselection->lower = removeselection.upper + 1;
1767 /* bisect the range */
1768 LISTVIEW_SELECTION *newselection;
1770 newselection = (LISTVIEW_SELECTION *)
1771 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1772 newselection -> lower = checkselection->lower;
1773 newselection -> upper = removeselection.lower - 1;
1774 checkselection -> lower = removeselection.upper + 1;
1775 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1777 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1779 LISTVIEW_PrintSelectionRanges(hwnd);
1784 * Updates the various indices after an item has been inserted or deleted.
1787 * [I] HWND : window handle
1788 * [I] INT : item index
1789 * [I] INT : Direction of shift, +1 or -1.
1794 static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
1796 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1797 LISTVIEW_SELECTION selection,*checkselection;
1800 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1802 selection.upper = nItem;
1803 selection.lower = nItem;
1805 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1806 LISTVIEW_CompareSelectionRanges,
1807 0,DPAS_SORTED|DPAS_INSERTAFTER);
1809 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1811 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1812 if ((checkselection->lower >= nItem)&&
1813 (checkselection->lower + direction >= 0))
1814 checkselection->lower += direction;
1815 if ((checkselection->upper >= nItem)&&
1816 (checkselection->upper + direction >=0))
1817 checkselection->upper += direction;
1821 /* Note that the following will fail if direction != +1 and -1 */
1822 if (infoPtr->nSelectionMark > nItem)
1823 infoPtr->nSelectionMark += direction;
1824 else if (infoPtr->nSelectionMark == nItem)
1827 infoPtr->nSelectionMark += direction;
1828 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1829 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1832 if (infoPtr->nFocusedItem > nItem)
1833 infoPtr->nFocusedItem += direction;
1834 else if (infoPtr->nFocusedItem == nItem)
1837 infoPtr->nFocusedItem += direction;
1840 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1841 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1842 if (infoPtr->nFocusedItem >= 0)
1843 LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
1846 /* But we are not supposed to modify nHotItem! */
1852 * Adds a block of selections.
1855 * [I] HWND : window handle
1856 * [I] INT : item index
1861 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1863 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1864 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1865 INT nLast = max(infoPtr->nSelectionMark, nItem);
1872 ZeroMemory(&item,sizeof(item));
1873 item.stateMask = LVIS_SELECTED;
1874 item.state = LVIS_SELECTED;
1876 for (i = nFirst; i <= nLast; i++)
1877 LISTVIEW_SetItemState(hwnd,i,&item);
1879 LISTVIEW_SetItemFocus(hwnd, nItem);
1880 infoPtr->nSelectionMark = nItem;
1886 * Adds a single selection.
1889 * [I] HWND : window handle
1890 * [I] INT : item index
1895 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1897 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1900 ZeroMemory(&item,sizeof(item));
1901 item.state = LVIS_SELECTED;
1902 item.stateMask = LVIS_SELECTED;
1904 LISTVIEW_SetItemState(hwnd,nItem,&item);
1906 LISTVIEW_SetItemFocus(hwnd, nItem);
1907 infoPtr->nSelectionMark = nItem;
1912 * Selects or unselects an item.
1915 * [I] HWND : window handle
1916 * [I] INT : item index
1922 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1924 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1928 ZeroMemory(&item,sizeof(item));
1929 item.stateMask = LVIS_SELECTED;
1931 if (LISTVIEW_IsSelected(hwnd,nItem))
1933 LISTVIEW_SetItemState(hwnd,nItem,&item);
1938 item.state = LVIS_SELECTED;
1939 LISTVIEW_SetItemState(hwnd,nItem,&item);
1943 LISTVIEW_SetItemFocus(hwnd, nItem);
1944 infoPtr->nSelectionMark = nItem;
1951 * Selects items based on view coordinates.
1954 * [I] HWND : window handle
1955 * [I] RECT : selection rectangle
1960 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
1962 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1967 ZeroMemory(&item,sizeof(item));
1968 item.stateMask = LVIS_SELECTED;
1970 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1972 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
1974 if (PtInRect(&rcSelRect, ptItem) != FALSE)
1975 item.state = LVIS_SELECTED;
1978 LISTVIEW_SetItemState(hwnd,i,&item);
1984 * Sets a single group selection.
1987 * [I] HWND : window handle
1988 * [I] INT : item index
1993 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
1995 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1996 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1999 ZeroMemory(&item,sizeof(item));
2000 item.stateMask = LVIS_SELECTED;
2002 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2007 if (infoPtr->nSelectionMark == -1)
2009 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2013 nFirst = min(infoPtr->nSelectionMark, nItem);
2014 nLast = max(infoPtr->nSelectionMark, nItem);
2017 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2019 if ((i < nFirst) || (i > nLast))
2022 item.state = LVIS_SELECTED;
2023 LISTVIEW_SetItemState(hwnd,i,&item);
2031 LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcItem);
2032 LISTVIEW_GetItemBoundBox(hwnd, infoPtr->nSelectionMark, &rcSelMark);
2033 rcSel.left = min(rcSelMark.left, rcItem.left);
2034 rcSel.top = min(rcSelMark.top, rcItem.top);
2035 rcSel.right = max(rcSelMark.right, rcItem.right);
2036 rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
2037 LISTVIEW_SetSelectionRect(hwnd, rcSel);
2038 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2039 nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
2040 infoPtr->nSelectionMark,
2041 rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
2042 rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2046 LISTVIEW_SetItemFocus(hwnd, nItem);
2051 * Manages the item focus.
2054 * [I] HWND : window handle
2055 * [I] INT : item index
2058 * TRUE : focused item changed
2059 * FALSE : focused item has NOT changed
2061 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
2063 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2064 BOOL bResult = FALSE;
2067 if (infoPtr->nFocusedItem != nItem)
2069 if (infoPtr->nFocusedItem >= 0)
2071 INT oldFocus = infoPtr->nFocusedItem;
2073 infoPtr->nFocusedItem = -1;
2074 ZeroMemory(&lvItem, sizeof(lvItem));
2075 lvItem.stateMask = LVIS_FOCUSED;
2076 ListView_SetItemState(hwnd, oldFocus, &lvItem);
2080 lvItem.state = LVIS_FOCUSED;
2081 lvItem.stateMask = LVIS_FOCUSED;
2082 ListView_SetItemState(hwnd, nItem, &lvItem);
2084 infoPtr->nFocusedItem = nItem;
2085 ListView_EnsureVisible(hwnd, nItem, FALSE);
2093 * Sets a single selection.
2096 * [I] HWND : window handle
2097 * [I] INT : item index
2102 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
2104 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2107 ZeroMemory(&lvItem, sizeof(lvItem));
2108 lvItem.stateMask = LVIS_FOCUSED;
2109 ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
2111 LISTVIEW_RemoveAllSelections(hwnd);
2113 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
2114 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2115 ListView_SetItemState(hwnd, nItem, &lvItem);
2117 infoPtr->nFocusedItem = nItem;
2118 infoPtr->nSelectionMark = nItem;
2123 * Set selection(s) with keyboard.
2126 * [I] HWND : window handle
2127 * [I] INT : item index
2130 * SUCCESS : TRUE (needs to be repainted)
2131 * FAILURE : FALSE (nothing has changed)
2133 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
2135 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2136 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2137 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2138 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2139 BOOL bResult = FALSE;
2141 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2143 if (lStyle & LVS_SINGLESEL)
2146 LISTVIEW_SetSelection(hwnd, nItem);
2147 ListView_EnsureVisible(hwnd, nItem, FALSE);
2154 LISTVIEW_SetGroupSelection(hwnd, nItem);
2158 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
2163 LISTVIEW_SetSelection(hwnd, nItem);
2164 ListView_EnsureVisible(hwnd, nItem, FALSE);
2174 * Called when the mouse is being actively tracked and has hovered for a specified
2178 * [I] HWND : window handle
2179 * [I] wParam : key indicator
2180 * [I] lParam : mouse position
2183 * 0 if the message was processed, non-zero if there was an error
2186 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2187 * over the item for a certain period of time.
2190 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
2192 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2195 pt.x = (INT)LOWORD(lParam);
2196 pt.y = (INT)HIWORD(lParam);
2198 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2199 /* select the item under the cursor */
2200 LISTVIEW_MouseSelection(hwnd, pt);
2208 * Called whenever WM_MOUSEMOVE is received.
2211 * [I] HWND : window handle
2212 * [I] wParam : key indicators
2213 * [I] lParam : cursor position
2216 * 0 if the message is processed, non-zero if there was an error
2218 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
2220 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2221 TRACKMOUSEEVENT trackinfo;
2223 /* see if we are supposed to be tracking mouse hovering */
2224 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2225 /* fill in the trackinfo struct */
2226 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2227 trackinfo.dwFlags = TME_QUERY;
2228 trackinfo.hwndTrack = hwnd;
2229 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2231 /* see if we are already tracking this hwnd */
2232 _TrackMouseEvent(&trackinfo);
2234 if(!(trackinfo.dwFlags & TME_HOVER)) {
2235 trackinfo.dwFlags = TME_HOVER;
2237 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2238 _TrackMouseEvent(&trackinfo);
2247 * Selects an item based on coordinates.
2250 * [I] HWND : window handle
2251 * [I] POINT : mouse click ccordinates
2254 * SUCCESS : item index
2257 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
2259 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2261 INT i,topindex,bottomindex;
2262 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2263 UINT uView = lStyle & LVS_TYPEMASK;
2265 topindex = ListView_GetTopIndex(hwnd);
2266 if (uView == LVS_REPORT)
2268 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
2269 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
2273 bottomindex = GETITEMCOUNT(infoPtr);
2276 for (i = topindex; i < bottomindex; i++)
2278 rcItem.left = LVIR_SELECTBOUNDS;
2279 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
2281 if (PtInRect(&rcItem, pt) != FALSE)
2296 * [IO] HDPA : dynamic pointer array handle
2297 * [I] INT : column index (subitem index)
2303 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2305 BOOL bResult = TRUE;
2309 for (i = 0; i < hdpaItems->nItemCount; i++)
2311 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2312 if (hdpaSubItems != NULL)
2314 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2326 * Removes a subitem at a given position.
2329 * [IO] HDPA : dynamic pointer array handle
2330 * [I] INT : subitem index
2336 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2338 LISTVIEW_SUBITEM *lpSubItem;
2341 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2343 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2344 if (lpSubItem != NULL)
2346 if (lpSubItem->iSubItem == nSubItem)
2349 if (is_textW(lpSubItem->pszText))
2350 COMCTL32_Free(lpSubItem->pszText);
2353 COMCTL32_Free(lpSubItem);
2355 /* free dpa memory */
2356 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2359 else if (lpSubItem->iSubItem > nSubItem)
2369 * Compares the item information.
2372 * [I] LISTVIEW_ITEM *: destination item
2373 * [I] LPLVITEM : source item
2374 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2377 * SUCCCESS : TRUE (EQUAL)
2378 * FAILURE : FALSE (NOT EQUAL)
2380 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2384 if ((lpItem != NULL) && (lpLVItem != NULL))
2386 if (lpLVItem->mask & LVIF_STATE)
2388 if ((lpItem->state & lpLVItem->stateMask) !=
2389 (lpLVItem->state & lpLVItem->stateMask))
2390 uChanged |= LVIF_STATE;
2393 if (lpLVItem->mask & LVIF_IMAGE)
2395 if (lpItem->iImage != lpLVItem->iImage)
2396 uChanged |= LVIF_IMAGE;
2399 if (lpLVItem->mask & LVIF_PARAM)
2401 if (lpItem->lParam != lpLVItem->lParam)
2402 uChanged |= LVIF_PARAM;
2405 if (lpLVItem->mask & LVIF_INDENT)
2407 if (lpItem->iIndent != lpLVItem->iIndent)
2408 uChanged |= LVIF_INDENT;
2411 if (lpLVItem->mask & LVIF_TEXT)
2413 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2415 if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2416 uChanged |= LVIF_TEXT;
2420 if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2422 uChanged |= LVIF_TEXT;
2426 if (lpLVItem->pszText)
2428 if (lpItem->pszText)
2430 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2431 if (pszText && strcmpW(pszText, lpItem->pszText))
2432 uChanged |= LVIF_TEXT;
2433 textfreeT(pszText, isW);
2437 uChanged |= LVIF_TEXT;
2442 if (lpItem->pszText)
2443 uChanged |= LVIF_TEXT;
2454 * Initializes item attributes.
2457 * [I] HWND : window handle
2458 * [O] LISTVIEW_ITEM *: destination item
2459 * [I] LPLVITEM : source item
2460 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2466 static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem,
2467 LPLVITEMW lpLVItem, BOOL isW)
2469 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2470 BOOL bResult = FALSE;
2472 if ((lpItem != NULL) && (lpLVItem != NULL))
2476 if (lpLVItem->mask & LVIF_STATE)
2478 lpItem->state &= ~lpLVItem->stateMask;
2479 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2482 if (lpLVItem->mask & LVIF_IMAGE)
2483 lpItem->iImage = lpLVItem->iImage;
2485 if (lpLVItem->mask & LVIF_PARAM)
2486 lpItem->lParam = lpLVItem->lParam;
2488 if (lpLVItem->mask & LVIF_INDENT)
2489 lpItem->iIndent = lpLVItem->iIndent;
2491 if (lpLVItem->mask & LVIF_TEXT)
2493 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2495 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2498 if (is_textW(lpItem->pszText))
2499 COMCTL32_Free(lpItem->pszText);
2501 lpItem->pszText = LPSTR_TEXTCALLBACKW;
2504 bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
2513 * Initializes subitem attributes.
2515 * NOTE: The documentation specifies that the operation fails if the user
2516 * tries to set the indent of a subitem.
2519 * [I] HWND : window handle
2520 * [O] LISTVIEW_SUBITEM *: destination subitem
2521 * [I] LPLVITEM : source subitem
2522 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2528 static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2529 LPLVITEMW lpLVItem, BOOL isW)
2531 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2532 BOOL bResult = FALSE;
2534 TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
2535 hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW);
2537 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2539 if (!(lpLVItem->mask & LVIF_INDENT))
2543 lpSubItem->iSubItem = lpLVItem->iSubItem;
2545 if (lpLVItem->mask & LVIF_IMAGE)
2546 lpSubItem->iImage = lpLVItem->iImage;
2548 if (lpLVItem->mask & LVIF_TEXT)
2550 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2552 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2555 if (is_textW(lpSubItem->pszText))
2556 COMCTL32_Free(lpSubItem->pszText);
2558 lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2561 bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
2571 * Adds a subitem at a given position (column index).
2574 * [I] HWND : window handle
2575 * [I] LPLVITEM : new subitem atttributes
2576 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2582 static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2584 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2585 LISTVIEW_SUBITEM *lpSubItem = NULL;
2586 BOOL bResult = FALSE;
2588 INT nPosition, nItem;
2589 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2591 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2593 if (lStyle & LVS_OWNERDATA)
2596 if (lpLVItem != NULL)
2598 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2599 if (hdpaSubItems != NULL)
2601 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2602 if (lpSubItem != NULL)
2604 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2605 if (LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW))
2607 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2608 lpSubItem->iSubItem);
2609 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2610 if (nItem != -1) bResult = TRUE;
2616 /* cleanup if unsuccessful */
2617 if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2624 * Finds the dpa insert position (array index).
2627 * [I] HWND : window handle
2628 * [I] INT : subitem index
2634 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2636 LISTVIEW_SUBITEM *lpSubItem;
2639 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2641 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2642 if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2646 return hdpaSubItems->nItemCount;
2651 * Retrieves a listview subitem at a given position (column index).
2654 * [I] HWND : window handle
2655 * [I] INT : subitem index
2661 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2663 LISTVIEW_SUBITEM *lpSubItem;
2666 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2668 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2669 if (lpSubItem != NULL)
2671 if (lpSubItem->iSubItem == nSubItem)
2673 else if (lpSubItem->iSubItem > nSubItem)
2683 * Sets item attributes.
2686 * [I] HWND : window handle
2687 * [I] LPLVITEM : new item atttributes
2688 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2694 static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2696 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2697 BOOL bResult = FALSE;
2699 LISTVIEW_ITEM *lpItem;
2701 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2703 UINT uView = lStyle & LVS_TYPEMASK;
2707 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2709 if (lStyle & LVS_OWNERDATA)
2711 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2715 ZeroMemory(&itm, sizeof(itm));
2716 itm.mask = LVIF_STATE | LVIF_PARAM;
2717 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2718 itm.iItem = lpLVItem->iItem;
2720 ListView_GetItemW(hwnd, &itm);
2723 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2724 nmlv.uNewState = lpLVItem->state;
2725 nmlv.uOldState = itm.state;
2726 nmlv.uChanged = LVIF_STATE;
2727 nmlv.lParam = itm.lParam;
2728 nmlv.iItem = lpLVItem->iItem;
2730 if ((itm.state & lpLVItem->stateMask) !=
2731 (lpLVItem->state & lpLVItem->stateMask))
2733 /* send LVN_ITEMCHANGING notification */
2734 if (!listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv))
2736 if (lpLVItem->stateMask & LVIS_FOCUSED)
2738 if (lpLVItem->state & LVIS_FOCUSED)
2739 infoPtr->nFocusedItem = lpLVItem->iItem;
2740 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2741 infoPtr->nFocusedItem = -1;
2743 if (lpLVItem->stateMask & LVIS_SELECTED)
2745 if (lpLVItem->state & LVIS_SELECTED)
2747 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd);
2748 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2751 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2755 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2757 rcItem.left = LVIR_BOUNDS;
2758 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2759 InvalidateRect(hwnd, &rcItem, TRUE);
2767 if (lpLVItem != NULL)
2769 if (lpLVItem->iSubItem == 0)
2771 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2772 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2774 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2777 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2778 nmlv.lParam = lpItem->lParam;
2779 uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2782 if (uChanged & LVIF_STATE)
2784 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2785 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2787 if (nmlv.uNewState & LVIS_SELECTED)
2790 * This is redundant if called through SetSelection
2792 * however is required if the used directly calls SetItem
2793 * to set the selection.
2795 if (lStyle & LVS_SINGLESEL)
2796 LISTVIEW_RemoveAllSelections(hwnd);
2798 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2801 else if (lpLVItem->stateMask & LVIS_SELECTED)
2803 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2806 if (nmlv.uNewState & LVIS_FOCUSED)
2809 * This is a fun hoop to jump to try to catch if
2810 * the user is calling us directly to call focus or if
2811 * this function is being called as a result of a
2812 * SetItemFocus call.
2814 if (infoPtr->nFocusedItem >= 0)
2815 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2819 nmlv.uChanged = uChanged;
2820 nmlv.iItem = lpLVItem->iItem;
2821 nmlv.lParam = lpItem->lParam;
2822 /* send LVN_ITEMCHANGING notification */
2823 listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv);
2825 /* copy information */
2826 bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW);
2828 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2829 based on the width of the items text */
2830 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2832 item_width = LISTVIEW_GetStringWidthT(hwnd, lpItem->pszText, TRUE);
2834 if(item_width > infoPtr->nItemWidth)
2835 infoPtr->nItemWidth = item_width;
2838 /* send LVN_ITEMCHANGED notification */
2839 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2848 rcItem.left = LVIR_BOUNDS;
2849 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2850 InvalidateRect(hwnd, &rcItem, TRUE);
2862 * Sets subitem attributes.
2865 * [I] HWND : window handle
2866 * [I] LPLVITEM : new subitem atttributes
2867 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2873 static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2875 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2876 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2877 BOOL bResult = FALSE;
2879 LISTVIEW_SUBITEM *lpSubItem;
2882 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2884 if (lStyle & LVS_OWNERDATA)
2887 if (lpLVItem != NULL)
2889 if (lpLVItem->iSubItem > 0)
2891 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2892 if (hdpaSubItems != NULL)
2894 /* set subitem only if column is present */
2895 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2897 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2898 if (lpSubItem != NULL)
2899 bResult = LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW);
2901 bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW);
2903 rcItem.left = LVIR_BOUNDS;
2904 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2905 InvalidateRect(hwnd, &rcItem, FALSE);
2916 * Sets item attributes.
2919 * [I] HWND : window handle
2920 * [I] LPLVITEM : new item atttributes
2921 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2927 static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2929 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2931 if (!lpLVItem || lpLVItem->iItem < 0 ||
2932 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2934 if (lpLVItem->iSubItem == 0)
2935 return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW);
2937 return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW);
2942 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2945 * [I] HWND : window handle
2950 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2952 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2953 UINT uView = lStyle & LVS_TYPEMASK;
2955 SCROLLINFO scrollInfo;
2957 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2958 scrollInfo.cbSize = sizeof(SCROLLINFO);
2959 scrollInfo.fMask = SIF_POS;
2961 if (uView == LVS_LIST)
2963 if ((lStyle & WS_HSCROLL) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo))
2964 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2966 else if (uView == LVS_REPORT)
2968 if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo))
2969 nItem = scrollInfo.nPos;
2980 * [I] HWND : window handle
2981 * [I] HDC : device context handle
2982 * [I] INT : item index
2983 * [I] INT : subitem index
2984 * [I] RECT * : clipping rectangle
2989 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
2990 RECT rcItem, BOOL Selected)
2992 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2993 WCHAR szDispText[DISP_TEXT_SIZE];
2995 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
2998 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
3001 /* get information needed for drawing the item */
3002 ZeroMemory(&lvItem, sizeof(lvItem));
3003 lvItem.mask = LVIF_TEXT;
3004 lvItem.iItem = nItem;
3005 lvItem.iSubItem = nSubItem;
3006 lvItem.cchTextMax = DISP_TEXT_SIZE;
3007 lvItem.pszText = szDispText;
3008 *lvItem.pszText = '\0';
3009 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3010 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3012 /* redraw the background of the item */
3014 if(infoPtr->nColumnCount == (nSubItem + 1))
3015 rcTemp.right = infoPtr->rcList.right;
3017 rcTemp.right += WIDTH_PADDING;
3019 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3021 /* set item colors */
3022 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
3024 if (infoPtr->bFocus)
3026 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3027 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3031 SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3032 SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3037 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3039 SetBkMode(hdc, TRANSPARENT);
3040 textoutOptions &= ~ETO_OPAQUE;
3044 SetBkMode(hdc, OPAQUE);
3045 SetBkColor(hdc, infoPtr->clrTextBk);
3048 SetTextColor(hdc, infoPtr->clrText);
3051 ExtTextOutW(hdc, rcItem.left, rcItem.top, textoutOptions,
3052 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3056 /* fill in the gap */
3058 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
3060 CopyRect(&rec,&rcItem);
3061 rec.left = rec.right;
3062 rec.right = rec.left+REPORT_MARGINX;
3063 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3064 &rec, NULL, 0, NULL);
3066 CopyRect(&rec,&rcItem);
3067 rec.right = rec.left;
3068 rec.left = rec.left - REPORT_MARGINX;
3069 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3070 &rec, NULL, 0, NULL);
3080 * [I] HWND : window handle
3081 * [I] HDC : device context handle
3082 * [I] INT : item index
3083 * [I] RECT * : clipping rectangle
3088 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
3090 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3091 WCHAR szDispText[DISP_TEXT_SIZE];
3096 DWORD dwTextColor,dwTextX;
3097 BOOL bImage = FALSE;
3099 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
3102 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
3105 /* get information needed for drawing the item */
3106 ZeroMemory(&lvItem, sizeof(lvItem));
3107 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3108 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
3109 lvItem.iItem = nItem;
3110 lvItem.iSubItem = 0;
3111 lvItem.cchTextMax = DISP_TEXT_SIZE;
3112 lvItem.pszText = szDispText;
3113 *lvItem.pszText = '\0';
3114 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3115 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3117 /* redraw the background of the item */
3119 if(infoPtr->nColumnCount == (nItem + 1))
3120 rcTemp.right = infoPtr->rcList.right;
3122 rcTemp.right+=WIDTH_PADDING;
3124 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3127 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3129 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3132 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3136 if (infoPtr->himlState != NULL)
3138 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3139 if (uStateImage > 0)
3141 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3142 rcItem.top, ILD_NORMAL);
3145 rcItem.left += infoPtr->iconSize.cx;
3147 SuggestedFocus->left += infoPtr->iconSize.cx;
3152 if (infoPtr->himlSmall != NULL)
3154 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
3157 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3158 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3159 rcItem.top, ILD_SELECTED);
3161 else if (lvItem.iImage>=0)
3163 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3164 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3165 rcItem.top, ILD_NORMAL);
3168 rcItem.left += infoPtr->iconSize.cx;
3171 SuggestedFocus->left += infoPtr->iconSize.cx;
3175 /* Don't bother painting item being edited */
3176 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
3179 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
3181 /* set item colors */
3182 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3183 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3184 /* set raster mode */
3185 nMixMode = SetROP2(hdc, R2_XORPEN);
3187 else if ((GetWindowLongW(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3188 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
3190 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3191 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3192 /* set raster mode */
3193 nMixMode = SetROP2(hdc, R2_COPYPEN);
3197 /* set item colors */
3198 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3200 dwBkColor = GetBkColor(hdc);
3201 iBkMode = SetBkMode(hdc, TRANSPARENT);
3202 textoutOptions &= ~ETO_OPAQUE;
3206 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3207 iBkMode = SetBkMode(hdc, OPAQUE);
3210 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3211 /* set raster mode */
3212 nMixMode = SetROP2(hdc, R2_COPYPEN);
3215 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
3216 if (rcItem.left + nLabelWidth < rcItem.right)
3219 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3221 rcItem.right += IMAGE_PADDING;
3225 dwTextX = rcItem.left + 1;
3227 dwTextX += IMAGE_PADDING;
3230 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3231 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3233 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3235 /* fill in the gap */
3237 CopyRect(&rec,&rcItem);
3238 rec.left = rec.right;
3239 rec.right = rec.left+REPORT_MARGINX;
3240 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3241 &rec, NULL, 0, NULL);
3245 CopyRect(SuggestedFocus,&rcItem);
3249 SetROP2(hdc, R2_COPYPEN);
3250 SetBkColor(hdc, dwBkColor);
3251 SetTextColor(hdc, dwTextColor);
3253 SetBkMode(hdc, iBkMode);
3259 * Draws an item when in large icon display mode.
3262 * [I] HWND : window handle
3263 * [I] HDC : device context handle
3264 * [I] INT : item index
3265 * [I] RECT : clipping rectangle
3266 * [O] RECT * : The text rectangle about which to draw the focus
3271 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3272 RECT *SuggestedFocus)
3274 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3275 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3277 UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
3279 /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
3282 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3283 hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3285 /* get information needed for drawing the item */
3286 ZeroMemory(&lvItem, sizeof(lvItem));
3287 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3288 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3289 lvItem.iItem = nItem;
3290 lvItem.iSubItem = 0;
3291 lvItem.cchTextMax = DISP_TEXT_SIZE;
3292 lvItem.pszText = szDispText;
3293 *lvItem.pszText = '\0';
3294 LISTVIEW_GetItemW(hwnd, &lvItem, FALSE);
3295 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3297 /* redraw the background of the item */
3299 if(infoPtr->nColumnCount == (nItem + 1))
3300 rcTemp.right = infoPtr->rcList.right;
3302 rcTemp.right+=WIDTH_PADDING;
3303 /* The comment doesn't say WIDTH_PADDING applies to large icons */
3305 TRACE("background rect (%d,%d)-(%d,%d)\n",
3306 rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
3308 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3311 /* Figure out text colours etc. depending on state
3312 * At least the following states exist; there may be more.
3313 * Many items may be selected
3314 * At most one item may have the focus
3315 * The application may not actually be active currently
3316 * 1. The item is not selected in any way
3317 * 2. The cursor is flying over the icon or text and the text is being
3318 * expanded because it is not fully displayed currently.
3319 * 3. The item is selected and is focussed, i.e. the user has not clicked
3320 * in the blank area of the window, and the window (or application?)
3321 * still has the focus.
3322 * 4. As 3 except that a different window has the focus
3323 * 5. The item is the selected item of all the items, but the user has
3324 * clicked somewhere else on the window.
3325 * Only a few of these are handled currently. In particular 2 is not yet
3326 * handled since we do not support the functionality currently (or at least
3327 * we didn't when I wrote this)
3330 if (lvItem.state & LVIS_SELECTED)
3332 /* set item colors */
3333 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3334 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3335 SetBkMode (hdc, OPAQUE);
3336 /* set raster mode */
3337 SetROP2(hdc, R2_XORPEN);
3338 /* When exactly is it in XOR? while being dragged? */
3342 /* set item colors */
3343 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3345 SetBkMode(hdc, TRANSPARENT);
3349 SetBkMode(hdc, OPAQUE);
3350 SetBkColor(hdc, infoPtr->clrTextBk);
3353 SetTextColor(hdc, infoPtr->clrText);
3354 /* set raster mode */
3355 SetROP2(hdc, R2_COPYPEN);
3358 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3359 * wrapping and long words split.
3360 * In cases 1 and 4 only a portion of the text is displayed with word
3361 * wrapping and both word and end ellipsis. (I don't yet know about path
3364 uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
3367 DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3370 if (infoPtr->himlNormal != NULL)
3372 if (lvItem.iImage >= 0)
3374 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3376 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3380 /* Draw the text below the icon */
3382 /* Don't bother painting item being edited */
3383 if ((infoPtr->hwndEdit && (lvItem.state & LVIS_FOCUSED)) ||
3384 !lstrlenW(lvItem.pszText))
3386 SetRectEmpty(SuggestedFocus);
3390 /* Since rcItem.left is left point of icon, compute left point of item box */
3391 rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3392 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3393 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3394 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3395 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3396 infoPtr->iconSize.cx, infoPtr->nItemWidth);
3397 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3398 infoPtr->rcList.left, infoPtr->rcList.top,
3399 infoPtr->rcList.right, infoPtr->rcList.bottom,
3400 infoPtr->rcView.left, infoPtr->rcView.top,
3401 infoPtr->rcView.right, infoPtr->rcView.bottom);
3403 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3404 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3409 /* I am sure of most of the uFormat values. However I am not sure about
3410 * whether we need or do not need the following:
3411 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3412 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3413 * We certainly do not need
3414 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3415 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3418 /* If the text is being drawn without clipping (i.e. the full text) then we
3419 * need to jump through a few hoops to ensure that it all gets displayed and
3420 * that the background is complete
3422 if (uFormat & DT_NOCLIP)
3425 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3426 int dx, dy, old_wid, new_wid;
3427 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3428 /* Microsoft, in their great wisdom, have decided that the rectangle
3429 * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3430 * not the location. So we have to do the centring ourselves (and take
3431 * responsibility for agreeing off-by-one consistency with them).
3433 old_wid = rcItem.right-rcItem.left;
3434 new_wid = rcBack.right - rcBack.left;
3435 dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
3436 dy = rcBack.top - rcItem.top;
3437 OffsetRect (&rcItem, dx, dy);
3438 FillRect(hdc, &rcItem, hBrush);
3439 DeleteObject(hBrush);
3441 /* else ? What if we are losing the focus? will we not get a complete
3444 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3446 CopyRect(SuggestedFocus, &rcItem);
3451 * Draws listview items when in report display mode.
3454 * [I] HWND : window handle
3455 * [I] HDC : device context handle
3460 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3462 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3463 SCROLLINFO scrollInfo;
3464 INT nDrawPosY = infoPtr->rcList.top;
3466 RECT rcItem, rcTemp;
3471 DWORD cditemmode = CDRF_DODEFAULT;
3472 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3475 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3476 scrollInfo.cbSize = sizeof(SCROLLINFO);
3477 scrollInfo.fMask = SIF_POS;
3479 nItem = ListView_GetTopIndex(hwnd);
3481 /* add 1 for displaying a partial item at the bottom */
3482 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3483 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3485 /* send cache hint notification */
3486 if (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3490 nmlv.hdr.hwndFrom = hwnd;
3491 nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID);
3492 nmlv.hdr.code = LVN_ODCACHEHINT;
3496 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3500 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3501 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3502 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3504 /* clear the background of any part of the control that doesn't contain items */
3505 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3506 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3508 /* nothing to draw */
3509 if(GETITEMCOUNT(infoPtr) == 0)
3512 /* Get scroll bar info once before loop */
3513 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3514 scrollOffset = scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
3516 for (; nItem < nLast; nItem++)
3518 RECT SuggestedFocusRect;
3521 if (lStyle & LVS_OWNERDRAWFIXED)
3523 UINT uID = GetWindowLongW( hwnd, GWL_ID);
3528 TRACE("Owner Drawn\n");
3529 dis.CtlType = ODT_LISTVIEW;
3532 dis.itemAction = ODA_DRAWENTIRE;
3535 if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3536 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3538 dis.hwndItem = hwnd;
3541 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3543 dis.rcItem.left = -scrollOffset;
3544 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3545 dis.rcItem.top = nDrawPosY;
3546 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3548 ZeroMemory(&item,sizeof(item));
3550 item.mask = LVIF_PARAM;
3551 ListView_GetItemW(hwnd, &item);
3553 dis.itemData = item.lParam;
3555 if (SendMessageW(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3557 nDrawPosY += infoPtr->nItemHeight;
3566 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3567 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3569 ir.left += REPORT_MARGINX;
3570 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3572 ir.bottom = ir.top + infoPtr->nItemHeight;
3574 CopyRect(&SuggestedFocusRect,&ir);
3577 for (j = 0; j < nColumnCount; j++)
3579 if (cdmode & CDRF_NOTIFYITEMDRAW)
3580 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3582 if (cditemmode & CDRF_SKIPDEFAULT)
3585 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3587 rcItem.left += REPORT_MARGINX;
3588 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3589 rcItem.top = nDrawPosY;
3590 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3592 /* Offset the Scroll Bar Pos */
3593 rcItem.left -= scrollOffset;
3594 rcItem.right -= scrollOffset;
3598 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3599 &SuggestedFocusRect);
3603 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, FullSelected);
3606 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3607 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3608 CDDS_ITEMPOSTPAINT);
3613 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3616 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3617 rop = SetROP2(hdc, R2_XORPEN);
3619 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3620 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3623 SetROP2(hdc, R2_COPYPEN);
3625 nDrawPosY += infoPtr->nItemHeight;
3631 * Retrieves the number of items that can fit vertically in the client area.
3634 * [I] HWND : window handle
3637 * Number of items per row.
3639 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3641 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3642 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3643 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3644 INT nCountPerRow = 1;
3648 if (uView != LVS_REPORT)
3650 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3651 if (nCountPerRow == 0) nCountPerRow = 1;
3655 return nCountPerRow;
3660 * Retrieves the number of items that can fit horizontally in the client
3664 * [I] HWND : window handle
3667 * Number of items per column.
3669 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3671 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3672 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3673 INT nCountPerColumn = 1;
3675 if (nListHeight > 0)
3677 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3678 if (nCountPerColumn == 0) nCountPerColumn = 1;
3681 return nCountPerColumn;
3686 * Retrieves the number of columns needed to display all the items when in
3687 * list display mode.
3690 * [I] HWND : window handle
3693 * Number of columns.
3695 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3697 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3698 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3699 INT nColumnCount = 0;
3701 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3703 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3704 if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3707 return nColumnCount;
3713 * Draws listview items when in list display mode.
3716 * [I] HWND : window handle
3717 * [I] HDC : device context handle
3722 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3724 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3725 RECT rcItem, FocusRect, rcTemp;
3729 INT nCountPerColumn;
3730 INT nItemWidth = infoPtr->nItemWidth;
3731 INT nItemHeight = infoPtr->nItemHeight;
3732 DWORD cditemmode = CDRF_DODEFAULT;
3734 /* get number of fully visible columns */
3735 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3736 infoPtr->nColumnCount = nColumnCount;
3737 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3738 nItem = ListView_GetTopIndex(hwnd);
3740 /* paint the background of the control that doesn't contain any items */
3741 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3742 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3744 /* nothing to draw, return here */
3745 if(GETITEMCOUNT(infoPtr) == 0)
3748 for (i = 0; i < nColumnCount; i++)
3750 for (j = 0; j < nCountPerColumn; j++, nItem++)
3752 if (nItem >= GETITEMCOUNT(infoPtr))
3755 if (cdmode & CDRF_NOTIFYITEMDRAW)
3756 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3758 if (cditemmode & CDRF_SKIPDEFAULT)
3761 rcItem.top = j * nItemHeight;
3762 rcItem.left = i * nItemWidth;
3763 rcItem.bottom = rcItem.top + nItemHeight;
3764 rcItem.right = rcItem.left + nItemWidth;
3765 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3769 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3770 Rectangle(hdc, FocusRect.left, FocusRect.top,
3771 FocusRect.right,FocusRect.bottom);
3773 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3774 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3775 CDDS_ITEMPOSTPAINT);
3783 * Draws listview items when in icon or small icon display mode.
3786 * [I] HWND : window handle
3787 * [I] HDC : device context handle
3792 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3794 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3797 RECT rcItem, SuggestedFocus, rcTemp;
3799 DWORD cditemmode = CDRF_DODEFAULT;
3801 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3802 /* DrawItem from erasing the incorrect background area */
3804 /* paint the background of the control that doesn't contain any items */
3805 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3806 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3808 /* nothing to draw, return here */
3809 if(GETITEMCOUNT(infoPtr) == 0)
3812 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3813 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3815 if (cdmode & CDRF_NOTIFYITEMDRAW)
3816 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3818 if (cditemmode & CDRF_SKIPDEFAULT)
3821 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3822 ptPosition.x += ptOrigin.x;
3823 ptPosition.y += ptOrigin.y;
3825 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3827 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3829 if (ptPosition.y < infoPtr->rcList.bottom)
3831 if (ptPosition.x < infoPtr->rcList.right)
3833 rcItem.top = ptPosition.y;
3834 rcItem.left = ptPosition.x;
3835 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3836 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3838 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3840 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3844 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3845 infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
3846 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3847 SuggestedFocus.right,SuggestedFocus.bottom);
3852 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3853 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3854 CDDS_ITEMPOSTPAINT);
3860 * Draws listview items.
3863 * [I] HWND : window handle
3864 * [I] HDC : device context handle
3869 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3871 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3872 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3878 LISTVIEW_DumpListview (infoPtr, __LINE__);
3880 GetClientRect(hwnd, &rect);
3881 cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3883 if (cdmode == CDRF_SKIPDEFAULT) return;
3886 hOldFont = SelectObject(hdc, infoPtr->hFont);
3888 /* select the dotted pen (for drawing the focus box) */
3889 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3890 hOldPen = SelectObject(hdc, hPen);
3892 /* select transparent brush (for drawing the focus box) */
3893 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3895 if (uView == LVS_LIST)
3896 LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3897 else if (uView == LVS_REPORT)
3898 LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3899 else if (uView == LVS_SMALLICON)
3900 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3901 else if (uView == LVS_ICON)
3902 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3904 /* unselect objects */
3905 SelectObject(hdc, hOldFont);
3906 SelectObject(hdc, hOldPen);
3911 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3912 LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
3918 * Calculates the approximate width and height of a given number of items.
3921 * [I] HWND : window handle
3922 * [I] INT : number of items
3927 * Returns a DWORD. The width in the low word and the height in high word.
3929 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
3930 WORD wWidth, WORD wHeight)
3932 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3933 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3934 INT nItemCountPerColumn = 1;
3935 INT nColumnCount = 0;
3936 DWORD dwViewRect = 0;
3938 if (nItemCount == -1)
3939 nItemCount = GETITEMCOUNT(infoPtr);
3941 if (uView == LVS_LIST)
3943 if (wHeight == 0xFFFF)
3945 /* use current height */
3946 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3949 if (wHeight < infoPtr->nItemHeight)
3950 wHeight = infoPtr->nItemHeight;
3954 if (infoPtr->nItemHeight > 0)
3956 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3957 if (nItemCountPerColumn == 0)
3958 nItemCountPerColumn = 1;
3960 if (nItemCount % nItemCountPerColumn != 0)
3961 nColumnCount = nItemCount / nItemCountPerColumn;
3963 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3967 /* Microsoft padding magic */
3968 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3969 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3971 dwViewRect = MAKELONG(wWidth, wHeight);
3973 else if (uView == LVS_REPORT)
3974 FIXME("uView == LVS_REPORT: not implemented\n");
3975 else if (uView == LVS_SMALLICON)
3976 FIXME("uView == LVS_SMALLICON: not implemented\n");
3977 else if (uView == LVS_ICON)
3978 FIXME("uView == LVS_ICON: not implemented\n");
3985 * Arranges listview items in icon display mode.
3988 * [I] HWND : window handle
3989 * [I] INT : alignment code
3995 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
3997 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3998 BOOL bResult = FALSE;
4000 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4005 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
4008 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
4011 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4013 case LVA_SNAPTOGRID:
4014 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4022 /* << LISTVIEW_CreateDragImage >> */
4027 * Removes all listview items and subitems.
4030 * [I] HWND : window handle
4036 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
4038 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4039 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4040 UINT uView = lStyle & LVS_TYPEMASK;
4041 LISTVIEW_ITEM *lpItem;
4042 LISTVIEW_SUBITEM *lpSubItem;
4045 BOOL bResult = FALSE;
4048 TRACE("(hwnd=%x,)\n", hwnd);
4050 LISTVIEW_RemoveAllSelections(hwnd);
4051 infoPtr->nSelectionMark=-1;
4052 infoPtr->nFocusedItem=-1;
4053 /* But we are supposed to leave nHotItem as is! */
4055 if (lStyle & LVS_OWNERDATA)
4057 infoPtr->hdpaItems->nItemCount = 0;
4058 InvalidateRect(hwnd, NULL, TRUE);
4062 if (GETITEMCOUNT(infoPtr) > 0)
4066 /* send LVN_DELETEALLITEMS notification */
4067 /* verify if subsequent LVN_DELETEITEM notifications should be
4069 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4071 bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &nmlv);
4073 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4075 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4076 if (hdpaSubItems != NULL)
4078 for (j = 1; j < hdpaSubItems->nItemCount; j++)
4080 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
4081 if (lpSubItem != NULL)
4083 /* free subitem string */
4084 if (is_textW(lpSubItem->pszText))
4085 COMCTL32_Free(lpSubItem->pszText);
4088 COMCTL32_Free(lpSubItem);
4092 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4097 /* send LVN_DELETEITEM notification */
4099 nmlv.lParam = lpItem->lParam;
4100 listview_notify(hwnd, LVN_DELETEITEM, &nmlv);
4103 /* free item string */
4104 if (is_textW(lpItem->pszText))
4105 COMCTL32_Free(lpItem->pszText);
4108 COMCTL32_Free(lpItem);
4111 DPA_Destroy(hdpaSubItems);
4115 /* reinitialize listview memory */
4116 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
4118 /* align items (set position of each item) */
4119 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4121 if (lStyle & LVS_ALIGNLEFT)
4123 LISTVIEW_AlignLeft(hwnd);
4127 LISTVIEW_AlignTop(hwnd);
4131 LISTVIEW_UpdateScroll(hwnd);
4133 /* invalidate client area (optimization needed) */
4134 InvalidateRect(hwnd, NULL, TRUE);
4142 * Removes a column from the listview control.
4145 * [I] HWND : window handle
4146 * [I] INT : column index
4152 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
4154 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4155 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4156 UINT uOwnerData = GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA;
4157 BOOL bResult = FALSE;
4159 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
4162 bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
4164 /* Need to reset the item width when deleting a column */
4165 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
4167 /* reset scroll parameters */
4168 if (uView == LVS_REPORT)
4170 /* update scrollbar(s) */
4171 LISTVIEW_UpdateScroll(hwnd);
4173 /* refresh client area */
4174 InvalidateRect(hwnd, NULL, FALSE);
4183 * Removes an item from the listview control.
4186 * [I] HWND : window handle
4187 * [I] INT : item index
4193 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
4195 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4196 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4197 UINT uView = lStyle & LVS_TYPEMASK;
4198 LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID);
4200 BOOL bResult = FALSE;
4202 LISTVIEW_ITEM *lpItem;
4203 LISTVIEW_SUBITEM *lpSubItem;
4207 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
4210 /* First, send LVN_DELETEITEM notification. */
4211 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4212 nmlv.hdr.hwndFrom = hwnd;
4213 nmlv.hdr.idFrom = lCtrlId;
4214 nmlv.hdr.code = LVN_DELETEITEM;
4216 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
4220 /* remove it from the selection range */
4221 ZeroMemory(&item,sizeof(item));
4222 item.stateMask = LVIS_SELECTED;
4223 LISTVIEW_SetItemState(hwnd,nItem,&item);
4225 if (lStyle & LVS_OWNERDATA)
4227 infoPtr->hdpaItems->nItemCount --;
4228 InvalidateRect(hwnd, NULL, TRUE);
4232 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4234 /* initialize memory */
4235 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4237 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4238 if (hdpaSubItems != NULL)
4240 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4242 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4243 if (lpSubItem != NULL)
4245 /* free item string */
4246 if (is_textW(lpSubItem->pszText))
4247 COMCTL32_Free(lpSubItem->pszText);
4250 COMCTL32_Free(lpSubItem);
4254 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4257 /* free item string */
4258 if (is_textW(lpItem->pszText))
4259 COMCTL32_Free(lpItem->pszText);
4262 COMCTL32_Free(lpItem);
4265 bResult = DPA_Destroy(hdpaSubItems);
4268 LISTVIEW_ShiftIndices(hwnd,nItem,-1);
4270 /* align items (set position of each item) */
4271 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4273 if (lStyle & LVS_ALIGNLEFT)
4274 LISTVIEW_AlignLeft(hwnd);
4276 LISTVIEW_AlignTop(hwnd);
4279 LISTVIEW_UpdateScroll(hwnd);
4281 /* refresh client area */
4282 InvalidateRect(hwnd, NULL, TRUE);
4291 * Return edit control handle of current edit label
4294 * [I] HWND : window handle
4300 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4302 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4303 return infoPtr->hwndEdit;
4309 * Callback implementation for editlabel control
4312 * [I] HWND : window handle
4313 * [I] LPSTR : modified text
4314 * [I] DWORD : item index
4315 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4321 static BOOL LISTVIEW_EndEditLabelT(HWND hwnd, LPWSTR pszText, DWORD nItem, BOOL isW)
4323 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4324 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4325 NMLVDISPINFOW dispInfo;
4326 LISTVIEW_ITEM *lpItem;
4328 LISTVIEW_ITEM lvItemRef;
4330 BOOL bResult = TRUE;
4332 TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd, debugstr_t(pszText, isW), nItem, isW);
4334 if (!(lStyle & LVS_OWNERDATA))
4336 if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4339 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4344 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4345 ZeroMemory(&item,sizeof(item));
4348 item.mask = LVIF_PARAM | LVIF_STATE;
4349 ListView_GetItemW(hwnd, &item);
4350 lvItemRef.state = item.state;
4351 lvItemRef.iImage = item.iImage;
4352 lvItemRef.lParam = item.lParam;
4353 lpItem = &lvItemRef;
4356 ZeroMemory(&dispInfo, sizeof(dispInfo));
4357 dispInfo.item.mask = 0;
4358 dispInfo.item.iItem = nItem;
4359 dispInfo.item.state = lpItem->state;
4360 dispInfo.item.stateMask = 0;
4361 dispInfo.item.pszText = pszText;
4362 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4363 dispInfo.item.iImage = lpItem->iImage;
4364 dispInfo.item.lParam = lpItem->lParam;
4365 infoPtr->hwndEdit = 0;
4367 /* Do we need to update the Item Text */
4368 if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, isW))
4369 if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
4370 bResult = textsetptrT(&lpItem->pszText, pszText, isW);
4377 * Callback implementation for editlabel control
4380 * [I] HWND : window handle
4381 * [I] LPSTR : modified text
4382 * [I] DWORD : item index
4388 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem)
4390 return LISTVIEW_EndEditLabelT(hwnd, pszText, nItem, TRUE);
4395 * Callback implementation for editlabel control
4398 * [I] HWND : window handle
4399 * [I] LPSTR : modified text
4400 * [I] DWORD : item index
4406 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem)
4408 return LISTVIEW_EndEditLabelT(hwnd, (LPWSTR)pszText, nItem, FALSE);
4413 * Begin in place editing of specified list view item
4416 * [I] HWND : window handle
4417 * [I] INT : item index
4418 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4424 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW)
4426 NMLVDISPINFOW dispInfo;
4428 LISTVIEW_ITEM *lpItem;
4430 HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE);
4431 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4433 WCHAR szDispText[DISP_TEXT_SIZE];
4435 LISTVIEW_ITEM lvItemRef;
4436 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4438 if (~GetWindowLongW(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4441 /* Is the EditBox still there, if so remove it */
4442 if(infoPtr->hwndEdit != 0)
4445 LISTVIEW_SetSelection(hwnd, nItem);
4446 LISTVIEW_SetItemFocus(hwnd, nItem);
4448 if (!(lStyle & LVS_OWNERDATA))
4450 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4453 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4459 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4460 ZeroMemory(&item, sizeof(item));
4463 item.mask = LVIF_PARAM | LVIF_STATE;
4464 ListView_GetItemW(hwnd, &item);
4465 lvItemRef.iImage = item.iImage;
4466 lvItemRef.state = item.state;
4467 lvItemRef.lParam = item.lParam;
4468 lpItem = &lvItemRef;
4471 /* get information needed for drawing the item */
4472 ZeroMemory(&lvItem, sizeof(lvItem));
4473 lvItem.mask = LVIF_TEXT;
4474 lvItem.iItem = nItem;
4475 lvItem.iSubItem = 0;
4476 lvItem.cchTextMax = DISP_TEXT_SIZE;
4477 lvItem.pszText = szDispText;
4478 *lvItem.pszText = '\0';
4479 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW);
4481 ZeroMemory(&dispInfo, sizeof(dispInfo));
4482 dispInfo.item.mask = 0;
4483 dispInfo.item.iItem = nItem;
4484 dispInfo.item.state = lpItem->state;
4485 dispInfo.item.stateMask = 0;
4486 dispInfo.item.pszText = lvItem.pszText;
4487 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4488 dispInfo.item.iImage = lpItem->iImage;
4489 dispInfo.item.lParam = lpItem->lParam;
4491 if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW))
4494 rect.left = LVIR_LABEL;
4495 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4498 if (!(hedit = CreateEditLabelT(szDispText , WS_VISIBLE,
4499 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, hwnd, hinst,
4500 isW ? LISTVIEW_EndEditLabelW : (EditlblCallbackW)LISTVIEW_EndEditLabelA,
4504 infoPtr->hwndEdit = hedit;
4506 SendMessageW(hedit, EM_SETSEL, 0, -1);
4514 * Ensures the specified item is visible, scrolling into view if necessary.
4517 * [I] HWND : window handle
4518 * [I] INT : item index
4519 * [I] BOOL : partially or entirely visible
4525 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4527 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4528 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4529 INT nScrollPosHeight = 0;
4530 INT nScrollPosWidth = 0;
4531 SCROLLINFO scrollInfo;
4533 BOOL bRedraw = FALSE;
4535 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4536 scrollInfo.cbSize = sizeof(SCROLLINFO);
4537 scrollInfo.fMask = SIF_POS;
4539 /* ALWAYS bPartial == FALSE, FOR NOW! */
4541 rcItem.left = LVIR_BOUNDS;
4542 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4544 if (rcItem.left < infoPtr->rcList.left)
4546 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4550 if (uView == LVS_LIST)
4552 nScrollPosWidth = infoPtr->nItemWidth;
4553 rcItem.left += infoPtr->rcList.left;
4555 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4557 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4558 rcItem.left += infoPtr->rcList.left;
4561 /* When in LVS_REPORT view, the scroll position should
4563 if (nScrollPosWidth != 0)
4565 if (rcItem.left % nScrollPosWidth == 0)
4566 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4568 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4570 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4574 else if (rcItem.right > infoPtr->rcList.right)
4576 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4580 if (uView == LVS_LIST)
4582 rcItem.right -= infoPtr->rcList.right;
4583 nScrollPosWidth = infoPtr->nItemWidth;
4585 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4587 rcItem.right -= infoPtr->rcList.right;
4588 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4591 /* When in LVS_REPORT view, the scroll position should
4593 if (nScrollPosWidth != 0)
4595 if (rcItem.right % nScrollPosWidth == 0)
4596 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4598 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4600 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4605 if (rcItem.top < infoPtr->rcList.top)
4609 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4611 if (uView == LVS_REPORT)
4613 rcItem.top -= infoPtr->rcList.top;
4614 nScrollPosHeight = infoPtr->nItemHeight;
4616 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4618 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4619 rcItem.top += infoPtr->rcList.top;
4622 if (rcItem.top % nScrollPosHeight == 0)
4623 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4625 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4627 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4630 else if (rcItem.bottom > infoPtr->rcList.bottom)
4634 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4636 if (uView == LVS_REPORT)
4638 rcItem.bottom -= infoPtr->rcList.bottom;
4639 nScrollPosHeight = infoPtr->nItemHeight;
4641 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4643 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4644 rcItem.bottom -= infoPtr->rcList.bottom;
4647 if (rcItem.bottom % nScrollPosHeight == 0)
4648 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4650 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4652 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4658 InvalidateRect(hwnd,NULL,TRUE);
4664 * Retrieves the nearest item, given a position and a direction.
4667 * [I] HWND : window handle
4668 * [I] POINT : start position
4669 * [I] UINT : direction
4672 * Item index if successdful, -1 otherwise.
4674 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4676 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4681 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4682 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4683 ((vkDirection == VK_UP) ? "VK_UP" :
4684 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4686 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4688 ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4689 LISTVIEW_GetOrigin(hwnd, &lvIntHit.ht.pt);
4690 lvIntHit.ht.pt.x += pt.x;
4691 lvIntHit.ht.pt.y += pt.y;
4693 if (vkDirection == VK_DOWN)
4694 lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4695 else if (vkDirection == VK_UP)
4696 lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4697 else if (vkDirection == VK_LEFT)
4698 lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4699 else if (vkDirection == VK_RIGHT)
4700 lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4702 if (PtInRect(&rcView, lvIntHit.ht.pt) == FALSE)
4706 nItem = LISTVIEW_SuperHitTestItem(hwnd, &lvIntHit, TRUE);
4707 return nItem == -1 ? lvIntHit.iDistItem : nItem;
4716 * Searches for an item with specific characteristics.
4719 * [I] hwnd : window handle
4720 * [I] nStart : base item index
4721 * [I] lpFindInfo : item information to look for
4724 * SUCCESS : index of item
4727 static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart,
4728 LPLVFINDINFOW lpFindInfo)
4730 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4732 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4736 INT nLast = GETITEMCOUNT(infoPtr);
4738 if ((nItem >= -1) && (lpFindInfo != NULL))
4740 ZeroMemory(&lvItem, sizeof(lvItem));
4742 if (lpFindInfo->flags & LVFI_PARAM)
4744 lvItem.mask |= LVIF_PARAM;
4747 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4749 lvItem.mask |= LVIF_TEXT;
4750 lvItem.pszText = szDispText;
4751 lvItem.cchTextMax = DISP_TEXT_SIZE;
4754 if (lpFindInfo->flags & LVFI_WRAP)
4757 if (lpFindInfo->flags & LVFI_NEARESTXY)
4759 ptItem.x = lpFindInfo->pt.x;
4760 ptItem.y = lpFindInfo->pt.y;
4765 while (nItem < nLast)
4767 if (lpFindInfo->flags & LVFI_NEARESTXY)
4769 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4770 lpFindInfo->vkDirection);
4773 /* get position of the new item index */
4774 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4785 lvItem.iItem = nItem;
4786 lvItem.iSubItem = 0;
4787 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
4789 if (lvItem.mask & LVIF_TEXT)
4791 if (lpFindInfo->flags & LVFI_PARTIAL)
4793 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4798 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4803 if (lvItem.mask & LVIF_PARAM)
4805 if (lpFindInfo->lParam != lvItem.lParam)
4831 * Searches for an item with specific characteristics.
4834 * [I] hwnd : window handle
4835 * [I] nStart : base item index
4836 * [I] lpFindInfo : item information to look for
4839 * SUCCESS : index of item
4842 static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart,
4843 LPLVFINDINFOA lpFindInfo)
4845 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4849 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4850 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4851 res = LISTVIEW_FindItemW(hwnd, nStart, &fiw);
4852 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4858 * Retrieves the background color of the listview control.
4861 * [I] HWND : window handle
4864 * COLORREF associated with the background.
4866 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4868 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4870 return infoPtr->clrBk;
4875 * Retrieves the background image of the listview control.
4878 * [I] HWND : window handle
4879 * [O] LPLVMKBIMAGE : background image attributes
4885 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4887 /* FIXME (listview, "empty stub!\n"); */
4893 * Retrieves the callback mask.
4896 * [I] HWND : window handle
4901 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4903 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4905 return infoPtr->uCallbackMask;
4910 * Retrieves column attributes.
4913 * [I] HWND : window handle
4914 * [I] INT : column index
4915 * [IO] LPLVCOLUMNW : column information
4916 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4917 * otherwise it is in fact a LPLVCOLUMNA
4923 static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4925 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4927 BOOL bResult = FALSE;
4929 if (lpColumn != NULL)
4932 TRACE("(hwnd=%x, col=%d, lpColumn=%s, isW=%d)\n",
4933 hwnd, nItem, debuglvcolumn_t(lpColumn, isW), isW);
4935 /* initialize memory */
4936 ZeroMemory(&hdi, sizeof(hdi));
4938 if (lpColumn->mask & LVCF_FMT)
4939 hdi.mask |= HDI_FORMAT;
4941 if (lpColumn->mask & LVCF_WIDTH)
4942 hdi.mask |= HDI_WIDTH;
4944 if (lpColumn->mask & LVCF_TEXT)
4946 hdi.mask |= HDI_TEXT;
4947 hdi.cchTextMax = lpColumn->cchTextMax;
4948 hdi.pszText = lpColumn->pszText;
4951 if (lpColumn->mask & LVCF_IMAGE)
4952 hdi.mask |= HDI_IMAGE;
4954 if (lpColumn->mask & LVCF_ORDER)
4955 hdi.mask |= HDI_ORDER;
4958 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4960 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4962 if (bResult != FALSE)
4964 if (lpColumn->mask & LVCF_FMT)
4968 if (hdi.fmt & HDF_LEFT)
4969 lpColumn->fmt |= LVCFMT_LEFT;
4970 else if (hdi.fmt & HDF_RIGHT)
4971 lpColumn->fmt |= LVCFMT_RIGHT;
4972 else if (hdi.fmt & HDF_CENTER)
4973 lpColumn->fmt |= LVCFMT_CENTER;
4975 if (hdi.fmt & HDF_IMAGE)
4976 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4978 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4979 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4982 if (lpColumn->mask & LVCF_WIDTH)
4983 lpColumn->cx = hdi.cxy;
4985 if (lpColumn->mask & LVCF_IMAGE)
4986 lpColumn->iImage = hdi.iImage;
4988 if (lpColumn->mask & LVCF_ORDER)
4989 lpColumn->iOrder = hdi.iOrder;
4997 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
4999 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
5006 for (i = 0; i < iCount; i++)
5014 * Retrieves the column width.
5017 * [I] HWND : window handle
5018 * [I] int : column index
5021 * SUCCESS : column width
5024 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
5026 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5027 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5028 INT nColumnWidth = 0;
5031 if (uView == LVS_LIST)
5033 nColumnWidth = infoPtr->nItemWidth;
5035 else if (uView == LVS_REPORT)
5037 /* get column width from header */
5038 ZeroMemory(&hdi, sizeof(hdi));
5039 hdi.mask = HDI_WIDTH;
5040 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
5041 nColumnWidth = hdi.cxy;
5044 return nColumnWidth;
5049 * In list or report display mode, retrieves the number of items that can fit
5050 * vertically in the visible area. In icon or small icon display mode,
5051 * retrieves the total number of visible items.
5054 * [I] HWND : window handle
5057 * Number of fully visible items.
5059 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
5061 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5062 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5065 if (uView == LVS_LIST)
5067 if (infoPtr->rcList.right > infoPtr->nItemWidth)
5069 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
5070 LISTVIEW_GetCountPerColumn(hwnd);
5073 else if (uView == LVS_REPORT)
5075 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
5079 nItemCount = GETITEMCOUNT(infoPtr);
5085 /* LISTVIEW_GetEditControl */
5089 * Retrieves the extended listview style.
5092 * [I] HWND : window handle
5095 * SUCCESS : previous style
5098 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
5100 LISTVIEW_INFO *infoPtr;
5102 /* make sure we can get the listview info */
5103 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
5106 return (infoPtr->dwExStyle);
5111 * Retrieves the handle to the header control.
5114 * [I] HWND : window handle
5119 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
5121 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5123 return infoPtr->hwndHeader;
5126 /* LISTVIEW_GetHotCursor */
5130 * Returns the time that the mouse cursor must hover over an item
5131 * before it is selected.
5134 * [I] HWND : window handle
5137 * Returns the previously set hover time or (DWORD)-1 to indicate that the
5138 * hover time is set to the default hover time.
5140 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
5142 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5144 return infoPtr->dwHoverTime;
5149 * Retrieves an image list handle.
5152 * [I] HWND : window handle
5153 * [I] INT : image list identifier
5156 * SUCCESS : image list handle
5159 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
5161 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5162 HIMAGELIST himl = NULL;
5167 himl = infoPtr->himlNormal;
5170 himl = infoPtr->himlSmall;
5173 himl = infoPtr->himlState;
5177 return (LRESULT)himl;
5180 /* LISTVIEW_GetISearchString */
5184 * Retrieves item attributes.
5187 * [I] hwnd : window handle
5188 * [IO] lpLVItem : item info
5189 * [I] internal : if true then we will use tricks that avoid copies
5190 * but are not compatible with the regular interface
5191 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5192 * if FALSE, the lpLVItem is a LPLVITEMA.
5198 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
5200 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5201 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5202 NMLVDISPINFOW dispInfo;
5203 LISTVIEW_SUBITEM *lpSubItem;
5204 LISTVIEW_ITEM *lpItem;
5207 INT* piImage = (INT*)&null;
5208 LPWSTR* ppszText= (LPWSTR*)&null;
5209 LPARAM* plParam = (LPARAM*)&null;
5211 if (internal && !isW)
5213 ERR("We can't have internal non-Unicode GetItem!\n");
5217 /* In the following:
5218 * lpLVItem describes the information requested by the user
5219 * lpItem/lpSubItem is what we have
5220 * dispInfo is a structure we use to request the missing
5221 * information from the application
5224 TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n",
5225 hwnd, debuglvitem_t(lpLVItem, isW), internal, isW);
5227 if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5228 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5231 ZeroMemory(&dispInfo, sizeof(dispInfo));
5233 if (lStyle & LVS_OWNERDATA)
5235 if (lpLVItem->mask & ~LVIF_STATE)
5237 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5238 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5239 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5240 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5243 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5245 lpLVItem->state = 0;
5246 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5247 lpLVItem->state |= LVIS_FOCUSED;
5248 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5249 lpLVItem->state |= LVIS_SELECTED;
5255 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5256 if (hdpaSubItems == NULL) return FALSE;
5258 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5261 ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5262 if (lpLVItem->iSubItem == 0)
5264 piImage=&lpItem->iImage;
5265 ppszText=&lpItem->pszText;
5266 plParam=&lpItem->lParam;
5267 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5269 dispInfo.item.mask |= LVIF_STATE;
5270 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5275 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5276 if (lpSubItem != NULL)
5278 piImage=&lpSubItem->iImage;
5279 ppszText=&lpSubItem->pszText;
5283 if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
5285 dispInfo.item.mask |= LVIF_IMAGE;
5288 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5290 dispInfo.item.mask |= LVIF_TEXT;
5291 dispInfo.item.pszText = lpLVItem->pszText;
5292 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5293 if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5294 *dispInfo.item.pszText = '\0';
5295 if (dispInfo.item.pszText && (*ppszText == NULL))
5296 *dispInfo.item.pszText = '\0';
5299 if (dispInfo.item.mask != 0)
5301 /* We don't have all the requested info, query the application */
5302 dispInfo.item.iItem = lpLVItem->iItem;
5303 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5304 dispInfo.item.lParam = lpItem->lParam;
5305 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5306 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5309 if (dispInfo.item.mask & LVIF_IMAGE)
5311 lpLVItem->iImage = dispInfo.item.iImage;
5312 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
5313 *piImage = dispInfo.item.iImage;
5315 else if (lpLVItem->mask & LVIF_IMAGE)
5317 lpLVItem->iImage = *piImage;
5320 if (dispInfo.item.mask & LVIF_PARAM)
5322 lpLVItem->lParam = dispInfo.item.lParam;
5323 if (dispInfo.item.mask & LVIF_DI_SETITEM)
5324 *plParam = dispInfo.item.lParam;
5326 else if (lpLVItem->mask & LVIF_PARAM)
5327 lpLVItem->lParam = lpItem->lParam;
5329 if (dispInfo.item.mask & LVIF_TEXT)
5331 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5332 textsetptrT(ppszText, dispInfo.item.pszText, isW);
5334 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5335 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5336 if (lpLVItem->pszText != dispInfo.item.pszText)
5337 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5340 else if (lpLVItem->mask & LVIF_TEXT)
5342 if (internal) lpLVItem->pszText = *ppszText;
5343 else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5346 if (lpLVItem->iSubItem == 0)
5348 if (dispInfo.item.mask & LVIF_STATE)
5350 lpLVItem->state = lpItem->state;
5351 lpLVItem->state &= ~dispInfo.item.stateMask;
5352 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5354 lpLVItem->state &= ~LVIS_SELECTED;
5355 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5356 LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem))
5357 lpLVItem->state |= LVIS_SELECTED;
5359 else if (lpLVItem->mask & LVIF_STATE)
5361 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5363 lpLVItem->state &= ~LVIS_SELECTED;
5364 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5365 LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5366 lpLVItem->state |= LVIS_SELECTED;
5369 if (lpLVItem->mask & LVIF_PARAM)
5370 lpLVItem->lParam = lpItem->lParam;
5372 if (lpLVItem->mask & LVIF_INDENT)
5373 lpLVItem->iIndent = lpItem->iIndent;
5379 /* LISTVIEW_GetHotCursor */
5383 * Retrieves the index of the hot item.
5386 * [I] HWND : window handle
5389 * SUCCESS : hot item index
5390 * FAILURE : -1 (no hot item)
5392 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5394 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5396 return infoPtr->nHotItem;
5399 /* LISTVIEW_GetHoverTime */
5403 * Retrieves the number of items in the listview control.
5406 * [I] HWND : window handle
5411 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5413 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5415 return GETITEMCOUNT(infoPtr);
5420 * Retrieves the rectangle enclosing the item icon and text.
5423 * [I] HWND : window handle
5424 * [I] INT : item index
5425 * [O] LPRECT : coordinate information
5431 static BOOL LISTVIEW_GetItemBoundBox(HWND hwnd, INT nItem, LPRECT lpRect)
5433 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5434 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5435 UINT uView = lStyle & LVS_TYPEMASK;
5436 BOOL bResult = FALSE;
5438 LISTVIEW_ITEM *lpItem;
5439 INT nCountPerColumn;
5442 TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd, nItem, lpRect);
5444 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5447 if (uView == LVS_LIST)
5450 nItem = nItem - ListView_GetTopIndex(hwnd);
5451 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5454 nRow = nItem % nCountPerColumn;
5457 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5462 lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5463 lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5468 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5469 lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
5472 else if (uView == LVS_REPORT)
5475 lpRect->left = REPORT_MARGINX;
5476 lpRect->top = ((nItem - ListView_GetTopIndex(hwnd)) *
5477 infoPtr->nItemHeight) + infoPtr->rcList.top;
5479 if (!(lStyle & LVS_NOSCROLL))
5481 SCROLLINFO scrollInfo;
5482 /* Adjust position by scrollbar offset */
5483 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5484 scrollInfo.cbSize = sizeof(SCROLLINFO);
5485 scrollInfo.fMask = SIF_POS;
5486 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5487 lpRect->left -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5490 else /* either LVS_ICON or LVS_SMALLICON */
5492 if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
5494 if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
5497 lpRect->left = lpItem->ptPosition.x;
5498 lpRect->top = lpItem->ptPosition.y;
5503 lpRect->right = lpRect->left + infoPtr->nItemWidth;
5504 lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
5505 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5506 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
5512 * Retrieves the position (upper-left) of the listview control item.
5513 * Note that for LVS_ICON style, the upper-left is that of the icon
5514 * and not the bounding box.
5517 * [I] HWND : window handle
5518 * [I] INT : item index
5519 * [O] LPPOINT : coordinate information
5525 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, LPPOINT lpptPosition)
5527 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5528 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5529 BOOL bResult = FALSE;
5532 TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd, nItem, lpptPosition);
5534 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5535 (lpptPosition != NULL))
5537 bResult = LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcBounding);
5538 lpptPosition->x = rcBounding.left;
5539 lpptPosition->y = rcBounding.top;
5540 if (uView == LVS_ICON)
5542 lpptPosition->y += ICON_TOP_PADDING;
5543 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5545 TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
5546 lpptPosition->x, lpptPosition->y);
5552 * Update the bounding rectangle around the text under a large icon.
5553 * This depends on whether it has the focus or not.
5554 * On entry the rectangle's top, left and right should be set.
5555 * On return the bottom will also be set and the width may have been
5558 * This appears to be weird, even in the Microsoft implementation.
5561 static void ListView_UpdateLargeItemLabelRect (
5562 HWND hwnd, /* The window of the listview */
5563 const LISTVIEW_INFO *infoPtr, /* The listview itself */
5564 int nItem, /* The item for which we are calculating this */
5565 RECT *rect) /* The rectangle to be updated */
5567 HDC hdc = GetDC (hwnd);
5568 HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
5570 if (infoPtr->bFocus && infoPtr->nFocusedItem == nItem)
5572 /* We (aim to) display the full text. In Windows 95 it appears to
5573 * calculate the size assuming the specified font and then it draws
5574 * the text in that region with the specified font except scaled to
5575 * 10 point (or the height of the system font or ...). Thus if the
5576 * window has 24 point Helvetica the highlit rectangle will be
5577 * taller than the text and if it is 7 point Helvetica then the text
5579 * For now we will simply say that it is the correct size to display
5580 * the text in the specified font.
5583 lvItem.mask = LVIF_TEXT;
5584 lvItem.iItem = nItem;
5585 lvItem.iSubItem = 0;
5586 /* We will specify INTERNAL and so will receive back a const
5587 * pointer to the text, rather than specifying a buffer to which
5590 LISTVIEW_GetItemW (hwnd, &lvItem, TRUE);
5591 DrawTextW (hdc, lvItem.pszText, -1, rect, DT_CALCRECT |
5592 DT_NOCLIP | DT_EDITCONTROL | DT_TOP | DT_CENTER |
5593 DT_WORDBREAK | DT_NOPREFIX);
5594 /* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
5598 /* As far as I can see the text region seems to be trying to be
5599 * "tall enough for two lines of text". Once again (comctl32.dll ver
5600 * 5.81?) it measures this on the basis of the selected font and then
5601 * draws it with the same font except in 10 point size. This can lead
5602 * to more or less than the two rows appearing.
5603 * Question; are we supposed to be including DT_EXTERNALLEADING?
5604 * Question; should the width be shrunk to the space required to
5605 * display the two lines?
5607 rect->bottom = rect->top + 2 * infoPtr->ntmHeight;
5610 SelectObject (hdc, hOldFont);
5611 ReleaseDC (hwnd, hdc);
5616 * Retrieves the bounding rectangle for a listview control item.
5619 * [I] HWND : window handle
5620 * [I] INT : item index
5621 * [IO] LPRECT : bounding rectangle coordinates
5622 * lprc->left specifies the portion of the item for which the bounding
5623 * rectangle will be retrieved.
5625 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5626 * including the icon and label.
5627 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5628 * LVIR_LABEL Returns the bounding rectangle of the item text.
5629 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5630 * rectangles, but excludes columns in report view.
5637 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5638 * upon whether the window has the focus currently and on whether the item
5639 * is the one with the focus. Ensure that the control's record of which
5640 * item has the focus agrees with the items' records.
5642 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5644 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5645 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5646 BOOL bResult = FALSE;
5655 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5657 if (uView & LVS_REPORT)
5659 ZeroMemory(&lvItem, sizeof(lvItem));
5660 lvItem.mask = LVIF_INDENT;
5661 lvItem.iItem = nItem;
5662 lvItem.iSubItem = 0;
5663 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
5666 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5667 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5674 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5679 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5680 if (uView == LVS_ICON)
5682 if (infoPtr->himlNormal != NULL)
5684 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5687 lprc->left = ptItem.x + ptOrigin.x;
5688 lprc->top = ptItem.y + ptOrigin.y;
5689 lprc->right = lprc->left + infoPtr->iconSize.cx;
5690 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5691 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5695 else if (uView == LVS_SMALLICON)
5697 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5700 lprc->left = ptItem.x + ptOrigin.x;
5701 lprc->top = ptItem.y + ptOrigin.y;
5702 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5704 if (infoPtr->himlState != NULL)
5705 lprc->left += infoPtr->iconSize.cx;
5707 if (infoPtr->himlSmall != NULL)
5708 lprc->right = lprc->left + infoPtr->iconSize.cx;
5710 lprc->right = lprc->left;
5716 lprc->left = ptItem.x;
5717 if (uView & LVS_REPORT)
5718 lprc->left += nIndent;
5719 lprc->top = ptItem.y;
5720 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5722 if (infoPtr->himlState != NULL)
5723 lprc->left += infoPtr->iconSize.cx;
5725 if (infoPtr->himlSmall != NULL)
5726 lprc->right = lprc->left + infoPtr->iconSize.cx;
5728 lprc->right = lprc->left;
5733 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5734 if (uView == LVS_ICON)
5736 if (infoPtr->himlNormal != NULL)
5738 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5741 lprc->left = ptItem.x + ptOrigin.x;
5742 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5743 ICON_BOTTOM_PADDING);
5744 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5745 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5747 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5748 lprc->right = lprc->left + nLabelWidth;
5753 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5754 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, lprc);
5759 else if (uView == LVS_SMALLICON)
5761 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5764 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5765 lprc->top = ptItem.y + ptOrigin.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->left += infoPtr->iconSize.cx;
5774 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5775 nLabelWidth += TRAILING_PADDING;
5776 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5777 lprc->right = lprc->left + nLabelWidth;
5779 lprc->right = nLeftPos + infoPtr->nItemWidth;
5785 if (uView == LVS_REPORT)
5786 nLeftPos = lprc->left = ptItem.x + nIndent;
5788 nLeftPos = lprc->left = ptItem.x;
5789 lprc->top = ptItem.y;
5790 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5792 if (infoPtr->himlState != NULL)
5793 lprc->left += infoPtr->iconSize.cx;
5795 if (infoPtr->himlSmall != NULL)
5796 lprc->left += infoPtr->iconSize.cx;
5798 if (uView != LVS_REPORT)
5800 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5801 nLabelWidth += TRAILING_PADDING;
5802 if (infoPtr->himlSmall)
5803 nLabelWidth += IMAGE_PADDING;
5806 nLabelWidth = LISTVIEW_GetColumnWidth(hwnd, 0)-lprc->left;
5807 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5808 lprc->right = lprc->left + nLabelWidth;
5810 lprc->right = nLeftPos + infoPtr->nItemWidth;
5815 if (!LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcInternal)) break;
5816 ptItem.x = rcInternal.left;
5817 ptItem.y = rcInternal.top;
5818 if (uView == LVS_ICON)
5820 if (infoPtr->himlNormal != NULL)
5822 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5825 INT text_left, text_right, icon_left, text_pos_x;
5826 /* for style LVS_ICON bounds
5827 * left = min(icon.left, text.left)
5828 * right = max(icon.right, text.right)
5829 * top = boundbox.top + NOTHITABLE
5830 * bottom = text.bottom + 1
5833 icon_left = text_left = ptItem.x;
5835 /* Correct ptItem to icon upper-left */
5836 icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5837 ptItem.y += ICON_TOP_PADDING;
5839 /* Compute the label left and right */
5840 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5841 text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
5844 text_left += text_pos_x / 2;
5845 text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
5850 text_right = text_left + infoPtr->iconSpacing.cx - 1;
5853 /* Compute rectangle w/o the text height */
5854 lprc->left = min(icon_left, text_left) + ptOrigin.x;
5855 lprc->right = max(icon_left + infoPtr->iconSize.cx,
5856 text_right) + ptOrigin.x;
5857 lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
5858 lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
5859 + infoPtr->iconSize.cy + 1
5860 + ICON_BOTTOM_PADDING;
5862 CopyRect (&label_rect, lprc);
5863 label_rect.top = lprc->bottom;
5864 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, &label_rect);
5865 UnionRect (lprc, lprc, &label_rect);
5869 else if (uView == LVS_SMALLICON)
5871 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5874 lprc->left = ptItem.x + ptOrigin.x;
5875 lprc->right = lprc->left;
5876 lprc->top = ptItem.y + ptOrigin.y;
5877 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5878 if (infoPtr->himlState != NULL)
5879 lprc->right += infoPtr->iconSize.cx;
5880 if (infoPtr->himlSmall != NULL)
5881 lprc->right += infoPtr->iconSize.cx;
5883 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5884 nLabelWidth += TRAILING_PADDING;
5885 if (infoPtr->himlSmall)
5886 nLabelWidth += IMAGE_PADDING;
5887 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5888 lprc->right += nLabelWidth;
5890 lprc->right = lprc->left + infoPtr->nItemWidth;
5896 lprc->left = ptItem.x;
5897 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5898 lprc->left += nIndent;
5899 lprc->right = lprc->left;
5900 lprc->top = ptItem.y;
5901 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5903 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
5906 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5907 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5909 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5913 if (infoPtr->himlState != NULL)
5914 lprc->right += infoPtr->iconSize.cx;
5916 if (infoPtr->himlSmall != NULL)
5917 lprc->right += infoPtr->iconSize.cx;
5919 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5920 nLabelWidth += TRAILING_PADDING;
5921 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5922 lprc->right += nLabelWidth;
5924 lprc->right = lprc->left + infoPtr->nItemWidth;
5929 case LVIR_SELECTBOUNDS:
5930 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5931 if (uView == LVS_ICON)
5933 if (infoPtr->himlNormal != NULL)
5935 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5938 lprc->left = ptItem.x + ptOrigin.x;
5939 lprc->top = ptItem.y + ptOrigin.y;
5940 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5941 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5945 else if (uView == LVS_SMALLICON)
5947 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5950 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5951 lprc->top = ptItem.y + ptOrigin.y;
5952 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5954 if (infoPtr->himlState != NULL)
5955 lprc->left += infoPtr->iconSize.cx;
5957 lprc->right = lprc->left;
5959 if (infoPtr->himlSmall != NULL)
5960 lprc->right += infoPtr->iconSize.cx;
5962 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5963 nLabelWidth += TRAILING_PADDING;
5964 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5965 lprc->right += nLabelWidth;
5967 lprc->right = nLeftPos + infoPtr->nItemWidth;
5973 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5974 nLeftPos = lprc->left = ptItem.x + nIndent;
5976 nLeftPos = lprc->left = ptItem.x;
5977 lprc->top = ptItem.y;
5978 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5980 if (infoPtr->himlState != NULL)
5981 lprc->left += infoPtr->iconSize.cx;
5983 lprc->right = lprc->left;
5985 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5988 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5989 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5991 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5995 if (infoPtr->himlSmall != NULL)
5996 lprc->right += infoPtr->iconSize.cx;
5998 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5999 nLabelWidth += TRAILING_PADDING;
6000 if (infoPtr->himlSmall)
6001 nLabelWidth += IMAGE_PADDING;
6002 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6003 lprc->right += nLabelWidth;
6005 lprc->right = nLeftPos + infoPtr->nItemWidth;
6012 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
6013 lprc->left, lprc->top, lprc->right, lprc->bottom);
6020 * Retrieves the width of a label.
6023 * [I] HWND : window handle
6026 * SUCCESS : string width (in pixels)
6029 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
6031 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6032 INT nLabelWidth = 0;
6035 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
6037 ZeroMemory(&lvItem, sizeof(lvItem));
6038 lvItem.mask = LVIF_TEXT;
6039 lvItem.iItem = nItem;
6040 lvItem.cchTextMax = DISP_TEXT_SIZE;
6041 lvItem.pszText = szDispText;
6042 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6043 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
6050 * Retrieves the spacing between listview control items.
6053 * [I] HWND : window handle
6054 * [I] BOOL : flag for small or large icon
6057 * Horizontal + vertical spacing
6059 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
6061 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6064 if (bSmall == FALSE)
6066 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6070 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
6071 if ((style & LVS_TYPEMASK) == LVS_ICON)
6072 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6074 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6081 * Retrieves the state of a listview control item.
6084 * [I] HWND : window handle
6085 * [I] INT : item index
6086 * [I] UINT : state mask
6089 * State specified by the mask.
6091 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
6093 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6097 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6099 ZeroMemory(&lvItem, sizeof(lvItem));
6100 lvItem.iItem = nItem;
6101 lvItem.stateMask = uMask;
6102 lvItem.mask = LVIF_STATE;
6103 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6104 uState = lvItem.state;
6112 * Retrieves the text of a listview control item or subitem.
6115 * [I] hwnd : window handle
6116 * [I] nItem : item index
6117 * [IO] lpLVItem : item information
6118 * [I] isW : TRUE if lpLVItem is Unicode
6121 * SUCCESS : string length
6124 static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6126 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6129 if (lpLVItem != NULL)
6131 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6133 lpLVItem->mask = LVIF_TEXT;
6134 lpLVItem->iItem = nItem;
6135 if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
6136 nLength = textlenT(lpLVItem->pszText, isW);
6145 * Searches for an item based on properties + relationships.
6148 * [I] HWND : window handle
6149 * [I] INT : item index
6150 * [I] INT : relationship flag
6153 * SUCCESS : item index
6156 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
6158 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6159 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6161 LVFINDINFOW lvFindInfo;
6162 INT nCountPerColumn;
6165 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6167 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6169 if (uFlags & LVNI_CUT)
6172 if (uFlags & LVNI_DROPHILITED)
6173 uMask |= LVIS_DROPHILITED;
6175 if (uFlags & LVNI_FOCUSED)
6176 uMask |= LVIS_FOCUSED;
6178 if (uFlags & LVNI_SELECTED)
6179 uMask |= LVIS_SELECTED;
6181 if (uFlags & LVNI_ABOVE)
6183 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6188 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6194 lvFindInfo.flags = LVFI_NEARESTXY;
6195 lvFindInfo.vkDirection = VK_UP;
6196 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6197 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6199 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6204 else if (uFlags & LVNI_BELOW)
6206 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6208 while (nItem < GETITEMCOUNT(infoPtr))
6211 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6217 lvFindInfo.flags = LVFI_NEARESTXY;
6218 lvFindInfo.vkDirection = VK_DOWN;
6219 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6220 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6222 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6227 else if (uFlags & LVNI_TOLEFT)
6229 if (uView == LVS_LIST)
6231 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6232 while (nItem - nCountPerColumn >= 0)
6234 nItem -= nCountPerColumn;
6235 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6239 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6241 lvFindInfo.flags = LVFI_NEARESTXY;
6242 lvFindInfo.vkDirection = VK_LEFT;
6243 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6244 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6246 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6251 else if (uFlags & LVNI_TORIGHT)
6253 if (uView == LVS_LIST)
6255 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6256 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6258 nItem += nCountPerColumn;
6259 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6263 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6265 lvFindInfo.flags = LVFI_NEARESTXY;
6266 lvFindInfo.vkDirection = VK_RIGHT;
6267 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6268 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6270 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6279 /* search by index */
6280 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6282 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
6291 /* LISTVIEW_GetNumberOfWorkAreas */
6295 * Retrieves the origin coordinates when in icon or small icon display mode.
6298 * [I] HWND : window handle
6299 * [O] LPPOINT : coordinate information
6305 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6307 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6308 UINT uView = lStyle & LVS_TYPEMASK;
6309 BOOL bResult = FALSE;
6311 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6313 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6315 SCROLLINFO scrollInfo;
6316 ZeroMemory(lpptOrigin, sizeof(POINT));
6317 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6318 scrollInfo.cbSize = sizeof(SCROLLINFO);
6320 if (lStyle & WS_HSCROLL)
6322 scrollInfo.fMask = SIF_POS;
6323 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6324 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6327 if (lStyle & WS_VSCROLL)
6329 scrollInfo.fMask = SIF_POS;
6330 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6331 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6342 * Retrieves the number of items that are marked as selected.
6345 * [I] HWND : window handle
6348 * Number of items selected.
6350 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6353 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6354 INT nSelectedCount = 0;
6357 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6359 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6363 return nSelectedCount;
6368 * Retrieves item index that marks the start of a multiple selection.
6371 * [I] HWND : window handle
6374 * Index number or -1 if there is no selection mark.
6376 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6378 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6380 return infoPtr->nSelectionMark;
6386 * Retrieves the width of a string.
6389 * [I] hwnd : window handle
6390 * [I] lpszText : text string to process
6391 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6394 * SUCCESS : string width (in pixels)
6397 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
6399 if (is_textT(lpszText, isW))
6401 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6402 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6403 HDC hdc = GetDC(hwnd);
6404 HFONT hOldFont = SelectObject(hdc, hFont);
6406 ZeroMemory(&stringSize, sizeof(SIZE));
6408 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6410 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6411 SelectObject(hdc, hOldFont);
6412 ReleaseDC(hwnd, hdc);
6413 return stringSize.cx;
6420 * Retrieves the text backgound color.
6423 * [I] HWND : window handle
6426 * COLORREF associated with the the background.
6428 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6430 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6432 return infoPtr->clrTextBk;
6437 * Retrieves the text color.
6440 * [I] HWND : window handle
6443 * COLORREF associated with the text.
6445 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6447 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6449 return infoPtr->clrText;
6454 * Determines item if a hit or closest if not
6457 * [I] HWND : window handle
6458 * [IO] LPLV_INTHIT : hit test information
6459 * [I] subitem : fill out iSubItem.
6462 * SUCCESS : item index of hit
6465 static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem)
6467 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6468 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6469 UINT uView = lStyle & LVS_TYPEMASK;
6470 INT i,topindex,bottomindex;
6472 DWORD xterm, yterm, dist;
6474 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.pt.y);
6476 topindex = ListView_GetTopIndex(hwnd);
6477 if (uView == LVS_REPORT)
6479 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6480 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6484 bottomindex = GETITEMCOUNT(infoPtr);
6487 lpInt->distance = 0x7fffffff;
6488 lpInt->iDistItem = -1;
6490 for (i = topindex; i < bottomindex; i++)
6492 rcItem.left = LVIR_BOUNDS;
6493 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6495 if (PtInRect(&rcItem, lpInt->ht.pt))
6497 rcItem.left = LVIR_ICON;
6498 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6500 if (PtInRect(&rcItem, lpInt->ht.pt))
6502 lpInt->ht.flags = LVHT_ONITEMICON;
6503 lpInt->ht.iItem = i;
6504 if (subitem) lpInt->ht.iSubItem = 0;
6509 rcItem.left = LVIR_LABEL;
6510 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6512 if (PtInRect(&rcItem, lpInt->ht.pt))
6514 lpInt->ht.flags = LVHT_ONITEMLABEL;
6515 lpInt->ht.iItem = i;
6516 if (subitem) lpInt->ht.iSubItem = 0;
6521 lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6522 lpInt->ht.iItem = i;
6523 if (subitem) lpInt->ht.iSubItem = 0;
6529 * Now compute distance from point to center of boundary
6530 * box. Since we are only interested in the relative
6531 * distance, we can skip the nasty square root operation
6533 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6534 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6535 dist = xterm * xterm + yterm * yterm;
6536 if (dist < lpInt->distance)
6538 lpInt->distance = dist;
6539 lpInt->iDistItem = i;
6545 lpInt->ht.flags = LVHT_NOWHERE;
6546 TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6553 * Determines which section of the item was selected (if any).
6556 * [I] HWND : window handle
6557 * [IO] LPLVHITTESTINFO : hit test information
6558 * [I] subitem : fill out iSubItem.
6561 * SUCCESS : item index
6564 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6567 LV_INTHIT lv_inthit;
6569 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6570 lpHitTestInfo->pt.y);
6572 memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6573 ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem);
6574 memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6580 * Determines which listview item is located at the specified position.
6583 * [I] HWND : window handle
6584 * [IO} LPLVHITTESTINFO : hit test information
6587 * SUCCESS : item index
6590 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6592 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6595 lpHitTestInfo->flags = 0;
6597 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6598 lpHitTestInfo->flags = LVHT_TOLEFT;
6599 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6600 lpHitTestInfo->flags = LVHT_TORIGHT;
6601 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6602 lpHitTestInfo->flags |= LVHT_ABOVE;
6603 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6604 lpHitTestInfo->flags |= LVHT_BELOW;
6606 if (lpHitTestInfo->flags == 0)
6608 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6609 * an app might pass only a structure with space up to iItem!
6610 * (MS Office 97 does that for instance in the file open dialog)
6612 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6620 * Inserts a new column.
6623 * [I] HWND : window handle
6624 * [I] INT : column index
6625 * [I] LPLVCOLUMNW : column information
6628 * SUCCESS : new column index
6631 static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn,
6632 LPLVCOLUMNW lpColumn, BOOL isW)
6634 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6635 INT nNewColumn = -1;
6638 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
6640 if (lpColumn != NULL)
6642 /* initialize memory */
6643 ZeroMemory(&hdi, sizeof(hdi));
6645 if (lpColumn->mask & LVCF_FMT)
6647 /* format member is valid */
6648 hdi.mask |= HDI_FORMAT;
6650 /* set text alignment (leftmost column must be left-aligned) */
6653 hdi.fmt |= HDF_LEFT;
6657 if (lpColumn->fmt & LVCFMT_LEFT)
6659 hdi.fmt |= HDF_LEFT;
6661 else if (lpColumn->fmt & LVCFMT_RIGHT)
6663 hdi.fmt |= HDF_RIGHT;
6665 else if (lpColumn->fmt & LVCFMT_CENTER)
6667 hdi.fmt |= HDF_CENTER;
6671 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6673 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6677 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6682 if (lpColumn->fmt & LVCFMT_IMAGE)
6684 hdi.fmt |= HDF_IMAGE;
6685 hdi.iImage = I_IMAGECALLBACK;
6689 if (lpColumn->mask & LVCF_WIDTH)
6691 hdi.mask |= HDI_WIDTH;
6692 hdi.cxy = lpColumn->cx;
6695 if (lpColumn->mask & LVCF_TEXT)
6697 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6698 hdi.pszText = lpColumn->pszText;
6699 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6700 hdi.fmt |= HDF_STRING;
6703 if (lpColumn->mask & LVCF_IMAGE)
6705 hdi.mask |= HDI_IMAGE;
6706 hdi.iImage = lpColumn->iImage;
6709 if (lpColumn->mask & LVCF_ORDER)
6711 hdi.mask |= HDI_ORDER;
6712 hdi.iOrder = lpColumn->iOrder;
6715 /* insert item in header control */
6716 nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6717 (WPARAM)nColumn, (LPARAM)&hdi);
6719 /* Need to reset the item width when inserting a new column */
6720 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6722 LISTVIEW_UpdateScroll(hwnd);
6723 InvalidateRect(hwnd, NULL, FALSE);
6729 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6730 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6731 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6732 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6733 their own sort proc. when sending LVM_SORTITEMS.
6736 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6738 LVS_SORTXXX must be specified,
6739 LVS_OWNERDRAW is not set,
6740 <item>.pszText is not LPSTR_TEXTCALLBACK.
6742 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6743 are sorted based on item text..."
6745 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6747 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6748 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6749 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6750 INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6751 /* if we're sorting descending, negate the return value */
6752 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6757 * Inserts a new item in the listview control.
6760 * [I] HWND : window handle
6761 * [I] LPLVITEMW : item information
6762 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6765 * SUCCESS : new item index
6768 static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
6770 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6771 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6772 UINT uView = lStyle & LVS_TYPEMASK;
6776 LISTVIEW_ITEM *lpItem = NULL;
6778 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
6779 hwnd, debuglvitem_t(lpLVItem, isW), isW);
6781 if (lStyle & LVS_OWNERDATA)
6783 nItem = infoPtr->hdpaItems->nItemCount;
6784 infoPtr->hdpaItems->nItemCount ++;
6788 if (lpLVItem != NULL)
6790 /* make sure it's not a subitem; cannot insert a subitem */
6791 if (lpLVItem->iSubItem == 0)
6793 if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6795 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6796 if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
6798 /* insert item in listview control data structure */
6799 if ( (hdpaSubItems = DPA_Create(8)) )
6801 if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
6803 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6804 && !(lStyle & LVS_OWNERDRAWFIXED)
6805 && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
6807 /* Insert the item in the proper sort order based on the pszText
6808 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6809 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6810 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6811 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6812 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6816 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6823 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6825 /* manage item focus */
6826 if (lpLVItem->mask & LVIF_STATE)
6828 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6829 if (lpLVItem->stateMask & LVIS_SELECTED)
6830 LISTVIEW_SetSelection(hwnd, nItem);
6831 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6832 LISTVIEW_SetItemFocus(hwnd, nItem);
6835 /* send LVN_INSERTITEM notification */
6836 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6838 nmlv.lParam = lpItem->lParam;
6839 listview_notify(hwnd, LVN_INSERTITEM, &nmlv);
6841 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6843 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6844 if (nItemWidth > infoPtr->nItemWidth)
6845 infoPtr->nItemWidth = nItemWidth;
6848 /* align items (set position of each item) */
6849 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6851 if (lStyle & LVS_ALIGNLEFT)
6852 LISTVIEW_AlignLeft(hwnd);
6854 LISTVIEW_AlignTop(hwnd);
6857 LISTVIEW_UpdateScroll(hwnd);
6858 /* refresh client area */
6859 InvalidateRect(hwnd, NULL, FALSE);
6868 /* free memory if unsuccessful */
6869 if ((nItem == -1) && (lpItem != NULL))
6870 COMCTL32_Free(lpItem);
6877 * Redraws a range of items.
6880 * [I] HWND : window handle
6881 * [I] INT : first item
6882 * [I] INT : last item
6888 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6890 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6891 BOOL bResult = FALSE;
6895 if (nFirst <= nLast)
6897 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6899 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6901 for (i = nFirst; i <= nLast; i++)
6903 rcItem.left = LVIR_BOUNDS;
6904 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
6905 InvalidateRect(hwnd, &rcItem, TRUE);
6914 /* LISTVIEW_Scroll */
6918 * Sets the background color.
6921 * [I] HWND : window handle
6922 * [I] COLORREF : background color
6928 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6930 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6932 if(infoPtr->clrBk!=clrBk){
6933 infoPtr->clrBk = clrBk;
6934 InvalidateRect(hwnd, NULL, TRUE);
6940 /* LISTVIEW_SetBkImage */
6944 * Sets the callback mask. This mask will be used when the parent
6945 * window stores state information (some or all).
6948 * [I] HWND : window handle
6949 * [I] UINT : state mask
6955 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
6957 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6959 infoPtr->uCallbackMask = uMask;
6966 * Sets the attributes of a header item.
6969 * [I] HWND : window handle
6970 * [I] INT : column index
6971 * [I] LPLVCOLUMNW : column attributes
6972 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6973 * otherwise it is in fact a LPLVCOLUMNA
6979 static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn,
6980 LPLVCOLUMNW lpColumn, BOOL isW)
6982 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6983 BOOL bResult = FALSE;
6984 HDITEMW hdi, hdiget;
6986 if ((lpColumn != NULL) && (nColumn >= 0) &&
6987 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6989 /* initialize memory */
6990 ZeroMemory(&hdi, sizeof(hdi));
6992 if (lpColumn->mask & LVCF_FMT)
6994 /* format member is valid */
6995 hdi.mask |= HDI_FORMAT;
6997 /* get current format first */
6998 hdiget.mask = HDI_FORMAT;
6999 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7000 /* preserve HDF_STRING if present */
7001 hdi.fmt = hdiget.fmt & HDF_STRING;
7003 /* set text alignment (leftmost column must be left-aligned) */
7006 hdi.fmt |= HDF_LEFT;
7010 if (lpColumn->fmt & LVCFMT_LEFT)
7011 hdi.fmt |= HDF_LEFT;
7012 else if (lpColumn->fmt & LVCFMT_RIGHT)
7013 hdi.fmt |= HDF_RIGHT;
7014 else if (lpColumn->fmt & LVCFMT_CENTER)
7015 hdi.fmt |= HDF_CENTER;
7018 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7019 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
7021 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7022 hdi.fmt |= HDF_IMAGE;
7024 if (lpColumn->fmt & LVCFMT_IMAGE)
7026 hdi.fmt |= HDF_IMAGE;
7027 hdi.iImage = I_IMAGECALLBACK;
7031 if (lpColumn->mask & LVCF_WIDTH)
7033 hdi.mask |= HDI_WIDTH;
7034 hdi.cxy = lpColumn->cx;
7037 if (lpColumn->mask & LVCF_TEXT)
7039 hdi.mask |= HDI_TEXT | HDI_FORMAT;
7040 hdi.pszText = lpColumn->pszText;
7041 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
7042 hdi.fmt |= HDF_STRING;
7045 if (lpColumn->mask & LVCF_IMAGE)
7047 hdi.mask |= HDI_IMAGE;
7048 hdi.iImage = lpColumn->iImage;
7051 if (lpColumn->mask & LVCF_ORDER)
7053 hdi.mask |= HDI_ORDER;
7054 hdi.iOrder = lpColumn->iOrder;
7057 /* set header item attributes */
7058 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7066 * Sets the column order array
7069 * [I] HWND : window handle
7070 * [I] INT : number of elements in column order array
7071 * [I] INT : pointer to column order array
7077 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
7079 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
7081 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7092 * Sets the width of a column
7095 * [I] HWND : window handle
7096 * [I] INT : column index
7097 * [I] INT : column width
7103 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
7105 LISTVIEW_INFO *infoPtr;
7108 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7109 UINT uView = lStyle & LVS_TYPEMASK;
7114 WCHAR text_buffer[DISP_TEXT_SIZE];
7115 INT header_item_count;
7120 /* make sure we can get the listview info */
7121 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
7124 if (!infoPtr->hwndHeader) /* make sure we have a header */
7127 /* set column width only if in report or list mode */
7128 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7131 /* take care of invalid cx values */
7132 if((uView == LVS_REPORT) && (cx < -2))
7133 cx = LVSCW_AUTOSIZE;
7134 else if (uView == LVS_LIST && (cx < 1))
7137 /* resize all columns if in LVS_LIST mode */
7138 if(uView == LVS_LIST) {
7139 infoPtr->nItemWidth = cx;
7140 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7144 /* autosize based on listview items width */
7145 if(cx == LVSCW_AUTOSIZE)
7147 /* set the width of the header to the width of the widest item */
7148 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7150 if(cx < LISTVIEW_GetLabelWidth(hwnd, item_index))
7151 cx = LISTVIEW_GetLabelWidth(hwnd, item_index);
7153 } /* autosize based on listview header width */
7154 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7156 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7158 /* if iCol is the last column make it fill the remainder of the controls width */
7159 if(iCol == (header_item_count - 1)) {
7160 /* get the width of every item except the current one */
7161 hdi.mask = HDI_WIDTH;
7164 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7165 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7169 /* retrieve the layout of the header */
7170 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7172 cx = (rcHeader.right - rcHeader.left) - cx;
7176 /* retrieve header font */
7177 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7179 /* retrieve header text */
7180 hdi.mask = HDI_TEXT;
7181 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7182 hdi.pszText = text_buffer;
7184 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7186 /* determine the width of the text in the header */
7188 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7190 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7192 SelectObject(hdc, old_font); /* restore the old font */
7193 ReleaseDC(hwnd, hdc);
7195 /* set the width of this column to the width of the text */
7200 /* call header to update the column change */
7201 hdi.mask = HDI_WIDTH;
7204 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7206 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7213 * Sets the extended listview style.
7216 * [I] HWND : window handle
7221 * SUCCESS : previous style
7224 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
7226 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7227 DWORD dwOldStyle = infoPtr->dwExStyle;
7231 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7233 infoPtr->dwExStyle = dwStyle;
7238 /* LISTVIEW_SetHotCursor */
7242 * Sets the hot item index.
7245 * [I] HWND : window handle
7249 * SUCCESS : previous hot item index
7250 * FAILURE : -1 (no hot item)
7252 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7254 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7255 INT iOldIndex = infoPtr->nHotItem;
7258 infoPtr->nHotItem = iIndex;
7265 * Sets the amount of time the cursor must hover over an item before it is selected.
7268 * [I] HWND : window handle
7269 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7272 * Returns the previous hover time
7274 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7276 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7277 DWORD oldHoverTime = infoPtr->dwHoverTime;
7279 infoPtr->dwHoverTime = dwHoverTime;
7281 return oldHoverTime;
7286 * Sets spacing for icons of LVS_ICON style.
7289 * [I] HWND : window handle
7290 * [I] DWORD : MAKELONG(cx, cy)
7293 * MAKELONG(oldcx, oldcy)
7295 static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing)
7297 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7298 INT cy = HIWORD(spacing);
7299 INT cx = LOWORD(spacing);
7301 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7302 UINT uView = lStyle & LVS_TYPEMASK;
7304 oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7305 if (cx == -1) /* set to default */
7306 cx = GetSystemMetrics(SM_CXICONSPACING);
7307 if (cy == -1) /* set to default */
7308 cy = GetSystemMetrics(SM_CYICONSPACING);
7311 infoPtr->iconSpacing.cx = cx;
7313 { /* if 0 then compute width */
7314 if (uView == LVS_ICON)
7315 FIXME("width computation not yet done\n");
7317 * Should scan each item and determine max width of
7318 * icon or label, then make that the width
7320 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7321 infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd);
7324 infoPtr->iconSpacing.cy = cy;
7326 { /* if 0 then compute height */
7327 if (uView == LVS_ICON)
7328 infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + infoPtr->ntmHeight
7329 + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7330 /* FIXME. I don't think so; I think it is based on twice the ntmHeight */
7331 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7332 infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd);
7335 TRACE("old=(%d,%d), new=(%ld,%ld)\n", LOWORD(oldspacing), HIWORD(oldspacing),
7336 infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7338 /* these depend on the iconSpacing */
7339 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7340 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7350 * [I] HWND : window handle
7351 * [I] INT : image list type
7352 * [I] HIMAGELIST : image list handle
7355 * SUCCESS : old image list
7358 static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7360 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7361 HIMAGELIST himlOld = 0;
7367 himlOld = infoPtr->himlNormal;
7368 infoPtr->himlNormal = himl;
7372 himlOld = infoPtr->himlSmall;
7373 infoPtr->himlSmall = himl;
7377 himlOld = infoPtr->himlState;
7378 infoPtr->himlState = himl;
7379 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7383 oldHeight = infoPtr->nItemHeight;
7384 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7385 if (infoPtr->nItemHeight != oldHeight)
7386 LISTVIEW_UpdateScroll(hwnd);
7393 * Preallocates memory (does *not* set the actual count of items !)
7396 * [I] HWND : window handle
7397 * [I] INT : item count (projected number of items to allocate)
7398 * [I] DWORD : update flags
7404 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7406 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7408 TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
7410 if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7412 int precount,topvisible;
7414 TRACE("LVS_OWNERDATA is set!\n");
7415 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7416 FIXME("flags %s %s not implemented\n",
7417 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7419 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7422 * Internally remove all the selections.
7426 LISTVIEW_SELECTION *selection;
7427 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7429 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7432 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7434 precount = infoPtr->hdpaItems->nItemCount;
7435 topvisible = ListView_GetTopIndex(hwnd) +
7436 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7438 infoPtr->hdpaItems->nItemCount = nItems;
7440 LISTVIEW_UpdateSize(hwnd);
7441 LISTVIEW_UpdateScroll(hwnd);
7442 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7443 InvalidateRect(hwnd, NULL, TRUE);
7447 /* According to MSDN for non-LVS_OWNERDATA this is just
7448 * a performance issue. The control allocates its internal
7449 * data structures for the number of items specified. It
7450 * cuts down on the number of memory allocations. Therefore
7451 * we will just issue a WARN here
7453 WARN("for non-ownerdata performance option not implemented.\n");
7461 * Sets the position of an item.
7464 * [I] HWND : window handle
7465 * [I] INT : item index
7466 * [I] LONG : x coordinate
7467 * [I] LONG : y coordinate
7473 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7474 LONG nPosX, LONG nPosY)
7476 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7477 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7478 UINT uView = lStyle & LVS_TYPEMASK;
7479 LISTVIEW_ITEM *lpItem;
7481 BOOL bResult = FALSE;
7483 TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7485 if (lStyle & LVS_OWNERDATA)
7488 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7490 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7492 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7494 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7498 orig = lpItem->ptPosition;
7499 if ((nPosX == -1) && (nPosY == -1))
7501 /* This point value seems to be an undocumented feature. The
7502 * best guess is that it means either at the origin, or at
7503 * the true beginning of the list. I will assume the origin.
7506 if (!LISTVIEW_GetOrigin(hwnd, &pt1))
7513 if (uView == LVS_ICON)
7515 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7516 nPosY += ICON_TOP_PADDING;
7518 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7522 lpItem->ptPosition.x = nPosX;
7523 lpItem->ptPosition.y = nPosY;
7524 if (uView == LVS_ICON)
7526 lpItem->ptPosition.y -= ICON_TOP_PADDING;
7527 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7528 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7530 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7531 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7534 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7535 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7540 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7541 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7554 * Sets the state of one or many items.
7557 * [I] HWND : window handle
7558 * [I]INT : item index
7559 * [I] LPLVITEM : item or subitem info
7565 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem)
7567 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7568 BOOL bResult = TRUE;
7571 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
7572 hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
7574 ZeroMemory(&lvItem, sizeof(lvItem));
7575 lvItem.mask = LVIF_STATE;
7576 lvItem.state = lpLVItem->state;
7577 lvItem.stateMask = lpLVItem->stateMask ;
7578 lvItem.iItem = nItem;
7582 /* apply to all items */
7583 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7584 if (!ListView_SetItemW(hwnd, &lvItem)) bResult = FALSE;
7587 bResult = ListView_SetItemW(hwnd, &lvItem);
7594 * Sets the text of an item or subitem.
7597 * [I] hwnd : window handle
7598 * [I] nItem : item index
7599 * [I] lpLVItem : item or subitem info
7600 * [I] isW : TRUE if input is Unicode
7606 static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7608 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7609 BOOL bResult = FALSE;
7612 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
7613 hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
7615 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7617 ZeroMemory(&lvItem, sizeof(LVITEMW));
7618 lvItem.mask = LVIF_TEXT;
7619 lvItem.pszText = lpLVItem->pszText;
7620 lvItem.iItem = nItem;
7621 lvItem.iSubItem = lpLVItem->iSubItem;
7622 if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
7623 else bResult = ListView_SetItemA(hwnd, &lvItem);
7631 * Set item index that marks the start of a multiple selection.
7634 * [I] HWND : window handle
7638 * Index number or -1 if there is no selection mark.
7640 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7642 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7643 INT nOldIndex = infoPtr->nSelectionMark;
7645 TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
7647 infoPtr->nSelectionMark = nIndex;
7654 * Sets the text background color.
7657 * [I] HWND : window handle
7658 * [I] COLORREF : text background color
7664 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7666 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7668 TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
7670 infoPtr->clrTextBk = clrTextBk;
7671 InvalidateRect(hwnd, NULL, TRUE);
7678 * Sets the text foreground color.
7681 * [I] HWND : window handle
7682 * [I] COLORREF : text color
7688 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7690 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7692 TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
7694 infoPtr->clrText = clrText;
7695 InvalidateRect(hwnd, NULL, TRUE);
7700 /* LISTVIEW_SetToolTips */
7701 /* LISTVIEW_SetUnicodeFormat */
7702 /* LISTVIEW_SetWorkAreas */
7706 * Callback internally used by LISTVIEW_SortItems()
7709 * [I] LPVOID : first LISTVIEW_ITEM to compare
7710 * [I] LPVOID : second LISTVIEW_ITEM to compare
7711 * [I] LPARAM : HWND of control
7714 * if first comes before second : negative
7715 * if first comes after second : positive
7716 * if first and second are equivalent : zero
7718 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7720 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7721 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
7722 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
7724 /* Forward the call to the client defined callback */
7725 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7730 * Sorts the listview items.
7733 * [I] HWND : window handle
7734 * [I] WPARAM : application-defined value
7735 * [I] LPARAM : pointer to comparision callback
7741 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7743 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7744 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7745 HDPA hdpaSubItems=NULL;
7746 LISTVIEW_ITEM *pLVItem=NULL;
7747 LPVOID selectionMarkItem;
7750 TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
7752 if (lStyle & LVS_OWNERDATA) return FALSE;
7754 if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
7756 nCount = GETITEMCOUNT(infoPtr);
7757 /* if there are 0 or 1 items, there is no need to sort */
7761 infoPtr->pfnCompare = pfnCompare;
7762 infoPtr->lParamSort = lParamSort;
7763 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7765 /* Adjust selections and indices so that they are the way they should
7766 * be after the sort (otherwise, the list items move around, but
7767 * whatever is at the item's previous original position will be
7770 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7771 for (i=0; i < nCount; i++)
7773 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7774 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7776 if (pLVItem->state & LVIS_SELECTED)
7777 LISTVIEW_AddSelectionRange(hwnd, i, i);
7779 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7780 if (pLVItem->state & LVIS_FOCUSED)
7781 infoPtr->nFocusedItem=i;
7783 if (selectionMarkItem != NULL)
7784 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7785 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7787 /* align the items */
7788 LISTVIEW_AlignTop(hwnd);
7790 /* refresh the display */
7791 InvalidateRect(hwnd, NULL, TRUE);
7796 /* LISTVIEW_SubItemHitTest */
7800 * Updates an items or rearranges the listview control.
7803 * [I] HWND : window handle
7804 * [I] INT : item index
7810 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7812 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7813 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7814 BOOL bResult = FALSE;
7817 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
7819 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7823 /* rearrange with default alignment style */
7824 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7825 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7827 ListView_Arrange(hwnd, 0);
7831 /* get item bounding rectangle */
7832 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
7833 InvalidateRect(hwnd, &rc, TRUE);
7842 * Creates the listview control.
7845 * [I] HWND : window handle
7850 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7852 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7853 UINT uView = lpcs->style & LVS_TYPEMASK;
7856 TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
7858 /* initialize info pointer */
7859 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7861 /* determine the type of structures to use */
7862 infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT,
7863 (WPARAM)hwnd, (LPARAM)NF_QUERY);
7865 /* initialize color information */
7866 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7867 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7868 infoPtr->clrTextBk = CLR_DEFAULT;
7870 /* set default values */
7871 infoPtr->hwndSelf = hwnd;
7872 infoPtr->uCallbackMask = 0;
7873 infoPtr->nFocusedItem = -1;
7874 infoPtr->nSelectionMark = -1;
7875 infoPtr->nHotItem = -1;
7876 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7877 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7878 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7879 infoPtr->hwndEdit = 0;
7880 infoPtr->pedititem = NULL;
7881 infoPtr->nEditLabelItem = -1;
7883 /* get default font (icon title) */
7884 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7885 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7886 infoPtr->hFont = infoPtr->hDefaultFont;
7887 LISTVIEW_SaveTextMetrics(hwnd);
7890 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7891 WS_CHILD | HDS_HORZ | HDS_BUTTONS,
7892 0, 0, 0, 0, hwnd, (HMENU)0,
7893 lpcs->hInstance, NULL);
7895 /* set header font */
7896 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7899 if (uView == LVS_ICON)
7901 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7902 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7904 else if (uView == LVS_REPORT)
7906 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7908 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7912 /* set HDS_HIDDEN flag to hide the header bar */
7913 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7914 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7918 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7919 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7923 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7924 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7927 /* display unsupported listview window styles */
7928 LISTVIEW_UnsupportedStyles(lpcs->style);
7930 /* allocate memory for the data structure */
7931 infoPtr->hdpaItems = DPA_Create(10);
7933 /* allocate memory for the selection ranges */
7934 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7936 /* initialize size of items */
7937 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7938 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7940 /* initialize the hover time to -1(indicating the default system hover time) */
7941 infoPtr->dwHoverTime = -1;
7948 * Erases the background of the listview control.
7951 * [I] HWND : window handle
7952 * [I] WPARAM : device context handle
7953 * [I] LPARAM : not used
7959 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
7962 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7965 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
7967 if (infoPtr->clrBk == CLR_NONE)
7969 bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
7974 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7975 GetClientRect(hwnd, &rc);
7976 FillRect((HDC)wParam, &rc, hBrush);
7977 DeleteObject(hBrush);
7985 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
7987 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7989 TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
7991 if (infoPtr->clrBk != CLR_NONE)
7993 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7994 FillRect(hdc, rc, hBrush);
7995 DeleteObject(hBrush);
8001 * Retrieves the listview control font.
8004 * [I] HWND : window handle
8009 static LRESULT LISTVIEW_GetFont(HWND hwnd)
8011 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8013 TRACE("(hwnd=%x)\n", hwnd);
8015 return infoPtr->hFont;
8020 * Performs vertical scrolling.
8023 * [I] HWND : window handle
8024 * [I] INT : scroll code
8025 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8027 * [I] HWND : scrollbar control window handle
8032 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8035 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8036 SCROLLINFO scrollInfo;
8038 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8039 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8041 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8043 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8044 scrollInfo.cbSize = sizeof(SCROLLINFO);
8045 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8047 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8049 INT nOldScrollPos = scrollInfo.nPos;
8050 switch (nScrollCode)
8053 if (scrollInfo.nPos > scrollInfo.nMin)
8058 if (scrollInfo.nPos < scrollInfo.nMax)
8063 if (scrollInfo.nPos > scrollInfo.nMin)
8065 if (scrollInfo.nPos >= scrollInfo.nPage)
8066 scrollInfo.nPos -= scrollInfo.nPage;
8068 scrollInfo.nPos = scrollInfo.nMin;
8073 if (scrollInfo.nPos < scrollInfo.nMax)
8075 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8076 scrollInfo.nPos += scrollInfo.nPage;
8078 scrollInfo.nPos = scrollInfo.nMax;
8082 case SB_THUMBPOSITION:
8084 scrollInfo.nPos = nCurrentPos;
8085 if (scrollInfo.nPos > scrollInfo.nMax)
8086 scrollInfo.nPos=scrollInfo.nMax;
8088 if (scrollInfo.nPos < scrollInfo.nMin)
8089 scrollInfo.nPos=scrollInfo.nMin;
8094 if (nOldScrollPos != scrollInfo.nPos)
8096 scrollInfo.fMask = SIF_POS;
8097 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
8098 if (IsWindowVisible(infoPtr->hwndHeader))
8100 RECT rListview, rcHeader, rDest;
8101 GetClientRect(hwnd, &rListview);
8102 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
8103 MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2);
8104 SubtractRect(&rDest, &rListview, &rcHeader);
8105 InvalidateRect(hwnd, &rDest, TRUE);
8108 InvalidateRect(hwnd, NULL, TRUE);
8117 * Performs horizontal scrolling.
8120 * [I] HWND : window handle
8121 * [I] INT : scroll code
8122 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8124 * [I] HWND : scrollbar control window handle
8129 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8132 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8133 SCROLLINFO scrollInfo;
8135 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8136 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8138 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8140 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8141 scrollInfo.cbSize = sizeof(SCROLLINFO);
8142 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8144 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
8146 INT nOldScrollPos = scrollInfo.nPos;
8148 switch (nScrollCode)
8151 if (scrollInfo.nPos > scrollInfo.nMin)
8156 if (scrollInfo.nPos < scrollInfo.nMax)
8161 if (scrollInfo.nPos > scrollInfo.nMin)
8163 if (scrollInfo.nPos >= scrollInfo.nPage)
8164 scrollInfo.nPos -= scrollInfo.nPage;
8166 scrollInfo.nPos = scrollInfo.nMin;
8171 if (scrollInfo.nPos < scrollInfo.nMax)
8173 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8174 scrollInfo.nPos += scrollInfo.nPage;
8176 scrollInfo.nPos = scrollInfo.nMax;
8180 case SB_THUMBPOSITION:
8182 scrollInfo.nPos = nCurrentPos;
8184 if (scrollInfo.nPos > scrollInfo.nMax)
8185 scrollInfo.nPos=scrollInfo.nMax;
8187 if (scrollInfo.nPos < scrollInfo.nMin)
8188 scrollInfo.nPos=scrollInfo.nMin;
8192 if (nOldScrollPos != scrollInfo.nPos)
8194 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8195 scrollInfo.fMask = SIF_POS;
8196 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
8197 if(uView == LVS_REPORT)
8199 scrollInfo.fMask = SIF_POS;
8200 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
8201 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
8203 InvalidateRect(hwnd, NULL, TRUE);
8210 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
8212 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8213 INT gcWheelDelta = 0;
8214 UINT pulScrollLines = 3;
8215 SCROLLINFO scrollInfo;
8217 TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
8219 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8220 gcWheelDelta -= wheelDelta;
8222 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8223 scrollInfo.cbSize = sizeof(SCROLLINFO);
8224 scrollInfo.fMask = SIF_POS | SIF_RANGE;
8231 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8232 * should be fixed in the future.
8234 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8235 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
8239 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8241 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8243 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
8244 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8245 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8251 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8262 * [I] HWND : window handle
8263 * [I] INT : virtual key
8264 * [I] LONG : key data
8269 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
8271 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8272 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8274 NMLVKEYDOWN nmKeyDown;
8276 TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
8278 /* send LVN_KEYDOWN notification */
8279 nmKeyDown.wVKey = nVirtualKey;
8280 nmKeyDown.flags = 0;
8281 notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
8283 switch (nVirtualKey)
8286 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8288 hdr_notify(hwnd, NM_RETURN); /* NM_RETURN notification */
8289 hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
8294 if (GETITEMCOUNT(infoPtr) > 0)
8299 if (GETITEMCOUNT(infoPtr) > 0)
8300 nItem = GETITEMCOUNT(infoPtr) - 1;
8304 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8308 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8312 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8316 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8320 if (uView == LVS_REPORT)
8321 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8323 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8324 * LISTVIEW_GetCountPerRow(hwnd);
8325 if(nItem < 0) nItem = 0;
8329 if (uView == LVS_REPORT)
8330 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8332 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8333 * LISTVIEW_GetCountPerRow(hwnd);
8334 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8338 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8340 if (LISTVIEW_KeySelection(hwnd, nItem))
8341 UpdateWindow(hwnd); /* update client area */
8352 * [I] HWND : window handle
8357 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8359 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
8360 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8363 TRACE("(hwnd=%x)\n", hwnd);
8365 /* send NM_KILLFOCUS notification */
8366 hdr_notify(hwnd, NM_KILLFOCUS);
8368 /* set window focus flag */
8369 infoPtr->bFocus = FALSE;
8371 /* NEED drawing optimization ; redraw the selected items */
8372 if (uView & LVS_REPORT)
8374 nTop = LISTVIEW_GetTopIndex(hwnd);
8376 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8381 nBottom = GETITEMCOUNT(infoPtr);
8383 for (i = nTop; i<nBottom; i++)
8385 if (LISTVIEW_IsSelected(hwnd,i))
8388 rcItem.left = LVIR_BOUNDS;
8389 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8390 InvalidateRect(hwnd, &rcItem, FALSE);
8399 * Processes double click messages (left mouse button).
8402 * [I] HWND : window handle
8403 * [I] WORD : key flag
8404 * [I] WORD : x coordinate
8405 * [I] WORD : y coordinate
8410 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8413 LVHITTESTINFO htInfo;
8416 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8418 htInfo.pt.x = wPosX;
8419 htInfo.pt.y = wPosY;
8421 /* send NM_DBLCLK notification */
8422 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8423 if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -1)
8425 nmlv.iItem = htInfo.iItem;
8426 nmlv.iSubItem = htInfo.iSubItem;
8433 nmlv.ptAction.x = wPosX;
8434 nmlv.ptAction.y = wPosY;
8435 listview_notify(hwnd, NM_DBLCLK, &nmlv);
8438 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8439 if(nmlv.iItem != -1)
8440 hdr_notify(hwnd, LVN_ITEMACTIVATE);
8447 * Processes mouse down messages (left mouse button).
8450 * [I] HWND : window handle
8451 * [I] WORD : key flag
8452 * [I] WORD : x coordinate
8453 * [I] WORD : y coordinate
8458 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8461 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8462 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8463 static BOOL bGroupSelect = TRUE;
8467 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8469 /* send NM_RELEASEDCAPTURE notification */
8470 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8472 if (infoPtr->bFocus == FALSE)
8475 /* set left button down flag */
8476 infoPtr->bLButtonDown = TRUE;
8478 ptPosition.x = wPosX;
8479 ptPosition.y = wPosY;
8480 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8481 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8483 if (lStyle & LVS_SINGLESEL)
8485 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8486 && infoPtr->nEditLabelItem == -1)
8487 infoPtr->nEditLabelItem = nItem;
8489 LISTVIEW_SetSelection(hwnd, nItem);
8493 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8496 LISTVIEW_AddGroupSelection(hwnd, nItem);
8498 LISTVIEW_AddSelection(hwnd, nItem);
8500 else if (wKey & MK_CONTROL)
8502 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8504 else if (wKey & MK_SHIFT)
8506 LISTVIEW_SetGroupSelection(hwnd, nItem);
8511 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8513 /* set selection (clears other pre-existing selections) */
8514 LISTVIEW_SetSelection(hwnd, nItem);
8516 if (was_selected && infoPtr->nEditLabelItem == -1)
8517 infoPtr->nEditLabelItem = nItem;
8523 /* remove all selections */
8524 LISTVIEW_RemoveAllSelections(hwnd);
8527 /* redraw if we could have possibly selected something */
8528 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8535 * Processes mouse up messages (left mouse button).
8538 * [I] HWND : window handle
8539 * [I] WORD : key flag
8540 * [I] WORD : x coordinate
8541 * [I] WORD : y coordinate
8546 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8549 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8551 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8553 if (infoPtr->bLButtonDown != FALSE)
8555 LVHITTESTINFO lvHitTestInfo;
8558 lvHitTestInfo.pt.x = wPosX;
8559 lvHitTestInfo.pt.y = wPosY;
8561 /* send NM_CLICK notification */
8562 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8563 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8565 nmlv.iItem = lvHitTestInfo.iItem;
8566 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8573 nmlv.ptAction.x = wPosX;
8574 nmlv.ptAction.y = wPosY;
8575 listview_notify(hwnd, NM_CLICK, &nmlv);
8577 /* set left button flag */
8578 infoPtr->bLButtonDown = FALSE;
8580 if(infoPtr->nEditLabelItem != -1)
8582 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem)
8583 LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
8584 infoPtr->nEditLabelItem = -1;
8593 * Creates the listview control (called before WM_CREATE).
8596 * [I] HWND : window handle
8597 * [I] WPARAM : unhandled
8598 * [I] LPARAM : widow creation info
8603 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8605 LISTVIEW_INFO *infoPtr;
8607 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8609 /* allocate memory for info structure */
8610 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8611 if (infoPtr == NULL)
8613 ERR("could not allocate info memory!\n");
8617 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
8618 if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr)
8620 ERR("pointer assignment error!\n");
8624 return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam);
8629 * Destroys the listview control (called after WM_DESTROY).
8632 * [I] HWND : window handle
8637 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8639 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8641 TRACE("(hwnd=%x)\n", hwnd);
8643 /* delete all items */
8644 LISTVIEW_DeleteAllItems(hwnd);
8646 /* destroy data structure */
8647 DPA_Destroy(infoPtr->hdpaItems);
8648 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8651 infoPtr->hFont = (HFONT)0;
8652 if (infoPtr->hDefaultFont)
8654 DeleteObject(infoPtr->hDefaultFont);
8657 /* free listview info pointer*/
8658 COMCTL32_Free(infoPtr);
8660 SetWindowLongW(hwnd, 0, 0);
8666 * Handles notifications from children.
8669 * [I] HWND : window handle
8670 * [I] INT : control identifier
8671 * [I] LPNMHDR : notification information
8676 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8678 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8680 TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
8682 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8684 /* handle notification from header control */
8685 if (lpnmh->code == HDN_ENDTRACKW)
8687 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8688 InvalidateRect(hwnd, NULL, TRUE);
8690 else if(lpnmh->code == HDN_ITEMCLICKW)
8692 /* Handle sorting by Header Column */
8695 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8697 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
8698 listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv);
8700 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8702 /* Idealy this should be done in HDN_ENDTRACKA
8703 * but since SetItemBounds in Header.c is called after
8704 * the notification is sent, it is neccessary to handle the
8705 * update of the scroll bar here (Header.c works fine as it is,
8706 * no need to disturb it)
8708 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8709 LISTVIEW_UpdateScroll(hwnd);
8710 InvalidateRect(hwnd, NULL, TRUE);
8720 * Determines the type of structure to use.
8723 * [I] HWND : window handle of the sender
8724 * [I] HWND : listview window handle
8725 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8730 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8732 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8734 TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
8736 if (nCommand == NF_REQUERY)
8737 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
8738 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8744 * Paints/Repaints the listview control.
8747 * [I] HWND : window handle
8748 * [I] HDC : device context handle
8753 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8757 TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc);
8761 hdc = BeginPaint(hwnd, &ps);
8762 LISTVIEW_Refresh(hwnd, hdc);
8763 EndPaint(hwnd, &ps);
8767 LISTVIEW_Refresh(hwnd, hdc);
8775 * Processes double click messages (right mouse button).
8778 * [I] HWND : window handle
8779 * [I] WORD : key flag
8780 * [I] WORD : x coordinate
8781 * [I] WORD : y coordinate
8786 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8789 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8791 /* send NM_RELEASEDCAPTURE notification */
8792 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8794 /* send NM_RDBLCLK notification */
8795 hdr_notify(hwnd, NM_RDBLCLK);
8802 * Processes mouse down messages (right mouse button).
8805 * [I] HWND : window handle
8806 * [I] WORD : key flag
8807 * [I] WORD : x coordinate
8808 * [I] WORD : y coordinate
8813 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8816 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8820 LVHITTESTINFO lvHitTestInfo;
8822 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8824 /* send NM_RELEASEDCAPTURE notification */
8825 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8827 /* make sure the listview control window has the focus */
8828 if (infoPtr->bFocus == FALSE)
8831 /* set right button down flag */
8832 infoPtr->bRButtonDown = TRUE;
8834 /* determine the index of the selected item */
8835 ptPosition.x = wPosX;
8836 ptPosition.y = wPosY;
8837 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8838 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8840 LISTVIEW_SetItemFocus(hwnd,nItem);
8841 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8842 !LISTVIEW_IsSelected(hwnd,nItem))
8843 LISTVIEW_SetSelection(hwnd, nItem);
8847 LISTVIEW_RemoveAllSelections(hwnd);
8850 lvHitTestInfo.pt.x = wPosX;
8851 lvHitTestInfo.pt.y = wPosY;
8853 /* Send NM_RClICK notification */
8854 ZeroMemory(&nmlv, sizeof(nmlv));
8855 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8857 nmlv.iItem = lvHitTestInfo.iItem;
8858 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8865 nmlv.ptAction.x = wPosX;
8866 nmlv.ptAction.y = wPosY;
8867 listview_notify(hwnd, NM_RCLICK, &nmlv);
8874 * Processes mouse up messages (right mouse button).
8877 * [I] HWND : window handle
8878 * [I] WORD : key flag
8879 * [I] WORD : x coordinate
8880 * [I] WORD : y coordinate
8885 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8888 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8890 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8892 if (infoPtr->bRButtonDown)
8899 /* set button flag */
8900 infoPtr->bRButtonDown = FALSE;
8902 /* Change to screen coordinate for WM_CONTEXTMENU */
8903 ClientToScreen(hwnd, &pt);
8905 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8906 SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
8917 * [I] HWND : window handle
8918 * [I] HWND : window handle of previously focused window
8923 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
8925 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8927 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
8929 /* send NM_SETFOCUS notification */
8930 hdr_notify(hwnd, NM_SETFOCUS);
8932 /* set window focus flag */
8933 infoPtr->bFocus = TRUE;
8945 * [I] HWND : window handle
8946 * [I] HFONT : font handle
8947 * [I] WORD : redraw flag
8952 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
8954 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8955 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8957 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
8959 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8960 LISTVIEW_SaveTextMetrics(hwnd);
8962 if (uView == LVS_REPORT)
8964 /* set header font */
8965 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
8966 MAKELPARAM(fRedraw, 0));
8969 /* invalidate listview control client area */
8970 InvalidateRect(hwnd, NULL, TRUE);
8972 if (fRedraw != FALSE)
8980 * Message handling for WM_SETREDRAW.
8981 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8984 * [I] HWND : window handle
8985 * [I] bRedraw: state of redraw flag
8988 * DefWinProc return value
8990 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
8992 LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0);
8994 RedrawWindow(hwnd, NULL, 0,
8995 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
9001 * Resizes the listview control. This function processes WM_SIZE
9002 * messages. At this time, the width and height are not used.
9005 * [I] HWND : window handle
9006 * [I] WORD : new width
9007 * [I] WORD : new height
9012 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
9014 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9015 UINT uView = lStyle & LVS_TYPEMASK;
9017 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
9019 LISTVIEW_UpdateSize(hwnd);
9021 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
9023 if (lStyle & LVS_ALIGNLEFT)
9024 LISTVIEW_AlignLeft(hwnd);
9026 LISTVIEW_AlignTop(hwnd);
9029 LISTVIEW_UpdateScroll(hwnd);
9031 /* invalidate client area + erase background */
9032 InvalidateRect(hwnd, NULL, TRUE);
9039 * Sets the size information.
9042 * [I] HWND : window handle
9047 static VOID LISTVIEW_UpdateSize(HWND hwnd)
9049 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9050 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9051 UINT uView = lStyle & LVS_TYPEMASK;
9054 TRACE("(hwnd=%x)\n", hwnd);
9056 GetClientRect(hwnd, &rcList);
9057 infoPtr->rcList.left = 0;
9058 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
9059 infoPtr->rcList.top = 0;
9060 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
9062 if (uView == LVS_LIST)
9064 if (lStyle & WS_HSCROLL)
9066 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
9067 if (infoPtr->rcList.bottom > nHScrollHeight)
9068 infoPtr->rcList.bottom -= nHScrollHeight;
9071 else if (uView == LVS_REPORT)
9078 Header_Layout(infoPtr->hwndHeader, &hl);
9080 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9082 if (!(LVS_NOCOLUMNHEADER & lStyle))
9083 infoPtr->rcList.top = max(wp.cy, 0);
9089 * Processes WM_STYLECHANGED messages.
9092 * [I] HWND : window handle
9093 * [I] WPARAM : window style type (normal or extended)
9094 * [I] LPSTYLESTRUCT : window style information
9099 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
9102 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9103 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9104 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9105 RECT rcList = infoPtr->rcList;
9107 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
9108 hwnd, wStyleType, lpss);
9110 if (wStyleType == GWL_STYLE)
9112 if (uOldView == LVS_REPORT)
9113 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9115 if ((lpss->styleOld & WS_HSCROLL) != 0)
9116 ShowScrollBar(hwnd, SB_HORZ, FALSE);
9118 if ((lpss->styleOld & WS_VSCROLL) != 0)
9119 ShowScrollBar(hwnd, SB_VERT, FALSE);
9121 if (uNewView == LVS_ICON)
9123 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9124 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9125 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9126 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9127 if (lpss->styleNew & LVS_ALIGNLEFT)
9128 LISTVIEW_AlignLeft(hwnd);
9130 LISTVIEW_AlignTop(hwnd);
9132 else if (uNewView == LVS_REPORT)
9139 Header_Layout(infoPtr->hwndHeader, &hl);
9140 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
9142 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9143 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9145 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9146 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9147 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9148 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9150 else if (uNewView == LVS_LIST)
9152 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9153 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9154 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9155 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9159 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9160 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9161 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9162 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9163 if (lpss->styleNew & LVS_ALIGNLEFT)
9164 LISTVIEW_AlignLeft(hwnd);
9166 LISTVIEW_AlignTop(hwnd);
9169 /* update the size of the client area */
9170 LISTVIEW_UpdateSize(hwnd);
9172 /* add scrollbars if needed */
9173 LISTVIEW_UpdateScroll(hwnd);
9175 /* invalidate client area + erase background */
9176 InvalidateRect(hwnd, NULL, TRUE);
9178 /* print the list of unsupported window styles */
9179 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9182 /* If they change the view and we have an active edit control
9183 we will need to kill the control since the redraw will
9184 misplace the edit control.
9186 if (infoPtr->hwndEdit &&
9187 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9188 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9190 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9198 * Window procedure of the listview control.
9201 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9204 TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
9205 if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
9206 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9209 case LVM_APPROXIMATEVIEWRECT:
9210 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9211 LOWORD(lParam), HIWORD(lParam));
9213 return LISTVIEW_Arrange(hwnd, (INT)wParam);
9215 /* case LVM_CREATEDRAGIMAGE: */
9217 case LVM_DELETEALLITEMS:
9218 return LISTVIEW_DeleteAllItems(hwnd);
9220 case LVM_DELETECOLUMN:
9221 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9223 case LVM_DELETEITEM:
9224 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9226 case LVM_EDITLABELW:
9227 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
9229 case LVM_EDITLABELA:
9230 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
9232 case LVM_ENSUREVISIBLE:
9233 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9236 return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
9239 return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
9241 case LVM_GETBKCOLOR:
9242 return LISTVIEW_GetBkColor(hwnd);
9244 /* case LVM_GETBKIMAGE: */
9246 case LVM_GETCALLBACKMASK:
9247 return LISTVIEW_GetCallbackMask(hwnd);
9249 case LVM_GETCOLUMNA:
9250 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9252 case LVM_GETCOLUMNW:
9253 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9255 case LVM_GETCOLUMNORDERARRAY:
9256 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9258 case LVM_GETCOLUMNWIDTH:
9259 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9261 case LVM_GETCOUNTPERPAGE:
9262 return LISTVIEW_GetCountPerPage(hwnd);
9264 case LVM_GETEDITCONTROL:
9265 return LISTVIEW_GetEditControl(hwnd);
9267 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9268 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9271 return LISTVIEW_GetHeader(hwnd);
9273 case LVM_GETHOTCURSOR:
9274 FIXME("LVM_GETHOTCURSOR: unimplemented\n");
9277 case LVM_GETHOTITEM:
9278 return LISTVIEW_GetHotItem(hwnd);
9280 case LVM_GETHOVERTIME:
9281 return LISTVIEW_GetHoverTime(hwnd);
9283 case LVM_GETIMAGELIST:
9284 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9286 case LVM_GETISEARCHSTRINGA:
9287 case LVM_GETISEARCHSTRINGW:
9288 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9292 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
9295 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
9297 case LVM_GETITEMCOUNT:
9298 return LISTVIEW_GetItemCount(hwnd);
9300 case LVM_GETITEMPOSITION:
9301 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9303 case LVM_GETITEMRECT:
9304 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9306 case LVM_GETITEMSPACING:
9307 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9309 case LVM_GETITEMSTATE:
9310 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9312 case LVM_GETITEMTEXTA:
9313 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9315 case LVM_GETITEMTEXTW:
9316 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9318 case LVM_GETNEXTITEM:
9319 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9321 case LVM_GETNUMBEROFWORKAREAS:
9322 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9326 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9328 case LVM_GETSELECTEDCOUNT:
9329 return LISTVIEW_GetSelectedCount(hwnd);
9331 case LVM_GETSELECTIONMARK:
9332 return LISTVIEW_GetSelectionMark(hwnd);
9334 case LVM_GETSTRINGWIDTHA:
9335 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
9337 case LVM_GETSTRINGWIDTHW:
9338 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
9340 case LVM_GETSUBITEMRECT:
9341 FIXME("LVM_GETSUBITEMRECT: unimplemented\n");
9344 case LVM_GETTEXTBKCOLOR:
9345 return LISTVIEW_GetTextBkColor(hwnd);
9347 case LVM_GETTEXTCOLOR:
9348 return LISTVIEW_GetTextColor(hwnd);
9350 case LVM_GETTOOLTIPS:
9351 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9354 case LVM_GETTOPINDEX:
9355 return LISTVIEW_GetTopIndex(hwnd);
9357 /*case LVM_GETUNICODEFORMAT:
9358 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9361 case LVM_GETVIEWRECT:
9362 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9364 case LVM_GETWORKAREAS:
9365 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9369 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9371 case LVM_INSERTCOLUMNA:
9372 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9374 case LVM_INSERTCOLUMNW:
9375 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9377 case LVM_INSERTITEMA:
9378 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9380 case LVM_INSERTITEMW:
9381 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9383 case LVM_REDRAWITEMS:
9384 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9386 /* case LVM_SCROLL: */
9387 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9389 case LVM_SETBKCOLOR:
9390 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9392 /* case LVM_SETBKIMAGE: */
9394 case LVM_SETCALLBACKMASK:
9395 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9397 case LVM_SETCOLUMNA:
9398 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9400 case LVM_SETCOLUMNW:
9401 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9403 case LVM_SETCOLUMNORDERARRAY:
9404 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9406 case LVM_SETCOLUMNWIDTH:
9407 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9409 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9410 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9412 /* case LVM_SETHOTCURSOR: */
9414 case LVM_SETHOTITEM:
9415 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9417 case LVM_SETHOVERTIME:
9418 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9420 case LVM_SETICONSPACING:
9421 return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam);
9423 case LVM_SETIMAGELIST:
9424 return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9427 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9430 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9432 case LVM_SETITEMCOUNT:
9433 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9435 case LVM_SETITEMPOSITION:
9436 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9437 (INT)HIWORD(lParam));
9439 case LVM_SETITEMPOSITION32:
9440 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9441 ((POINT*)lParam)->y);
9443 case LVM_SETITEMSTATE:
9444 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam);
9446 case LVM_SETITEMTEXTA:
9447 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9449 case LVM_SETITEMTEXTW:
9450 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9452 case LVM_SETSELECTIONMARK:
9453 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9455 case LVM_SETTEXTBKCOLOR:
9456 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9458 case LVM_SETTEXTCOLOR:
9459 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9461 /* case LVM_SETTOOLTIPS: */
9462 /* case LVM_SETUNICODEFORMAT: */
9463 /* case LVM_SETWORKAREAS: */
9466 return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9468 /* case LVM_SUBITEMHITTEST: */
9471 return LISTVIEW_Update(hwnd, (INT)wParam);
9474 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9477 return LISTVIEW_Command(hwnd, wParam, lParam);
9480 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9483 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9486 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9489 return LISTVIEW_GetFont(hwnd);
9492 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
9493 (INT)HIWORD(wParam), (HWND)lParam);
9496 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9499 return LISTVIEW_KillFocus(hwnd);
9501 case WM_LBUTTONDBLCLK:
9502 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9505 case WM_LBUTTONDOWN:
9506 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9509 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9512 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9515 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9518 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9521 return LISTVIEW_NCDestroy(hwnd);
9524 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9526 case WM_NOTIFYFORMAT:
9527 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9530 return LISTVIEW_Paint(hwnd, (HDC)wParam);
9532 case WM_RBUTTONDBLCLK:
9533 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9536 case WM_RBUTTONDOWN:
9537 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9541 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9545 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9548 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9551 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9554 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9556 case WM_STYLECHANGED:
9557 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9559 /* case WM_TIMER: */
9562 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
9563 (INT)HIWORD(wParam), (HWND)lParam);
9566 if (wParam & (MK_SHIFT | MK_CONTROL))
9567 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9568 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
9570 /* case WM_WININICHANGE: */
9573 if (uMsg >= WM_USER)
9575 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
9579 /* call default window procedure */
9580 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9588 * Registers the window class.
9596 VOID LISTVIEW_Register(void)
9600 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9601 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9602 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9603 wndClass.cbClsExtra = 0;
9604 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9605 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
9606 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9607 wndClass.lpszClassName = WC_LISTVIEWW;
9608 RegisterClassW(&wndClass);
9613 * Unregisters the window class.
9621 VOID LISTVIEW_Unregister(void)
9623 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
9628 * Handle any WM_COMMAND messages
9634 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9636 switch (HIWORD(wParam))
9641 * Adjust the edit window size
9644 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9645 HDC hdc = GetDC(infoPtr->hwndEdit);
9646 HFONT hFont, hOldFont = 0;
9651 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9652 GetWindowRect(infoPtr->hwndEdit, &rect);
9654 /* Select font to get the right dimension of the string */
9655 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9658 hOldFont = SelectObject(hdc, hFont);
9661 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9663 TEXTMETRICW textMetric;
9665 /* Add Extra spacing for the next character */
9666 GetTextMetricsW(hdc, &textMetric);
9667 sz.cx += (textMetric.tmMaxCharWidth * 2);
9675 rect.bottom - rect.top,
9676 SWP_DRAWFRAME|SWP_NOMOVE);
9679 SelectObject(hdc, hOldFont);
9681 ReleaseDC(hwnd, hdc);
9687 return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9696 * Subclassed edit control windproc function
9702 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
9703 WPARAM wParam, LPARAM lParam, BOOL isW)
9705 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9706 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9707 static BOOL bIgnoreKillFocus = FALSE;
9708 BOOL cancel = FALSE;
9710 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9711 hwnd, uMsg, wParam, lParam, isW);
9716 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9719 if(bIgnoreKillFocus) return TRUE;
9724 WNDPROC editProc = einfo->EditWndProc;
9725 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9726 COMCTL32_Free(einfo);
9727 infoPtr->pedititem = NULL;
9728 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9732 if (VK_ESCAPE == (INT)wParam)
9737 else if (VK_RETURN == (INT)wParam)
9741 return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9744 if (einfo->EditLblCb)
9746 LPWSTR buffer = NULL;
9750 DWORD len = 1 + isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9754 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9756 if (isW) GetWindowTextW(hwnd, buffer, len);
9757 else GetWindowTextA(hwnd, (CHAR*)buffer, len);
9761 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9762 /* eg. Using a messagebox */
9763 bIgnoreKillFocus = TRUE;
9764 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9766 if (buffer) COMCTL32_Free(buffer);
9768 einfo->EditLblCb = NULL;
9769 bIgnoreKillFocus = FALSE;
9772 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9778 * Subclassed edit control windproc function
9784 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9786 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9791 * Subclassed edit control windproc function
9797 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9799 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9804 * Creates a subclassed edit cotrol
9810 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
9811 INT width, INT height, HWND parent, HINSTANCE hinst,
9812 EditlblCallbackW EditLblCb, DWORD param, BOOL isW)
9814 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
9815 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9820 TEXTMETRICW textMetric;
9822 TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
9824 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9827 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9828 hdc = GetDC(parent);
9830 /* Select the font to get appropriate metric dimensions */
9831 if(infoPtr->hFont != 0)
9832 hOldFont = SelectObject(hdc, infoPtr->hFont);
9834 /*Get String Lenght in pixels */
9835 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9837 /*Add Extra spacing for the next character */
9838 GetTextMetricsW(hdc, &textMetric);
9839 sz.cx += (textMetric.tmMaxCharWidth * 2);
9841 if(infoPtr->hFont != 0)
9842 SelectObject(hdc, hOldFont);
9844 ReleaseDC(parent, hdc);
9846 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
9848 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
9852 COMCTL32_Free(infoPtr->pedititem);
9856 infoPtr->pedititem->param = param;
9857 infoPtr->pedititem->EditLblCb = EditLblCb;
9858 infoPtr->pedititem->EditWndProc = (WNDPROC)
9859 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9860 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9862 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);