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);
261 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
262 #define KEY_DELAY 450
264 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
266 static inline BOOL is_textW(LPCWSTR text)
268 return text != NULL && text != LPSTR_TEXTCALLBACKW;
271 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
273 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
274 return is_textW(text);
277 static inline int textlenT(LPCWSTR text, BOOL isW)
279 return !is_textT(text, isW) ? 0 :
280 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
283 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
286 if (isSrcW) lstrcpynW(dest, src, max);
287 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
289 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
290 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
293 static inline LPCSTR debugstr_t(LPCWSTR text, BOOL isW)
295 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
298 static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n)
300 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
303 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
305 LPWSTR wstr = (LPWSTR)text;
307 TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW);
310 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
311 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
312 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
314 TRACE(" wstr=%s\n", debugstr_w(wstr));
318 static inline void textfreeT(LPWSTR wstr, BOOL isW)
320 if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
324 * dest is a pointer to a Unicode string
325 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
327 static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
329 LPWSTR pszText = textdupTtoW(src, isW);
331 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
332 bResult = Str_SetPtrW(dest, pszText);
333 textfreeT(pszText, isW);
337 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
338 WPARAM wParam, LPARAM lParam, BOOL isW)
341 return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
343 return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
346 static inline BOOL notify(HWND self, INT code, LPNMHDR pnmh)
348 pnmh->hwndFrom = self;
349 pnmh->idFrom = GetWindowLongW(self, GWL_ID);
351 return (BOOL)SendMessageW(GetParent(self), WM_NOTIFY,
352 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
355 static inline BOOL hdr_notify(HWND self, INT code)
358 return notify(self, code, &nmh);
361 static inline BOOL listview_notify(HWND self, INT code, LPNMLISTVIEW plvnm)
363 return notify(self, code, (LPNMHDR)plvnm);
366 static int tabNotification[] = {
367 LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
368 LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
369 LVN_GETDISPINFOW, LVN_GETDISPINFOA,
370 LVN_SETDISPINFOW, LVN_SETDISPINFOA,
371 LVN_ODFINDITEMW, LVN_ODFINDITEMA,
372 LVN_GETINFOTIPW, LVN_GETINFOTIPA,
376 static int get_ansi_notification(INT unicodeNotificationCode)
378 int *pTabNotif = tabNotification;
379 while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
380 if (*pTabNotif) return *pTabNotif;
381 ERR("unknown notification %x\n", unicodeNotificationCode);
382 return unicodeNotificationCode;
386 Send notification. depends on dispinfoW having same
387 structure as dispinfoA.
388 self : listview handle
389 notificationCode : *Unicode* notification code
390 pdi : dispinfo structure (can be unicode or ansi)
391 isW : TRUE if dispinfo is Unicode
393 static BOOL dispinfo_notifyT(HWND self, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
395 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(self, 0);
396 BOOL bResult = FALSE;
397 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
399 INT cchTempBufMax = 0, savCchTextMax = 0;
400 LPWSTR pszTempBuf = NULL, savPszText = NULL;
402 TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self, notificationCode, pdi, isW);
403 TRACE(" notifyFormat=%s\n",
404 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
405 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
406 if (infoPtr->notifyFormat == NFR_ANSI)
407 realNotifCode = get_ansi_notification(notificationCode);
409 realNotifCode = notificationCode;
411 if (is_textT(pdi->item.pszText, isW))
413 if (isW && infoPtr->notifyFormat == NFR_ANSI)
414 convertToAnsi = TRUE;
415 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
416 convertToUnicode = TRUE;
419 if (convertToAnsi || convertToUnicode)
421 TRACE(" we have to convert the text to the correct format\n");
422 if (notificationCode != LVN_GETDISPINFOW)
423 { /* length of existing text */
424 cchTempBufMax = convertToUnicode ?
425 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
426 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
429 cchTempBufMax = pdi->item.cchTextMax;
431 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
432 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
433 if (!pszTempBuf) return FALSE;
434 if (convertToUnicode)
435 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
436 pszTempBuf, cchTempBufMax);
438 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
439 cchTempBufMax, NULL, NULL);
440 TRACE(" text=%s\n", debugstr_t(pszTempBuf, convertToUnicode));
441 savCchTextMax = pdi->item.cchTextMax;
442 savPszText = pdi->item.pszText;
443 pdi->item.pszText = pszTempBuf;
444 pdi->item.cchTextMax = cchTempBufMax;
447 bResult = notify(self, realNotifCode, (LPNMHDR)pdi);
449 if (convertToUnicode || convertToAnsi)
450 { /* convert back result */
451 TRACE(" returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode));
452 if (convertToUnicode) /* note : pointer can be changed by app ! */
453 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
454 savCchTextMax, NULL, NULL);
456 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
457 savPszText, savCchTextMax);
458 pdi->item.pszText = savPszText; /* restores our buffer */
459 pdi->item.cchTextMax = savCchTextMax;
460 HeapFree(GetProcessHeap(), 0, pszTempBuf);
465 static inline LRESULT LISTVIEW_GetItemW(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal)
467 return LISTVIEW_GetItemT(hwnd, lpLVItem, internal, TRUE);
470 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
474 n = min(min(n, strlenW(s1)), strlenW(s2));
475 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
476 return res ? res - 2 : res;
479 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
481 static int index = 0;
482 static char buffers[20][256];
483 char* buf = buffers[index++ % 20];
484 if (lpLVItem == NULL) return "(null)";
485 snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
486 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
487 lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
488 lpLVItem->state, lpLVItem->stateMask,
489 lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
490 debugstr_tn(lpLVItem->pszText, isW, 80),
491 lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
496 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
498 static int index = 0;
499 static char buffers[20][256];
500 char* buf = buffers[index++ % 20];
501 if (lpColumn == NULL) return "(null)";
502 snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
503 " pszText=%s, cchTextMax=%d, iSubItem=%d}",
504 lpColumn->mask, lpColumn->fmt, lpColumn->cx,
505 lpColumn->mask & LVCF_TEXT ? lpColumn->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
506 debugstr_tn(lpColumn->pszText, isW, 80): "",
507 lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
511 static void LISTVIEW_DumpListview(LISTVIEW_INFO *iP, INT line)
513 DWORD dwStyle = GetWindowLongW (iP->hwndSelf, GWL_STYLE);
514 TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n",
515 iP->hwndSelf, line, iP->clrBk, iP->clrText, iP->clrTextBk,
516 iP->nItemHeight, iP->nItemWidth, dwStyle);
517 TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n",
518 iP->hwndSelf, line, iP->himlNormal, iP->himlSmall, iP->himlState,
519 iP->nFocusedItem, iP->nHotItem, iP->dwExStyle);
523 LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
526 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
527 NMLVCUSTOMDRAW nmcdhdr;
530 TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd, dwDrawStage, hdc);
532 nmcd= & nmcdhdr.nmcd;
533 nmcd->hdr.hwndFrom = hwnd;
534 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
535 nmcd->hdr.code = NM_CUSTOMDRAW;
536 nmcd->dwDrawStage= dwDrawStage;
538 nmcd->rc.left = rc.left;
539 nmcd->rc.right = rc.right;
540 nmcd->rc.bottom = rc.bottom;
541 nmcd->rc.top = rc.top;
542 nmcd->dwItemSpec = 0;
543 nmcd->uItemState = 0;
544 nmcd->lItemlParam= 0;
545 nmcdhdr.clrText = infoPtr->clrText;
546 nmcdhdr.clrTextBk= infoPtr->clrBk;
548 return (BOOL)SendMessageW (GetParent (hwnd), WM_NOTIFY,
549 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
553 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
554 UINT iItem, UINT iSubItem,
557 LISTVIEW_INFO *infoPtr;
558 NMLVCUSTOMDRAW nmcdhdr;
560 DWORD dwDrawStage,dwItemSpec;
566 infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
568 ZeroMemory(&item,sizeof(item));
570 item.mask = LVIF_PARAM;
571 ListView_GetItemW(hwnd,&item);
573 dwDrawStage=CDDS_ITEM | uItemDrawState;
577 if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
578 if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
579 if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
581 itemRect.left = LVIR_BOUNDS;
582 LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
584 nmcd= & nmcdhdr.nmcd;
585 nmcd->hdr.hwndFrom = hwnd;
586 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
587 nmcd->hdr.code = NM_CUSTOMDRAW;
588 nmcd->dwDrawStage= dwDrawStage;
590 nmcd->rc.left = itemRect.left;
591 nmcd->rc.right = itemRect.right;
592 nmcd->rc.bottom = itemRect.bottom;
593 nmcd->rc.top = itemRect.top;
594 nmcd->dwItemSpec = dwItemSpec;
595 nmcd->uItemState = uItemState;
596 nmcd->lItemlParam= item.lParam;
597 nmcdhdr.clrText = infoPtr->clrText;
598 nmcdhdr.clrTextBk= infoPtr->clrBk;
599 nmcdhdr.iSubItem =iSubItem;
601 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
602 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
603 nmcd->uItemState, nmcd->lItemlParam);
605 retval=SendMessageW (GetParent (hwnd), WM_NOTIFY,
606 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
608 infoPtr->clrText=nmcdhdr.clrText;
609 infoPtr->clrBk =nmcdhdr.clrTextBk;
610 return (BOOL) retval;
614 /*************************************************************************
615 * LISTVIEW_ProcessLetterKeys
617 * Processes keyboard messages generated by pressing the letter keys
619 * What this does is perform a case insensitive search from the
620 * current position with the following quirks:
621 * - If two chars or more are pressed in quick succession we search
622 * for the corresponding string (e.g. 'abc').
623 * - If there is a delay we wipe away the current search string and
624 * restart with just that char.
625 * - If the user keeps pressing the same character, whether slowly or
626 * fast, so that the search string is entirely composed of this
627 * character ('aaaaa' for instance), then we search for first item
628 * that starting with that character.
629 * - If the user types the above character in quick succession, then
630 * we must also search for the corresponding string ('aaaaa'), and
631 * go to that string if there is a match.
639 * - The current implementation has a list of characters it will
640 * accept and it ignores averything else. In particular it will
641 * ignore accentuated characters which seems to match what
642 * Windows does. But I'm not sure it makes sense to follow
644 * - We don't sound a beep when the search fails.
648 * TREEVIEW_ProcessLetterKeys
650 static INT LISTVIEW_ProcessLetterKeys(
651 HWND hwnd, /* handle to the window */
652 WPARAM charCode, /* the character code, the actual character */
653 LPARAM keyData /* key data */
656 LISTVIEW_INFO *infoPtr;
661 WCHAR buffer[MAX_PATH];
662 DWORD timestamp,elapsed;
664 /* simple parameter checking */
665 if (!hwnd || !charCode || !keyData)
668 infoPtr=(LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
672 /* only allow the valid WM_CHARs through */
673 if (!isalnum(charCode) &&
674 charCode != '.' && charCode != '`' && 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 != '~')
685 nSize=GETITEMCOUNT(infoPtr);
686 /* if there's one item or less, there is no where to go */
690 /* compute how much time elapsed since last keypress */
691 timestamp=GetTickCount();
692 if (timestamp > infoPtr->lastKeyPressTimestamp) {
693 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
695 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
698 /* update the search parameters */
699 infoPtr->lastKeyPressTimestamp=timestamp;
700 if (elapsed < KEY_DELAY) {
701 if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
702 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
704 if (infoPtr->charCode != charCode) {
705 infoPtr->charCode=charCode=0;
708 infoPtr->charCode=charCode;
709 infoPtr->szSearchParam[0]=charCode;
710 infoPtr->nSearchParamLength=1;
711 /* Redundant with the 1 char string */
715 /* and search from the current position */
717 if (infoPtr->nFocusedItem >= 0) {
718 endidx=infoPtr->nFocusedItem;
720 /* if looking for single character match,
721 * then we must always move forward
723 if (infoPtr->nSearchParamLength == 1)
737 ZeroMemory(&item, sizeof(item));
738 item.mask = LVIF_TEXT;
741 item.pszText = buffer;
742 item.cchTextMax = COUNTOF(buffer);
743 ListView_GetItemW( hwnd, &item );
745 /* check for a match */
746 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
749 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
750 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
751 /* This would work but we must keep looking for a longer match */
755 } while (idx != endidx);
758 if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
759 /* refresh client area */
760 InvalidateRect(hwnd, NULL, TRUE);
768 /*************************************************************************
769 * LISTVIEW_UpdateHeaderSize [Internal]
771 * Function to resize the header control
774 * hwnd [I] handle to a window
775 * nNewScrollPos [I] Scroll Pos to Set
782 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
784 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
788 GetWindowRect(infoPtr->hwndHeader, &winRect);
789 point[0].x = winRect.left;
790 point[0].y = winRect.top;
791 point[1].x = winRect.right;
792 point[1].y = winRect.bottom;
794 MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
795 point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
796 point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
798 SetWindowPos(infoPtr->hwndHeader,0,
799 point[0].x,point[0].y,point[1].x,point[1].y,
800 SWP_NOZORDER | SWP_NOACTIVATE);
805 * Update the scrollbars. This functions should be called whenever
806 * the content, size or view changes.
809 * [I] HWND : window handle
814 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
816 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
817 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
818 UINT uView = lStyle & LVS_TYPEMASK;
819 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
820 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
821 SCROLLINFO scrollInfo;
823 if (lStyle & LVS_NOSCROLL) return;
825 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
826 scrollInfo.cbSize = sizeof(SCROLLINFO);
828 if (uView == LVS_LIST)
830 /* update horizontal scrollbar */
832 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
833 INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
834 INT nNumOfItems = GETITEMCOUNT(infoPtr);
836 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
837 if((nNumOfItems % nCountPerColumn) == 0)
841 scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
842 scrollInfo.nPage = nCountPerRow;
843 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
844 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
845 ShowScrollBar(hwnd, SB_VERT, FALSE);
847 else if (uView == LVS_REPORT)
849 /* update vertical scrollbar */
851 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
852 scrollInfo.nPos = ListView_GetTopIndex(hwnd);
853 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
854 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
855 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
857 /* update horizontal scrollbar */
858 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
859 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
860 || GETITEMCOUNT(infoPtr) == 0)
865 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
866 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
867 scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
868 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
870 /* Update the Header Control */
871 scrollInfo.fMask = SIF_POS;
872 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
873 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
880 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
882 INT nViewWidth = rcView.right - rcView.left;
883 INT nViewHeight = rcView.bottom - rcView.top;
885 /* Update Horizontal Scrollbar */
886 scrollInfo.fMask = SIF_POS;
887 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
888 || GETITEMCOUNT(infoPtr) == 0)
892 scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
894 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
895 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
896 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
898 /* Update Vertical Scrollbar */
899 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
900 scrollInfo.fMask = SIF_POS;
901 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
902 || GETITEMCOUNT(infoPtr) == 0)
906 scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
908 scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
909 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
910 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
917 * Prints a message for unsupported window styles.
918 * A kind of TODO list for window styles.
921 * [I] LONG : window style
926 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
928 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
929 FIXME(" LVS_NOSCROLL\n");
931 if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSORTHEADER)
932 FIXME(" LVS_NOSORTHEADER\n");
934 if (lStyle & LVS_EDITLABELS)
935 FIXME(" LVS_EDITLABELS\n");
937 if (lStyle & LVS_NOLABELWRAP)
938 FIXME(" LVS_NOLABELWRAP\n");
940 if (lStyle & LVS_SHAREIMAGELISTS)
941 FIXME(" LVS_SHAREIMAGELISTS\n");
943 if (lStyle & LVS_SORTASCENDING)
944 FIXME(" LVS_SORTASCENDING\n");
946 if (lStyle & LVS_SORTDESCENDING)
947 FIXME(" LVS_SORTDESCENDING\n");
952 * Aligns the items with the top edge of the window.
955 * [I] HWND : window handle
960 static VOID LISTVIEW_AlignTop(HWND hwnd)
962 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
963 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
964 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
967 INT i, off_x=0, off_y=0;
969 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
971 /* Since SetItemPosition uses upper-left of icon, and for
972 style=LVS_ICON the icon is not left adjusted, get the offset */
973 if (uView == LVS_ICON)
975 off_y = ICON_TOP_PADDING;
976 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
980 ZeroMemory(&rcView, sizeof(RECT));
982 if (nListWidth > infoPtr->nItemWidth)
984 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
986 if (ptItem.x + infoPtr->nItemWidth > nListWidth)
989 ptItem.y += infoPtr->nItemHeight;
992 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
993 ptItem.x += infoPtr->nItemWidth;
994 rcView.right = max(rcView.right, ptItem.x);
997 rcView.bottom = ptItem.y + infoPtr->nItemHeight;
1001 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1003 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1004 ptItem.y += infoPtr->nItemHeight;
1007 rcView.right = infoPtr->nItemWidth;
1008 rcView.bottom = ptItem.y;
1011 LISTVIEW_SetViewRect(hwnd, &rcView);
1017 * Aligns the items with the left edge of the window.
1020 * [I] HWND : window handle
1025 static VOID LISTVIEW_AlignLeft(HWND hwnd)
1027 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1028 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1029 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1032 INT i, off_x=0, off_y=0;
1034 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1036 /* Since SetItemPosition uses upper-left of icon, and for
1037 style=LVS_ICON the icon is not left adjusted, get the offset */
1038 if (uView == LVS_ICON)
1040 off_y = ICON_TOP_PADDING;
1041 off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1045 ZeroMemory(&rcView, sizeof(RECT));
1047 if (nListHeight > infoPtr->nItemHeight)
1049 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1051 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1054 ptItem.x += infoPtr->nItemWidth;
1057 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1058 ptItem.y += infoPtr->nItemHeight;
1059 rcView.bottom = max(rcView.bottom, ptItem.y);
1062 rcView.right = ptItem.x + infoPtr->nItemWidth;
1066 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1068 LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1069 ptItem.x += infoPtr->nItemWidth;
1072 rcView.bottom = infoPtr->nItemHeight;
1073 rcView.right = ptItem.x;
1076 LISTVIEW_SetViewRect(hwnd, &rcView);
1082 * Set the bounding rectangle of all the items.
1085 * [I] HWND : window handle
1086 * [I] LPRECT : bounding rectangle
1092 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
1094 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1095 BOOL bResult = FALSE;
1097 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
1098 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1100 if (lprcView != NULL)
1103 infoPtr->rcView.left = lprcView->left;
1104 infoPtr->rcView.top = lprcView->top;
1105 infoPtr->rcView.right = lprcView->right;
1106 infoPtr->rcView.bottom = lprcView->bottom;
1114 * Retrieves the bounding rectangle of all the items.
1117 * [I] HWND : window handle
1118 * [O] LPRECT : bounding rectangle
1124 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
1126 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1127 BOOL bResult = FALSE;
1130 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
1132 if (lprcView != NULL)
1134 bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
1135 if (bResult != FALSE)
1137 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1138 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1139 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1140 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1143 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1144 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1152 * Retrieves the subitem pointer associated with the subitem index.
1155 * [I] HDPA : DPA handle for a specific item
1156 * [I] INT : index of subitem
1159 * SUCCESS : subitem pointer
1162 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1165 LISTVIEW_SUBITEM *lpSubItem;
1168 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1170 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1171 if (lpSubItem != NULL)
1173 if (lpSubItem->iSubItem == nSubItem)
1185 * Calculates the width of an item.
1188 * [I] HWND : window handle
1189 * [I] LONG : window style
1192 * Returns item width.
1194 static INT LISTVIEW_GetItemWidth(HWND hwnd)
1196 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1197 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
1198 UINT uView = style & LVS_TYPEMASK;
1199 INT nHeaderItemCount;
1205 TRACE("(hwnd=%x)\n", hwnd);
1207 if (uView == LVS_ICON)
1209 nItemWidth = infoPtr->iconSpacing.cx;
1211 else if (uView == LVS_REPORT)
1213 /* calculate width of header */
1214 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1215 for (i = 0; i < nHeaderItemCount; i++)
1217 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1219 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1225 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1227 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
1228 nItemWidth = max(nItemWidth, nLabelWidth);
1231 /* default label size */
1232 if (GETITEMCOUNT(infoPtr) == 0)
1234 nItemWidth = DEFAULT_COLUMN_WIDTH;
1238 if (nItemWidth == 0)
1240 nItemWidth = DEFAULT_LABEL_WIDTH;
1245 nItemWidth += WIDTH_PADDING;
1247 if (infoPtr->himlSmall != NULL)
1249 nItemWidth += infoPtr->iconSize.cx;
1252 if (infoPtr->himlState != NULL)
1254 nItemWidth += infoPtr->iconSize.cx;
1261 /* nItemWidth Cannot be Zero */
1269 * Calculates the width of a specific item.
1272 * [I] HWND : window handle
1273 * [I] LPSTR : string
1276 * Returns the width of an item width a specified string.
1278 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1280 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1281 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1282 INT nHeaderItemCount;
1287 TRACE("(hwnd=%x)\n", hwnd);
1289 if (uView == LVS_ICON)
1291 nItemWidth = infoPtr->iconSpacing.cx;
1293 else if (uView == LVS_REPORT)
1295 /* calculate width of header */
1296 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1297 for (i = 0; i < nHeaderItemCount; i++)
1299 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1301 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1307 /* get width of string */
1308 nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1310 /* default label size */
1311 if (GETITEMCOUNT(infoPtr) == 0)
1313 nItemWidth = DEFAULT_COLUMN_WIDTH;
1317 if (nItemWidth == 0)
1319 nItemWidth = DEFAULT_LABEL_WIDTH;
1324 nItemWidth += WIDTH_PADDING;
1326 if (infoPtr->himlSmall != NULL)
1328 nItemWidth += infoPtr->iconSize.cx;
1331 if (infoPtr->himlState != NULL)
1333 nItemWidth += infoPtr->iconSize.cx;
1344 * Retrieves and saves important text metrics info for the current
1348 * [I] HWND : window handle
1351 static VOID LISTVIEW_SaveTextMetrics(HWND hwnd)
1353 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1355 HDC hdc = GetDC(hwnd);
1356 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1357 INT oldHeight, oldACW;
1359 GetTextMetricsW(hdc, &tm);
1361 oldHeight = infoPtr->ntmHeight;
1362 oldACW = infoPtr->ntmAveCharWidth;
1363 infoPtr->ntmHeight = tm.tmHeight;
1364 infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1366 SelectObject(hdc, hOldFont);
1367 ReleaseDC(hwnd, hdc);
1368 TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1369 oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1375 * Calculates the height of an item.
1378 * [I] HWND : window handle
1381 * Returns item height.
1383 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1385 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1386 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1387 INT nItemHeight = 0;
1389 if (uView == LVS_ICON)
1391 nItemHeight = infoPtr->iconSpacing.cy;
1395 if(infoPtr->himlState || infoPtr->himlSmall)
1396 nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1398 nItemHeight = infoPtr->ntmHeight;
1405 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1407 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1408 LISTVIEW_SELECTION *selection;
1409 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1412 TRACE("Selections are:\n");
1413 for (i = 0; i < topSelection; i++)
1415 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1416 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1422 * A compare function for selection ranges
1425 * [I] LPVOID : Item 1;
1426 * [I] LPVOID : Item 2;
1427 * [I] LPARAM : flags
1430 * >0 : if Item 1 > Item 2
1431 * <0 : if Item 2 > Item 1
1432 * 0 : if Item 1 == Item 2
1434 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1437 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1438 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1439 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1440 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1454 * Adds a selection range.
1457 * [I] HWND : window handle
1458 * [I] INT : lower item index
1459 * [I] INT : upper item index
1464 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1466 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1467 LISTVIEW_SELECTION *selection;
1468 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1469 BOOL lowerzero=FALSE;
1471 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1472 selection->lower = lItem;
1473 selection->upper = uItem;
1475 TRACE("Add range %i - %i\n", lItem, uItem);
1478 LISTVIEW_SELECTION *checkselection,*checkselection2;
1479 INT index,mergeindex;
1481 /* find overlapping selections */
1482 /* we want to catch adjacent ranges so expand our range by 1 */
1485 if (selection->lower == 0)
1490 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1491 LISTVIEW_CompareSelectionRanges,
1493 selection->upper --;
1497 selection->lower ++;
1501 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1502 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1503 checkselection->upper);
1505 checkselection->lower = min(selection->lower,checkselection->lower);
1506 checkselection->upper = max(selection->upper,checkselection->upper);
1508 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1509 checkselection->upper);
1511 COMCTL32_Free(selection);
1513 /* merge now common selection ranges in the lower group*/
1516 checkselection->upper ++;
1517 if (checkselection->lower == 0)
1520 checkselection->lower --;
1522 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1523 checkselection->upper);
1525 /* not sorted yet */
1526 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1527 LISTVIEW_CompareSelectionRanges, 0,
1530 checkselection->upper --;
1534 checkselection->lower ++;
1536 if (mergeindex >=0 && mergeindex != index)
1538 TRACE("Merge with index %i\n",mergeindex);
1539 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1541 checkselection->lower = min(checkselection->lower,
1542 checkselection2->lower);
1543 checkselection->upper = max(checkselection->upper,
1544 checkselection2->upper);
1545 COMCTL32_Free(checkselection2);
1546 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1550 while (mergeindex > -1 && mergeindex <index);
1552 /* merge now common selection ranges in the upper group*/
1555 checkselection->upper ++;
1556 if (checkselection->lower == 0)
1559 checkselection->lower --;
1561 TRACE("search upper range %i (%lu - %lu)\n",index,
1562 checkselection->lower, checkselection->upper);
1564 /* not sorted yet */
1565 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1567 LISTVIEW_CompareSelectionRanges, 0,
1570 checkselection->upper --;
1574 checkselection->lower ++;
1576 if (mergeindex >=0 && mergeindex !=index)
1578 TRACE("Merge with index %i\n",mergeindex);
1579 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1581 checkselection->lower = min(checkselection->lower,
1582 checkselection2->lower);
1583 checkselection->upper = max(checkselection->upper,
1584 checkselection2->upper);
1585 COMCTL32_Free(checkselection2);
1586 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1589 while (mergeindex > -1);
1594 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1595 LISTVIEW_CompareSelectionRanges, 0,
1598 TRACE("Insert before index %i\n",index);
1601 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1606 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1611 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1612 LISTVIEW_PrintSelectionRanges(hwnd);
1617 * check if a specified index is selected.
1620 * [I] HWND : window handle
1621 * [I] INT : item index
1626 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1628 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1629 LISTVIEW_SELECTION selection;
1632 selection.upper = nItem;
1633 selection.lower = nItem;
1635 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1636 LISTVIEW_CompareSelectionRanges,
1646 * Removes all selection ranges
1649 * HWND: window handle
1655 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1657 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1658 LISTVIEW_SELECTION *selection;
1662 TRACE("(0x%x)\n",hwnd);
1664 ZeroMemory(&item,sizeof(item));
1665 item.stateMask = LVIS_SELECTED;
1669 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1672 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1673 for (i = selection->lower; i<=selection->upper; i++)
1674 LISTVIEW_SetItemState(hwnd,i,&item);
1675 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1678 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1686 * Removes a range selections.
1689 * [I] HWND : window handle
1690 * [I] INT : lower item index
1691 * [I] INT : upper item index
1696 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1698 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1699 LISTVIEW_SELECTION removeselection,*checkselection;
1702 removeselection.lower = lItem;
1703 removeselection.upper = uItem;
1705 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1706 LISTVIEW_PrintSelectionRanges(hwnd);
1708 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1709 LISTVIEW_CompareSelectionRanges,
1716 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1719 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1720 checkselection->upper);
1723 if ((checkselection->upper == removeselection.upper) &&
1724 (checkselection->lower == removeselection.lower))
1726 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1729 /* case 2: engulf */
1730 else if (((checkselection->upper < removeselection.upper) &&
1731 (checkselection->lower > removeselection.lower))||
1732 ((checkselection->upper <= removeselection.upper) &&
1733 (checkselection->lower > removeselection.lower)) ||
1734 ((checkselection->upper < removeselection.upper) &&
1735 (checkselection->lower >= removeselection.lower)))
1738 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1739 /* do it again because others may also get caught */
1741 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1743 /* case 3: overlap upper */
1744 else if ((checkselection->upper < removeselection.upper) &&
1745 (checkselection->lower < removeselection.lower))
1747 checkselection->upper = removeselection.lower - 1;
1749 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1751 /* case 4: overlap lower */
1752 else if ((checkselection->upper > removeselection.upper) &&
1753 (checkselection->lower > removeselection.lower))
1755 checkselection->lower = removeselection.upper + 1;
1757 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1759 /* case 5: fully internal */
1760 else if (checkselection->upper == removeselection.upper)
1761 checkselection->upper = removeselection.lower - 1;
1762 else if (checkselection->lower == removeselection.lower)
1763 checkselection->lower = removeselection.upper + 1;
1766 /* bisect the range */
1767 LISTVIEW_SELECTION *newselection;
1769 newselection = (LISTVIEW_SELECTION *)
1770 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1771 newselection -> lower = checkselection->lower;
1772 newselection -> upper = removeselection.lower - 1;
1773 checkselection -> lower = removeselection.upper + 1;
1774 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1776 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1778 LISTVIEW_PrintSelectionRanges(hwnd);
1783 * Updates the various indices after an item has been inserted or deleted.
1786 * [I] HWND : window handle
1787 * [I] INT : item index
1788 * [I] INT : Direction of shift, +1 or -1.
1793 static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
1795 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1796 LISTVIEW_SELECTION selection,*checkselection;
1799 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1801 selection.upper = nItem;
1802 selection.lower = nItem;
1804 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1805 LISTVIEW_CompareSelectionRanges,
1806 0,DPAS_SORTED|DPAS_INSERTAFTER);
1808 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1810 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1811 if ((checkselection->lower >= nItem)&&
1812 (checkselection->lower + direction >= 0))
1813 checkselection->lower += direction;
1814 if ((checkselection->upper >= nItem)&&
1815 (checkselection->upper + direction >=0))
1816 checkselection->upper += direction;
1820 /* Note that the following will fail if direction != +1 and -1 */
1821 if (infoPtr->nSelectionMark > nItem)
1822 infoPtr->nSelectionMark += direction;
1823 else if (infoPtr->nSelectionMark == nItem)
1826 infoPtr->nSelectionMark += direction;
1827 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1828 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1831 if (infoPtr->nFocusedItem > nItem)
1832 infoPtr->nFocusedItem += direction;
1833 else if (infoPtr->nFocusedItem == nItem)
1836 infoPtr->nFocusedItem += direction;
1839 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1840 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1841 if (infoPtr->nFocusedItem >= 0)
1842 LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
1845 /* But we are not supposed to modify nHotItem! */
1851 * Adds a block of selections.
1854 * [I] HWND : window handle
1855 * [I] INT : item index
1860 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1862 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1863 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1864 INT nLast = max(infoPtr->nSelectionMark, nItem);
1871 ZeroMemory(&item,sizeof(item));
1872 item.stateMask = LVIS_SELECTED;
1873 item.state = LVIS_SELECTED;
1875 for (i = nFirst; i <= nLast; i++)
1876 LISTVIEW_SetItemState(hwnd,i,&item);
1878 LISTVIEW_SetItemFocus(hwnd, nItem);
1879 infoPtr->nSelectionMark = nItem;
1885 * Adds a single selection.
1888 * [I] HWND : window handle
1889 * [I] INT : item index
1894 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1896 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1899 ZeroMemory(&item,sizeof(item));
1900 item.state = LVIS_SELECTED;
1901 item.stateMask = LVIS_SELECTED;
1903 LISTVIEW_SetItemState(hwnd,nItem,&item);
1905 LISTVIEW_SetItemFocus(hwnd, nItem);
1906 infoPtr->nSelectionMark = nItem;
1911 * Selects or unselects an item.
1914 * [I] HWND : window handle
1915 * [I] INT : item index
1921 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1923 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1927 ZeroMemory(&item,sizeof(item));
1928 item.stateMask = LVIS_SELECTED;
1930 if (LISTVIEW_IsSelected(hwnd,nItem))
1932 LISTVIEW_SetItemState(hwnd,nItem,&item);
1937 item.state = LVIS_SELECTED;
1938 LISTVIEW_SetItemState(hwnd,nItem,&item);
1942 LISTVIEW_SetItemFocus(hwnd, nItem);
1943 infoPtr->nSelectionMark = nItem;
1950 * Selects items based on view coordinates.
1953 * [I] HWND : window handle
1954 * [I] RECT : selection rectangle
1959 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
1961 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1966 ZeroMemory(&item,sizeof(item));
1967 item.stateMask = LVIS_SELECTED;
1969 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1971 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
1973 if (PtInRect(&rcSelRect, ptItem) != FALSE)
1974 item.state = LVIS_SELECTED;
1977 LISTVIEW_SetItemState(hwnd,i,&item);
1983 * Sets a single group selection.
1986 * [I] HWND : window handle
1987 * [I] INT : item index
1992 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
1994 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1995 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1998 ZeroMemory(&item,sizeof(item));
1999 item.stateMask = LVIS_SELECTED;
2001 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2006 if (infoPtr->nSelectionMark == -1)
2008 infoPtr->nSelectionMark = nFirst = nLast = nItem;
2012 nFirst = min(infoPtr->nSelectionMark, nItem);
2013 nLast = max(infoPtr->nSelectionMark, nItem);
2016 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2018 if ((i < nFirst) || (i > nLast))
2021 item.state = LVIS_SELECTED;
2022 LISTVIEW_SetItemState(hwnd,i,&item);
2030 LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcItem);
2031 LISTVIEW_GetItemBoundBox(hwnd, infoPtr->nSelectionMark, &rcSelMark);
2032 rcSel.left = min(rcSelMark.left, rcItem.left);
2033 rcSel.top = min(rcSelMark.top, rcItem.top);
2034 rcSel.right = max(rcSelMark.right, rcItem.right);
2035 rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
2036 LISTVIEW_SetSelectionRect(hwnd, rcSel);
2037 TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2038 nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
2039 infoPtr->nSelectionMark,
2040 rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
2041 rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2045 LISTVIEW_SetItemFocus(hwnd, nItem);
2050 * Manages the item focus.
2053 * [I] HWND : window handle
2054 * [I] INT : item index
2057 * TRUE : focused item changed
2058 * FALSE : focused item has NOT changed
2060 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
2062 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2063 BOOL bResult = FALSE;
2066 if (infoPtr->nFocusedItem != nItem)
2068 if (infoPtr->nFocusedItem >= 0)
2070 INT oldFocus = infoPtr->nFocusedItem;
2072 infoPtr->nFocusedItem = -1;
2073 ZeroMemory(&lvItem, sizeof(lvItem));
2074 lvItem.stateMask = LVIS_FOCUSED;
2075 ListView_SetItemState(hwnd, oldFocus, &lvItem);
2079 lvItem.state = LVIS_FOCUSED;
2080 lvItem.stateMask = LVIS_FOCUSED;
2081 ListView_SetItemState(hwnd, nItem, &lvItem);
2083 infoPtr->nFocusedItem = nItem;
2084 ListView_EnsureVisible(hwnd, nItem, FALSE);
2092 * Sets a single selection.
2095 * [I] HWND : window handle
2096 * [I] INT : item index
2101 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
2103 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2106 ZeroMemory(&lvItem, sizeof(lvItem));
2107 lvItem.stateMask = LVIS_FOCUSED;
2108 ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
2110 LISTVIEW_RemoveAllSelections(hwnd);
2112 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
2113 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2114 ListView_SetItemState(hwnd, nItem, &lvItem);
2116 infoPtr->nFocusedItem = nItem;
2117 infoPtr->nSelectionMark = nItem;
2122 * Set selection(s) with keyboard.
2125 * [I] HWND : window handle
2126 * [I] INT : item index
2129 * SUCCESS : TRUE (needs to be repainted)
2130 * FAILURE : FALSE (nothing has changed)
2132 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
2134 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2135 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2136 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2137 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2138 BOOL bResult = FALSE;
2140 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2142 if (lStyle & LVS_SINGLESEL)
2145 LISTVIEW_SetSelection(hwnd, nItem);
2146 ListView_EnsureVisible(hwnd, nItem, FALSE);
2153 LISTVIEW_SetGroupSelection(hwnd, nItem);
2157 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
2162 LISTVIEW_SetSelection(hwnd, nItem);
2163 ListView_EnsureVisible(hwnd, nItem, FALSE);
2173 * Called when the mouse is being actively tracked and has hovered for a specified
2177 * [I] HWND : window handle
2178 * [I] wParam : key indicator
2179 * [I] lParam : mouse position
2182 * 0 if the message was processed, non-zero if there was an error
2185 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2186 * over the item for a certain period of time.
2189 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
2191 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2194 pt.x = (INT)LOWORD(lParam);
2195 pt.y = (INT)HIWORD(lParam);
2197 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2198 /* select the item under the cursor */
2199 LISTVIEW_MouseSelection(hwnd, pt);
2207 * Called whenever WM_MOUSEMOVE is received.
2210 * [I] HWND : window handle
2211 * [I] wParam : key indicators
2212 * [I] lParam : cursor position
2215 * 0 if the message is processed, non-zero if there was an error
2217 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
2219 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2220 TRACKMOUSEEVENT trackinfo;
2222 /* see if we are supposed to be tracking mouse hovering */
2223 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2224 /* fill in the trackinfo struct */
2225 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2226 trackinfo.dwFlags = TME_QUERY;
2227 trackinfo.hwndTrack = hwnd;
2228 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2230 /* see if we are already tracking this hwnd */
2231 _TrackMouseEvent(&trackinfo);
2233 if(!(trackinfo.dwFlags & TME_HOVER)) {
2234 trackinfo.dwFlags = TME_HOVER;
2236 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2237 _TrackMouseEvent(&trackinfo);
2246 * Selects an item based on coordinates.
2249 * [I] HWND : window handle
2250 * [I] POINT : mouse click ccordinates
2253 * SUCCESS : item index
2256 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
2258 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2260 INT i,topindex,bottomindex;
2261 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2262 UINT uView = lStyle & LVS_TYPEMASK;
2264 topindex = ListView_GetTopIndex(hwnd);
2265 if (uView == LVS_REPORT)
2267 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
2268 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
2272 bottomindex = GETITEMCOUNT(infoPtr);
2275 for (i = topindex; i < bottomindex; i++)
2277 rcItem.left = LVIR_SELECTBOUNDS;
2278 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
2280 if (PtInRect(&rcItem, pt) != FALSE)
2295 * [IO] HDPA : dynamic pointer array handle
2296 * [I] INT : column index (subitem index)
2302 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2304 BOOL bResult = TRUE;
2308 for (i = 0; i < hdpaItems->nItemCount; i++)
2310 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2311 if (hdpaSubItems != NULL)
2313 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2325 * Removes a subitem at a given position.
2328 * [IO] HDPA : dynamic pointer array handle
2329 * [I] INT : subitem index
2335 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2337 LISTVIEW_SUBITEM *lpSubItem;
2340 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2342 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2343 if (lpSubItem != NULL)
2345 if (lpSubItem->iSubItem == nSubItem)
2348 if (is_textW(lpSubItem->pszText))
2349 COMCTL32_Free(lpSubItem->pszText);
2352 COMCTL32_Free(lpSubItem);
2354 /* free dpa memory */
2355 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2358 else if (lpSubItem->iSubItem > nSubItem)
2368 * Compares the item information.
2371 * [I] LISTVIEW_ITEM *: destination item
2372 * [I] LPLVITEM : source item
2373 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2376 * SUCCCESS : TRUE (EQUAL)
2377 * FAILURE : FALSE (NOT EQUAL)
2379 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2383 if ((lpItem != NULL) && (lpLVItem != NULL))
2385 if (lpLVItem->mask & LVIF_STATE)
2387 if ((lpItem->state & lpLVItem->stateMask) !=
2388 (lpLVItem->state & lpLVItem->stateMask))
2389 uChanged |= LVIF_STATE;
2392 if (lpLVItem->mask & LVIF_IMAGE)
2394 if (lpItem->iImage != lpLVItem->iImage)
2395 uChanged |= LVIF_IMAGE;
2398 if (lpLVItem->mask & LVIF_PARAM)
2400 if (lpItem->lParam != lpLVItem->lParam)
2401 uChanged |= LVIF_PARAM;
2404 if (lpLVItem->mask & LVIF_INDENT)
2406 if (lpItem->iIndent != lpLVItem->iIndent)
2407 uChanged |= LVIF_INDENT;
2410 if (lpLVItem->mask & LVIF_TEXT)
2412 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2414 if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2415 uChanged |= LVIF_TEXT;
2419 if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2421 uChanged |= LVIF_TEXT;
2425 if (lpLVItem->pszText)
2427 if (lpItem->pszText)
2429 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2430 if (pszText && strcmpW(pszText, lpItem->pszText))
2431 uChanged |= LVIF_TEXT;
2432 textfreeT(pszText, isW);
2436 uChanged |= LVIF_TEXT;
2441 if (lpItem->pszText)
2442 uChanged |= LVIF_TEXT;
2453 * Initializes item attributes.
2456 * [I] HWND : window handle
2457 * [O] LISTVIEW_ITEM *: destination item
2458 * [I] LPLVITEM : source item
2459 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2465 static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem,
2466 LPLVITEMW lpLVItem, BOOL isW)
2468 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2469 BOOL bResult = FALSE;
2471 if ((lpItem != NULL) && (lpLVItem != NULL))
2475 if (lpLVItem->mask & LVIF_STATE)
2477 lpItem->state &= ~lpLVItem->stateMask;
2478 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2481 if (lpLVItem->mask & LVIF_IMAGE)
2482 lpItem->iImage = lpLVItem->iImage;
2484 if (lpLVItem->mask & LVIF_PARAM)
2485 lpItem->lParam = lpLVItem->lParam;
2487 if (lpLVItem->mask & LVIF_INDENT)
2488 lpItem->iIndent = lpLVItem->iIndent;
2490 if (lpLVItem->mask & LVIF_TEXT)
2492 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2494 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2497 if (is_textW(lpItem->pszText))
2498 COMCTL32_Free(lpItem->pszText);
2500 lpItem->pszText = LPSTR_TEXTCALLBACKW;
2503 bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
2512 * Initializes subitem attributes.
2514 * NOTE: The documentation specifies that the operation fails if the user
2515 * tries to set the indent of a subitem.
2518 * [I] HWND : window handle
2519 * [O] LISTVIEW_SUBITEM *: destination subitem
2520 * [I] LPLVITEM : source subitem
2521 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2527 static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2528 LPLVITEMW lpLVItem, BOOL isW)
2530 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2531 BOOL bResult = FALSE;
2533 TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
2534 hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW);
2536 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2538 if (!(lpLVItem->mask & LVIF_INDENT))
2542 lpSubItem->iSubItem = lpLVItem->iSubItem;
2544 if (lpLVItem->mask & LVIF_IMAGE)
2545 lpSubItem->iImage = lpLVItem->iImage;
2547 if (lpLVItem->mask & LVIF_TEXT)
2549 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2551 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2554 if (is_textW(lpSubItem->pszText))
2555 COMCTL32_Free(lpSubItem->pszText);
2557 lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2560 bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
2570 * Adds a subitem at a given position (column index).
2573 * [I] HWND : window handle
2574 * [I] LPLVITEM : new subitem atttributes
2575 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2581 static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2583 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2584 LISTVIEW_SUBITEM *lpSubItem = NULL;
2585 BOOL bResult = FALSE;
2587 INT nPosition, nItem;
2588 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2590 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2592 if (lStyle & LVS_OWNERDATA)
2595 if (lpLVItem != NULL)
2597 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2598 if (hdpaSubItems != NULL)
2600 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2601 if (lpSubItem != NULL)
2603 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2604 if (LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW))
2606 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2607 lpSubItem->iSubItem);
2608 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2609 if (nItem != -1) bResult = TRUE;
2615 /* cleanup if unsuccessful */
2616 if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2623 * Finds the dpa insert position (array index).
2626 * [I] HWND : window handle
2627 * [I] INT : subitem index
2633 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2635 LISTVIEW_SUBITEM *lpSubItem;
2638 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2640 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2641 if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2645 return hdpaSubItems->nItemCount;
2650 * Retrieves a listview subitem at a given position (column index).
2653 * [I] HWND : window handle
2654 * [I] INT : subitem index
2660 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2662 LISTVIEW_SUBITEM *lpSubItem;
2665 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2667 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2668 if (lpSubItem != NULL)
2670 if (lpSubItem->iSubItem == nSubItem)
2672 else if (lpSubItem->iSubItem > nSubItem)
2682 * Sets item attributes.
2685 * [I] HWND : window handle
2686 * [I] LPLVITEM : new item atttributes
2687 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2693 static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2695 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2696 BOOL bResult = FALSE;
2698 LISTVIEW_ITEM *lpItem;
2700 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2702 UINT uView = lStyle & LVS_TYPEMASK;
2706 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2708 if (lStyle & LVS_OWNERDATA)
2710 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2714 ZeroMemory(&itm, sizeof(itm));
2715 itm.mask = LVIF_STATE | LVIF_PARAM;
2716 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2717 itm.iItem = lpLVItem->iItem;
2719 ListView_GetItemW(hwnd, &itm);
2722 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2723 nmlv.uNewState = lpLVItem->state;
2724 nmlv.uOldState = itm.state;
2725 nmlv.uChanged = LVIF_STATE;
2726 nmlv.lParam = itm.lParam;
2727 nmlv.iItem = lpLVItem->iItem;
2729 if ((itm.state & lpLVItem->stateMask) !=
2730 (lpLVItem->state & lpLVItem->stateMask))
2732 /* send LVN_ITEMCHANGING notification */
2733 if (!listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv))
2735 if (lpLVItem->stateMask & LVIS_FOCUSED)
2737 if (lpLVItem->state & LVIS_FOCUSED)
2738 infoPtr->nFocusedItem = lpLVItem->iItem;
2739 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2740 infoPtr->nFocusedItem = -1;
2742 if (lpLVItem->stateMask & LVIS_SELECTED)
2744 if (lpLVItem->state & LVIS_SELECTED)
2746 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd);
2747 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2750 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2754 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2756 rcItem.left = LVIR_BOUNDS;
2757 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2758 InvalidateRect(hwnd, &rcItem, TRUE);
2766 if (lpLVItem != NULL)
2768 if (lpLVItem->iSubItem == 0)
2770 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2771 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2773 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2776 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2777 nmlv.lParam = lpItem->lParam;
2778 uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2781 if (uChanged & LVIF_STATE)
2783 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2784 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2786 if (nmlv.uNewState & LVIS_SELECTED)
2789 * This is redundant if called through SetSelection
2791 * however is required if the used directly calls SetItem
2792 * to set the selection.
2794 if (lStyle & LVS_SINGLESEL)
2795 LISTVIEW_RemoveAllSelections(hwnd);
2797 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2800 else if (lpLVItem->stateMask & LVIS_SELECTED)
2802 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2805 if (nmlv.uNewState & LVIS_FOCUSED)
2808 * This is a fun hoop to jump to try to catch if
2809 * the user is calling us directly to call focus or if
2810 * this function is being called as a result of a
2811 * SetItemFocus call.
2813 if (infoPtr->nFocusedItem >= 0)
2814 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2818 nmlv.uChanged = uChanged;
2819 nmlv.iItem = lpLVItem->iItem;
2820 nmlv.lParam = lpItem->lParam;
2821 /* send LVN_ITEMCHANGING notification */
2822 listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv);
2824 /* copy information */
2825 bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW);
2827 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2828 based on the width of the items text */
2829 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2831 item_width = LISTVIEW_GetStringWidthT(hwnd, lpItem->pszText, TRUE);
2833 if(item_width > infoPtr->nItemWidth)
2834 infoPtr->nItemWidth = item_width;
2837 /* send LVN_ITEMCHANGED notification */
2838 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2847 rcItem.left = LVIR_BOUNDS;
2848 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2849 InvalidateRect(hwnd, &rcItem, TRUE);
2861 * Sets subitem attributes.
2864 * [I] HWND : window handle
2865 * [I] LPLVITEM : new subitem atttributes
2866 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2872 static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2874 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2875 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2876 BOOL bResult = FALSE;
2878 LISTVIEW_SUBITEM *lpSubItem;
2881 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2883 if (lStyle & LVS_OWNERDATA)
2886 if (lpLVItem != NULL)
2888 if (lpLVItem->iSubItem > 0)
2890 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2891 if (hdpaSubItems != NULL)
2893 /* set subitem only if column is present */
2894 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2896 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2897 if (lpSubItem != NULL)
2898 bResult = LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW);
2900 bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW);
2902 rcItem.left = LVIR_BOUNDS;
2903 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2904 InvalidateRect(hwnd, &rcItem, FALSE);
2915 * Sets item attributes.
2918 * [I] HWND : window handle
2919 * [I] LPLVITEM : new item atttributes
2920 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2926 static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2928 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2930 if (!lpLVItem || lpLVItem->iItem < 0 ||
2931 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2933 if (lpLVItem->iSubItem == 0)
2934 return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW);
2936 return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW);
2941 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2944 * [I] HWND : window handle
2949 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2951 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2952 UINT uView = lStyle & LVS_TYPEMASK;
2954 SCROLLINFO scrollInfo;
2956 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2957 scrollInfo.cbSize = sizeof(SCROLLINFO);
2958 scrollInfo.fMask = SIF_POS;
2960 if (uView == LVS_LIST)
2962 if ((lStyle & WS_HSCROLL) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo))
2963 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2965 else if (uView == LVS_REPORT)
2967 if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo))
2968 nItem = scrollInfo.nPos;
2979 * [I] HWND : window handle
2980 * [I] HDC : device context handle
2981 * [I] INT : item index
2982 * [I] INT : subitem index
2983 * [I] RECT * : clipping rectangle
2988 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
2989 RECT rcItem, BOOL Selected)
2991 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2992 WCHAR szDispText[DISP_TEXT_SIZE];
2994 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
2997 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
3000 /* get information needed for drawing the item */
3001 ZeroMemory(&lvItem, sizeof(lvItem));
3002 lvItem.mask = LVIF_TEXT;
3003 lvItem.iItem = nItem;
3004 lvItem.iSubItem = nSubItem;
3005 lvItem.cchTextMax = DISP_TEXT_SIZE;
3006 lvItem.pszText = szDispText;
3007 *lvItem.pszText = '\0';
3008 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3009 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3011 /* redraw the background of the item */
3013 if(infoPtr->nColumnCount == (nSubItem + 1))
3014 rcTemp.right = infoPtr->rcList.right;
3016 rcTemp.right += WIDTH_PADDING;
3018 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3020 /* set item colors */
3021 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
3023 if (infoPtr->bFocus)
3025 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3026 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3030 SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3031 SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3036 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3038 SetBkMode(hdc, TRANSPARENT);
3039 textoutOptions &= ~ETO_OPAQUE;
3043 SetBkMode(hdc, OPAQUE);
3044 SetBkColor(hdc, infoPtr->clrTextBk);
3047 SetTextColor(hdc, infoPtr->clrText);
3050 ExtTextOutW(hdc, rcItem.left, rcItem.top, textoutOptions,
3051 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3055 /* fill in the gap */
3057 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
3059 CopyRect(&rec,&rcItem);
3060 rec.left = rec.right;
3061 rec.right = rec.left+REPORT_MARGINX;
3062 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3063 &rec, NULL, 0, NULL);
3065 CopyRect(&rec,&rcItem);
3066 rec.right = rec.left;
3067 rec.left = rec.left - REPORT_MARGINX;
3068 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3069 &rec, NULL, 0, NULL);
3079 * [I] HWND : window handle
3080 * [I] HDC : device context handle
3081 * [I] INT : item index
3082 * [I] RECT * : clipping rectangle
3087 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
3089 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3090 WCHAR szDispText[DISP_TEXT_SIZE];
3095 DWORD dwTextColor,dwTextX;
3096 BOOL bImage = FALSE;
3098 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
3101 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
3104 /* get information needed for drawing the item */
3105 ZeroMemory(&lvItem, sizeof(lvItem));
3106 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3107 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
3108 lvItem.iItem = nItem;
3109 lvItem.iSubItem = 0;
3110 lvItem.cchTextMax = DISP_TEXT_SIZE;
3111 lvItem.pszText = szDispText;
3112 *lvItem.pszText = '\0';
3113 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3114 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3116 /* redraw the background of the item */
3118 if(infoPtr->nColumnCount == (nItem + 1))
3119 rcTemp.right = infoPtr->rcList.right;
3121 rcTemp.right+=WIDTH_PADDING;
3123 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3126 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3128 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3131 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3135 if (infoPtr->himlState != NULL)
3137 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3138 if (uStateImage > 0)
3140 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3141 rcItem.top, ILD_NORMAL);
3144 rcItem.left += infoPtr->iconSize.cx;
3146 SuggestedFocus->left += infoPtr->iconSize.cx;
3151 if (infoPtr->himlSmall != NULL)
3153 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
3156 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3157 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3158 rcItem.top, ILD_SELECTED);
3160 else if (lvItem.iImage>=0)
3162 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3163 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3164 rcItem.top, ILD_NORMAL);
3167 rcItem.left += infoPtr->iconSize.cx;
3170 SuggestedFocus->left += infoPtr->iconSize.cx;
3174 /* Don't bother painting item being edited */
3175 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
3178 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
3180 /* set item colors */
3181 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3182 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3183 /* set raster mode */
3184 nMixMode = SetROP2(hdc, R2_XORPEN);
3186 else if ((GetWindowLongW(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3187 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
3189 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3190 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3191 /* set raster mode */
3192 nMixMode = SetROP2(hdc, R2_COPYPEN);
3196 /* set item colors */
3197 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3199 dwBkColor = GetBkColor(hdc);
3200 iBkMode = SetBkMode(hdc, TRANSPARENT);
3201 textoutOptions &= ~ETO_OPAQUE;
3205 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3206 iBkMode = SetBkMode(hdc, OPAQUE);
3209 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3210 /* set raster mode */
3211 nMixMode = SetROP2(hdc, R2_COPYPEN);
3214 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
3215 if (rcItem.left + nLabelWidth < rcItem.right)
3218 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3220 rcItem.right += IMAGE_PADDING;
3224 dwTextX = rcItem.left + 1;
3226 dwTextX += IMAGE_PADDING;
3229 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3230 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3232 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3234 /* fill in the gap */
3236 CopyRect(&rec,&rcItem);
3237 rec.left = rec.right;
3238 rec.right = rec.left+REPORT_MARGINX;
3239 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3240 &rec, NULL, 0, NULL);
3244 CopyRect(SuggestedFocus,&rcItem);
3248 SetROP2(hdc, R2_COPYPEN);
3249 SetBkColor(hdc, dwBkColor);
3250 SetTextColor(hdc, dwTextColor);
3252 SetBkMode(hdc, iBkMode);
3258 * Draws an item when in large icon display mode.
3261 * [I] HWND : window handle
3262 * [I] HDC : device context handle
3263 * [I] INT : item index
3264 * [I] RECT : clipping rectangle
3265 * [O] RECT * : The text rectangle about which to draw the focus
3270 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3271 RECT *SuggestedFocus)
3273 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3274 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3276 UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
3278 /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemRect*/
3281 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3282 hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3284 /* get information needed for drawing the item */
3285 ZeroMemory(&lvItem, sizeof(lvItem));
3286 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3287 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3288 lvItem.iItem = nItem;
3289 lvItem.iSubItem = 0;
3290 lvItem.cchTextMax = DISP_TEXT_SIZE;
3291 lvItem.pszText = szDispText;
3292 *lvItem.pszText = '\0';
3293 LISTVIEW_GetItemW(hwnd, &lvItem, FALSE);
3294 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3296 /* redraw the background of the item */
3298 if(infoPtr->nColumnCount == (nItem + 1))
3299 rcTemp.right = infoPtr->rcList.right;
3301 rcTemp.right+=WIDTH_PADDING;
3302 /* The comment doesn't say WIDTH_PADDING applies to large icons */
3304 TRACE("background rect (%d,%d)-(%d,%d)\n",
3305 rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
3307 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3310 /* Figure out text colours etc. depending on state
3311 * At least the following states exist; there may be more.
3312 * Many items may be selected
3313 * At most one item may have the focus
3314 * The application may not actually be active currently
3315 * 1. The item is not selected in any way
3316 * 2. The cursor is flying over the icon or text and the text is being
3317 * expanded because it is not fully displayed currently.
3318 * 3. The item is selected and is focussed, i.e. the user has not clicked
3319 * in the blank area of the window, and the window (or application?)
3320 * still has the focus.
3321 * 4. As 3 except that a different window has the focus
3322 * 5. The item is the selected item of all the items, but the user has
3323 * clicked somewhere else on the window.
3324 * Only a few of these are handled currently. In particular 2 is not yet
3325 * handled since we do not support the functionality currently (or at least
3326 * we didn't when I wrote this)
3329 if (lvItem.state & LVIS_SELECTED)
3331 /* set item colors */
3332 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3333 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3334 SetBkMode (hdc, OPAQUE);
3335 /* set raster mode */
3336 SetROP2(hdc, R2_XORPEN);
3337 /* When exactly is it in XOR? while being dragged? */
3341 /* set item colors */
3342 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3344 SetBkMode(hdc, TRANSPARENT);
3348 SetBkMode(hdc, OPAQUE);
3349 SetBkColor(hdc, infoPtr->clrTextBk);
3352 SetTextColor(hdc, infoPtr->clrText);
3353 /* set raster mode */
3354 SetROP2(hdc, R2_COPYPEN);
3357 /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3358 * wrapping and long words split.
3359 * In cases 1 and 4 only a portion of the text is displayed with word
3360 * wrapping and both word and end ellipsis. (I don't yet know about path
3363 uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
3366 DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3369 if (infoPtr->himlNormal != NULL)
3371 if (lvItem.iImage >= 0)
3373 ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3375 (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3379 /* Draw the text below the icon */
3381 /* Don't bother painting item being edited */
3382 if ((infoPtr->hwndEdit && (lvItem.state & LVIS_FOCUSED)) ||
3383 !lstrlenW(lvItem.pszText))
3385 SetRectEmpty(SuggestedFocus);
3389 /* Since rcItem.left is left point of icon, compute left point of item box */
3390 rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3391 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3392 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3393 TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3394 rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3395 infoPtr->iconSize.cx, infoPtr->nItemWidth);
3396 TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3397 infoPtr->rcList.left, infoPtr->rcList.top,
3398 infoPtr->rcList.right, infoPtr->rcList.bottom,
3399 infoPtr->rcView.left, infoPtr->rcView.top,
3400 infoPtr->rcView.right, infoPtr->rcView.bottom);
3402 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3403 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3408 /* I am sure of most of the uFormat values. However I am not sure about
3409 * whether we need or do not need the following:
3410 * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3411 * DT_PATH_ELLIPSIS, DT_RTLREADING,
3412 * We certainly do not need
3413 * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3414 * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3417 /* If the text is being drawn without clipping (i.e. the full text) then we
3418 * need to jump through a few hoops to ensure that it all gets displayed and
3419 * that the background is complete
3421 if (uFormat & DT_NOCLIP)
3423 HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3424 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3425 FillRect(hdc, &rcItem, hBrush);
3426 DeleteObject(hBrush);
3428 /* else ? What if we are losing the focus? will we not get a complete
3431 DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3433 CopyRect(SuggestedFocus, &rcItem);
3438 * Draws listview items when in report display mode.
3441 * [I] HWND : window handle
3442 * [I] HDC : device context handle
3447 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3449 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3450 SCROLLINFO scrollInfo;
3451 INT nDrawPosY = infoPtr->rcList.top;
3453 RECT rcItem, rcTemp;
3458 DWORD cditemmode = CDRF_DODEFAULT;
3459 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3462 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3463 scrollInfo.cbSize = sizeof(SCROLLINFO);
3464 scrollInfo.fMask = SIF_POS;
3466 nItem = ListView_GetTopIndex(hwnd);
3468 /* add 1 for displaying a partial item at the bottom */
3469 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3470 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3472 /* send cache hint notification */
3473 if (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3477 nmlv.hdr.hwndFrom = hwnd;
3478 nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID);
3479 nmlv.hdr.code = LVN_ODCACHEHINT;
3483 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3487 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3488 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3489 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3491 /* clear the background of any part of the control that doesn't contain items */
3492 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3493 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3495 /* nothing to draw */
3496 if(GETITEMCOUNT(infoPtr) == 0)
3499 /* Get scroll bar info once before loop */
3500 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3501 scrollOffset = scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
3503 for (; nItem < nLast; nItem++)
3505 RECT SuggestedFocusRect;
3508 if (lStyle & LVS_OWNERDRAWFIXED)
3510 UINT uID = GetWindowLongW( hwnd, GWL_ID);
3515 TRACE("Owner Drawn\n");
3516 dis.CtlType = ODT_LISTVIEW;
3519 dis.itemAction = ODA_DRAWENTIRE;
3522 if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3523 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3525 dis.hwndItem = hwnd;
3528 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3530 dis.rcItem.left = -scrollOffset;
3531 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3532 dis.rcItem.top = nDrawPosY;
3533 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3535 ZeroMemory(&item,sizeof(item));
3537 item.mask = LVIF_PARAM;
3538 ListView_GetItemW(hwnd, &item);
3540 dis.itemData = item.lParam;
3542 if (SendMessageW(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3544 nDrawPosY += infoPtr->nItemHeight;
3553 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3554 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3556 ir.left += REPORT_MARGINX;
3557 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3559 ir.bottom = ir.top + infoPtr->nItemHeight;
3561 CopyRect(&SuggestedFocusRect,&ir);
3564 for (j = 0; j < nColumnCount; j++)
3566 if (cdmode & CDRF_NOTIFYITEMDRAW)
3567 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3569 if (cditemmode & CDRF_SKIPDEFAULT)
3572 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3574 rcItem.left += REPORT_MARGINX;
3575 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3576 rcItem.top = nDrawPosY;
3577 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3579 /* Offset the Scroll Bar Pos */
3580 rcItem.left -= scrollOffset;
3581 rcItem.right -= scrollOffset;
3585 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3586 &SuggestedFocusRect);
3590 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, FullSelected);
3593 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3594 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3595 CDDS_ITEMPOSTPAINT);
3600 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3603 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3604 rop = SetROP2(hdc, R2_XORPEN);
3606 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3607 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3610 SetROP2(hdc, R2_COPYPEN);
3612 nDrawPosY += infoPtr->nItemHeight;
3618 * Retrieves the number of items that can fit vertically in the client area.
3621 * [I] HWND : window handle
3624 * Number of items per row.
3626 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3628 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3629 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3630 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3631 INT nCountPerRow = 1;
3635 if (uView != LVS_REPORT)
3637 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3638 if (nCountPerRow == 0) nCountPerRow = 1;
3642 return nCountPerRow;
3647 * Retrieves the number of items that can fit horizontally in the client
3651 * [I] HWND : window handle
3654 * Number of items per column.
3656 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3658 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3659 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3660 INT nCountPerColumn = 1;
3662 if (nListHeight > 0)
3664 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3665 if (nCountPerColumn == 0) nCountPerColumn = 1;
3668 return nCountPerColumn;
3673 * Retrieves the number of columns needed to display all the items when in
3674 * list display mode.
3677 * [I] HWND : window handle
3680 * Number of columns.
3682 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3684 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3685 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3686 INT nColumnCount = 0;
3688 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3690 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3691 if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3694 return nColumnCount;
3700 * Draws listview items when in list display mode.
3703 * [I] HWND : window handle
3704 * [I] HDC : device context handle
3709 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3711 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3712 RECT rcItem, FocusRect, rcTemp;
3716 INT nCountPerColumn;
3717 INT nItemWidth = infoPtr->nItemWidth;
3718 INT nItemHeight = infoPtr->nItemHeight;
3719 DWORD cditemmode = CDRF_DODEFAULT;
3721 /* get number of fully visible columns */
3722 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3723 infoPtr->nColumnCount = nColumnCount;
3724 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3725 nItem = ListView_GetTopIndex(hwnd);
3727 /* paint the background of the control that doesn't contain any items */
3728 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3729 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3731 /* nothing to draw, return here */
3732 if(GETITEMCOUNT(infoPtr) == 0)
3735 for (i = 0; i < nColumnCount; i++)
3737 for (j = 0; j < nCountPerColumn; j++, nItem++)
3739 if (nItem >= GETITEMCOUNT(infoPtr))
3742 if (cdmode & CDRF_NOTIFYITEMDRAW)
3743 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3745 if (cditemmode & CDRF_SKIPDEFAULT)
3748 rcItem.top = j * nItemHeight;
3749 rcItem.left = i * nItemWidth;
3750 rcItem.bottom = rcItem.top + nItemHeight;
3751 rcItem.right = rcItem.left + nItemWidth;
3752 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3756 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3757 Rectangle(hdc, FocusRect.left, FocusRect.top,
3758 FocusRect.right,FocusRect.bottom);
3760 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3761 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3762 CDDS_ITEMPOSTPAINT);
3770 * Draws listview items when in icon or small icon display mode.
3773 * [I] HWND : window handle
3774 * [I] HDC : device context handle
3779 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3781 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3784 RECT rcItem, SuggestedFocus, rcTemp;
3786 DWORD cditemmode = CDRF_DODEFAULT;
3788 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3789 /* DrawItem from erasing the incorrect background area */
3791 /* paint the background of the control that doesn't contain any items */
3792 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3793 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3795 /* nothing to draw, return here */
3796 if(GETITEMCOUNT(infoPtr) == 0)
3799 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3800 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3802 if (cdmode & CDRF_NOTIFYITEMDRAW)
3803 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3805 if (cditemmode & CDRF_SKIPDEFAULT)
3808 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3809 ptPosition.x += ptOrigin.x;
3810 ptPosition.y += ptOrigin.y;
3812 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3814 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3816 if (ptPosition.y < infoPtr->rcList.bottom)
3818 if (ptPosition.x < infoPtr->rcList.right)
3820 rcItem.top = ptPosition.y;
3821 rcItem.left = ptPosition.x;
3822 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3823 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3825 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3827 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3831 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3832 infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
3833 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3834 SuggestedFocus.right,SuggestedFocus.bottom);
3839 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3840 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3841 CDDS_ITEMPOSTPAINT);
3847 * Draws listview items.
3850 * [I] HWND : window handle
3851 * [I] HDC : device context handle
3856 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3858 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3859 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3865 LISTVIEW_DumpListview (infoPtr, __LINE__);
3867 GetClientRect(hwnd, &rect);
3868 cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3870 if (cdmode == CDRF_SKIPDEFAULT) return;
3873 hOldFont = SelectObject(hdc, infoPtr->hFont);
3875 /* select the dotted pen (for drawing the focus box) */
3876 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3877 hOldPen = SelectObject(hdc, hPen);
3879 /* select transparent brush (for drawing the focus box) */
3880 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3882 if (uView == LVS_LIST)
3883 LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3884 else if (uView == LVS_REPORT)
3885 LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3886 else if (uView == LVS_SMALLICON)
3887 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3888 else if (uView == LVS_ICON)
3889 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3891 /* unselect objects */
3892 SelectObject(hdc, hOldFont);
3893 SelectObject(hdc, hOldPen);
3898 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3899 LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
3905 * Calculates the approximate width and height of a given number of items.
3908 * [I] HWND : window handle
3909 * [I] INT : number of items
3914 * Returns a DWORD. The width in the low word and the height in high word.
3916 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
3917 WORD wWidth, WORD wHeight)
3919 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3920 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3921 INT nItemCountPerColumn = 1;
3922 INT nColumnCount = 0;
3923 DWORD dwViewRect = 0;
3925 if (nItemCount == -1)
3926 nItemCount = GETITEMCOUNT(infoPtr);
3928 if (uView == LVS_LIST)
3930 if (wHeight == 0xFFFF)
3932 /* use current height */
3933 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3936 if (wHeight < infoPtr->nItemHeight)
3937 wHeight = infoPtr->nItemHeight;
3941 if (infoPtr->nItemHeight > 0)
3943 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3944 if (nItemCountPerColumn == 0)
3945 nItemCountPerColumn = 1;
3947 if (nItemCount % nItemCountPerColumn != 0)
3948 nColumnCount = nItemCount / nItemCountPerColumn;
3950 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3954 /* Microsoft padding magic */
3955 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3956 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3958 dwViewRect = MAKELONG(wWidth, wHeight);
3960 else if (uView == LVS_REPORT)
3961 FIXME("uView == LVS_REPORT: not implemented\n");
3962 else if (uView == LVS_SMALLICON)
3963 FIXME("uView == LVS_SMALLICON: not implemented\n");
3964 else if (uView == LVS_ICON)
3965 FIXME("uView == LVS_ICON: not implemented\n");
3972 * Arranges listview items in icon display mode.
3975 * [I] HWND : window handle
3976 * [I] INT : alignment code
3982 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
3984 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3985 BOOL bResult = FALSE;
3987 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3992 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3995 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3998 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4000 case LVA_SNAPTOGRID:
4001 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4009 /* << LISTVIEW_CreateDragImage >> */
4014 * Removes all listview items and subitems.
4017 * [I] HWND : window handle
4023 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
4025 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4026 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4027 UINT uView = lStyle & LVS_TYPEMASK;
4028 LISTVIEW_ITEM *lpItem;
4029 LISTVIEW_SUBITEM *lpSubItem;
4032 BOOL bResult = FALSE;
4035 TRACE("(hwnd=%x,)\n", hwnd);
4037 LISTVIEW_RemoveAllSelections(hwnd);
4038 infoPtr->nSelectionMark=-1;
4039 infoPtr->nFocusedItem=-1;
4040 /* But we are supposed to leave nHotItem as is! */
4042 if (lStyle & LVS_OWNERDATA)
4044 infoPtr->hdpaItems->nItemCount = 0;
4045 InvalidateRect(hwnd, NULL, TRUE);
4049 if (GETITEMCOUNT(infoPtr) > 0)
4053 /* send LVN_DELETEALLITEMS notification */
4054 /* verify if subsequent LVN_DELETEITEM notifications should be
4056 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4058 bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &nmlv);
4060 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4062 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4063 if (hdpaSubItems != NULL)
4065 for (j = 1; j < hdpaSubItems->nItemCount; j++)
4067 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
4068 if (lpSubItem != NULL)
4070 /* free subitem string */
4071 if (is_textW(lpSubItem->pszText))
4072 COMCTL32_Free(lpSubItem->pszText);
4075 COMCTL32_Free(lpSubItem);
4079 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4084 /* send LVN_DELETEITEM notification */
4086 nmlv.lParam = lpItem->lParam;
4087 listview_notify(hwnd, LVN_DELETEITEM, &nmlv);
4090 /* free item string */
4091 if (is_textW(lpItem->pszText))
4092 COMCTL32_Free(lpItem->pszText);
4095 COMCTL32_Free(lpItem);
4098 DPA_Destroy(hdpaSubItems);
4102 /* reinitialize listview memory */
4103 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
4105 /* align items (set position of each item) */
4106 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4108 if (lStyle & LVS_ALIGNLEFT)
4110 LISTVIEW_AlignLeft(hwnd);
4114 LISTVIEW_AlignTop(hwnd);
4118 LISTVIEW_UpdateScroll(hwnd);
4120 /* invalidate client area (optimization needed) */
4121 InvalidateRect(hwnd, NULL, TRUE);
4129 * Removes a column from the listview control.
4132 * [I] HWND : window handle
4133 * [I] INT : column index
4139 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
4141 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4142 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4143 UINT uOwnerData = GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA;
4144 BOOL bResult = FALSE;
4146 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
4149 bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
4151 /* Need to reset the item width when deleting a column */
4152 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
4154 /* reset scroll parameters */
4155 if (uView == LVS_REPORT)
4157 /* update scrollbar(s) */
4158 LISTVIEW_UpdateScroll(hwnd);
4160 /* refresh client area */
4161 InvalidateRect(hwnd, NULL, FALSE);
4170 * Removes an item from the listview control.
4173 * [I] HWND : window handle
4174 * [I] INT : item index
4180 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
4182 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4183 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4184 UINT uView = lStyle & LVS_TYPEMASK;
4185 LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID);
4187 BOOL bResult = FALSE;
4189 LISTVIEW_ITEM *lpItem;
4190 LISTVIEW_SUBITEM *lpSubItem;
4194 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
4197 /* First, send LVN_DELETEITEM notification. */
4198 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4199 nmlv.hdr.hwndFrom = hwnd;
4200 nmlv.hdr.idFrom = lCtrlId;
4201 nmlv.hdr.code = LVN_DELETEITEM;
4203 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
4207 /* remove it from the selection range */
4208 ZeroMemory(&item,sizeof(item));
4209 item.stateMask = LVIS_SELECTED;
4210 LISTVIEW_SetItemState(hwnd,nItem,&item);
4212 if (lStyle & LVS_OWNERDATA)
4214 infoPtr->hdpaItems->nItemCount --;
4215 InvalidateRect(hwnd, NULL, TRUE);
4219 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4221 /* initialize memory */
4222 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4224 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4225 if (hdpaSubItems != NULL)
4227 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4229 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4230 if (lpSubItem != NULL)
4232 /* free item string */
4233 if (is_textW(lpSubItem->pszText))
4234 COMCTL32_Free(lpSubItem->pszText);
4237 COMCTL32_Free(lpSubItem);
4241 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4244 /* free item string */
4245 if (is_textW(lpItem->pszText))
4246 COMCTL32_Free(lpItem->pszText);
4249 COMCTL32_Free(lpItem);
4252 bResult = DPA_Destroy(hdpaSubItems);
4255 LISTVIEW_ShiftIndices(hwnd,nItem,-1);
4257 /* align items (set position of each item) */
4258 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4260 if (lStyle & LVS_ALIGNLEFT)
4261 LISTVIEW_AlignLeft(hwnd);
4263 LISTVIEW_AlignTop(hwnd);
4266 LISTVIEW_UpdateScroll(hwnd);
4268 /* refresh client area */
4269 InvalidateRect(hwnd, NULL, TRUE);
4278 * Return edit control handle of current edit label
4281 * [I] HWND : window handle
4287 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4289 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4290 return infoPtr->hwndEdit;
4296 * Callback implementation for editlabel control
4299 * [I] HWND : window handle
4300 * [I] LPSTR : modified text
4301 * [I] DWORD : item index
4302 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4308 static BOOL LISTVIEW_EndEditLabelT(HWND hwnd, LPWSTR pszText, DWORD nItem, BOOL isW)
4310 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4311 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4312 NMLVDISPINFOW dispInfo;
4313 LISTVIEW_ITEM *lpItem;
4315 LISTVIEW_ITEM lvItemRef;
4317 BOOL bResult = TRUE;
4319 TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd, debugstr_t(pszText, isW), nItem, isW);
4321 if (!(lStyle & LVS_OWNERDATA))
4323 if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4326 if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4331 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4332 ZeroMemory(&item,sizeof(item));
4335 item.mask = LVIF_PARAM | LVIF_STATE;
4336 ListView_GetItemW(hwnd, &item);
4337 lvItemRef.state = item.state;
4338 lvItemRef.iImage = item.iImage;
4339 lvItemRef.lParam = item.lParam;
4340 lpItem = &lvItemRef;
4343 ZeroMemory(&dispInfo, sizeof(dispInfo));
4344 dispInfo.item.mask = 0;
4345 dispInfo.item.iItem = nItem;
4346 dispInfo.item.state = lpItem->state;
4347 dispInfo.item.stateMask = 0;
4348 dispInfo.item.pszText = pszText;
4349 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4350 dispInfo.item.iImage = lpItem->iImage;
4351 dispInfo.item.lParam = lpItem->lParam;
4352 infoPtr->hwndEdit = 0;
4354 /* Do we need to update the Item Text */
4355 if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, isW))
4356 if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
4357 bResult = textsetptrT(&lpItem->pszText, pszText, isW);
4364 * Callback implementation for editlabel control
4367 * [I] HWND : window handle
4368 * [I] LPSTR : modified text
4369 * [I] DWORD : item index
4375 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem)
4377 return LISTVIEW_EndEditLabelT(hwnd, pszText, nItem, TRUE);
4382 * Callback implementation for editlabel control
4385 * [I] HWND : window handle
4386 * [I] LPSTR : modified text
4387 * [I] DWORD : item index
4393 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem)
4395 return LISTVIEW_EndEditLabelT(hwnd, (LPWSTR)pszText, nItem, FALSE);
4400 * Begin in place editing of specified list view item
4403 * [I] HWND : window handle
4404 * [I] INT : item index
4405 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4411 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW)
4413 NMLVDISPINFOW dispInfo;
4415 LISTVIEW_ITEM *lpItem;
4417 HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE);
4418 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4420 WCHAR szDispText[DISP_TEXT_SIZE];
4422 LISTVIEW_ITEM lvItemRef;
4423 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4425 if (~GetWindowLongW(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4428 /* Is the EditBox still there, if so remove it */
4429 if(infoPtr->hwndEdit != 0)
4432 LISTVIEW_SetSelection(hwnd, nItem);
4433 LISTVIEW_SetItemFocus(hwnd, nItem);
4435 if (!(lStyle & LVS_OWNERDATA))
4437 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4440 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4446 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4447 ZeroMemory(&item, sizeof(item));
4450 item.mask = LVIF_PARAM | LVIF_STATE;
4451 ListView_GetItemW(hwnd, &item);
4452 lvItemRef.iImage = item.iImage;
4453 lvItemRef.state = item.state;
4454 lvItemRef.lParam = item.lParam;
4455 lpItem = &lvItemRef;
4458 /* get information needed for drawing the item */
4459 ZeroMemory(&lvItem, sizeof(lvItem));
4460 lvItem.mask = LVIF_TEXT;
4461 lvItem.iItem = nItem;
4462 lvItem.iSubItem = 0;
4463 lvItem.cchTextMax = DISP_TEXT_SIZE;
4464 lvItem.pszText = szDispText;
4465 *lvItem.pszText = '\0';
4466 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW);
4468 ZeroMemory(&dispInfo, sizeof(dispInfo));
4469 dispInfo.item.mask = 0;
4470 dispInfo.item.iItem = nItem;
4471 dispInfo.item.state = lpItem->state;
4472 dispInfo.item.stateMask = 0;
4473 dispInfo.item.pszText = lvItem.pszText;
4474 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4475 dispInfo.item.iImage = lpItem->iImage;
4476 dispInfo.item.lParam = lpItem->lParam;
4478 if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW))
4481 rect.left = LVIR_LABEL;
4482 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4485 if (!(hedit = CreateEditLabelT(szDispText , WS_VISIBLE,
4486 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, hwnd, hinst,
4487 isW ? LISTVIEW_EndEditLabelW : (EditlblCallbackW)LISTVIEW_EndEditLabelA,
4491 infoPtr->hwndEdit = hedit;
4493 SendMessageW(hedit, EM_SETSEL, 0, -1);
4501 * Ensures the specified item is visible, scrolling into view if necessary.
4504 * [I] HWND : window handle
4505 * [I] INT : item index
4506 * [I] BOOL : partially or entirely visible
4512 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4514 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4515 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4516 INT nScrollPosHeight = 0;
4517 INT nScrollPosWidth = 0;
4518 SCROLLINFO scrollInfo;
4520 BOOL bRedraw = FALSE;
4522 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4523 scrollInfo.cbSize = sizeof(SCROLLINFO);
4524 scrollInfo.fMask = SIF_POS;
4526 /* ALWAYS bPartial == FALSE, FOR NOW! */
4528 rcItem.left = LVIR_BOUNDS;
4529 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4531 if (rcItem.left < infoPtr->rcList.left)
4533 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4537 if (uView == LVS_LIST)
4539 nScrollPosWidth = infoPtr->nItemWidth;
4540 rcItem.left += infoPtr->rcList.left;
4542 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4544 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4545 rcItem.left += infoPtr->rcList.left;
4548 /* When in LVS_REPORT view, the scroll position should
4550 if (nScrollPosWidth != 0)
4552 if (rcItem.left % nScrollPosWidth == 0)
4553 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4555 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4557 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4561 else if (rcItem.right > infoPtr->rcList.right)
4563 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4567 if (uView == LVS_LIST)
4569 rcItem.right -= infoPtr->rcList.right;
4570 nScrollPosWidth = infoPtr->nItemWidth;
4572 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4574 rcItem.right -= infoPtr->rcList.right;
4575 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4578 /* When in LVS_REPORT view, the scroll position should
4580 if (nScrollPosWidth != 0)
4582 if (rcItem.right % nScrollPosWidth == 0)
4583 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4585 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4587 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4592 if (rcItem.top < infoPtr->rcList.top)
4596 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4598 if (uView == LVS_REPORT)
4600 rcItem.top -= infoPtr->rcList.top;
4601 nScrollPosHeight = infoPtr->nItemHeight;
4603 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4605 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4606 rcItem.top += infoPtr->rcList.top;
4609 if (rcItem.top % nScrollPosHeight == 0)
4610 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4612 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4614 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4617 else if (rcItem.bottom > infoPtr->rcList.bottom)
4621 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4623 if (uView == LVS_REPORT)
4625 rcItem.bottom -= infoPtr->rcList.bottom;
4626 nScrollPosHeight = infoPtr->nItemHeight;
4628 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4630 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4631 rcItem.bottom -= infoPtr->rcList.bottom;
4634 if (rcItem.bottom % nScrollPosHeight == 0)
4635 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4637 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4639 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4645 InvalidateRect(hwnd,NULL,TRUE);
4651 * Retrieves the nearest item, given a position and a direction.
4654 * [I] HWND : window handle
4655 * [I] POINT : start position
4656 * [I] UINT : direction
4659 * Item index if successdful, -1 otherwise.
4661 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4663 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4668 TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4669 (vkDirection == VK_DOWN) ? "VK_DOWN" :
4670 ((vkDirection == VK_UP) ? "VK_UP" :
4671 ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4673 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4675 ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4676 LISTVIEW_GetOrigin(hwnd, &lvIntHit.ht.pt);
4677 lvIntHit.ht.pt.x += pt.x;
4678 lvIntHit.ht.pt.y += pt.y;
4680 if (vkDirection == VK_DOWN)
4681 lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4682 else if (vkDirection == VK_UP)
4683 lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4684 else if (vkDirection == VK_LEFT)
4685 lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4686 else if (vkDirection == VK_RIGHT)
4687 lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4689 if (PtInRect(&rcView, lvIntHit.ht.pt) == FALSE)
4693 nItem = LISTVIEW_SuperHitTestItem(hwnd, &lvIntHit, TRUE);
4694 return nItem == -1 ? lvIntHit.iDistItem : nItem;
4703 * Searches for an item with specific characteristics.
4706 * [I] hwnd : window handle
4707 * [I] nStart : base item index
4708 * [I] lpFindInfo : item information to look for
4711 * SUCCESS : index of item
4714 static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart,
4715 LPLVFINDINFOW lpFindInfo)
4717 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4719 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4723 INT nLast = GETITEMCOUNT(infoPtr);
4725 if ((nItem >= -1) && (lpFindInfo != NULL))
4727 ZeroMemory(&lvItem, sizeof(lvItem));
4729 if (lpFindInfo->flags & LVFI_PARAM)
4731 lvItem.mask |= LVIF_PARAM;
4734 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4736 lvItem.mask |= LVIF_TEXT;
4737 lvItem.pszText = szDispText;
4738 lvItem.cchTextMax = DISP_TEXT_SIZE;
4741 if (lpFindInfo->flags & LVFI_WRAP)
4744 if (lpFindInfo->flags & LVFI_NEARESTXY)
4746 ptItem.x = lpFindInfo->pt.x;
4747 ptItem.y = lpFindInfo->pt.y;
4752 while (nItem < nLast)
4754 if (lpFindInfo->flags & LVFI_NEARESTXY)
4756 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4757 lpFindInfo->vkDirection);
4760 /* get position of the new item index */
4761 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4772 lvItem.iItem = nItem;
4773 lvItem.iSubItem = 0;
4774 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
4776 if (lvItem.mask & LVIF_TEXT)
4778 if (lpFindInfo->flags & LVFI_PARTIAL)
4780 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4785 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4790 if (lvItem.mask & LVIF_PARAM)
4792 if (lpFindInfo->lParam != lvItem.lParam)
4818 * Searches for an item with specific characteristics.
4821 * [I] hwnd : window handle
4822 * [I] nStart : base item index
4823 * [I] lpFindInfo : item information to look for
4826 * SUCCESS : index of item
4829 static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart,
4830 LPLVFINDINFOA lpFindInfo)
4832 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4836 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4837 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4838 res = LISTVIEW_FindItemW(hwnd, nStart, &fiw);
4839 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4845 * Retrieves the background color of the listview control.
4848 * [I] HWND : window handle
4851 * COLORREF associated with the background.
4853 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4855 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4857 return infoPtr->clrBk;
4862 * Retrieves the background image of the listview control.
4865 * [I] HWND : window handle
4866 * [O] LPLVMKBIMAGE : background image attributes
4872 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4874 /* FIXME (listview, "empty stub!\n"); */
4880 * Retrieves the callback mask.
4883 * [I] HWND : window handle
4888 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4890 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4892 return infoPtr->uCallbackMask;
4897 * Retrieves column attributes.
4900 * [I] HWND : window handle
4901 * [I] INT : column index
4902 * [IO] LPLVCOLUMNW : column information
4903 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4904 * otherwise it is in fact a LPLVCOLUMNA
4910 static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4912 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4914 BOOL bResult = FALSE;
4916 if (lpColumn != NULL)
4919 TRACE("(hwnd=%x, col=%d, lpColumn=%s, isW=%d)\n",
4920 hwnd, nItem, debuglvcolumn_t(lpColumn, isW), isW);
4922 /* initialize memory */
4923 ZeroMemory(&hdi, sizeof(hdi));
4925 if (lpColumn->mask & LVCF_FMT)
4926 hdi.mask |= HDI_FORMAT;
4928 if (lpColumn->mask & LVCF_WIDTH)
4929 hdi.mask |= HDI_WIDTH;
4931 if (lpColumn->mask & LVCF_TEXT)
4933 hdi.mask |= HDI_TEXT;
4934 hdi.cchTextMax = lpColumn->cchTextMax;
4935 hdi.pszText = lpColumn->pszText;
4938 if (lpColumn->mask & LVCF_IMAGE)
4939 hdi.mask |= HDI_IMAGE;
4941 if (lpColumn->mask & LVCF_ORDER)
4942 hdi.mask |= HDI_ORDER;
4945 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4947 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4949 if (bResult != FALSE)
4951 if (lpColumn->mask & LVCF_FMT)
4955 if (hdi.fmt & HDF_LEFT)
4956 lpColumn->fmt |= LVCFMT_LEFT;
4957 else if (hdi.fmt & HDF_RIGHT)
4958 lpColumn->fmt |= LVCFMT_RIGHT;
4959 else if (hdi.fmt & HDF_CENTER)
4960 lpColumn->fmt |= LVCFMT_CENTER;
4962 if (hdi.fmt & HDF_IMAGE)
4963 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4965 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4966 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4969 if (lpColumn->mask & LVCF_WIDTH)
4970 lpColumn->cx = hdi.cxy;
4972 if (lpColumn->mask & LVCF_IMAGE)
4973 lpColumn->iImage = hdi.iImage;
4975 if (lpColumn->mask & LVCF_ORDER)
4976 lpColumn->iOrder = hdi.iOrder;
4984 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
4986 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
4993 for (i = 0; i < iCount; i++)
5001 * Retrieves the column width.
5004 * [I] HWND : window handle
5005 * [I] int : column index
5008 * SUCCESS : column width
5011 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
5013 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5014 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5015 INT nColumnWidth = 0;
5018 if (uView == LVS_LIST)
5020 nColumnWidth = infoPtr->nItemWidth;
5022 else if (uView == LVS_REPORT)
5024 /* get column width from header */
5025 ZeroMemory(&hdi, sizeof(hdi));
5026 hdi.mask = HDI_WIDTH;
5027 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
5028 nColumnWidth = hdi.cxy;
5031 return nColumnWidth;
5036 * In list or report display mode, retrieves the number of items that can fit
5037 * vertically in the visible area. In icon or small icon display mode,
5038 * retrieves the total number of visible items.
5041 * [I] HWND : window handle
5044 * Number of fully visible items.
5046 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
5048 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5049 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5052 if (uView == LVS_LIST)
5054 if (infoPtr->rcList.right > infoPtr->nItemWidth)
5056 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
5057 LISTVIEW_GetCountPerColumn(hwnd);
5060 else if (uView == LVS_REPORT)
5062 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
5066 nItemCount = GETITEMCOUNT(infoPtr);
5072 /* LISTVIEW_GetEditControl */
5076 * Retrieves the extended listview style.
5079 * [I] HWND : window handle
5082 * SUCCESS : previous style
5085 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
5087 LISTVIEW_INFO *infoPtr;
5089 /* make sure we can get the listview info */
5090 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
5093 return (infoPtr->dwExStyle);
5098 * Retrieves the handle to the header control.
5101 * [I] HWND : window handle
5106 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
5108 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5110 return infoPtr->hwndHeader;
5113 /* LISTVIEW_GetHotCursor */
5117 * Returns the time that the mouse cursor must hover over an item
5118 * before it is selected.
5121 * [I] HWND : window handle
5124 * Returns the previously set hover time or (DWORD)-1 to indicate that the
5125 * hover time is set to the default hover time.
5127 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
5129 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5131 return infoPtr->dwHoverTime;
5136 * Retrieves an image list handle.
5139 * [I] HWND : window handle
5140 * [I] INT : image list identifier
5143 * SUCCESS : image list handle
5146 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
5148 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5149 HIMAGELIST himl = NULL;
5154 himl = infoPtr->himlNormal;
5157 himl = infoPtr->himlSmall;
5160 himl = infoPtr->himlState;
5164 return (LRESULT)himl;
5167 /* LISTVIEW_GetISearchString */
5171 * Retrieves item attributes.
5174 * [I] hwnd : window handle
5175 * [IO] lpLVItem : item info
5176 * [I] internal : if true then we will use tricks that avoid copies
5177 * but are not compatible with the regular interface
5178 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5179 * if FALSE, the lpLVItem is a LPLVITEMA.
5185 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
5187 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5188 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5189 NMLVDISPINFOW dispInfo;
5190 LISTVIEW_SUBITEM *lpSubItem;
5191 LISTVIEW_ITEM *lpItem;
5194 INT* piImage = (INT*)&null;
5195 LPWSTR* ppszText= (LPWSTR*)&null;
5196 LPARAM* plParam = (LPARAM*)&null;
5198 if (internal && !isW)
5200 ERR("We can't have internal non-Unicode GetItem!\n");
5204 /* In the following:
5205 * lpLVItem describes the information requested by the user
5206 * lpItem/lpSubItem is what we have
5207 * dispInfo is a structure we use to request the missing
5208 * information from the application
5211 TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n",
5212 hwnd, debuglvitem_t(lpLVItem, isW), internal, isW);
5214 if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5215 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5218 ZeroMemory(&dispInfo, sizeof(dispInfo));
5220 if (lStyle & LVS_OWNERDATA)
5222 if (lpLVItem->mask & ~LVIF_STATE)
5224 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5225 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5226 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5227 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5230 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5232 lpLVItem->state = 0;
5233 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5234 lpLVItem->state |= LVIS_FOCUSED;
5235 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5236 lpLVItem->state |= LVIS_SELECTED;
5242 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5243 if (hdpaSubItems == NULL) return FALSE;
5245 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5248 ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5249 if (lpLVItem->iSubItem == 0)
5251 piImage=&lpItem->iImage;
5252 ppszText=&lpItem->pszText;
5253 plParam=&lpItem->lParam;
5254 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5256 dispInfo.item.mask |= LVIF_STATE;
5257 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5262 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5263 if (lpSubItem != NULL)
5265 piImage=&lpSubItem->iImage;
5266 ppszText=&lpSubItem->pszText;
5270 if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
5272 dispInfo.item.mask |= LVIF_IMAGE;
5275 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5277 dispInfo.item.mask |= LVIF_TEXT;
5278 dispInfo.item.pszText = lpLVItem->pszText;
5279 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5280 if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5281 *dispInfo.item.pszText = '\0';
5282 if (dispInfo.item.pszText && (*ppszText == NULL))
5283 *dispInfo.item.pszText = '\0';
5286 if (dispInfo.item.mask != 0)
5288 /* We don't have all the requested info, query the application */
5289 dispInfo.item.iItem = lpLVItem->iItem;
5290 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5291 dispInfo.item.lParam = lpItem->lParam;
5292 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5293 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5296 if (dispInfo.item.mask & LVIF_IMAGE)
5298 lpLVItem->iImage = dispInfo.item.iImage;
5299 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
5300 *piImage = dispInfo.item.iImage;
5302 else if (lpLVItem->mask & LVIF_IMAGE)
5304 lpLVItem->iImage = *piImage;
5307 if (dispInfo.item.mask & LVIF_PARAM)
5309 lpLVItem->lParam = dispInfo.item.lParam;
5310 if (dispInfo.item.mask & LVIF_DI_SETITEM)
5311 *plParam = dispInfo.item.lParam;
5313 else if (lpLVItem->mask & LVIF_PARAM)
5314 lpLVItem->lParam = lpItem->lParam;
5316 if (dispInfo.item.mask & LVIF_TEXT)
5318 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5319 textsetptrT(ppszText, dispInfo.item.pszText, isW);
5321 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5322 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5323 if (lpLVItem->pszText != dispInfo.item.pszText)
5324 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5327 else if (lpLVItem->mask & LVIF_TEXT)
5329 if (internal) lpLVItem->pszText = *ppszText;
5330 else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5333 if (lpLVItem->iSubItem == 0)
5335 if (dispInfo.item.mask & LVIF_STATE)
5337 lpLVItem->state = lpItem->state;
5338 lpLVItem->state &= ~dispInfo.item.stateMask;
5339 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5341 lpLVItem->state &= ~LVIS_SELECTED;
5342 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5343 LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem))
5344 lpLVItem->state |= LVIS_SELECTED;
5346 else if (lpLVItem->mask & LVIF_STATE)
5348 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5350 lpLVItem->state &= ~LVIS_SELECTED;
5351 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5352 LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5353 lpLVItem->state |= LVIS_SELECTED;
5356 if (lpLVItem->mask & LVIF_PARAM)
5357 lpLVItem->lParam = lpItem->lParam;
5359 if (lpLVItem->mask & LVIF_INDENT)
5360 lpLVItem->iIndent = lpItem->iIndent;
5366 /* LISTVIEW_GetHotCursor */
5370 * Retrieves the index of the hot item.
5373 * [I] HWND : window handle
5376 * SUCCESS : hot item index
5377 * FAILURE : -1 (no hot item)
5379 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5381 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5383 return infoPtr->nHotItem;
5386 /* LISTVIEW_GetHoverTime */
5390 * Retrieves the number of items in the listview control.
5393 * [I] HWND : window handle
5398 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5400 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5402 return GETITEMCOUNT(infoPtr);
5407 * Retrieves the rectangle enclosing the item icon and text.
5410 * [I] HWND : window handle
5411 * [I] INT : item index
5412 * [O] LPRECT : coordinate information
5418 static BOOL LISTVIEW_GetItemBoundBox(HWND hwnd, INT nItem, LPRECT lpRect)
5420 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5421 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5422 UINT uView = lStyle & LVS_TYPEMASK;
5423 BOOL bResult = FALSE;
5425 LISTVIEW_ITEM *lpItem;
5426 INT nCountPerColumn;
5429 TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd, nItem, lpRect);
5431 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5434 if (uView == LVS_LIST)
5437 nItem = nItem - ListView_GetTopIndex(hwnd);
5438 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5441 nRow = nItem % nCountPerColumn;
5444 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5449 lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5450 lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5455 lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5456 lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
5459 else if (uView == LVS_REPORT)
5462 lpRect->left = REPORT_MARGINX;
5463 lpRect->top = ((nItem - ListView_GetTopIndex(hwnd)) *
5464 infoPtr->nItemHeight) + infoPtr->rcList.top;
5466 if (!(lStyle & LVS_NOSCROLL))
5468 SCROLLINFO scrollInfo;
5469 /* Adjust position by scrollbar offset */
5470 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5471 scrollInfo.cbSize = sizeof(SCROLLINFO);
5472 scrollInfo.fMask = SIF_POS;
5473 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5474 lpRect->left -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5477 else /* either LVS_ICON or LVS_SMALLICON */
5479 if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
5481 if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
5484 lpRect->left = lpItem->ptPosition.x;
5485 lpRect->top = lpItem->ptPosition.y;
5490 lpRect->right = lpRect->left + infoPtr->nItemWidth;
5491 lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
5492 TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5493 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
5499 * Retrieves the position (upper-left) of the listview control item.
5500 * Note that for LVS_ICON style, the upper-left is that of the icon
5501 * and not the bounding box.
5504 * [I] HWND : window handle
5505 * [I] INT : item index
5506 * [O] LPPOINT : coordinate information
5512 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, LPPOINT lpptPosition)
5514 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5515 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5516 BOOL bResult = FALSE;
5519 TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd, nItem, lpptPosition);
5521 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5522 (lpptPosition != NULL))
5524 bResult = LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcBounding);
5525 lpptPosition->x = rcBounding.left;
5526 lpptPosition->y = rcBounding.top;
5527 if (uView == LVS_ICON)
5529 lpptPosition->y += ICON_TOP_PADDING;
5530 lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5532 TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
5533 lpptPosition->x, lpptPosition->y);
5541 * Retrieves the bounding rectangle for a listview control item.
5544 * [I] HWND : window handle
5545 * [I] INT : item index
5546 * [IO] LPRECT : bounding rectangle coordinates
5547 * lprc->left specifies the portion of the item for which the bounding
5548 * rectangle will be retrieved.
5550 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5551 * including the icon and label.
5552 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5553 * LVIR_LABEL Returns the bounding rectangle of the item text.
5554 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5555 * rectangles, but excludes columns in report view.
5561 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5563 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5564 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5565 BOOL bResult = FALSE;
5574 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5576 if (uView & LVS_REPORT)
5578 ZeroMemory(&lvItem, sizeof(lvItem));
5579 lvItem.mask = LVIF_INDENT;
5580 lvItem.iItem = nItem;
5581 lvItem.iSubItem = 0;
5582 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
5585 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5586 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5593 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5598 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5599 if (uView == LVS_ICON)
5601 if (infoPtr->himlNormal != NULL)
5603 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5606 lprc->left = ptItem.x + ptOrigin.x;
5607 lprc->top = ptItem.y + ptOrigin.y;
5608 lprc->right = lprc->left + infoPtr->iconSize.cx;
5609 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5610 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5614 else if (uView == LVS_SMALLICON)
5616 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5619 lprc->left = ptItem.x + ptOrigin.x;
5620 lprc->top = ptItem.y + ptOrigin.y;
5621 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5623 if (infoPtr->himlState != NULL)
5624 lprc->left += infoPtr->iconSize.cx;
5626 if (infoPtr->himlSmall != NULL)
5627 lprc->right = lprc->left + infoPtr->iconSize.cx;
5629 lprc->right = lprc->left;
5635 lprc->left = ptItem.x;
5636 if (uView & LVS_REPORT)
5637 lprc->left += nIndent;
5638 lprc->top = ptItem.y;
5639 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5641 if (infoPtr->himlState != NULL)
5642 lprc->left += infoPtr->iconSize.cx;
5644 if (infoPtr->himlSmall != NULL)
5645 lprc->right = lprc->left + infoPtr->iconSize.cx;
5647 lprc->right = lprc->left;
5652 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5653 if (uView == LVS_ICON)
5655 if (infoPtr->himlNormal != NULL)
5657 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5660 lprc->left = ptItem.x + ptOrigin.x;
5661 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5662 ICON_BOTTOM_PADDING);
5663 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5664 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5666 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5667 lprc->right = lprc->left + nLabelWidth;
5672 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5675 lprc->bottom = lprc->top + infoPtr->ntmHeight + HEIGHT_PADDING;
5679 else if (uView == LVS_SMALLICON)
5681 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5684 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5685 lprc->top = ptItem.y + ptOrigin.y;
5686 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5688 if (infoPtr->himlState != NULL)
5689 lprc->left += infoPtr->iconSize.cx;
5691 if (infoPtr->himlSmall != NULL)
5692 lprc->left += infoPtr->iconSize.cx;
5694 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5695 nLabelWidth += TRAILING_PADDING;
5696 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5697 lprc->right = lprc->left + nLabelWidth;
5699 lprc->right = nLeftPos + infoPtr->nItemWidth;
5705 if (uView == LVS_REPORT)
5706 nLeftPos = lprc->left = ptItem.x + nIndent;
5708 nLeftPos = lprc->left = ptItem.x;
5709 lprc->top = ptItem.y;
5710 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5712 if (infoPtr->himlState != NULL)
5713 lprc->left += infoPtr->iconSize.cx;
5715 if (infoPtr->himlSmall != NULL)
5716 lprc->left += infoPtr->iconSize.cx;
5718 if (uView != LVS_REPORT)
5720 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5721 nLabelWidth += TRAILING_PADDING;
5722 if (infoPtr->himlSmall)
5723 nLabelWidth += IMAGE_PADDING;
5726 nLabelWidth = LISTVIEW_GetColumnWidth(hwnd, 0)-lprc->left;
5727 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5728 lprc->right = lprc->left + nLabelWidth;
5730 lprc->right = nLeftPos + infoPtr->nItemWidth;
5735 if (!LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcInternal)) break;
5736 ptItem.x = rcInternal.left;
5737 ptItem.y = rcInternal.top;
5738 if (uView == LVS_ICON)
5740 if (infoPtr->himlNormal != NULL)
5742 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5744 INT text_left, text_right, icon_left, text_pos_x;
5745 /* for style LVS_ICON bounds
5746 * left = min(icon.left, text.left)
5747 * right = max(icon.right, text.right)
5748 * top = boundbox.top + NOTHITABLE
5749 * bottom = text.bottom + 1
5752 icon_left = text_left = ptItem.x;
5754 /* Correct ptItem to icon upper-left */
5755 icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5756 ptItem.y += ICON_TOP_PADDING;
5758 /* Compute the label left and right */
5759 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5760 text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
5763 text_left += text_pos_x / 2;
5764 text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
5769 text_right = text_left + infoPtr->iconSpacing.cx - 1;
5772 /* Compute rectangle w/o the text height */
5773 lprc->left = min(icon_left, text_left) + ptOrigin.x;
5774 lprc->right = max(icon_left + infoPtr->iconSize.cx,
5775 text_right) + ptOrigin.x;
5776 lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
5777 lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
5778 + infoPtr->iconSize.cy + 1
5779 + ICON_BOTTOM_PADDING;
5781 lprc->bottom += (infoPtr->ntmHeight + HEIGHT_PADDING);
5785 else if (uView == LVS_SMALLICON)
5787 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5790 lprc->left = ptItem.x + ptOrigin.x;
5791 lprc->right = lprc->left;
5792 lprc->top = ptItem.y + ptOrigin.y;
5793 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5794 if (infoPtr->himlState != NULL)
5795 lprc->right += infoPtr->iconSize.cx;
5796 if (infoPtr->himlSmall != NULL)
5797 lprc->right += infoPtr->iconSize.cx;
5799 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5800 nLabelWidth += TRAILING_PADDING;
5801 if (infoPtr->himlSmall)
5802 nLabelWidth += IMAGE_PADDING;
5803 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5804 lprc->right += nLabelWidth;
5806 lprc->right = lprc->left + infoPtr->nItemWidth;
5812 lprc->left = ptItem.x;
5813 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5814 lprc->left += nIndent;
5815 lprc->right = lprc->left;
5816 lprc->top = ptItem.y;
5817 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5819 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
5822 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5823 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5825 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5829 if (infoPtr->himlState != NULL)
5830 lprc->right += infoPtr->iconSize.cx;
5832 if (infoPtr->himlSmall != NULL)
5833 lprc->right += infoPtr->iconSize.cx;
5835 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5836 nLabelWidth += TRAILING_PADDING;
5837 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5838 lprc->right += nLabelWidth;
5840 lprc->right = lprc->left + infoPtr->nItemWidth;
5845 case LVIR_SELECTBOUNDS:
5846 if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break;
5847 if (uView == LVS_ICON)
5849 if (infoPtr->himlNormal != NULL)
5851 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5854 lprc->left = ptItem.x + ptOrigin.x;
5855 lprc->top = ptItem.y + ptOrigin.y;
5856 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5857 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5861 else if (uView == LVS_SMALLICON)
5863 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5866 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5867 lprc->top = ptItem.y + ptOrigin.y;
5868 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5870 if (infoPtr->himlState != NULL)
5871 lprc->left += infoPtr->iconSize.cx;
5873 lprc->right = lprc->left;
5875 if (infoPtr->himlSmall != NULL)
5876 lprc->right += infoPtr->iconSize.cx;
5878 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5879 nLabelWidth += TRAILING_PADDING;
5880 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5881 lprc->right += nLabelWidth;
5883 lprc->right = nLeftPos + infoPtr->nItemWidth;
5889 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5890 nLeftPos = lprc->left = ptItem.x + nIndent;
5892 nLeftPos = lprc->left = ptItem.x;
5893 lprc->top = ptItem.y;
5894 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5896 if (infoPtr->himlState != NULL)
5897 lprc->left += infoPtr->iconSize.cx;
5899 lprc->right = lprc->left;
5901 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5904 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5905 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5907 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5911 if (infoPtr->himlSmall != NULL)
5912 lprc->right += infoPtr->iconSize.cx;
5914 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5915 nLabelWidth += TRAILING_PADDING;
5916 if (infoPtr->himlSmall)
5917 nLabelWidth += IMAGE_PADDING;
5918 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5919 lprc->right += nLabelWidth;
5921 lprc->right = nLeftPos + infoPtr->nItemWidth;
5928 TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5929 lprc->left, lprc->top, lprc->right, lprc->bottom);
5936 * Retrieves the width of a label.
5939 * [I] HWND : window handle
5942 * SUCCESS : string width (in pixels)
5945 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
5947 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5948 INT nLabelWidth = 0;
5951 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
5953 ZeroMemory(&lvItem, sizeof(lvItem));
5954 lvItem.mask = LVIF_TEXT;
5955 lvItem.iItem = nItem;
5956 lvItem.cchTextMax = DISP_TEXT_SIZE;
5957 lvItem.pszText = szDispText;
5958 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
5959 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
5966 * Retrieves the spacing between listview control items.
5969 * [I] HWND : window handle
5970 * [I] BOOL : flag for small or large icon
5973 * Horizontal + vertical spacing
5975 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
5977 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5980 if (bSmall == FALSE)
5982 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5986 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
5987 if ((style & LVS_TYPEMASK) == LVS_ICON)
5988 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5990 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5997 * Retrieves the state of a listview control item.
6000 * [I] HWND : window handle
6001 * [I] INT : item index
6002 * [I] UINT : state mask
6005 * State specified by the mask.
6007 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
6009 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6013 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6015 ZeroMemory(&lvItem, sizeof(lvItem));
6016 lvItem.iItem = nItem;
6017 lvItem.stateMask = uMask;
6018 lvItem.mask = LVIF_STATE;
6019 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6020 uState = lvItem.state;
6028 * Retrieves the text of a listview control item or subitem.
6031 * [I] hwnd : window handle
6032 * [I] nItem : item index
6033 * [IO] lpLVItem : item information
6034 * [I] isW : TRUE if lpLVItem is Unicode
6037 * SUCCESS : string length
6040 static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6042 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6045 if (lpLVItem != NULL)
6047 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6049 lpLVItem->mask = LVIF_TEXT;
6050 lpLVItem->iItem = nItem;
6051 if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
6052 nLength = textlenT(lpLVItem->pszText, isW);
6061 * Searches for an item based on properties + relationships.
6064 * [I] HWND : window handle
6065 * [I] INT : item index
6066 * [I] INT : relationship flag
6069 * SUCCESS : item index
6072 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
6074 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6075 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6077 LVFINDINFOW lvFindInfo;
6078 INT nCountPerColumn;
6081 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6083 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6085 if (uFlags & LVNI_CUT)
6088 if (uFlags & LVNI_DROPHILITED)
6089 uMask |= LVIS_DROPHILITED;
6091 if (uFlags & LVNI_FOCUSED)
6092 uMask |= LVIS_FOCUSED;
6094 if (uFlags & LVNI_SELECTED)
6095 uMask |= LVIS_SELECTED;
6097 if (uFlags & LVNI_ABOVE)
6099 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6104 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6110 lvFindInfo.flags = LVFI_NEARESTXY;
6111 lvFindInfo.vkDirection = VK_UP;
6112 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6113 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6115 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6120 else if (uFlags & LVNI_BELOW)
6122 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6124 while (nItem < GETITEMCOUNT(infoPtr))
6127 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6133 lvFindInfo.flags = LVFI_NEARESTXY;
6134 lvFindInfo.vkDirection = VK_DOWN;
6135 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6136 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6138 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6143 else if (uFlags & LVNI_TOLEFT)
6145 if (uView == LVS_LIST)
6147 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6148 while (nItem - nCountPerColumn >= 0)
6150 nItem -= nCountPerColumn;
6151 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6155 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6157 lvFindInfo.flags = LVFI_NEARESTXY;
6158 lvFindInfo.vkDirection = VK_LEFT;
6159 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6160 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6162 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6167 else if (uFlags & LVNI_TORIGHT)
6169 if (uView == LVS_LIST)
6171 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6172 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6174 nItem += nCountPerColumn;
6175 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6179 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6181 lvFindInfo.flags = LVFI_NEARESTXY;
6182 lvFindInfo.vkDirection = VK_RIGHT;
6183 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6184 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6186 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6195 /* search by index */
6196 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6198 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
6207 /* LISTVIEW_GetNumberOfWorkAreas */
6211 * Retrieves the origin coordinates when in icon or small icon display mode.
6214 * [I] HWND : window handle
6215 * [O] LPPOINT : coordinate information
6221 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6223 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6224 UINT uView = lStyle & LVS_TYPEMASK;
6225 BOOL bResult = FALSE;
6227 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6229 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6231 SCROLLINFO scrollInfo;
6232 ZeroMemory(lpptOrigin, sizeof(POINT));
6233 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6234 scrollInfo.cbSize = sizeof(SCROLLINFO);
6236 if (lStyle & WS_HSCROLL)
6238 scrollInfo.fMask = SIF_POS;
6239 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6240 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6243 if (lStyle & WS_VSCROLL)
6245 scrollInfo.fMask = SIF_POS;
6246 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6247 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6258 * Retrieves the number of items that are marked as selected.
6261 * [I] HWND : window handle
6264 * Number of items selected.
6266 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6269 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6270 INT nSelectedCount = 0;
6273 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6275 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6279 return nSelectedCount;
6284 * Retrieves item index that marks the start of a multiple selection.
6287 * [I] HWND : window handle
6290 * Index number or -1 if there is no selection mark.
6292 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6294 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6296 return infoPtr->nSelectionMark;
6302 * Retrieves the width of a string.
6305 * [I] hwnd : window handle
6306 * [I] lpszText : text string to process
6307 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6310 * SUCCESS : string width (in pixels)
6313 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
6315 if (is_textT(lpszText, isW))
6317 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6318 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6319 HDC hdc = GetDC(hwnd);
6320 HFONT hOldFont = SelectObject(hdc, hFont);
6322 ZeroMemory(&stringSize, sizeof(SIZE));
6324 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6326 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6327 SelectObject(hdc, hOldFont);
6328 ReleaseDC(hwnd, hdc);
6329 return stringSize.cx;
6336 * Retrieves the text backgound color.
6339 * [I] HWND : window handle
6342 * COLORREF associated with the the background.
6344 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6346 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6348 return infoPtr->clrTextBk;
6353 * Retrieves the text color.
6356 * [I] HWND : window handle
6359 * COLORREF associated with the text.
6361 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6363 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6365 return infoPtr->clrText;
6370 * Determines item if a hit or closest if not
6373 * [I] HWND : window handle
6374 * [IO] LPLV_INTHIT : hit test information
6375 * [I] subitem : fill out iSubItem.
6378 * SUCCESS : item index of hit
6381 static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem)
6383 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6384 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6385 UINT uView = lStyle & LVS_TYPEMASK;
6386 INT i,topindex,bottomindex;
6388 DWORD xterm, yterm, dist;
6390 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.pt.y);
6392 topindex = ListView_GetTopIndex(hwnd);
6393 if (uView == LVS_REPORT)
6395 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6396 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6400 bottomindex = GETITEMCOUNT(infoPtr);
6403 lpInt->distance = 0x7fffffff;
6404 lpInt->iDistItem = -1;
6406 for (i = topindex; i < bottomindex; i++)
6408 rcItem.left = LVIR_BOUNDS;
6409 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6411 if (PtInRect(&rcItem, lpInt->ht.pt))
6413 rcItem.left = LVIR_ICON;
6414 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6416 if (PtInRect(&rcItem, lpInt->ht.pt))
6418 lpInt->ht.flags = LVHT_ONITEMICON;
6419 lpInt->ht.iItem = i;
6420 if (subitem) lpInt->ht.iSubItem = 0;
6425 rcItem.left = LVIR_LABEL;
6426 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6428 if (PtInRect(&rcItem, lpInt->ht.pt))
6430 lpInt->ht.flags = LVHT_ONITEMLABEL;
6431 lpInt->ht.iItem = i;
6432 if (subitem) lpInt->ht.iSubItem = 0;
6437 lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6438 lpInt->ht.iItem = i;
6439 if (subitem) lpInt->ht.iSubItem = 0;
6445 * Now compute distance from point to center of boundary
6446 * box. Since we are only interested in the relative
6447 * distance, we can skip the nasty square root operation
6449 xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6450 yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6451 dist = xterm * xterm + yterm * yterm;
6452 if (dist < lpInt->distance)
6454 lpInt->distance = dist;
6455 lpInt->iDistItem = i;
6461 lpInt->ht.flags = LVHT_NOWHERE;
6462 TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6469 * Determines which section of the item was selected (if any).
6472 * [I] HWND : window handle
6473 * [IO] LPLVHITTESTINFO : hit test information
6474 * [I] subitem : fill out iSubItem.
6477 * SUCCESS : item index
6480 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6483 LV_INTHIT lv_inthit;
6485 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6486 lpHitTestInfo->pt.y);
6488 memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6489 ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem);
6490 memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6496 * Determines which listview item is located at the specified position.
6499 * [I] HWND : window handle
6500 * [IO} LPLVHITTESTINFO : hit test information
6503 * SUCCESS : item index
6506 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6508 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6511 lpHitTestInfo->flags = 0;
6513 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6514 lpHitTestInfo->flags = LVHT_TOLEFT;
6515 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6516 lpHitTestInfo->flags = LVHT_TORIGHT;
6517 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6518 lpHitTestInfo->flags |= LVHT_ABOVE;
6519 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6520 lpHitTestInfo->flags |= LVHT_BELOW;
6522 if (lpHitTestInfo->flags == 0)
6524 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6525 * an app might pass only a structure with space up to iItem!
6526 * (MS Office 97 does that for instance in the file open dialog)
6528 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6536 * Inserts a new column.
6539 * [I] HWND : window handle
6540 * [I] INT : column index
6541 * [I] LPLVCOLUMNW : column information
6544 * SUCCESS : new column index
6547 static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn,
6548 LPLVCOLUMNW lpColumn, BOOL isW)
6550 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6551 INT nNewColumn = -1;
6554 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
6556 if (lpColumn != NULL)
6558 /* initialize memory */
6559 ZeroMemory(&hdi, sizeof(hdi));
6561 if (lpColumn->mask & LVCF_FMT)
6563 /* format member is valid */
6564 hdi.mask |= HDI_FORMAT;
6566 /* set text alignment (leftmost column must be left-aligned) */
6569 hdi.fmt |= HDF_LEFT;
6573 if (lpColumn->fmt & LVCFMT_LEFT)
6575 hdi.fmt |= HDF_LEFT;
6577 else if (lpColumn->fmt & LVCFMT_RIGHT)
6579 hdi.fmt |= HDF_RIGHT;
6581 else if (lpColumn->fmt & LVCFMT_CENTER)
6583 hdi.fmt |= HDF_CENTER;
6587 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6589 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6593 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6598 if (lpColumn->fmt & LVCFMT_IMAGE)
6600 hdi.fmt |= HDF_IMAGE;
6601 hdi.iImage = I_IMAGECALLBACK;
6605 if (lpColumn->mask & LVCF_WIDTH)
6607 hdi.mask |= HDI_WIDTH;
6608 hdi.cxy = lpColumn->cx;
6611 if (lpColumn->mask & LVCF_TEXT)
6613 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6614 hdi.pszText = lpColumn->pszText;
6615 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6616 hdi.fmt |= HDF_STRING;
6619 if (lpColumn->mask & LVCF_IMAGE)
6621 hdi.mask |= HDI_IMAGE;
6622 hdi.iImage = lpColumn->iImage;
6625 if (lpColumn->mask & LVCF_ORDER)
6627 hdi.mask |= HDI_ORDER;
6628 hdi.iOrder = lpColumn->iOrder;
6631 /* insert item in header control */
6632 nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6633 (WPARAM)nColumn, (LPARAM)&hdi);
6635 /* Need to reset the item width when inserting a new column */
6636 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6638 LISTVIEW_UpdateScroll(hwnd);
6639 InvalidateRect(hwnd, NULL, FALSE);
6645 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6646 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6647 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6648 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6649 their own sort proc. when sending LVM_SORTITEMS.
6652 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6654 LVS_SORTXXX must be specified,
6655 LVS_OWNERDRAW is not set,
6656 <item>.pszText is not LPSTR_TEXTCALLBACK.
6658 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6659 are sorted based on item text..."
6661 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6663 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6664 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6665 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6666 INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6667 /* if we're sorting descending, negate the return value */
6668 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6673 * Inserts a new item in the listview control.
6676 * [I] HWND : window handle
6677 * [I] LPLVITEMW : item information
6678 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6681 * SUCCESS : new item index
6684 static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
6686 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6687 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6688 UINT uView = lStyle & LVS_TYPEMASK;
6692 LISTVIEW_ITEM *lpItem = NULL;
6694 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
6695 hwnd, debuglvitem_t(lpLVItem, isW), isW);
6697 if (lStyle & LVS_OWNERDATA)
6699 nItem = infoPtr->hdpaItems->nItemCount;
6700 infoPtr->hdpaItems->nItemCount ++;
6704 if (lpLVItem != NULL)
6706 /* make sure it's not a subitem; cannot insert a subitem */
6707 if (lpLVItem->iSubItem == 0)
6709 if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6711 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6712 if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
6714 /* insert item in listview control data structure */
6715 if ( (hdpaSubItems = DPA_Create(8)) )
6717 if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
6719 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6720 && !(lStyle & LVS_OWNERDRAWFIXED)
6721 && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
6723 /* Insert the item in the proper sort order based on the pszText
6724 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6725 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6726 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6727 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6728 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6732 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6739 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6741 /* manage item focus */
6742 if (lpLVItem->mask & LVIF_STATE)
6744 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6745 if (lpLVItem->stateMask & LVIS_SELECTED)
6746 LISTVIEW_SetSelection(hwnd, nItem);
6747 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6748 LISTVIEW_SetItemFocus(hwnd, nItem);
6751 /* send LVN_INSERTITEM notification */
6752 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6754 nmlv.lParam = lpItem->lParam;
6755 listview_notify(hwnd, LVN_INSERTITEM, &nmlv);
6757 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6759 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6760 if (nItemWidth > infoPtr->nItemWidth)
6761 infoPtr->nItemWidth = nItemWidth;
6764 /* align items (set position of each item) */
6765 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6767 if (lStyle & LVS_ALIGNLEFT)
6768 LISTVIEW_AlignLeft(hwnd);
6770 LISTVIEW_AlignTop(hwnd);
6773 LISTVIEW_UpdateScroll(hwnd);
6774 /* refresh client area */
6775 InvalidateRect(hwnd, NULL, FALSE);
6784 /* free memory if unsuccessful */
6785 if ((nItem == -1) && (lpItem != NULL))
6786 COMCTL32_Free(lpItem);
6793 * Redraws a range of items.
6796 * [I] HWND : window handle
6797 * [I] INT : first item
6798 * [I] INT : last item
6804 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6806 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6807 BOOL bResult = FALSE;
6811 if (nFirst <= nLast)
6813 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6815 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6817 for (i = nFirst; i <= nLast; i++)
6819 rcItem.left = LVIR_BOUNDS;
6820 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
6821 InvalidateRect(hwnd, &rcItem, TRUE);
6830 /* LISTVIEW_Scroll */
6834 * Sets the background color.
6837 * [I] HWND : window handle
6838 * [I] COLORREF : background color
6844 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6846 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6848 if(infoPtr->clrBk!=clrBk){
6849 infoPtr->clrBk = clrBk;
6850 InvalidateRect(hwnd, NULL, TRUE);
6856 /* LISTVIEW_SetBkImage */
6860 * Sets the callback mask. This mask will be used when the parent
6861 * window stores state information (some or all).
6864 * [I] HWND : window handle
6865 * [I] UINT : state mask
6871 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
6873 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6875 infoPtr->uCallbackMask = uMask;
6882 * Sets the attributes of a header item.
6885 * [I] HWND : window handle
6886 * [I] INT : column index
6887 * [I] LPLVCOLUMNW : column attributes
6888 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6889 * otherwise it is in fact a LPLVCOLUMNA
6895 static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn,
6896 LPLVCOLUMNW lpColumn, BOOL isW)
6898 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6899 BOOL bResult = FALSE;
6900 HDITEMW hdi, hdiget;
6902 if ((lpColumn != NULL) && (nColumn >= 0) &&
6903 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6905 /* initialize memory */
6906 ZeroMemory(&hdi, sizeof(hdi));
6908 if (lpColumn->mask & LVCF_FMT)
6910 /* format member is valid */
6911 hdi.mask |= HDI_FORMAT;
6913 /* get current format first */
6914 hdiget.mask = HDI_FORMAT;
6915 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6916 /* preserve HDF_STRING if present */
6917 hdi.fmt = hdiget.fmt & HDF_STRING;
6919 /* set text alignment (leftmost column must be left-aligned) */
6922 hdi.fmt |= HDF_LEFT;
6926 if (lpColumn->fmt & LVCFMT_LEFT)
6927 hdi.fmt |= HDF_LEFT;
6928 else if (lpColumn->fmt & LVCFMT_RIGHT)
6929 hdi.fmt |= HDF_RIGHT;
6930 else if (lpColumn->fmt & LVCFMT_CENTER)
6931 hdi.fmt |= HDF_CENTER;
6934 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6935 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6937 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6938 hdi.fmt |= HDF_IMAGE;
6940 if (lpColumn->fmt & LVCFMT_IMAGE)
6942 hdi.fmt |= HDF_IMAGE;
6943 hdi.iImage = I_IMAGECALLBACK;
6947 if (lpColumn->mask & LVCF_WIDTH)
6949 hdi.mask |= HDI_WIDTH;
6950 hdi.cxy = lpColumn->cx;
6953 if (lpColumn->mask & LVCF_TEXT)
6955 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6956 hdi.pszText = lpColumn->pszText;
6957 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6958 hdi.fmt |= HDF_STRING;
6961 if (lpColumn->mask & LVCF_IMAGE)
6963 hdi.mask |= HDI_IMAGE;
6964 hdi.iImage = lpColumn->iImage;
6967 if (lpColumn->mask & LVCF_ORDER)
6969 hdi.mask |= HDI_ORDER;
6970 hdi.iOrder = lpColumn->iOrder;
6973 /* set header item attributes */
6974 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6982 * Sets the column order array
6985 * [I] HWND : window handle
6986 * [I] INT : number of elements in column order array
6987 * [I] INT : pointer to column order array
6993 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
6995 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
6997 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7008 * Sets the width of a column
7011 * [I] HWND : window handle
7012 * [I] INT : column index
7013 * [I] INT : column width
7019 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
7021 LISTVIEW_INFO *infoPtr;
7024 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7025 UINT uView = lStyle & LVS_TYPEMASK;
7030 WCHAR text_buffer[DISP_TEXT_SIZE];
7031 INT header_item_count;
7036 /* make sure we can get the listview info */
7037 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
7040 if (!infoPtr->hwndHeader) /* make sure we have a header */
7043 /* set column width only if in report or list mode */
7044 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7047 /* take care of invalid cx values */
7048 if((uView == LVS_REPORT) && (cx < -2))
7049 cx = LVSCW_AUTOSIZE;
7050 else if (uView == LVS_LIST && (cx < 1))
7053 /* resize all columns if in LVS_LIST mode */
7054 if(uView == LVS_LIST) {
7055 infoPtr->nItemWidth = cx;
7056 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7060 /* autosize based on listview items width */
7061 if(cx == LVSCW_AUTOSIZE)
7063 /* set the width of the header to the width of the widest item */
7064 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7066 if(cx < LISTVIEW_GetLabelWidth(hwnd, item_index))
7067 cx = LISTVIEW_GetLabelWidth(hwnd, item_index);
7069 } /* autosize based on listview header width */
7070 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7072 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7074 /* if iCol is the last column make it fill the remainder of the controls width */
7075 if(iCol == (header_item_count - 1)) {
7076 /* get the width of every item except the current one */
7077 hdi.mask = HDI_WIDTH;
7080 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7081 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7085 /* retrieve the layout of the header */
7086 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7088 cx = (rcHeader.right - rcHeader.left) - cx;
7092 /* retrieve header font */
7093 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7095 /* retrieve header text */
7096 hdi.mask = HDI_TEXT;
7097 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7098 hdi.pszText = text_buffer;
7100 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7102 /* determine the width of the text in the header */
7104 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7106 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7108 SelectObject(hdc, old_font); /* restore the old font */
7109 ReleaseDC(hwnd, hdc);
7111 /* set the width of this column to the width of the text */
7116 /* call header to update the column change */
7117 hdi.mask = HDI_WIDTH;
7120 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7122 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7129 * Sets the extended listview style.
7132 * [I] HWND : window handle
7137 * SUCCESS : previous style
7140 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
7142 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7143 DWORD dwOldStyle = infoPtr->dwExStyle;
7147 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7149 infoPtr->dwExStyle = dwStyle;
7154 /* LISTVIEW_SetHotCursor */
7158 * Sets the hot item index.
7161 * [I] HWND : window handle
7165 * SUCCESS : previous hot item index
7166 * FAILURE : -1 (no hot item)
7168 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7170 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7171 INT iOldIndex = infoPtr->nHotItem;
7174 infoPtr->nHotItem = iIndex;
7181 * Sets the amount of time the cursor must hover over an item before it is selected.
7184 * [I] HWND : window handle
7185 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7188 * Returns the previous hover time
7190 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7192 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7193 DWORD oldHoverTime = infoPtr->dwHoverTime;
7195 infoPtr->dwHoverTime = dwHoverTime;
7197 return oldHoverTime;
7202 * Sets spacing for icons of LVS_ICON style.
7205 * [I] HWND : window handle
7206 * [I] DWORD : MAKELONG(cx, cy)
7209 * MAKELONG(oldcx, oldcy)
7211 static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing)
7213 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7214 INT cy = HIWORD(spacing);
7215 INT cx = LOWORD(spacing);
7217 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7218 UINT uView = lStyle & LVS_TYPEMASK;
7220 oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7221 if (cx == -1) /* set to default */
7222 cx = GetSystemMetrics(SM_CXICONSPACING);
7223 if (cy == -1) /* set to default */
7224 cy = GetSystemMetrics(SM_CYICONSPACING);
7227 infoPtr->iconSpacing.cx = cx;
7229 { /* if 0 then compute width */
7230 if (uView == LVS_ICON)
7231 FIXME("width computation not yet done\n");
7233 * Should scan each item and determine max width of
7234 * icon or label, then make that the width
7236 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7237 infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd);
7240 infoPtr->iconSpacing.cy = cy;
7242 { /* if 0 then compute height */
7243 if (uView == LVS_ICON)
7244 infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + infoPtr->ntmHeight
7245 + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7246 else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7247 infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd);
7250 TRACE("old=(%d,%d), new=(%ld,%ld)\n", LOWORD(oldspacing), HIWORD(oldspacing),
7251 infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7253 /* these depend on the iconSpacing */
7254 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7255 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7265 * [I] HWND : window handle
7266 * [I] INT : image list type
7267 * [I] HIMAGELIST : image list handle
7270 * SUCCESS : old image list
7273 static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7275 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7276 HIMAGELIST himlOld = 0;
7282 himlOld = infoPtr->himlNormal;
7283 infoPtr->himlNormal = himl;
7287 himlOld = infoPtr->himlSmall;
7288 infoPtr->himlSmall = himl;
7292 himlOld = infoPtr->himlState;
7293 infoPtr->himlState = himl;
7294 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7298 oldHeight = infoPtr->nItemHeight;
7299 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7300 if (infoPtr->nItemHeight != oldHeight)
7301 LISTVIEW_UpdateScroll(hwnd);
7308 * Preallocates memory (does *not* set the actual count of items !)
7311 * [I] HWND : window handle
7312 * [I] INT : item count (projected number of items to allocate)
7313 * [I] DWORD : update flags
7319 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7321 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7323 TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
7325 if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7327 int precount,topvisible;
7329 TRACE("LVS_OWNERDATA is set!\n");
7330 if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7331 FIXME("flags %s %s not implemented\n",
7332 (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7334 (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7337 * Internally remove all the selections.
7341 LISTVIEW_SELECTION *selection;
7342 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7344 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7347 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7349 precount = infoPtr->hdpaItems->nItemCount;
7350 topvisible = ListView_GetTopIndex(hwnd) +
7351 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7353 infoPtr->hdpaItems->nItemCount = nItems;
7355 LISTVIEW_UpdateSize(hwnd);
7356 LISTVIEW_UpdateScroll(hwnd);
7357 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7358 InvalidateRect(hwnd, NULL, TRUE);
7362 /* According to MSDN for non-LVS_OWNERDATA this is just
7363 * a performance issue. The control allocates its internal
7364 * data structures for the number of items specified. It
7365 * cuts down on the number of memory allocations. Therefore
7366 * we will just issue a WARN here
7368 WARN("for non-ownerdata performance option not implemented.\n");
7376 * Sets the position of an item.
7379 * [I] HWND : window handle
7380 * [I] INT : item index
7381 * [I] LONG : x coordinate
7382 * [I] LONG : y coordinate
7388 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7389 LONG nPosX, LONG nPosY)
7391 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7392 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7393 UINT uView = lStyle & LVS_TYPEMASK;
7394 LISTVIEW_ITEM *lpItem;
7396 BOOL bResult = FALSE;
7398 TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7400 if (lStyle & LVS_OWNERDATA)
7403 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7405 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7407 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7409 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7413 orig = lpItem->ptPosition;
7414 if ((nPosX == -1) && (nPosY == -1))
7416 /* This point value seems to be an undocumented feature. The
7417 * best guess is that it means either at the origin, or at
7418 * the true beginning of the list. I will assume the origin.
7421 if (!LISTVIEW_GetOrigin(hwnd, &pt1))
7428 if (uView == LVS_ICON)
7430 nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7431 nPosY += ICON_TOP_PADDING;
7433 TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7437 lpItem->ptPosition.x = nPosX;
7438 lpItem->ptPosition.y = nPosY;
7439 if (uView == LVS_ICON)
7441 lpItem->ptPosition.y -= ICON_TOP_PADDING;
7442 lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7443 if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7445 FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7446 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7449 if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7450 if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7455 TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7456 orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7469 * Sets the state of one or many items.
7472 * [I] HWND : window handle
7473 * [I]INT : item index
7474 * [I] LPLVITEM : item or subitem info
7480 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem)
7482 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7483 BOOL bResult = TRUE;
7486 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
7487 hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
7489 ZeroMemory(&lvItem, sizeof(lvItem));
7490 lvItem.mask = LVIF_STATE;
7491 lvItem.state = lpLVItem->state;
7492 lvItem.stateMask = lpLVItem->stateMask ;
7493 lvItem.iItem = nItem;
7497 /* apply to all items */
7498 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7499 if (!ListView_SetItemW(hwnd, &lvItem)) bResult = FALSE;
7502 bResult = ListView_SetItemW(hwnd, &lvItem);
7509 * Sets the text of an item or subitem.
7512 * [I] hwnd : window handle
7513 * [I] nItem : item index
7514 * [I] lpLVItem : item or subitem info
7515 * [I] isW : TRUE if input is Unicode
7521 static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7523 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7524 BOOL bResult = FALSE;
7527 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
7528 hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
7530 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7532 ZeroMemory(&lvItem, sizeof(LVITEMW));
7533 lvItem.mask = LVIF_TEXT;
7534 lvItem.pszText = lpLVItem->pszText;
7535 lvItem.iItem = nItem;
7536 lvItem.iSubItem = lpLVItem->iSubItem;
7537 if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
7538 else bResult = ListView_SetItemA(hwnd, &lvItem);
7546 * Set item index that marks the start of a multiple selection.
7549 * [I] HWND : window handle
7553 * Index number or -1 if there is no selection mark.
7555 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7557 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7558 INT nOldIndex = infoPtr->nSelectionMark;
7560 TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
7562 infoPtr->nSelectionMark = nIndex;
7569 * Sets the text background color.
7572 * [I] HWND : window handle
7573 * [I] COLORREF : text background color
7579 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7581 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7583 TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
7585 infoPtr->clrTextBk = clrTextBk;
7586 InvalidateRect(hwnd, NULL, TRUE);
7593 * Sets the text foreground color.
7596 * [I] HWND : window handle
7597 * [I] COLORREF : text color
7603 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7605 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7607 TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
7609 infoPtr->clrText = clrText;
7610 InvalidateRect(hwnd, NULL, TRUE);
7615 /* LISTVIEW_SetToolTips */
7616 /* LISTVIEW_SetUnicodeFormat */
7617 /* LISTVIEW_SetWorkAreas */
7621 * Callback internally used by LISTVIEW_SortItems()
7624 * [I] LPVOID : first LISTVIEW_ITEM to compare
7625 * [I] LPVOID : second LISTVIEW_ITEM to compare
7626 * [I] LPARAM : HWND of control
7629 * if first comes before second : negative
7630 * if first comes after second : positive
7631 * if first and second are equivalent : zero
7633 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7635 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7636 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
7637 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
7639 /* Forward the call to the client defined callback */
7640 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7645 * Sorts the listview items.
7648 * [I] HWND : window handle
7649 * [I] WPARAM : application-defined value
7650 * [I] LPARAM : pointer to comparision callback
7656 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7658 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7659 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7660 HDPA hdpaSubItems=NULL;
7661 LISTVIEW_ITEM *pLVItem=NULL;
7662 LPVOID selectionMarkItem;
7665 TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
7667 if (lStyle & LVS_OWNERDATA) return FALSE;
7669 if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
7671 nCount = GETITEMCOUNT(infoPtr);
7672 /* if there are 0 or 1 items, there is no need to sort */
7676 infoPtr->pfnCompare = pfnCompare;
7677 infoPtr->lParamSort = lParamSort;
7678 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7680 /* Adjust selections and indices so that they are the way they should
7681 * be after the sort (otherwise, the list items move around, but
7682 * whatever is at the item's previous original position will be
7685 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7686 for (i=0; i < nCount; i++)
7688 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7689 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7691 if (pLVItem->state & LVIS_SELECTED)
7692 LISTVIEW_AddSelectionRange(hwnd, i, i);
7694 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7695 if (pLVItem->state & LVIS_FOCUSED)
7696 infoPtr->nFocusedItem=i;
7698 if (selectionMarkItem != NULL)
7699 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7700 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7702 /* align the items */
7703 LISTVIEW_AlignTop(hwnd);
7705 /* refresh the display */
7706 InvalidateRect(hwnd, NULL, TRUE);
7711 /* LISTVIEW_SubItemHitTest */
7715 * Updates an items or rearranges the listview control.
7718 * [I] HWND : window handle
7719 * [I] INT : item index
7725 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7727 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7728 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7729 BOOL bResult = FALSE;
7732 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
7734 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7738 /* rearrange with default alignment style */
7739 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7740 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7742 ListView_Arrange(hwnd, 0);
7746 /* get item bounding rectangle */
7747 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
7748 InvalidateRect(hwnd, &rc, TRUE);
7757 * Creates the listview control.
7760 * [I] HWND : window handle
7765 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7767 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7768 UINT uView = lpcs->style & LVS_TYPEMASK;
7771 TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
7773 /* initialize info pointer */
7774 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7776 /* determine the type of structures to use */
7777 infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT,
7778 (WPARAM)hwnd, (LPARAM)NF_QUERY);
7780 /* initialize color information */
7781 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7782 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7783 infoPtr->clrTextBk = CLR_DEFAULT;
7785 /* set default values */
7786 infoPtr->hwndSelf = hwnd;
7787 infoPtr->uCallbackMask = 0;
7788 infoPtr->nFocusedItem = -1;
7789 infoPtr->nSelectionMark = -1;
7790 infoPtr->nHotItem = -1;
7791 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7792 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7793 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7794 infoPtr->hwndEdit = 0;
7795 infoPtr->pedititem = NULL;
7796 infoPtr->nEditLabelItem = -1;
7798 /* get default font (icon title) */
7799 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7800 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7801 infoPtr->hFont = infoPtr->hDefaultFont;
7802 LISTVIEW_SaveTextMetrics(hwnd);
7805 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7806 WS_CHILD | HDS_HORZ | HDS_BUTTONS,
7807 0, 0, 0, 0, hwnd, (HMENU)0,
7808 lpcs->hInstance, NULL);
7810 /* set header font */
7811 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7814 if (uView == LVS_ICON)
7816 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7817 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7819 else if (uView == LVS_REPORT)
7821 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7823 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7827 /* set HDS_HIDDEN flag to hide the header bar */
7828 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7829 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7833 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7834 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7838 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7839 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7842 /* display unsupported listview window styles */
7843 LISTVIEW_UnsupportedStyles(lpcs->style);
7845 /* allocate memory for the data structure */
7846 infoPtr->hdpaItems = DPA_Create(10);
7848 /* allocate memory for the selection ranges */
7849 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7851 /* initialize size of items */
7852 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7853 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7855 /* initialize the hover time to -1(indicating the default system hover time) */
7856 infoPtr->dwHoverTime = -1;
7863 * Erases the background of the listview control.
7866 * [I] HWND : window handle
7867 * [I] WPARAM : device context handle
7868 * [I] LPARAM : not used
7874 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
7877 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7880 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
7882 if (infoPtr->clrBk == CLR_NONE)
7884 bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
7889 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7890 GetClientRect(hwnd, &rc);
7891 FillRect((HDC)wParam, &rc, hBrush);
7892 DeleteObject(hBrush);
7900 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
7902 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7904 TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
7906 if (infoPtr->clrBk != CLR_NONE)
7908 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7909 FillRect(hdc, rc, hBrush);
7910 DeleteObject(hBrush);
7916 * Retrieves the listview control font.
7919 * [I] HWND : window handle
7924 static LRESULT LISTVIEW_GetFont(HWND hwnd)
7926 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7928 TRACE("(hwnd=%x)\n", hwnd);
7930 return infoPtr->hFont;
7935 * Performs vertical scrolling.
7938 * [I] HWND : window handle
7939 * [I] INT : scroll code
7940 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
7942 * [I] HWND : scrollbar control window handle
7947 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7950 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7951 SCROLLINFO scrollInfo;
7953 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
7954 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
7956 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7958 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7959 scrollInfo.cbSize = sizeof(SCROLLINFO);
7960 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7962 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7964 INT nOldScrollPos = scrollInfo.nPos;
7965 switch (nScrollCode)
7968 if (scrollInfo.nPos > scrollInfo.nMin)
7973 if (scrollInfo.nPos < scrollInfo.nMax)
7978 if (scrollInfo.nPos > scrollInfo.nMin)
7980 if (scrollInfo.nPos >= scrollInfo.nPage)
7981 scrollInfo.nPos -= scrollInfo.nPage;
7983 scrollInfo.nPos = scrollInfo.nMin;
7988 if (scrollInfo.nPos < scrollInfo.nMax)
7990 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7991 scrollInfo.nPos += scrollInfo.nPage;
7993 scrollInfo.nPos = scrollInfo.nMax;
7997 case SB_THUMBPOSITION:
7999 scrollInfo.nPos = nCurrentPos;
8000 if (scrollInfo.nPos > scrollInfo.nMax)
8001 scrollInfo.nPos=scrollInfo.nMax;
8003 if (scrollInfo.nPos < scrollInfo.nMin)
8004 scrollInfo.nPos=scrollInfo.nMin;
8009 if (nOldScrollPos != scrollInfo.nPos)
8011 scrollInfo.fMask = SIF_POS;
8012 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
8013 if (IsWindowVisible(infoPtr->hwndHeader))
8015 RECT rListview, rcHeader, rDest;
8016 GetClientRect(hwnd, &rListview);
8017 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
8018 MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2);
8019 SubtractRect(&rDest, &rListview, &rcHeader);
8020 InvalidateRect(hwnd, &rDest, TRUE);
8023 InvalidateRect(hwnd, NULL, TRUE);
8032 * Performs horizontal scrolling.
8035 * [I] HWND : window handle
8036 * [I] INT : scroll code
8037 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8039 * [I] HWND : scrollbar control window handle
8044 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8047 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8048 SCROLLINFO scrollInfo;
8050 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8051 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8053 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8055 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8056 scrollInfo.cbSize = sizeof(SCROLLINFO);
8057 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8059 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
8061 INT nOldScrollPos = scrollInfo.nPos;
8063 switch (nScrollCode)
8066 if (scrollInfo.nPos > scrollInfo.nMin)
8071 if (scrollInfo.nPos < scrollInfo.nMax)
8076 if (scrollInfo.nPos > scrollInfo.nMin)
8078 if (scrollInfo.nPos >= scrollInfo.nPage)
8079 scrollInfo.nPos -= scrollInfo.nPage;
8081 scrollInfo.nPos = scrollInfo.nMin;
8086 if (scrollInfo.nPos < scrollInfo.nMax)
8088 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8089 scrollInfo.nPos += scrollInfo.nPage;
8091 scrollInfo.nPos = scrollInfo.nMax;
8095 case SB_THUMBPOSITION:
8097 scrollInfo.nPos = nCurrentPos;
8099 if (scrollInfo.nPos > scrollInfo.nMax)
8100 scrollInfo.nPos=scrollInfo.nMax;
8102 if (scrollInfo.nPos < scrollInfo.nMin)
8103 scrollInfo.nPos=scrollInfo.nMin;
8107 if (nOldScrollPos != scrollInfo.nPos)
8109 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8110 scrollInfo.fMask = SIF_POS;
8111 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
8112 if(uView == LVS_REPORT)
8114 scrollInfo.fMask = SIF_POS;
8115 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
8116 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
8118 InvalidateRect(hwnd, NULL, TRUE);
8125 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
8127 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8128 INT gcWheelDelta = 0;
8129 UINT pulScrollLines = 3;
8130 SCROLLINFO scrollInfo;
8132 TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
8134 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8135 gcWheelDelta -= wheelDelta;
8137 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8138 scrollInfo.cbSize = sizeof(SCROLLINFO);
8139 scrollInfo.fMask = SIF_POS | SIF_RANGE;
8146 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8147 * should be fixed in the future.
8149 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8150 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
8154 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8156 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8158 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
8159 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8160 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8166 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8177 * [I] HWND : window handle
8178 * [I] INT : virtual key
8179 * [I] LONG : key data
8184 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
8186 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8187 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8189 NMLVKEYDOWN nmKeyDown;
8191 TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
8193 /* send LVN_KEYDOWN notification */
8194 nmKeyDown.wVKey = nVirtualKey;
8195 nmKeyDown.flags = 0;
8196 notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
8198 switch (nVirtualKey)
8201 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8203 hdr_notify(hwnd, NM_RETURN); /* NM_RETURN notification */
8204 hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
8209 if (GETITEMCOUNT(infoPtr) > 0)
8214 if (GETITEMCOUNT(infoPtr) > 0)
8215 nItem = GETITEMCOUNT(infoPtr) - 1;
8219 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8223 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8227 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8231 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8235 if (uView == LVS_REPORT)
8236 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8238 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8239 * LISTVIEW_GetCountPerRow(hwnd);
8240 if(nItem < 0) nItem = 0;
8244 if (uView == LVS_REPORT)
8245 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8247 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8248 * LISTVIEW_GetCountPerRow(hwnd);
8249 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8253 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8255 if (LISTVIEW_KeySelection(hwnd, nItem))
8256 UpdateWindow(hwnd); /* update client area */
8267 * [I] HWND : window handle
8272 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8274 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
8275 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8278 TRACE("(hwnd=%x)\n", hwnd);
8280 /* send NM_KILLFOCUS notification */
8281 hdr_notify(hwnd, NM_KILLFOCUS);
8283 /* set window focus flag */
8284 infoPtr->bFocus = FALSE;
8286 /* NEED drawing optimization ; redraw the selected items */
8287 if (uView & LVS_REPORT)
8289 nTop = LISTVIEW_GetTopIndex(hwnd);
8291 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8296 nBottom = GETITEMCOUNT(infoPtr);
8298 for (i = nTop; i<nBottom; i++)
8300 if (LISTVIEW_IsSelected(hwnd,i))
8303 rcItem.left = LVIR_BOUNDS;
8304 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8305 InvalidateRect(hwnd, &rcItem, FALSE);
8314 * Processes double click messages (left mouse button).
8317 * [I] HWND : window handle
8318 * [I] WORD : key flag
8319 * [I] WORD : x coordinate
8320 * [I] WORD : y coordinate
8325 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8328 LVHITTESTINFO htInfo;
8331 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8333 htInfo.pt.x = wPosX;
8334 htInfo.pt.y = wPosY;
8336 /* send NM_DBLCLK notification */
8337 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8338 if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -1)
8340 nmlv.iItem = htInfo.iItem;
8341 nmlv.iSubItem = htInfo.iSubItem;
8348 nmlv.ptAction.x = wPosX;
8349 nmlv.ptAction.y = wPosY;
8350 listview_notify(hwnd, NM_DBLCLK, &nmlv);
8353 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8354 if(nmlv.iItem != -1)
8355 hdr_notify(hwnd, LVN_ITEMACTIVATE);
8362 * Processes mouse down messages (left mouse button).
8365 * [I] HWND : window handle
8366 * [I] WORD : key flag
8367 * [I] WORD : x coordinate
8368 * [I] WORD : y coordinate
8373 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8376 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8377 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8378 static BOOL bGroupSelect = TRUE;
8382 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8384 /* send NM_RELEASEDCAPTURE notification */
8385 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8387 if (infoPtr->bFocus == FALSE)
8390 /* set left button down flag */
8391 infoPtr->bLButtonDown = TRUE;
8393 ptPosition.x = wPosX;
8394 ptPosition.y = wPosY;
8395 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8396 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8398 if (lStyle & LVS_SINGLESEL)
8400 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8401 && infoPtr->nEditLabelItem == -1)
8402 infoPtr->nEditLabelItem = nItem;
8404 LISTVIEW_SetSelection(hwnd, nItem);
8408 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8411 LISTVIEW_AddGroupSelection(hwnd, nItem);
8413 LISTVIEW_AddSelection(hwnd, nItem);
8415 else if (wKey & MK_CONTROL)
8417 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8419 else if (wKey & MK_SHIFT)
8421 LISTVIEW_SetGroupSelection(hwnd, nItem);
8426 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8428 /* set selection (clears other pre-existing selections) */
8429 LISTVIEW_SetSelection(hwnd, nItem);
8431 if (was_selected && infoPtr->nEditLabelItem == -1)
8432 infoPtr->nEditLabelItem = nItem;
8438 /* remove all selections */
8439 LISTVIEW_RemoveAllSelections(hwnd);
8442 /* redraw if we could have possibly selected something */
8443 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8450 * Processes mouse up messages (left mouse button).
8453 * [I] HWND : window handle
8454 * [I] WORD : key flag
8455 * [I] WORD : x coordinate
8456 * [I] WORD : y coordinate
8461 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8464 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8466 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8468 if (infoPtr->bLButtonDown != FALSE)
8470 LVHITTESTINFO lvHitTestInfo;
8473 lvHitTestInfo.pt.x = wPosX;
8474 lvHitTestInfo.pt.y = wPosY;
8476 /* send NM_CLICK notification */
8477 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8478 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8480 nmlv.iItem = lvHitTestInfo.iItem;
8481 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8488 nmlv.ptAction.x = wPosX;
8489 nmlv.ptAction.y = wPosY;
8490 listview_notify(hwnd, NM_CLICK, &nmlv);
8492 /* set left button flag */
8493 infoPtr->bLButtonDown = FALSE;
8495 if(infoPtr->nEditLabelItem != -1)
8497 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem)
8498 LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
8499 infoPtr->nEditLabelItem = -1;
8508 * Creates the listview control (called before WM_CREATE).
8511 * [I] HWND : window handle
8512 * [I] WPARAM : unhandled
8513 * [I] LPARAM : widow creation info
8518 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8520 LISTVIEW_INFO *infoPtr;
8522 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8524 /* allocate memory for info structure */
8525 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8526 if (infoPtr == NULL)
8528 ERR("could not allocate info memory!\n");
8532 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
8533 if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr)
8535 ERR("pointer assignment error!\n");
8539 return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam);
8544 * Destroys the listview control (called after WM_DESTROY).
8547 * [I] HWND : window handle
8552 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8554 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8556 TRACE("(hwnd=%x)\n", hwnd);
8558 /* delete all items */
8559 LISTVIEW_DeleteAllItems(hwnd);
8561 /* destroy data structure */
8562 DPA_Destroy(infoPtr->hdpaItems);
8563 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8566 infoPtr->hFont = (HFONT)0;
8567 if (infoPtr->hDefaultFont)
8569 DeleteObject(infoPtr->hDefaultFont);
8572 /* free listview info pointer*/
8573 COMCTL32_Free(infoPtr);
8575 SetWindowLongW(hwnd, 0, 0);
8581 * Handles notifications from children.
8584 * [I] HWND : window handle
8585 * [I] INT : control identifier
8586 * [I] LPNMHDR : notification information
8591 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8593 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8595 TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
8597 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8599 /* handle notification from header control */
8600 if (lpnmh->code == HDN_ENDTRACKW)
8602 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8603 InvalidateRect(hwnd, NULL, TRUE);
8605 else if(lpnmh->code == HDN_ITEMCLICKW)
8607 /* Handle sorting by Header Column */
8610 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8612 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
8613 listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv);
8615 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8617 /* Idealy this should be done in HDN_ENDTRACKA
8618 * but since SetItemBounds in Header.c is called after
8619 * the notification is sent, it is neccessary to handle the
8620 * update of the scroll bar here (Header.c works fine as it is,
8621 * no need to disturb it)
8623 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8624 LISTVIEW_UpdateScroll(hwnd);
8625 InvalidateRect(hwnd, NULL, TRUE);
8635 * Determines the type of structure to use.
8638 * [I] HWND : window handle of the sender
8639 * [I] HWND : listview window handle
8640 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8645 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8647 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8649 TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
8651 if (nCommand == NF_REQUERY)
8652 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
8653 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8659 * Paints/Repaints the listview control.
8662 * [I] HWND : window handle
8663 * [I] HDC : device context handle
8668 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8672 TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc);
8676 hdc = BeginPaint(hwnd, &ps);
8677 LISTVIEW_Refresh(hwnd, hdc);
8678 EndPaint(hwnd, &ps);
8682 LISTVIEW_Refresh(hwnd, hdc);
8690 * Processes double click messages (right mouse button).
8693 * [I] HWND : window handle
8694 * [I] WORD : key flag
8695 * [I] WORD : x coordinate
8696 * [I] WORD : y coordinate
8701 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8704 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8706 /* send NM_RELEASEDCAPTURE notification */
8707 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8709 /* send NM_RDBLCLK notification */
8710 hdr_notify(hwnd, NM_RDBLCLK);
8717 * Processes mouse down messages (right mouse button).
8720 * [I] HWND : window handle
8721 * [I] WORD : key flag
8722 * [I] WORD : x coordinate
8723 * [I] WORD : y coordinate
8728 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8731 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8735 LVHITTESTINFO lvHitTestInfo;
8737 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8739 /* send NM_RELEASEDCAPTURE notification */
8740 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8742 /* make sure the listview control window has the focus */
8743 if (infoPtr->bFocus == FALSE)
8746 /* set right button down flag */
8747 infoPtr->bRButtonDown = TRUE;
8749 /* determine the index of the selected item */
8750 ptPosition.x = wPosX;
8751 ptPosition.y = wPosY;
8752 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8753 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8755 LISTVIEW_SetItemFocus(hwnd,nItem);
8756 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8757 !LISTVIEW_IsSelected(hwnd,nItem))
8758 LISTVIEW_SetSelection(hwnd, nItem);
8762 LISTVIEW_RemoveAllSelections(hwnd);
8765 lvHitTestInfo.pt.x = wPosX;
8766 lvHitTestInfo.pt.y = wPosY;
8768 /* Send NM_RClICK notification */
8769 ZeroMemory(&nmlv, sizeof(nmlv));
8770 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8772 nmlv.iItem = lvHitTestInfo.iItem;
8773 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8780 nmlv.ptAction.x = wPosX;
8781 nmlv.ptAction.y = wPosY;
8782 listview_notify(hwnd, NM_RCLICK, &nmlv);
8789 * Processes mouse up messages (right mouse button).
8792 * [I] HWND : window handle
8793 * [I] WORD : key flag
8794 * [I] WORD : x coordinate
8795 * [I] WORD : y coordinate
8800 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8803 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8805 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8807 if (infoPtr->bRButtonDown)
8814 /* set button flag */
8815 infoPtr->bRButtonDown = FALSE;
8817 /* Change to screen coordinate for WM_CONTEXTMENU */
8818 ClientToScreen(hwnd, &pt);
8820 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8821 SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
8832 * [I] HWND : window handle
8833 * [I] HWND : window handle of previously focused window
8838 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
8840 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8842 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
8844 /* send NM_SETFOCUS notification */
8845 hdr_notify(hwnd, NM_SETFOCUS);
8847 /* set window focus flag */
8848 infoPtr->bFocus = TRUE;
8860 * [I] HWND : window handle
8861 * [I] HFONT : font handle
8862 * [I] WORD : redraw flag
8867 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
8869 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8870 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8872 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
8874 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8875 LISTVIEW_SaveTextMetrics(hwnd);
8877 if (uView == LVS_REPORT)
8879 /* set header font */
8880 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
8881 MAKELPARAM(fRedraw, 0));
8884 /* invalidate listview control client area */
8885 InvalidateRect(hwnd, NULL, TRUE);
8887 if (fRedraw != FALSE)
8895 * Message handling for WM_SETREDRAW.
8896 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8899 * [I] HWND : window handle
8900 * [I] bRedraw: state of redraw flag
8903 * DefWinProc return value
8905 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
8907 LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0);
8909 RedrawWindow(hwnd, NULL, 0,
8910 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8916 * Resizes the listview control. This function processes WM_SIZE
8917 * messages. At this time, the width and height are not used.
8920 * [I] HWND : window handle
8921 * [I] WORD : new width
8922 * [I] WORD : new height
8927 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
8929 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8930 UINT uView = lStyle & LVS_TYPEMASK;
8932 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
8934 LISTVIEW_UpdateSize(hwnd);
8936 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8938 if (lStyle & LVS_ALIGNLEFT)
8939 LISTVIEW_AlignLeft(hwnd);
8941 LISTVIEW_AlignTop(hwnd);
8944 LISTVIEW_UpdateScroll(hwnd);
8946 /* invalidate client area + erase background */
8947 InvalidateRect(hwnd, NULL, TRUE);
8954 * Sets the size information.
8957 * [I] HWND : window handle
8962 static VOID LISTVIEW_UpdateSize(HWND hwnd)
8964 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8965 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8966 UINT uView = lStyle & LVS_TYPEMASK;
8969 TRACE("(hwnd=%x)\n", hwnd);
8971 GetClientRect(hwnd, &rcList);
8972 infoPtr->rcList.left = 0;
8973 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8974 infoPtr->rcList.top = 0;
8975 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8977 if (uView == LVS_LIST)
8979 if (lStyle & WS_HSCROLL)
8981 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8982 if (infoPtr->rcList.bottom > nHScrollHeight)
8983 infoPtr->rcList.bottom -= nHScrollHeight;
8986 else if (uView == LVS_REPORT)
8993 Header_Layout(infoPtr->hwndHeader, &hl);
8995 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8997 if (!(LVS_NOCOLUMNHEADER & lStyle))
8998 infoPtr->rcList.top = max(wp.cy, 0);
9004 * Processes WM_STYLECHANGED messages.
9007 * [I] HWND : window handle
9008 * [I] WPARAM : window style type (normal or extended)
9009 * [I] LPSTYLESTRUCT : window style information
9014 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
9017 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9018 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9019 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9020 RECT rcList = infoPtr->rcList;
9022 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
9023 hwnd, wStyleType, lpss);
9025 if (wStyleType == GWL_STYLE)
9027 if (uOldView == LVS_REPORT)
9028 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9030 if ((lpss->styleOld & WS_HSCROLL) != 0)
9031 ShowScrollBar(hwnd, SB_HORZ, FALSE);
9033 if ((lpss->styleOld & WS_VSCROLL) != 0)
9034 ShowScrollBar(hwnd, SB_VERT, FALSE);
9036 if (uNewView == LVS_ICON)
9038 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9039 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9040 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9041 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9042 if (lpss->styleNew & LVS_ALIGNLEFT)
9043 LISTVIEW_AlignLeft(hwnd);
9045 LISTVIEW_AlignTop(hwnd);
9047 else if (uNewView == LVS_REPORT)
9054 Header_Layout(infoPtr->hwndHeader, &hl);
9055 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
9057 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9058 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9060 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9061 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9062 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9063 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9065 else if (uNewView == LVS_LIST)
9067 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9068 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9069 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9070 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9074 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9075 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9076 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9077 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9078 if (lpss->styleNew & LVS_ALIGNLEFT)
9079 LISTVIEW_AlignLeft(hwnd);
9081 LISTVIEW_AlignTop(hwnd);
9084 /* update the size of the client area */
9085 LISTVIEW_UpdateSize(hwnd);
9087 /* add scrollbars if needed */
9088 LISTVIEW_UpdateScroll(hwnd);
9090 /* invalidate client area + erase background */
9091 InvalidateRect(hwnd, NULL, TRUE);
9093 /* print the list of unsupported window styles */
9094 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9097 /* If they change the view and we have an active edit control
9098 we will need to kill the control since the redraw will
9099 misplace the edit control.
9101 if (infoPtr->hwndEdit &&
9102 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9103 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9105 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9113 * Window procedure of the listview control.
9116 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9119 TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
9120 if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
9121 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9124 case LVM_APPROXIMATEVIEWRECT:
9125 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9126 LOWORD(lParam), HIWORD(lParam));
9128 return LISTVIEW_Arrange(hwnd, (INT)wParam);
9130 /* case LVM_CREATEDRAGIMAGE: */
9132 case LVM_DELETEALLITEMS:
9133 return LISTVIEW_DeleteAllItems(hwnd);
9135 case LVM_DELETECOLUMN:
9136 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9138 case LVM_DELETEITEM:
9139 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9141 case LVM_EDITLABELW:
9142 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
9144 case LVM_EDITLABELA:
9145 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
9147 case LVM_ENSUREVISIBLE:
9148 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9151 return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
9154 return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
9156 case LVM_GETBKCOLOR:
9157 return LISTVIEW_GetBkColor(hwnd);
9159 /* case LVM_GETBKIMAGE: */
9161 case LVM_GETCALLBACKMASK:
9162 return LISTVIEW_GetCallbackMask(hwnd);
9164 case LVM_GETCOLUMNA:
9165 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9167 case LVM_GETCOLUMNW:
9168 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9170 case LVM_GETCOLUMNORDERARRAY:
9171 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9173 case LVM_GETCOLUMNWIDTH:
9174 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9176 case LVM_GETCOUNTPERPAGE:
9177 return LISTVIEW_GetCountPerPage(hwnd);
9179 case LVM_GETEDITCONTROL:
9180 return LISTVIEW_GetEditControl(hwnd);
9182 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9183 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9186 return LISTVIEW_GetHeader(hwnd);
9188 case LVM_GETHOTCURSOR:
9189 FIXME("LVM_GETHOTCURSOR: unimplemented\n");
9192 case LVM_GETHOTITEM:
9193 return LISTVIEW_GetHotItem(hwnd);
9195 case LVM_GETHOVERTIME:
9196 return LISTVIEW_GetHoverTime(hwnd);
9198 case LVM_GETIMAGELIST:
9199 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9201 case LVM_GETISEARCHSTRINGA:
9202 case LVM_GETISEARCHSTRINGW:
9203 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9207 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
9210 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
9212 case LVM_GETITEMCOUNT:
9213 return LISTVIEW_GetItemCount(hwnd);
9215 case LVM_GETITEMPOSITION:
9216 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9218 case LVM_GETITEMRECT:
9219 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9221 case LVM_GETITEMSPACING:
9222 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9224 case LVM_GETITEMSTATE:
9225 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9227 case LVM_GETITEMTEXTA:
9228 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9230 case LVM_GETITEMTEXTW:
9231 return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9233 case LVM_GETNEXTITEM:
9234 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9236 case LVM_GETNUMBEROFWORKAREAS:
9237 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9241 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9243 case LVM_GETSELECTEDCOUNT:
9244 return LISTVIEW_GetSelectedCount(hwnd);
9246 case LVM_GETSELECTIONMARK:
9247 return LISTVIEW_GetSelectionMark(hwnd);
9249 case LVM_GETSTRINGWIDTHA:
9250 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
9252 case LVM_GETSTRINGWIDTHW:
9253 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
9255 case LVM_GETSUBITEMRECT:
9256 FIXME("LVM_GETSUBITEMRECT: unimplemented\n");
9259 case LVM_GETTEXTBKCOLOR:
9260 return LISTVIEW_GetTextBkColor(hwnd);
9262 case LVM_GETTEXTCOLOR:
9263 return LISTVIEW_GetTextColor(hwnd);
9265 case LVM_GETTOOLTIPS:
9266 FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9269 case LVM_GETTOPINDEX:
9270 return LISTVIEW_GetTopIndex(hwnd);
9272 /*case LVM_GETUNICODEFORMAT:
9273 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9276 case LVM_GETVIEWRECT:
9277 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9279 case LVM_GETWORKAREAS:
9280 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9284 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9286 case LVM_INSERTCOLUMNA:
9287 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9289 case LVM_INSERTCOLUMNW:
9290 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9292 case LVM_INSERTITEMA:
9293 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9295 case LVM_INSERTITEMW:
9296 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9298 case LVM_REDRAWITEMS:
9299 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9301 /* case LVM_SCROLL: */
9302 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9304 case LVM_SETBKCOLOR:
9305 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9307 /* case LVM_SETBKIMAGE: */
9309 case LVM_SETCALLBACKMASK:
9310 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9312 case LVM_SETCOLUMNA:
9313 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9315 case LVM_SETCOLUMNW:
9316 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9318 case LVM_SETCOLUMNORDERARRAY:
9319 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9321 case LVM_SETCOLUMNWIDTH:
9322 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9324 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9325 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9327 /* case LVM_SETHOTCURSOR: */
9329 case LVM_SETHOTITEM:
9330 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9332 case LVM_SETHOVERTIME:
9333 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9335 case LVM_SETICONSPACING:
9336 return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam);
9338 case LVM_SETIMAGELIST:
9339 return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9342 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9345 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9347 case LVM_SETITEMCOUNT:
9348 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9350 case LVM_SETITEMPOSITION:
9351 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9352 (INT)HIWORD(lParam));
9354 case LVM_SETITEMPOSITION32:
9355 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9356 ((POINT*)lParam)->y);
9358 case LVM_SETITEMSTATE:
9359 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam);
9361 case LVM_SETITEMTEXTA:
9362 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9364 case LVM_SETITEMTEXTW:
9365 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9367 case LVM_SETSELECTIONMARK:
9368 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9370 case LVM_SETTEXTBKCOLOR:
9371 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9373 case LVM_SETTEXTCOLOR:
9374 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9376 /* case LVM_SETTOOLTIPS: */
9377 /* case LVM_SETUNICODEFORMAT: */
9378 /* case LVM_SETWORKAREAS: */
9381 return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9383 /* case LVM_SUBITEMHITTEST: */
9386 return LISTVIEW_Update(hwnd, (INT)wParam);
9389 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9392 return LISTVIEW_Command(hwnd, wParam, lParam);
9395 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9398 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9401 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9404 return LISTVIEW_GetFont(hwnd);
9407 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
9408 (INT)HIWORD(wParam), (HWND)lParam);
9411 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9414 return LISTVIEW_KillFocus(hwnd);
9416 case WM_LBUTTONDBLCLK:
9417 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9420 case WM_LBUTTONDOWN:
9421 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9424 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9427 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9430 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9433 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9436 return LISTVIEW_NCDestroy(hwnd);
9439 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9441 case WM_NOTIFYFORMAT:
9442 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9445 return LISTVIEW_Paint(hwnd, (HDC)wParam);
9447 case WM_RBUTTONDBLCLK:
9448 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9451 case WM_RBUTTONDOWN:
9452 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9456 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9460 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9463 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9466 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9469 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9471 case WM_STYLECHANGED:
9472 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9474 /* case WM_TIMER: */
9477 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
9478 (INT)HIWORD(wParam), (HWND)lParam);
9481 if (wParam & (MK_SHIFT | MK_CONTROL))
9482 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9483 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
9485 /* case WM_WININICHANGE: */
9488 if (uMsg >= WM_USER)
9490 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
9494 /* call default window procedure */
9495 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9503 * Registers the window class.
9511 VOID LISTVIEW_Register(void)
9515 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9516 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9517 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9518 wndClass.cbClsExtra = 0;
9519 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9520 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
9521 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9522 wndClass.lpszClassName = WC_LISTVIEWW;
9523 RegisterClassW(&wndClass);
9528 * Unregisters the window class.
9536 VOID LISTVIEW_Unregister(void)
9538 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
9543 * Handle any WM_COMMAND messages
9549 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9551 switch (HIWORD(wParam))
9556 * Adjust the edit window size
9559 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9560 HDC hdc = GetDC(infoPtr->hwndEdit);
9561 HFONT hFont, hOldFont = 0;
9566 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9567 GetWindowRect(infoPtr->hwndEdit, &rect);
9569 /* Select font to get the right dimension of the string */
9570 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9573 hOldFont = SelectObject(hdc, hFont);
9576 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9578 TEXTMETRICW textMetric;
9580 /* Add Extra spacing for the next character */
9581 GetTextMetricsW(hdc, &textMetric);
9582 sz.cx += (textMetric.tmMaxCharWidth * 2);
9590 rect.bottom - rect.top,
9591 SWP_DRAWFRAME|SWP_NOMOVE);
9594 SelectObject(hdc, hOldFont);
9596 ReleaseDC(hwnd, hdc);
9602 return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9611 * Subclassed edit control windproc function
9617 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
9618 WPARAM wParam, LPARAM lParam, BOOL isW)
9620 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9621 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9622 static BOOL bIgnoreKillFocus = FALSE;
9623 BOOL cancel = FALSE;
9625 TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9626 hwnd, uMsg, wParam, lParam, isW);
9631 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9634 if(bIgnoreKillFocus) return TRUE;
9639 WNDPROC editProc = einfo->EditWndProc;
9640 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9641 COMCTL32_Free(einfo);
9642 infoPtr->pedititem = NULL;
9643 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9647 if (VK_ESCAPE == (INT)wParam)
9652 else if (VK_RETURN == (INT)wParam)
9656 return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9659 if (einfo->EditLblCb)
9661 LPWSTR buffer = NULL;
9665 DWORD len = 1 + isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9669 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9671 if (isW) GetWindowTextW(hwnd, buffer, len);
9672 else GetWindowTextA(hwnd, (CHAR*)buffer, len);
9676 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9677 /* eg. Using a messagebox */
9678 bIgnoreKillFocus = TRUE;
9679 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9681 if (buffer) COMCTL32_Free(buffer);
9683 einfo->EditLblCb = NULL;
9684 bIgnoreKillFocus = FALSE;
9687 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9693 * Subclassed edit control windproc function
9699 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9701 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9706 * Subclassed edit control windproc function
9712 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9714 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9719 * Creates a subclassed edit cotrol
9725 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
9726 INT width, INT height, HWND parent, HINSTANCE hinst,
9727 EditlblCallbackW EditLblCb, DWORD param, BOOL isW)
9729 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
9730 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9735 TEXTMETRICW textMetric;
9737 TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
9739 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9742 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9743 hdc = GetDC(parent);
9745 /* Select the font to get appropriate metric dimensions */
9746 if(infoPtr->hFont != 0)
9747 hOldFont = SelectObject(hdc, infoPtr->hFont);
9749 /*Get String Lenght in pixels */
9750 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9752 /*Add Extra spacing for the next character */
9753 GetTextMetricsW(hdc, &textMetric);
9754 sz.cx += (textMetric.tmMaxCharWidth * 2);
9756 if(infoPtr->hFont != 0)
9757 SelectObject(hdc, hOldFont);
9759 ReleaseDC(parent, hdc);
9761 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
9763 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
9767 COMCTL32_Free(infoPtr->pedititem);
9771 infoPtr->pedititem->param = param;
9772 infoPtr->pedititem->EditLblCb = EditLblCb;
9773 infoPtr->pedititem->EditWndProc = (WNDPROC)
9774 (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
9775 SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
9777 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);