4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2002 Dimitrie O. Paun
10 * Listview control implementation.
13 * 1. No horizontal scrolling when header is larger than the client area.
14 * 2. Drawing optimizations.
15 * 3. Hot item handling.
18 * LISTVIEW_Notify : most notifications from children (editbox and header)
21 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
23 * Advanced functionality:
24 * LISTVIEW_GetNumberOfWorkAreas : not implemented
25 * LISTVIEW_GetHotCursor : not implemented
26 * LISTVIEW_GetISearchString : not implemented
27 * LISTVIEW_GetBkImage : not implemented
28 * LISTVIEW_SetBkImage : not implemented
29 * LISTVIEW_GetColumnOrderArray : simple hack only
30 * LISTVIEW_SetColumnOrderArray : simple hack only
31 * LISTVIEW_Arrange : empty stub
32 * LISTVIEW_ApproximateViewRect : incomplete
33 * LISTVIEW_Scroll : not implemented
34 * LISTVIEW_Update : not completed
45 #include "debugtools.h"
47 DEFAULT_DEBUG_CHANNEL(listview);
49 /* Some definitions for inline edit control */
50 typedef BOOL (*EditlblCallbackW)(HWND, LPWSTR, DWORD);
52 typedef struct tagEDITLABEL_ITEM
56 EditlblCallbackW EditLblCb;
59 typedef struct tagLISTVIEW_SUBITEM
66 typedef struct tagLISTVIEW_ITEM
77 typedef struct tagLISTVIEW_SELECTION
83 typedef struct tagLISTVIEW_INFO
88 HIMAGELIST himlNormal;
94 HDPA hdpaSelectionRanges;
109 DWORD dwExStyle; /* extended listview style */
111 PFNLVCOMPARE pfnCompare;
115 EDITLABEL_ITEM *pedititem;
117 INT nColumnCount; /* the number of columns in this control */
119 DWORD lastKeyPressTimestamp; /* Added */
120 WPARAM charCode; /* Added */
121 INT nSearchParamLength; /* Added */
122 WCHAR szSearchParam[ MAX_PATH ]; /* Added */
129 /* maximum size of a label */
130 #define DISP_TEXT_SIZE 512
132 /* padding for items in list and small icon display modes */
133 #define WIDTH_PADDING 12
135 /* padding for items in list, report and small icon display modes */
136 #define HEIGHT_PADDING 1
138 /* offset of items in report display mode */
139 #define REPORT_MARGINX 2
141 /* padding for icon in large icon display mode */
142 #define ICON_TOP_PADDING 2
143 #define ICON_BOTTOM_PADDING 2
145 /* padding for label in large icon display mode */
146 #define LABEL_VERT_OFFSET 2
148 /* default label width for items in list and small icon display modes */
149 #define DEFAULT_LABEL_WIDTH 40
151 /* default column width for items in list display mode */
152 #define DEFAULT_COLUMN_WIDTH 96
154 /* Increment size of the horizontal scroll bar */
155 #define LISTVIEW_SCROLL_DIV_SIZE 10
157 /* Padding betwen image and label */
158 #define IMAGE_PADDING 2
160 /* Padding behind the label */
161 #define TRAILING_PADDING 5
163 /* Border for the icon caption */
164 #define CAPTION_BORDER 2
168 /* retrieve the number of items in the listview */
169 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
170 #define LVN_GETDISPINFOT(isW) ( (isW) ? LVN_GETDISPINFOW : LVN_GETDISPINFOA )
171 #define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA )
173 HWND CreateEditLabelW(LPCWSTR text, DWORD style, INT x, INT y,
174 INT width, INT height, HWND parent, HINSTANCE hinst,
175 EditlblCallbackW EditLblCb, DWORD param);
178 * forward declarations
180 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW);
181 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL);
182 static INT LISTVIEW_GetCountPerRow(HWND);
183 static INT LISTVIEW_GetCountPerColumn(HWND);
184 static VOID LISTVIEW_AlignLeft(HWND);
185 static VOID LISTVIEW_AlignTop(HWND);
186 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
187 static VOID LISTVIEW_AddSelection(HWND, INT);
188 static BOOL LISTVIEW_AddSubItemT(HWND, LPLVITEMW, BOOL);
189 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
190 static INT LISTVIEW_GetItemHeight(HWND);
191 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
192 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
193 static INT LISTVIEW_GetItemWidth(HWND);
194 static INT LISTVIEW_GetLabelWidth(HWND, INT);
195 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
196 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
197 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
198 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
199 static BOOL LISTVIEW_InitItemT(HWND, LISTVIEW_ITEM *, LPLVITEMW, BOOL);
200 static BOOL LISTVIEW_InitSubItemT(HWND, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL);
201 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
202 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
203 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
204 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
205 static BOOL LISTVIEW_SetItemT(HWND, LPLVITEMW, BOOL);
206 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
207 static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG);
208 static VOID LISTVIEW_UpdateScroll(HWND);
209 static VOID LISTVIEW_SetSelection(HWND, INT);
210 static VOID LISTVIEW_UpdateSize(HWND);
211 static BOOL LISTVIEW_SetSubItemT(HWND, LPLVITEMW, BOOL);
212 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
213 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
214 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
215 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW);
216 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem);
217 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
218 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
219 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW);
220 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
221 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
222 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
223 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem);
224 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem);
225 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
226 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
228 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
229 #define KEY_DELAY 450
232 static inline BOOL is_textW(LPCWSTR text)
234 return text != NULL && text != LPSTR_TEXTCALLBACKW;
237 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
239 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
240 return is_textW(text);
243 static inline int textlenT(LPCWSTR text, BOOL isW)
245 return !is_textT(text, isW) ? 0 :
246 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
249 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
252 if (isSrcW) lstrcpynW(dest, src, max);
253 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
255 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
256 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
259 static inline LPCSTR debugstr_t(LPCWSTR text, BOOL isW)
261 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
264 static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n)
266 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
269 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
271 LPWSTR wstr = (LPWSTR)text;
273 TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW);
276 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
277 wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
278 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
280 TRACE(" wstr=%s\n", debugstr_w(wstr));
284 static inline void textfreeT(LPWSTR wstr, BOOL isW)
286 if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
289 static inline BOOL notify(HWND self, INT code, LPNMHDR pnmh)
291 pnmh->hwndFrom = self;
292 pnmh->idFrom = GetWindowLongW(self, GWL_ID);
294 return (BOOL)SendMessageW(GetParent(self), WM_NOTIFY,
295 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
298 static inline BOOL hdr_notify(HWND self, INT code)
301 return notify(self, code, &nmh);
304 static inline BOOL listview_notify(HWND self, INT code, LPNMLISTVIEW plvnm)
306 return notify(self, code, (LPNMHDR)plvnm);
309 static int tabNotification[] = {
310 LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
311 LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
312 LVN_GETDISPINFOW, LVN_GETDISPINFOA,
313 LVN_SETDISPINFOW, LVN_SETDISPINFOA,
314 LVN_ODFINDITEMW, LVN_ODFINDITEMA,
315 LVN_GETINFOTIPW, LVN_GETINFOTIPA,
319 static int get_ansi_notification(INT unicodeNotificationCode)
321 int *pTabNotif = tabNotification;
322 while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
323 if (*pTabNotif) return *pTabNotif;
324 ERR("unknown notification %x\n", unicodeNotificationCode);
325 return unicodeNotificationCode;
329 Send notification. depends on dispinfoW having same
330 structure as dispinfoA.
331 self : listview handle
332 notificationCode : *Unicode* notification code
333 pdi : dispinfo structure (can be unicode or ansi)
334 isW : TRUE if dispinfo is Unicode
336 static BOOL dispinfo_notifyT(HWND self, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
338 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(self, 0);
339 BOOL bResult = FALSE;
340 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
342 INT cchTempBufMax = 0, savCchTextMax = 0;
343 LPWSTR pszTempBuf = NULL, savPszText = NULL;
345 TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self, notificationCode, pdi, isW);
346 TRACE(" notifyFormat=%s\n",
347 infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
348 infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
349 if (infoPtr->notifyFormat == NFR_ANSI)
350 realNotifCode = get_ansi_notification(notificationCode);
352 realNotifCode = notificationCode;
354 if (is_textT(pdi->item.pszText, isW))
356 if (isW && infoPtr->notifyFormat == NFR_ANSI)
357 convertToAnsi = TRUE;
358 if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
359 convertToUnicode = TRUE;
362 if (convertToAnsi || convertToUnicode)
364 TRACE(" we have to convert the text to the correct format\n");
365 if (notificationCode != LVN_GETDISPINFOW)
366 { /* length of existing text */
367 cchTempBufMax = convertToUnicode ?
368 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL) :
369 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0);
372 cchTempBufMax = pdi->item.cchTextMax;
374 pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
375 (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
376 if (!pszTempBuf) return FALSE;
377 if (convertToUnicode)
378 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
379 cchTempBufMax, NULL, NULL);
381 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
382 pszTempBuf, cchTempBufMax);
383 TRACE(" text=%s\n", debugstr_t(pszTempBuf, convertToUnicode));
384 savCchTextMax = pdi->item.cchTextMax;
385 savPszText = pdi->item.pszText;
386 pdi->item.pszText = pszTempBuf;
387 pdi->item.cchTextMax = cchTempBufMax;
390 bResult = notify(self, realNotifCode, (LPNMHDR)pdi);
392 if (convertToUnicode || convertToAnsi)
393 { /* convert back result */
394 TRACE(" returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode));
395 if (convertToUnicode) /* note : pointer can be changed by app ! */
396 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
397 savCchTextMax, NULL, NULL);
399 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
400 savPszText, savCchTextMax);
401 pdi->item.pszText = savPszText; /* restores our buffer */
402 pdi->item.cchTextMax = savCchTextMax;
403 HeapFree(GetProcessHeap(), 0, pszTempBuf);
408 static inline LRESULT LISTVIEW_GetItemW(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal)
410 return LISTVIEW_GetItemT(hwnd, lpLVItem, internal, TRUE);
413 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
417 n = min(min(n, strlenW(s1)), strlenW(s2));
418 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
419 return res ? res - 2 : res;
422 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
424 static int index = 0;
425 static char buffers[20][256];
426 char* buf = buffers[index++ % 20];
427 if (lpLVItem == NULL) return "(null)";
428 snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
429 " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
430 lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
431 lpLVItem->state, lpLVItem->stateMask,
432 lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
433 debugstr_tn(lpLVItem->pszText, isW, 80),
434 lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
440 LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
443 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
444 NMLVCUSTOMDRAW nmcdhdr;
447 TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd, dwDrawStage, hdc);
449 nmcd= & nmcdhdr.nmcd;
450 nmcd->hdr.hwndFrom = hwnd;
451 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
452 nmcd->hdr.code = NM_CUSTOMDRAW;
453 nmcd->dwDrawStage= dwDrawStage;
455 nmcd->rc.left = rc.left;
456 nmcd->rc.right = rc.right;
457 nmcd->rc.bottom = rc.bottom;
458 nmcd->rc.top = rc.top;
459 nmcd->dwItemSpec = 0;
460 nmcd->uItemState = 0;
461 nmcd->lItemlParam= 0;
462 nmcdhdr.clrText = infoPtr->clrText;
463 nmcdhdr.clrTextBk= infoPtr->clrBk;
465 return (BOOL)SendMessageW (GetParent (hwnd), WM_NOTIFY,
466 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
470 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
471 UINT iItem, UINT iSubItem,
474 LISTVIEW_INFO *infoPtr;
475 NMLVCUSTOMDRAW nmcdhdr;
477 DWORD dwDrawStage,dwItemSpec;
483 infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
485 ZeroMemory(&item,sizeof(item));
487 item.mask = LVIF_PARAM;
488 ListView_GetItemW(hwnd,&item);
490 dwDrawStage=CDDS_ITEM | uItemDrawState;
494 if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
495 if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
496 if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
498 itemRect.left = LVIR_BOUNDS;
499 LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
501 nmcd= & nmcdhdr.nmcd;
502 nmcd->hdr.hwndFrom = hwnd;
503 nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID);
504 nmcd->hdr.code = NM_CUSTOMDRAW;
505 nmcd->dwDrawStage= dwDrawStage;
507 nmcd->rc.left = itemRect.left;
508 nmcd->rc.right = itemRect.right;
509 nmcd->rc.bottom = itemRect.bottom;
510 nmcd->rc.top = itemRect.top;
511 nmcd->dwItemSpec = dwItemSpec;
512 nmcd->uItemState = uItemState;
513 nmcd->lItemlParam= item.lParam;
514 nmcdhdr.clrText = infoPtr->clrText;
515 nmcdhdr.clrTextBk= infoPtr->clrBk;
516 nmcdhdr.iSubItem =iSubItem;
518 TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
519 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
520 nmcd->uItemState, nmcd->lItemlParam);
522 retval=SendMessageW (GetParent (hwnd), WM_NOTIFY,
523 (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
525 infoPtr->clrText=nmcdhdr.clrText;
526 infoPtr->clrBk =nmcdhdr.clrTextBk;
527 return (BOOL) retval;
531 /*************************************************************************
532 * LISTVIEW_ProcessLetterKeys
534 * Processes keyboard messages generated by pressing the letter keys
536 * What this does is perform a case insensitive search from the
537 * current position with the following quirks:
538 * - If two chars or more are pressed in quick succession we search
539 * for the corresponding string (e.g. 'abc').
540 * - If there is a delay we wipe away the current search string and
541 * restart with just that char.
542 * - If the user keeps pressing the same character, whether slowly or
543 * fast, so that the search string is entirely composed of this
544 * character ('aaaaa' for instance), then we search for first item
545 * that starting with that character.
546 * - If the user types the above character in quick succession, then
547 * we must also search for the corresponding string ('aaaaa'), and
548 * go to that string if there is a match.
556 * - The current implementation has a list of characters it will
557 * accept and it ignores averything else. In particular it will
558 * ignore accentuated characters which seems to match what
559 * Windows does. But I'm not sure it makes sense to follow
561 * - We don't sound a beep when the search fails.
565 * TREEVIEW_ProcessLetterKeys
567 static INT LISTVIEW_ProcessLetterKeys(
568 HWND hwnd, /* handle to the window */
569 WPARAM charCode, /* the character code, the actual character */
570 LPARAM keyData /* key data */
573 LISTVIEW_INFO *infoPtr;
578 WCHAR buffer[MAX_PATH];
579 DWORD timestamp,elapsed;
581 /* simple parameter checking */
582 if (!hwnd || !charCode || !keyData)
585 infoPtr=(LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
589 /* only allow the valid WM_CHARs through */
590 if (!isalnum(charCode) &&
591 charCode != '.' && charCode != '`' && charCode != '!' &&
592 charCode != '@' && charCode != '#' && charCode != '$' &&
593 charCode != '%' && charCode != '^' && charCode != '&' &&
594 charCode != '*' && charCode != '(' && charCode != ')' &&
595 charCode != '-' && charCode != '_' && charCode != '+' &&
596 charCode != '=' && charCode != '\\'&& charCode != ']' &&
597 charCode != '}' && charCode != '[' && charCode != '{' &&
598 charCode != '/' && charCode != '?' && charCode != '>' &&
599 charCode != '<' && charCode != ',' && charCode != '~')
602 nSize=GETITEMCOUNT(infoPtr);
603 /* if there's one item or less, there is no where to go */
607 /* compute how much time elapsed since last keypress */
608 timestamp=GetTickCount();
609 if (timestamp > infoPtr->lastKeyPressTimestamp) {
610 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
612 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
615 /* update the search parameters */
616 infoPtr->lastKeyPressTimestamp=timestamp;
617 if (elapsed < KEY_DELAY) {
618 if (infoPtr->nSearchParamLength < sizeof(infoPtr->szSearchParam)) {
619 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
621 if (infoPtr->charCode != charCode) {
622 infoPtr->charCode=charCode=0;
625 infoPtr->charCode=charCode;
626 infoPtr->szSearchParam[0]=charCode;
627 infoPtr->nSearchParamLength=1;
628 /* Redundant with the 1 char string */
632 /* and search from the current position */
634 if (infoPtr->nFocusedItem >= 0) {
635 endidx=infoPtr->nFocusedItem;
637 /* if looking for single character match,
638 * then we must always move forward
640 if (infoPtr->nSearchParamLength == 1)
654 ZeroMemory(&item, sizeof(item));
655 item.mask = LVIF_TEXT;
658 item.pszText = buffer;
659 item.cchTextMax = sizeof(buffer);
660 ListView_GetItemW( hwnd, &item );
662 /* check for a match */
663 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
666 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
667 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
668 /* This would work but we must keep looking for a longer match */
672 } while (idx != endidx);
675 if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
676 /* refresh client area */
677 InvalidateRect(hwnd, NULL, TRUE);
685 /*************************************************************************
686 * LISTVIEW_UpdateHeaderSize [Internal]
688 * Function to resize the header control
691 * hwnd [I] handle to a window
692 * nNewScrollPos [I] Scroll Pos to Set
699 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
701 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
705 GetWindowRect(infoPtr->hwndHeader, &winRect);
706 point[0].x = winRect.left;
707 point[0].y = winRect.top;
708 point[1].x = winRect.right;
709 point[1].y = winRect.bottom;
711 MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
712 point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
713 point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
715 SetWindowPos(infoPtr->hwndHeader,0,
716 point[0].x,point[0].y,point[1].x,point[1].y,
717 SWP_NOZORDER | SWP_NOACTIVATE);
722 * Update the scrollbars. This functions should be called whenever
723 * the content, size or view changes.
726 * [I] HWND : window handle
731 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
733 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
734 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
735 UINT uView = lStyle & LVS_TYPEMASK;
736 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
737 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
738 SCROLLINFO scrollInfo;
740 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
741 scrollInfo.cbSize = sizeof(SCROLLINFO);
743 if (uView == LVS_LIST)
745 /* update horizontal scrollbar */
747 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
748 INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
749 INT nNumOfItems = GETITEMCOUNT(infoPtr);
751 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
752 if((nNumOfItems % nCountPerColumn) == 0)
756 scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
757 scrollInfo.nPage = nCountPerRow;
758 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
759 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
760 ShowScrollBar(hwnd, SB_VERT, FALSE);
762 else if (uView == LVS_REPORT)
764 /* update vertical scrollbar */
766 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
767 scrollInfo.nPos = ListView_GetTopIndex(hwnd);
768 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
769 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
770 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
772 /* update horizontal scrollbar */
773 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
774 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
775 || GETITEMCOUNT(infoPtr) == 0)
780 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
781 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
782 scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
783 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
785 /* Update the Header Control */
786 scrollInfo.fMask = SIF_POS;
787 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
788 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
795 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
797 INT nViewWidth = rcView.right - rcView.left;
798 INT nViewHeight = rcView.bottom - rcView.top;
800 /* Update Horizontal Scrollbar */
801 scrollInfo.fMask = SIF_POS;
802 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
803 || GETITEMCOUNT(infoPtr) == 0)
807 scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
809 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
810 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
811 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
813 /* Update Vertical Scrollbar */
814 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
815 scrollInfo.fMask = SIF_POS;
816 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
817 || GETITEMCOUNT(infoPtr) == 0)
821 scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
823 scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
824 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
825 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
832 * Prints a message for unsupported window styles.
833 * A kind of TODO list for window styles.
836 * [I] LONG : window style
841 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
843 if ((LVS_TYPEMASK & lStyle) == LVS_EDITLABELS)
844 FIXME(" LVS_EDITLABELS\n");
846 if ((LVS_TYPEMASK & lStyle) == LVS_NOLABELWRAP)
847 FIXME(" LVS_NOLABELWRAP\n");
849 if ((LVS_TYPEMASK & lStyle) == LVS_NOSCROLL)
850 FIXME(" LVS_NOSCROLL\n");
852 if ((LVS_TYPEMASK & lStyle) == LVS_NOSORTHEADER)
853 FIXME(" LVS_NOSORTHEADER\n");
855 if ((LVS_TYPEMASK & lStyle) == LVS_OWNERDRAWFIXED)
856 FIXME(" LVS_OWNERDRAWFIXED\n");
858 if ((LVS_TYPEMASK & lStyle) == LVS_SHAREIMAGELISTS)
859 FIXME(" LVS_SHAREIMAGELISTS\n");
861 if ((LVS_TYPEMASK & lStyle) == LVS_SORTASCENDING)
862 FIXME(" LVS_SORTASCENDING\n");
864 if ((LVS_TYPEMASK & lStyle) == LVS_SORTDESCENDING)
865 FIXME(" LVS_SORTDESCENDING\n");
870 * Aligns the items with the top edge of the window.
873 * [I] HWND : window handle
878 static VOID LISTVIEW_AlignTop(HWND hwnd)
880 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
881 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
882 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
887 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
889 ZeroMemory(&ptItem, sizeof(POINT));
890 ZeroMemory(&rcView, sizeof(RECT));
892 if (nListWidth > infoPtr->nItemWidth)
894 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
896 if (ptItem.x + infoPtr->nItemWidth > nListWidth)
899 ptItem.y += infoPtr->nItemHeight;
902 ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
903 ptItem.x += infoPtr->nItemWidth;
904 rcView.right = max(rcView.right, ptItem.x);
907 rcView.bottom = ptItem.y + infoPtr->nItemHeight;
911 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
913 ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
914 ptItem.y += infoPtr->nItemHeight;
917 rcView.right = infoPtr->nItemWidth;
918 rcView.bottom = ptItem.y;
921 LISTVIEW_SetViewRect(hwnd, &rcView);
927 * Aligns the items with the left edge of the window.
930 * [I] HWND : window handle
935 static VOID LISTVIEW_AlignLeft(HWND hwnd)
937 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
938 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
939 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
944 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
946 ZeroMemory(&ptItem, sizeof(POINT));
947 ZeroMemory(&rcView, sizeof(RECT));
949 if (nListHeight > infoPtr->nItemHeight)
951 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
953 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
956 ptItem.x += infoPtr->nItemWidth;
959 ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
960 ptItem.y += infoPtr->nItemHeight;
961 rcView.bottom = max(rcView.bottom, ptItem.y);
964 rcView.right = ptItem.x + infoPtr->nItemWidth;
968 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
970 ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
971 ptItem.x += infoPtr->nItemWidth;
974 rcView.bottom = infoPtr->nItemHeight;
975 rcView.right = ptItem.x;
978 LISTVIEW_SetViewRect(hwnd, &rcView);
984 * Set the bounding rectangle of all the items.
987 * [I] HWND : window handle
988 * [I] LPRECT : bounding rectangle
994 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
996 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
997 BOOL bResult = FALSE;
999 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
1000 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1002 if (lprcView != NULL)
1005 infoPtr->rcView.left = lprcView->left;
1006 infoPtr->rcView.top = lprcView->top;
1007 infoPtr->rcView.right = lprcView->right;
1008 infoPtr->rcView.bottom = lprcView->bottom;
1016 * Retrieves the bounding rectangle of all the items.
1019 * [I] HWND : window handle
1020 * [O] LPRECT : bounding rectangle
1026 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
1028 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1029 BOOL bResult = FALSE;
1032 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
1034 if (lprcView != NULL)
1036 bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
1037 if (bResult != FALSE)
1039 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1040 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1041 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1042 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1045 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1046 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1054 * Retrieves the subitem pointer associated with the subitem index.
1057 * [I] HDPA : DPA handle for a specific item
1058 * [I] INT : index of subitem
1061 * SUCCESS : subitem pointer
1064 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1067 LISTVIEW_SUBITEM *lpSubItem;
1070 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1072 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1073 if (lpSubItem != NULL)
1075 if (lpSubItem->iSubItem == nSubItem)
1087 * Calculates the width of an item.
1090 * [I] HWND : window handle
1091 * [I] LONG : window style
1094 * Returns item width.
1096 static INT LISTVIEW_GetItemWidth(HWND hwnd)
1098 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1099 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
1100 UINT uView = style & LVS_TYPEMASK;
1101 INT nHeaderItemCount;
1107 TRACE("(hwnd=%x)\n", hwnd);
1109 if (uView == LVS_ICON)
1111 nItemWidth = infoPtr->iconSpacing.cx;
1113 else if (uView == LVS_REPORT)
1115 /* calculate width of header */
1116 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1117 for (i = 0; i < nHeaderItemCount; i++)
1119 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1121 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1127 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1129 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
1130 nItemWidth = max(nItemWidth, nLabelWidth);
1133 /* default label size */
1134 if (GETITEMCOUNT(infoPtr) == 0)
1136 nItemWidth = DEFAULT_COLUMN_WIDTH;
1140 if (nItemWidth == 0)
1142 nItemWidth = DEFAULT_LABEL_WIDTH;
1147 nItemWidth += WIDTH_PADDING;
1149 if (infoPtr->himlSmall != NULL)
1151 nItemWidth += infoPtr->iconSize.cx;
1154 if (infoPtr->himlState != NULL)
1156 nItemWidth += infoPtr->iconSize.cx;
1163 /* nItemWidth Cannot be Zero */
1171 * Calculates the width of a specific item.
1174 * [I] HWND : window handle
1175 * [I] LPSTR : string
1178 * Returns the width of an item width a specified string.
1180 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1182 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1183 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1184 INT nHeaderItemCount;
1189 TRACE("(hwnd=%x)\n", hwnd);
1191 if (uView == LVS_ICON)
1193 nItemWidth = infoPtr->iconSpacing.cx;
1195 else if (uView == LVS_REPORT)
1197 /* calculate width of header */
1198 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1199 for (i = 0; i < nHeaderItemCount; i++)
1201 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1203 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1209 /* get width of string */
1210 nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1212 /* default label size */
1213 if (GETITEMCOUNT(infoPtr) == 0)
1215 nItemWidth = DEFAULT_COLUMN_WIDTH;
1219 if (nItemWidth == 0)
1221 nItemWidth = DEFAULT_LABEL_WIDTH;
1226 nItemWidth += WIDTH_PADDING;
1228 if (infoPtr->himlSmall != NULL)
1230 nItemWidth += infoPtr->iconSize.cx;
1233 if (infoPtr->himlState != NULL)
1235 nItemWidth += infoPtr->iconSize.cx;
1246 * Calculates the height of an item.
1249 * [I] HWND : window handle
1250 * [I] LONG : window style
1253 * Returns item height.
1255 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1257 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1258 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1259 INT nItemHeight = 0;
1261 if (uView == LVS_ICON)
1263 nItemHeight = infoPtr->iconSpacing.cy;
1268 HDC hdc = GetDC(hwnd);
1269 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1270 GetTextMetricsW(hdc, &tm);
1272 if(infoPtr->himlState || infoPtr->himlSmall)
1273 nItemHeight = max(tm.tmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1275 nItemHeight = tm.tmHeight;
1277 SelectObject(hdc, hOldFont);
1278 ReleaseDC(hwnd, hdc);
1285 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1287 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1288 LISTVIEW_SELECTION *selection;
1289 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1292 TRACE("Selections are:\n");
1293 for (i = 0; i < topSelection; i++)
1295 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1296 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1302 * A compare function for selection ranges
1305 * [I] LPVOID : Item 1;
1306 * [I] LPVOID : Item 2;
1307 * [I] LPARAM : flags
1310 * >0 : if Item 1 > Item 2
1311 * <0 : if Item 2 > Item 1
1312 * 0 : if Item 1 == Item 2
1314 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1317 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1318 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1319 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1320 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1334 * Adds a selection range.
1337 * [I] HWND : window handle
1338 * [I] INT : lower item index
1339 * [I] INT : upper item index
1344 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1346 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1347 LISTVIEW_SELECTION *selection;
1348 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1349 BOOL lowerzero=FALSE;
1351 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1352 selection->lower = lItem;
1353 selection->upper = uItem;
1355 TRACE("Add range %i - %i\n", lItem, uItem);
1358 LISTVIEW_SELECTION *checkselection,*checkselection2;
1359 INT index,mergeindex;
1361 /* find overlapping selections */
1362 /* we want to catch adjacent ranges so expand our range by 1 */
1365 if (selection->lower == 0)
1370 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1371 LISTVIEW_CompareSelectionRanges,
1373 selection->upper --;
1377 selection->lower ++;
1381 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1382 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1383 checkselection->upper);
1385 checkselection->lower = min(selection->lower,checkselection->lower);
1386 checkselection->upper = max(selection->upper,checkselection->upper);
1388 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1389 checkselection->upper);
1391 COMCTL32_Free(selection);
1393 /* merge now common selection ranges in the lower group*/
1396 checkselection->upper ++;
1397 if (checkselection->lower == 0)
1400 checkselection->lower --;
1402 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1403 checkselection->upper);
1405 /* not sorted yet */
1406 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1407 LISTVIEW_CompareSelectionRanges, 0,
1410 checkselection->upper --;
1414 checkselection->lower ++;
1416 if (mergeindex >=0 && mergeindex != index)
1418 TRACE("Merge with index %i\n",mergeindex);
1419 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1421 checkselection->lower = min(checkselection->lower,
1422 checkselection2->lower);
1423 checkselection->upper = max(checkselection->upper,
1424 checkselection2->upper);
1425 COMCTL32_Free(checkselection2);
1426 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1430 while (mergeindex > -1 && mergeindex <index);
1432 /* merge now common selection ranges in the upper group*/
1435 checkselection->upper ++;
1436 if (checkselection->lower == 0)
1439 checkselection->lower --;
1441 TRACE("search upper range %i (%lu - %lu)\n",index,
1442 checkselection->lower, checkselection->upper);
1444 /* not sorted yet */
1445 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1447 LISTVIEW_CompareSelectionRanges, 0,
1450 checkselection->upper --;
1454 checkselection->lower ++;
1456 if (mergeindex >=0 && mergeindex !=index)
1458 TRACE("Merge with index %i\n",mergeindex);
1459 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1461 checkselection->lower = min(checkselection->lower,
1462 checkselection2->lower);
1463 checkselection->upper = max(checkselection->upper,
1464 checkselection2->upper);
1465 COMCTL32_Free(checkselection2);
1466 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1469 while (mergeindex > -1);
1474 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1475 LISTVIEW_CompareSelectionRanges, 0,
1478 TRACE("Insert before index %i\n",index);
1481 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1486 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1491 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1492 LISTVIEW_PrintSelectionRanges(hwnd);
1497 * check if a specified index is selected.
1500 * [I] HWND : window handle
1501 * [I] INT : item index
1506 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1508 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1509 LISTVIEW_SELECTION selection;
1512 selection.upper = nItem;
1513 selection.lower = nItem;
1515 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1516 LISTVIEW_CompareSelectionRanges,
1526 * Removes all selection ranges
1529 * HWND: window handle
1535 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1537 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1538 LISTVIEW_SELECTION *selection;
1542 TRACE("(0x%x)\n",hwnd);
1544 ZeroMemory(&item,sizeof(item));
1545 item.stateMask = LVIS_SELECTED;
1549 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1552 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1553 for (i = selection->lower; i<=selection->upper; i++)
1554 LISTVIEW_SetItemState(hwnd,i,&item);
1555 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1558 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1566 * Removes a range selections.
1569 * [I] HWND : window handle
1570 * [I] INT : lower item index
1571 * [I] INT : upper item index
1576 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1578 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1579 LISTVIEW_SELECTION removeselection,*checkselection;
1582 removeselection.lower = lItem;
1583 removeselection.upper = uItem;
1585 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1586 LISTVIEW_PrintSelectionRanges(hwnd);
1588 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1589 LISTVIEW_CompareSelectionRanges,
1596 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1599 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1600 checkselection->upper);
1603 if ((checkselection->upper == removeselection.upper) &&
1604 (checkselection->lower == removeselection.lower))
1606 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1609 /* case 2: engulf */
1610 else if (((checkselection->upper < removeselection.upper) &&
1611 (checkselection->lower > removeselection.lower))||
1612 ((checkselection->upper <= removeselection.upper) &&
1613 (checkselection->lower > removeselection.lower)) ||
1614 ((checkselection->upper < removeselection.upper) &&
1615 (checkselection->lower >= removeselection.lower)))
1618 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1619 /* do it again because others may also get caught */
1621 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1623 /* case 3: overlap upper */
1624 else if ((checkselection->upper < removeselection.upper) &&
1625 (checkselection->lower < removeselection.lower))
1627 checkselection->upper = removeselection.lower - 1;
1629 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1631 /* case 4: overlap lower */
1632 else if ((checkselection->upper > removeselection.upper) &&
1633 (checkselection->lower > removeselection.lower))
1635 checkselection->lower = removeselection.upper + 1;
1637 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1639 /* case 5: fully internal */
1640 else if (checkselection->upper == removeselection.upper)
1641 checkselection->upper = removeselection.lower - 1;
1642 else if (checkselection->lower == removeselection.lower)
1643 checkselection->lower = removeselection.upper + 1;
1646 /* bisect the range */
1647 LISTVIEW_SELECTION *newselection;
1649 newselection = (LISTVIEW_SELECTION *)
1650 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1651 newselection -> lower = checkselection->lower;
1652 newselection -> upper = removeselection.lower - 1;
1653 checkselection -> lower = removeselection.upper + 1;
1654 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1656 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1658 LISTVIEW_PrintSelectionRanges(hwnd);
1663 * Updates the various indices after an item has been inserted or deleted.
1666 * [I] HWND : window handle
1667 * [I] INT : item index
1668 * [I] INT : Direction of shift, +1 or -1.
1673 static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
1675 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1676 LISTVIEW_SELECTION selection,*checkselection;
1679 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1681 selection.upper = nItem;
1682 selection.lower = nItem;
1684 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1685 LISTVIEW_CompareSelectionRanges,
1686 0,DPAS_SORTED|DPAS_INSERTAFTER);
1688 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1690 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1691 if ((checkselection->lower >= nItem)&&
1692 (checkselection->lower + direction >= 0))
1693 checkselection->lower += direction;
1694 if ((checkselection->upper >= nItem)&&
1695 (checkselection->upper + direction >=0))
1696 checkselection->upper += direction;
1700 /* Note that the following will fail if direction != +1 and -1 */
1701 if (infoPtr->nSelectionMark > nItem)
1702 infoPtr->nSelectionMark += direction;
1703 else if (infoPtr->nSelectionMark == nItem)
1706 infoPtr->nSelectionMark += direction;
1707 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1708 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1711 if (infoPtr->nFocusedItem > nItem)
1712 infoPtr->nFocusedItem += direction;
1713 else if (infoPtr->nFocusedItem == nItem)
1716 infoPtr->nFocusedItem += direction;
1719 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1720 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1721 if (infoPtr->nFocusedItem >= 0)
1722 LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
1725 /* But we are not supposed to modify nHotItem! */
1731 * Adds a block of selections.
1734 * [I] HWND : window handle
1735 * [I] INT : item index
1740 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1742 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1743 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1744 INT nLast = max(infoPtr->nSelectionMark, nItem);
1751 ZeroMemory(&item,sizeof(item));
1752 item.stateMask = LVIS_SELECTED;
1753 item.state = LVIS_SELECTED;
1755 for (i = nFirst; i <= nLast; i++)
1756 LISTVIEW_SetItemState(hwnd,i,&item);
1758 LISTVIEW_SetItemFocus(hwnd, nItem);
1759 infoPtr->nSelectionMark = nItem;
1765 * Adds a single selection.
1768 * [I] HWND : window handle
1769 * [I] INT : item index
1774 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1776 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1779 ZeroMemory(&item,sizeof(item));
1780 item.state = LVIS_SELECTED;
1781 item.stateMask = LVIS_SELECTED;
1783 LISTVIEW_SetItemState(hwnd,nItem,&item);
1785 LISTVIEW_SetItemFocus(hwnd, nItem);
1786 infoPtr->nSelectionMark = nItem;
1791 * Selects or unselects an item.
1794 * [I] HWND : window handle
1795 * [I] INT : item index
1801 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1803 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1807 ZeroMemory(&item,sizeof(item));
1808 item.stateMask = LVIS_SELECTED;
1810 if (LISTVIEW_IsSelected(hwnd,nItem))
1812 LISTVIEW_SetItemState(hwnd,nItem,&item);
1817 item.state = LVIS_SELECTED;
1818 LISTVIEW_SetItemState(hwnd,nItem,&item);
1822 LISTVIEW_SetItemFocus(hwnd, nItem);
1823 infoPtr->nSelectionMark = nItem;
1830 * Selects items based on view coordinates.
1833 * [I] HWND : window handle
1834 * [I] RECT : selection rectangle
1839 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
1841 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1846 ZeroMemory(&item,sizeof(item));
1847 item.stateMask = LVIS_SELECTED;
1849 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1851 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
1853 if (PtInRect(&rcSelRect, ptItem) != FALSE)
1854 item.state = LVIS_SELECTED;
1857 LISTVIEW_SetItemState(hwnd,i,&item);
1863 * Sets a single group selection.
1866 * [I] HWND : window handle
1867 * [I] INT : item index
1872 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
1874 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1875 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1878 ZeroMemory(&item,sizeof(item));
1879 item.stateMask = LVIS_SELECTED;
1881 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
1886 if (infoPtr->nSelectionMark == -1)
1888 infoPtr->nSelectionMark = nFirst = nLast = nItem;
1892 nFirst = min(infoPtr->nSelectionMark, nItem);
1893 nLast = max(infoPtr->nSelectionMark, nItem);
1896 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
1898 if ((i < nFirst) || (i > nLast))
1901 item.state = LVIS_SELECTED;
1902 LISTVIEW_SetItemState(hwnd,i,&item);
1910 LISTVIEW_GetItemPosition(hwnd, nItem, &ptItem);
1911 LISTVIEW_GetItemPosition(hwnd, infoPtr->nSelectionMark, &ptSelMark);
1912 rcSel.left = min(ptSelMark.x, ptItem.x);
1913 rcSel.top = min(ptSelMark.y, ptItem.y);
1914 rcSel.right = max(ptSelMark.x, ptItem.x) + infoPtr->nItemWidth;
1915 rcSel.bottom = max(ptSelMark.y, ptItem.y) + infoPtr->nItemHeight;
1916 LISTVIEW_SetSelectionRect(hwnd, rcSel);
1919 LISTVIEW_SetItemFocus(hwnd, nItem);
1924 * Manages the item focus.
1927 * [I] HWND : window handle
1928 * [I] INT : item index
1931 * TRUE : focused item changed
1932 * FALSE : focused item has NOT changed
1934 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
1936 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1937 BOOL bResult = FALSE;
1940 if (infoPtr->nFocusedItem != nItem)
1942 if (infoPtr->nFocusedItem >= 0)
1944 INT oldFocus = infoPtr->nFocusedItem;
1946 infoPtr->nFocusedItem = -1;
1947 ZeroMemory(&lvItem, sizeof(lvItem));
1948 lvItem.stateMask = LVIS_FOCUSED;
1949 ListView_SetItemState(hwnd, oldFocus, &lvItem);
1953 lvItem.state = LVIS_FOCUSED;
1954 lvItem.stateMask = LVIS_FOCUSED;
1955 ListView_SetItemState(hwnd, nItem, &lvItem);
1957 infoPtr->nFocusedItem = nItem;
1958 ListView_EnsureVisible(hwnd, nItem, FALSE);
1966 * Sets a single selection.
1969 * [I] HWND : window handle
1970 * [I] INT : item index
1975 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
1977 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1980 ZeroMemory(&lvItem, sizeof(lvItem));
1981 lvItem.stateMask = LVIS_FOCUSED;
1982 ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
1984 LISTVIEW_RemoveAllSelections(hwnd);
1986 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
1987 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
1988 ListView_SetItemState(hwnd, nItem, &lvItem);
1990 infoPtr->nFocusedItem = nItem;
1991 infoPtr->nSelectionMark = nItem;
1996 * Set selection(s) with keyboard.
1999 * [I] HWND : window handle
2000 * [I] INT : item index
2003 * SUCCESS : TRUE (needs to be repainted)
2004 * FAILURE : FALSE (nothing has changed)
2006 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
2008 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2009 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2010 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2011 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2012 BOOL bResult = FALSE;
2014 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2016 if (lStyle & LVS_SINGLESEL)
2019 LISTVIEW_SetSelection(hwnd, nItem);
2020 ListView_EnsureVisible(hwnd, nItem, FALSE);
2027 LISTVIEW_SetGroupSelection(hwnd, nItem);
2031 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
2036 LISTVIEW_SetSelection(hwnd, nItem);
2037 ListView_EnsureVisible(hwnd, nItem, FALSE);
2047 * Called when the mouse is being actively tracked and has hovered for a specified
2051 * [I] HWND : window handle
2052 * [I] wParam : key indicator
2053 * [I] lParam : mouse position
2056 * 0 if the message was processed, non-zero if there was an error
2059 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2060 * over the item for a certain period of time.
2063 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
2065 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2068 pt.x = (INT)LOWORD(lParam);
2069 pt.y = (INT)HIWORD(lParam);
2071 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2072 /* select the item under the cursor */
2073 LISTVIEW_MouseSelection(hwnd, pt);
2081 * Called whenever WM_MOUSEMOVE is received.
2084 * [I] HWND : window handle
2085 * [I] wParam : key indicators
2086 * [I] lParam : cursor position
2089 * 0 if the message is processed, non-zero if there was an error
2091 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
2093 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2094 TRACKMOUSEEVENT trackinfo;
2096 /* see if we are supposed to be tracking mouse hovering */
2097 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2098 /* fill in the trackinfo struct */
2099 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2100 trackinfo.dwFlags = TME_QUERY;
2101 trackinfo.hwndTrack = hwnd;
2102 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2104 /* see if we are already tracking this hwnd */
2105 _TrackMouseEvent(&trackinfo);
2107 if(!(trackinfo.dwFlags & TME_HOVER)) {
2108 trackinfo.dwFlags = TME_HOVER;
2110 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2111 _TrackMouseEvent(&trackinfo);
2120 * Selects an item based on coordinates.
2123 * [I] HWND : window handle
2124 * [I] POINT : mouse click ccordinates
2127 * SUCCESS : item index
2130 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
2132 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2134 INT i,topindex,bottomindex;
2135 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2136 UINT uView = lStyle & LVS_TYPEMASK;
2138 topindex = ListView_GetTopIndex(hwnd);
2139 if (uView == LVS_REPORT)
2141 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
2142 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
2146 bottomindex = GETITEMCOUNT(infoPtr);
2149 for (i = topindex; i < bottomindex; i++)
2151 rcItem.left = LVIR_SELECTBOUNDS;
2152 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
2154 if (PtInRect(&rcItem, pt) != FALSE)
2169 * [IO] HDPA : dynamic pointer array handle
2170 * [I] INT : column index (subitem index)
2176 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2178 BOOL bResult = TRUE;
2182 for (i = 0; i < hdpaItems->nItemCount; i++)
2184 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2185 if (hdpaSubItems != NULL)
2187 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2199 * Removes a subitem at a given position.
2202 * [IO] HDPA : dynamic pointer array handle
2203 * [I] INT : subitem index
2209 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2211 LISTVIEW_SUBITEM *lpSubItem;
2214 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2216 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2217 if (lpSubItem != NULL)
2219 if (lpSubItem->iSubItem == nSubItem)
2222 if (is_textW(lpSubItem->pszText))
2223 COMCTL32_Free(lpSubItem->pszText);
2226 COMCTL32_Free(lpSubItem);
2228 /* free dpa memory */
2229 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2232 else if (lpSubItem->iSubItem > nSubItem)
2242 * Compares the item information.
2245 * [I] LISTVIEW_ITEM *: destination item
2246 * [I] LPLVITEM : source item
2247 * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2250 * SUCCCESS : TRUE (EQUAL)
2251 * FAILURE : FALSE (NOT EQUAL)
2253 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2257 if ((lpItem != NULL) && (lpLVItem != NULL))
2259 if (lpLVItem->mask & LVIF_STATE)
2261 if ((lpItem->state & lpLVItem->stateMask) !=
2262 (lpLVItem->state & lpLVItem->stateMask))
2263 uChanged |= LVIF_STATE;
2266 if (lpLVItem->mask & LVIF_IMAGE)
2268 if (lpItem->iImage != lpLVItem->iImage)
2269 uChanged |= LVIF_IMAGE;
2272 if (lpLVItem->mask & LVIF_PARAM)
2274 if (lpItem->lParam != lpLVItem->lParam)
2275 uChanged |= LVIF_PARAM;
2278 if (lpLVItem->mask & LVIF_INDENT)
2280 if (lpItem->iIndent != lpLVItem->iIndent)
2281 uChanged |= LVIF_INDENT;
2284 if (lpLVItem->mask & LVIF_TEXT)
2286 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2288 if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2289 uChanged |= LVIF_TEXT;
2293 if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2295 uChanged |= LVIF_TEXT;
2299 if (lpLVItem->pszText)
2301 if (lpItem->pszText)
2303 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2304 if (pszText && strcmpW(pszText, lpItem->pszText))
2305 uChanged |= LVIF_TEXT;
2306 textfreeT(pszText, isW);
2310 uChanged |= LVIF_TEXT;
2315 if (lpItem->pszText)
2316 uChanged |= LVIF_TEXT;
2327 * Initializes item attributes.
2330 * [I] HWND : window handle
2331 * [O] LISTVIEW_ITEM *: destination item
2332 * [I] LPLVITEM : source item
2333 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2339 static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem,
2340 LPLVITEMW lpLVItem, BOOL isW)
2342 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2343 BOOL bResult = FALSE;
2345 if ((lpItem != NULL) && (lpLVItem != NULL))
2349 if (lpLVItem->mask & LVIF_STATE)
2351 lpItem->state &= ~lpLVItem->stateMask;
2352 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2355 if (lpLVItem->mask & LVIF_IMAGE)
2356 lpItem->iImage = lpLVItem->iImage;
2358 if (lpLVItem->mask & LVIF_PARAM)
2359 lpItem->lParam = lpLVItem->lParam;
2361 if (lpLVItem->mask & LVIF_INDENT)
2362 lpItem->iIndent = lpLVItem->iIndent;
2364 if (lpLVItem->mask & LVIF_TEXT)
2366 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2368 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2371 if (is_textW(lpItem->pszText))
2372 COMCTL32_Free(lpItem->pszText);
2374 lpItem->pszText = LPSTR_TEXTCALLBACKW;
2378 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2379 if (lpItem->pszText == LPSTR_TEXTCALLBACKW) lpItem->pszText = NULL;
2380 bResult = Str_SetPtrW(&lpItem->pszText, pszText);
2381 textfreeT(pszText, isW);
2391 * Initializes subitem attributes.
2393 * NOTE: The documentation specifies that the operation fails if the user
2394 * tries to set the indent of a subitem.
2397 * [I] HWND : window handle
2398 * [O] LISTVIEW_SUBITEM *: destination subitem
2399 * [I] LPLVITEM : source subitem
2400 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2406 static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2407 LPLVITEMW lpLVItem, BOOL isW)
2409 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2410 BOOL bResult = FALSE;
2412 TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
2413 hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW);
2415 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2417 if (!(lpLVItem->mask & LVIF_INDENT))
2421 lpSubItem->iSubItem = lpLVItem->iSubItem;
2423 if (lpLVItem->mask & LVIF_IMAGE)
2424 lpSubItem->iImage = lpLVItem->iImage;
2426 if (lpLVItem->mask & LVIF_TEXT)
2428 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2430 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2433 if (is_textW(lpSubItem->pszText))
2434 COMCTL32_Free(lpSubItem->pszText);
2436 lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2440 LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2441 if(lpSubItem->pszText == LPSTR_TEXTCALLBACKW) lpSubItem->pszText=NULL;
2442 bResult = Str_SetPtrW(&lpSubItem->pszText, pszText);
2443 textfreeT(pszText, isW);
2454 * Adds a subitem at a given position (column index).
2457 * [I] HWND : window handle
2458 * [I] LPLVITEM : new subitem atttributes
2459 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2465 static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2467 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2468 LISTVIEW_SUBITEM *lpSubItem = NULL;
2469 BOOL bResult = FALSE;
2471 INT nPosition, nItem;
2472 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2474 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2476 if (lStyle & LVS_OWNERDATA)
2479 if (lpLVItem != NULL)
2481 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2482 if (hdpaSubItems != NULL)
2484 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2485 if (lpSubItem != NULL)
2487 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2488 if (LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW))
2490 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2491 lpSubItem->iSubItem);
2492 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2493 if (nItem != -1) bResult = TRUE;
2499 /* cleanup if unsuccessful */
2500 if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2507 * Finds the dpa insert position (array index).
2510 * [I] HWND : window handle
2511 * [I] INT : subitem index
2517 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2519 LISTVIEW_SUBITEM *lpSubItem;
2522 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2524 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2525 if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2529 return hdpaSubItems->nItemCount;
2534 * Retrieves a listview subitem at a given position (column index).
2537 * [I] HWND : window handle
2538 * [I] INT : subitem index
2544 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2546 LISTVIEW_SUBITEM *lpSubItem;
2549 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2551 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2552 if (lpSubItem != NULL)
2554 if (lpSubItem->iSubItem == nSubItem)
2556 else if (lpSubItem->iSubItem > nSubItem)
2566 * Sets item attributes.
2569 * [I] HWND : window handle
2570 * [I] LPLVITEM : new item atttributes
2571 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2577 static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2579 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2580 BOOL bResult = FALSE;
2582 LISTVIEW_ITEM *lpItem;
2584 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2586 UINT uView = lStyle & LVS_TYPEMASK;
2590 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2592 if (lStyle & LVS_OWNERDATA)
2594 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2598 ZeroMemory(&itm, sizeof(itm));
2599 itm.mask = LVIF_STATE | LVIF_PARAM;
2600 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2601 itm.iItem = lpLVItem->iItem;
2603 ListView_GetItemW(hwnd, &itm);
2606 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2607 nmlv.uNewState = lpLVItem->state;
2608 nmlv.uOldState = itm.state;
2609 nmlv.uChanged = LVIF_STATE;
2610 nmlv.lParam = itm.lParam;
2611 nmlv.iItem = lpLVItem->iItem;
2613 if ((itm.state & lpLVItem->stateMask) !=
2614 (lpLVItem->state & lpLVItem->stateMask))
2616 /* send LVN_ITEMCHANGING notification */
2617 if (!listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv))
2619 if (lpLVItem->stateMask & LVIS_FOCUSED)
2621 if (lpLVItem->state & LVIS_FOCUSED)
2622 infoPtr->nFocusedItem = lpLVItem->iItem;
2623 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2624 infoPtr->nFocusedItem = -1;
2626 if (lpLVItem->stateMask & LVIS_SELECTED)
2628 if (lpLVItem->state & LVIS_SELECTED)
2630 if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd);
2631 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2634 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2638 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2640 rcItem.left = LVIR_BOUNDS;
2641 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2642 InvalidateRect(hwnd, &rcItem, TRUE);
2650 if (lpLVItem != NULL)
2652 if (lpLVItem->iSubItem == 0)
2654 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2655 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2657 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2660 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2661 nmlv.lParam = lpItem->lParam;
2662 uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2665 if (uChanged & LVIF_STATE)
2667 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2668 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2670 if (nmlv.uNewState & LVIS_SELECTED)
2673 * This is redundant if called through SetSelection
2675 * however is required if the used directly calls SetItem
2676 * to set the selection.
2678 if (lStyle & LVS_SINGLESEL)
2679 LISTVIEW_RemoveAllSelections(hwnd);
2681 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2684 else if (lpLVItem->stateMask & LVIS_SELECTED)
2686 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2689 if (nmlv.uNewState & LVIS_FOCUSED)
2692 * This is a fun hoop to jump to try to catch if
2693 * the user is calling us directly to call focus or if
2694 * this function is being called as a result of a
2695 * SetItemFocus call.
2697 if (infoPtr->nFocusedItem >= 0)
2698 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2702 nmlv.uChanged = uChanged;
2703 nmlv.iItem = lpLVItem->iItem;
2704 nmlv.lParam = lpItem->lParam;
2705 /* send LVN_ITEMCHANGING notification */
2706 listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv);
2708 /* copy information */
2709 bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW);
2711 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2712 based on the width of the items text */
2713 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2715 item_width = LISTVIEW_GetStringWidthT(hwnd, lpItem->pszText, TRUE);
2717 if(item_width > infoPtr->nItemWidth)
2718 infoPtr->nItemWidth = item_width;
2721 /* send LVN_ITEMCHANGED notification */
2722 listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2731 rcItem.left = LVIR_BOUNDS;
2732 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2733 InvalidateRect(hwnd, &rcItem, TRUE);
2745 * Sets subitem attributes.
2748 * [I] HWND : window handle
2749 * [I] LPLVITEM : new subitem atttributes
2750 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2756 static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2758 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2759 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2760 BOOL bResult = FALSE;
2762 LISTVIEW_SUBITEM *lpSubItem;
2765 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2767 if (lStyle & LVS_OWNERDATA)
2770 if (lpLVItem != NULL)
2772 if (lpLVItem->iSubItem > 0)
2774 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2775 if (hdpaSubItems != NULL)
2777 /* set subitem only if column is present */
2778 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2780 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2781 if (lpSubItem != NULL)
2782 bResult = LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW);
2784 bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW);
2786 rcItem.left = LVIR_BOUNDS;
2787 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2788 InvalidateRect(hwnd, &rcItem, FALSE);
2799 * Sets item attributes.
2802 * [I] HWND : window handle
2803 * [I] LPLVITEM : new item atttributes
2804 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2810 static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2812 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2814 if (!lpLVItem || lpLVItem->iItem < 0 ||
2815 lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2817 if (lpLVItem->iSubItem == 0)
2818 return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW);
2820 return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW);
2825 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2828 * [I] HWND : window handle
2833 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2835 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2836 UINT uView = lStyle & LVS_TYPEMASK;
2838 SCROLLINFO scrollInfo;
2840 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2841 scrollInfo.cbSize = sizeof(SCROLLINFO);
2842 scrollInfo.fMask = SIF_POS;
2844 if (uView == LVS_LIST)
2846 if ((lStyle & WS_HSCROLL) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo))
2847 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2849 else if (uView == LVS_REPORT)
2851 if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo))
2852 nItem = scrollInfo.nPos;
2863 * [I] HWND : window handle
2864 * [I] HDC : device context handle
2865 * [I] INT : item index
2866 * [I] INT : subitem index
2867 * [I] RECT * : clipping rectangle
2872 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
2873 RECT rcItem, BOOL Selected)
2875 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2876 WCHAR szDispText[DISP_TEXT_SIZE];
2878 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
2881 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
2884 /* get information needed for drawing the item */
2885 ZeroMemory(&lvItem, sizeof(lvItem));
2886 lvItem.mask = LVIF_TEXT;
2887 lvItem.iItem = nItem;
2888 lvItem.iSubItem = nSubItem;
2889 lvItem.cchTextMax = DISP_TEXT_SIZE;
2890 lvItem.pszText = szDispText;
2891 *lvItem.pszText = '\0';
2892 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
2893 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
2895 /* redraw the background of the item */
2897 if(infoPtr->nColumnCount == (nSubItem + 1))
2898 rcTemp.right = infoPtr->rcList.right;
2900 rcTemp.right += WIDTH_PADDING;
2902 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
2904 /* set item colors */
2905 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
2907 if (infoPtr->bFocus)
2909 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
2910 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2914 SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
2915 SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
2920 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
2922 SetBkMode(hdc, TRANSPARENT);
2923 textoutOptions &= ~ETO_OPAQUE;
2927 SetBkMode(hdc, OPAQUE);
2928 SetBkColor(hdc, infoPtr->clrTextBk);
2931 SetTextColor(hdc, infoPtr->clrText);
2934 ExtTextOutW(hdc, rcItem.left, rcItem.top, textoutOptions,
2935 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
2939 /* fill in the gap */
2941 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
2943 CopyRect(&rec,&rcItem);
2944 rec.left = rec.right;
2945 rec.right = rec.left+REPORT_MARGINX;
2946 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
2947 &rec, NULL, 0, NULL);
2949 CopyRect(&rec,&rcItem);
2950 rec.right = rec.left;
2951 rec.left = rec.left - REPORT_MARGINX;
2952 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
2953 &rec, NULL, 0, NULL);
2963 * [I] HWND : window handle
2964 * [I] HDC : device context handle
2965 * [I] INT : item index
2966 * [I] RECT * : clipping rectangle
2971 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
2973 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2974 WCHAR szDispText[DISP_TEXT_SIZE];
2979 DWORD dwTextColor,dwTextX;
2980 BOOL bImage = FALSE;
2982 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
2985 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
2988 /* get information needed for drawing the item */
2989 ZeroMemory(&lvItem, sizeof(lvItem));
2990 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
2991 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
2992 lvItem.iItem = nItem;
2993 lvItem.iSubItem = 0;
2994 lvItem.cchTextMax = DISP_TEXT_SIZE;
2995 lvItem.pszText = szDispText;
2996 *lvItem.pszText = '\0';
2997 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
2998 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3000 /* redraw the background of the item */
3002 if(infoPtr->nColumnCount == (nItem + 1))
3003 rcTemp.right = infoPtr->rcList.right;
3005 rcTemp.right+=WIDTH_PADDING;
3007 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3010 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3012 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3015 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3019 if (infoPtr->himlState != NULL)
3021 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3022 if (uStateImage > 0)
3024 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3025 rcItem.top, ILD_NORMAL);
3028 rcItem.left += infoPtr->iconSize.cx;
3030 SuggestedFocus->left += infoPtr->iconSize.cx;
3035 if (infoPtr->himlSmall != NULL)
3037 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
3040 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3041 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3042 rcItem.top, ILD_SELECTED);
3044 else if (lvItem.iImage>=0)
3046 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3047 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3048 rcItem.top, ILD_NORMAL);
3051 rcItem.left += infoPtr->iconSize.cx;
3054 SuggestedFocus->left += infoPtr->iconSize.cx;
3058 /* Don't bother painting item being edited */
3059 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
3062 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
3064 /* set item colors */
3065 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3066 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3067 /* set raster mode */
3068 nMixMode = SetROP2(hdc, R2_XORPEN);
3070 else if ((GetWindowLongW(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3071 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
3073 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
3074 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
3075 /* set raster mode */
3076 nMixMode = SetROP2(hdc, R2_COPYPEN);
3080 /* set item colors */
3081 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3083 dwBkColor = GetBkColor(hdc);
3084 iBkMode = SetBkMode(hdc, TRANSPARENT);
3085 textoutOptions &= ~ETO_OPAQUE;
3089 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3090 iBkMode = SetBkMode(hdc, OPAQUE);
3093 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3094 /* set raster mode */
3095 nMixMode = SetROP2(hdc, R2_COPYPEN);
3098 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
3099 if (rcItem.left + nLabelWidth < rcItem.right)
3102 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3104 rcItem.right += IMAGE_PADDING;
3108 dwTextX = rcItem.left + 1;
3110 dwTextX += IMAGE_PADDING;
3113 ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3114 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3116 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3118 /* fill in the gap */
3120 CopyRect(&rec,&rcItem);
3121 rec.left = rec.right;
3122 rec.right = rec.left+REPORT_MARGINX;
3123 ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3124 &rec, NULL, 0, NULL);
3128 CopyRect(SuggestedFocus,&rcItem);
3132 SetROP2(hdc, R2_COPYPEN);
3133 SetBkColor(hdc, dwBkColor);
3134 SetTextColor(hdc, dwTextColor);
3136 SetBkMode(hdc, iBkMode);
3142 * Draws an item when in large icon display mode.
3145 * [I] HWND : window handle
3146 * [I] HDC : device context handle
3147 * [I] LISTVIEW_ITEM * : item
3148 * [I] INT : item index
3149 * [I] RECT * : clipping rectangle
3154 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3155 RECT *SuggestedFocus)
3157 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3158 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
3159 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3160 INT nDrawPosX = rcItem.left;
3161 INT nLabelWidth, rcWidth;
3166 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3167 hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3169 /* get information needed for drawing the item */
3170 ZeroMemory(&lvItem, sizeof(lvItem));
3171 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3172 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3173 lvItem.iItem = nItem;
3174 lvItem.iSubItem = 0;
3175 lvItem.cchTextMax = DISP_TEXT_SIZE;
3176 lvItem.pszText = szDispText;
3177 *lvItem.pszText = '\0';
3178 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3179 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3181 /* redraw the background of the item */
3183 if(infoPtr->nColumnCount == (nItem + 1))
3184 rcTemp.right = infoPtr->rcList.right;
3186 rcTemp.right+=WIDTH_PADDING;
3188 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3190 if (lvItem.state & LVIS_SELECTED)
3192 /* set item colors */
3193 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3194 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3195 /* set raster mode */
3196 SetROP2(hdc, R2_XORPEN);
3200 /* set item colors */
3201 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3203 SetBkMode(hdc, TRANSPARENT);
3204 textoutOptions &= ~ETO_OPAQUE;
3208 SetBkMode(hdc, OPAQUE);
3209 SetBkColor(hdc, infoPtr->clrTextBk);
3212 SetTextColor(hdc, infoPtr->clrText);
3213 /* set raster mode */
3214 SetROP2(hdc, R2_COPYPEN);
3217 if (infoPtr->himlNormal != NULL)
3219 rcItem.top += ICON_TOP_PADDING;
3220 nDrawPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
3221 if ((lvItem.state & LVIS_SELECTED) && (lvItem.iImage>=0))
3223 ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX,
3224 rcItem.top, ILD_SELECTED);
3226 else if (lvItem.iImage>=0)
3228 ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX,
3229 rcItem.top, ILD_NORMAL);
3233 /* Don't bother painting item being edited */
3234 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED)
3237 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3238 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3239 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
3240 GetTextMetricsW(hdc, &tm);
3242 /* append an ellipse ('...') if the caption won't fit in the rect */
3243 rcWidth = max(0, rcItem.right - rcItem.left);
3244 if (nLabelWidth > rcWidth)
3246 /* give or take a couple, how many average sized chars would fit? */
3247 INT nCharsFit = tm.tmAveCharWidth > 0 ? rcWidth/tm.tmAveCharWidth+2 : 0;
3248 INT eos = min(strlenW(lvItem.pszText), nCharsFit);
3249 if (eos > 0) lvItem.pszText[eos-1] = '.';
3250 if (eos > 1) lvItem.pszText[eos-2] = '.';
3251 if (eos < 3) lvItem.pszText[eos] = '\0';
3252 for ( ; eos > 2; eos--)
3254 lvItem.pszText[eos - 3] = '.';
3255 lvItem.pszText[eos] = '\0';
3256 if (ListView_GetStringWidthW(hwnd, lvItem.pszText) <= rcWidth) break;
3260 InflateRect(&rcItem, 2*CAPTION_BORDER, 0);
3261 nDrawPosX = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
3264 rcItem.left += nDrawPosX / 2;
3265 rcItem.right = rcItem.left + nLabelWidth + 2*CAPTION_BORDER;
3270 rcItem.right = rcItem.left + infoPtr->iconSpacing.cx - 1;
3274 rcItem.bottom = rcItem.top + tm.tmHeight + HEIGHT_PADDING;
3276 ExtTextOutW(hdc, rcItem.left + CAPTION_BORDER, rcItem.top, textoutOptions,
3277 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3280 CopyRect(SuggestedFocus,&rcItem);
3285 * Draws listview items when in report display mode.
3288 * [I] HWND : window handle
3289 * [I] HDC : device context handle
3294 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3296 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3297 SCROLLINFO scrollInfo;
3298 INT nDrawPosY = infoPtr->rcList.top;
3300 RECT rcItem, rcTemp;
3305 DWORD cditemmode = CDRF_DODEFAULT;
3306 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3309 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3310 scrollInfo.cbSize = sizeof(SCROLLINFO);
3311 scrollInfo.fMask = SIF_POS;
3313 nItem = ListView_GetTopIndex(hwnd);
3315 /* add 1 for displaying a partial item at the bottom */
3316 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3317 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3319 /* send cache hint notification */
3320 if (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3324 nmlv.hdr.hwndFrom = hwnd;
3325 nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID);
3326 nmlv.hdr.code = LVN_ODCACHEHINT;
3330 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3334 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3335 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3336 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3338 /* clear the background of any part of the control that doesn't contain items */
3339 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3340 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3342 /* nothing to draw */
3343 if(GETITEMCOUNT(infoPtr) == 0)
3346 /* Get scroll bar info once before loop */
3347 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3348 scrollOffset = scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
3350 for (; nItem < nLast; nItem++)
3352 RECT SuggestedFocusRect;
3355 if (lStyle & LVS_OWNERDRAWFIXED)
3357 UINT uID = GetWindowLongW( hwnd, GWL_ID);
3362 TRACE("Owner Drawn\n");
3363 dis.CtlType = ODT_LISTVIEW;
3366 dis.itemAction = ODA_DRAWENTIRE;
3369 if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3370 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3372 dis.hwndItem = hwnd;
3375 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3377 dis.rcItem.left = -scrollOffset;
3378 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3379 dis.rcItem.top = nDrawPosY;
3380 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3382 ZeroMemory(&item,sizeof(item));
3384 item.mask = LVIF_PARAM;
3385 ListView_GetItemW(hwnd, &item);
3387 dis.itemData = item.lParam;
3389 if (SendMessageW(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3391 nDrawPosY += infoPtr->nItemHeight;
3400 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3401 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3403 ir.left += REPORT_MARGINX;
3404 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3406 ir.bottom = ir.top + infoPtr->nItemHeight;
3408 CopyRect(&SuggestedFocusRect,&ir);
3411 for (j = 0; j < nColumnCount; j++)
3413 if (cdmode & CDRF_NOTIFYITEMDRAW)
3414 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3416 if (cditemmode & CDRF_SKIPDEFAULT)
3419 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3421 rcItem.left += REPORT_MARGINX;
3422 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3423 rcItem.top = nDrawPosY;
3424 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3426 /* Offset the Scroll Bar Pos */
3427 rcItem.left -= scrollOffset;
3428 rcItem.right -= scrollOffset;
3432 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3433 &SuggestedFocusRect);
3437 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, FullSelected);
3440 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3441 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3442 CDDS_ITEMPOSTPAINT);
3447 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3450 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3451 rop = SetROP2(hdc, R2_XORPEN);
3453 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3454 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3457 SetROP2(hdc, R2_COPYPEN);
3459 nDrawPosY += infoPtr->nItemHeight;
3465 * Retrieves the number of items that can fit vertically in the client area.
3468 * [I] HWND : window handle
3471 * Number of items per row.
3473 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3475 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3476 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3477 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3478 INT nCountPerRow = 1;
3482 if (uView != LVS_REPORT)
3484 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3485 if (nCountPerRow == 0) nCountPerRow = 1;
3489 return nCountPerRow;
3494 * Retrieves the number of items that can fit horizontally in the client
3498 * [I] HWND : window handle
3501 * Number of items per column.
3503 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3505 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3506 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3507 INT nCountPerColumn = 1;
3509 if (nListHeight > 0)
3511 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3512 if (nCountPerColumn == 0) nCountPerColumn = 1;
3515 return nCountPerColumn;
3520 * Retrieves the number of columns needed to display all the items when in
3521 * list display mode.
3524 * [I] HWND : window handle
3527 * Number of columns.
3529 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3531 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3532 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3533 INT nColumnCount = 0;
3535 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3537 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3538 if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3541 return nColumnCount;
3547 * Draws listview items when in list display mode.
3550 * [I] HWND : window handle
3551 * [I] HDC : device context handle
3556 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3558 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3559 RECT rcItem, FocusRect, rcTemp;
3563 INT nCountPerColumn;
3564 INT nItemWidth = infoPtr->nItemWidth;
3565 INT nItemHeight = infoPtr->nItemHeight;
3566 DWORD cditemmode = CDRF_DODEFAULT;
3568 /* get number of fully visible columns */
3569 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3570 infoPtr->nColumnCount = nColumnCount;
3571 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3572 nItem = ListView_GetTopIndex(hwnd);
3574 /* paint the background of the control that doesn't contain any items */
3575 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3576 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3578 /* nothing to draw, return here */
3579 if(GETITEMCOUNT(infoPtr) == 0)
3582 for (i = 0; i < nColumnCount; i++)
3584 for (j = 0; j < nCountPerColumn; j++, nItem++)
3586 if (nItem >= GETITEMCOUNT(infoPtr))
3589 if (cdmode & CDRF_NOTIFYITEMDRAW)
3590 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3592 if (cditemmode & CDRF_SKIPDEFAULT)
3595 rcItem.top = j * nItemHeight;
3596 rcItem.left = i * nItemWidth;
3597 rcItem.bottom = rcItem.top + nItemHeight;
3598 rcItem.right = rcItem.left + nItemWidth;
3599 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3603 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3604 Rectangle(hdc, FocusRect.left, FocusRect.top,
3605 FocusRect.right,FocusRect.bottom);
3607 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3608 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3609 CDDS_ITEMPOSTPAINT);
3617 * Draws listview items when in icon or small icon display mode.
3620 * [I] HWND : window handle
3621 * [I] HDC : device context handle
3626 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3628 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3631 RECT rcItem, SuggestedFocus, rcTemp;
3633 DWORD cditemmode = CDRF_DODEFAULT;
3635 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3636 /* DrawItem from erasing the incorrect background area */
3638 /* paint the background of the control that doesn't contain any items */
3639 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3640 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3642 /* nothing to draw, return here */
3643 if(GETITEMCOUNT(infoPtr) == 0)
3646 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3647 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3649 if (cdmode & CDRF_NOTIFYITEMDRAW)
3650 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3652 if (cditemmode & CDRF_SKIPDEFAULT)
3655 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3656 ptPosition.x += ptOrigin.x;
3657 ptPosition.y += ptOrigin.y;
3659 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3661 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3663 if (ptPosition.y < infoPtr->rcList.bottom)
3665 if (ptPosition.x < infoPtr->rcList.right)
3667 rcItem.top = ptPosition.y;
3668 rcItem.left = ptPosition.x;
3669 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3670 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3672 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3674 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3678 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3680 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3681 SuggestedFocus.right,SuggestedFocus.bottom);
3686 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3687 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3688 CDDS_ITEMPOSTPAINT);
3694 * Draws listview items.
3697 * [I] HWND : window handle
3698 * [I] HDC : device context handle
3703 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3705 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3706 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3712 GetClientRect(hwnd, &rect);
3713 cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3715 if (cdmode == CDRF_SKIPDEFAULT) return;
3718 hOldFont = SelectObject(hdc, infoPtr->hFont);
3720 /* select the dotted pen (for drawing the focus box) */
3721 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3722 hOldPen = SelectObject(hdc, hPen);
3724 /* select transparent brush (for drawing the focus box) */
3725 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3727 if (uView == LVS_LIST)
3728 LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3729 else if (uView == LVS_REPORT)
3730 LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3731 else if (uView == LVS_SMALLICON)
3732 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3733 else if (uView == LVS_ICON)
3734 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3736 /* unselect objects */
3737 SelectObject(hdc, hOldFont);
3738 SelectObject(hdc, hOldPen);
3743 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3744 LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
3750 * Calculates the approximate width and height of a given number of items.
3753 * [I] HWND : window handle
3754 * [I] INT : number of items
3759 * Returns a DWORD. The width in the low word and the height in high word.
3761 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
3762 WORD wWidth, WORD wHeight)
3764 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3765 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3766 INT nItemCountPerColumn = 1;
3767 INT nColumnCount = 0;
3768 DWORD dwViewRect = 0;
3770 if (nItemCount == -1)
3771 nItemCount = GETITEMCOUNT(infoPtr);
3773 if (uView == LVS_LIST)
3775 if (wHeight == 0xFFFF)
3777 /* use current height */
3778 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3781 if (wHeight < infoPtr->nItemHeight)
3782 wHeight = infoPtr->nItemHeight;
3786 if (infoPtr->nItemHeight > 0)
3788 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3789 if (nItemCountPerColumn == 0)
3790 nItemCountPerColumn = 1;
3792 if (nItemCount % nItemCountPerColumn != 0)
3793 nColumnCount = nItemCount / nItemCountPerColumn;
3795 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3799 /* Microsoft padding magic */
3800 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3801 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3803 dwViewRect = MAKELONG(wWidth, wHeight);
3805 else if (uView == LVS_REPORT)
3806 FIXME("uView == LVS_REPORT: not implemented\n");
3807 else if (uView == LVS_SMALLICON)
3808 FIXME("uView == LVS_SMALLICON: not implemented\n");
3809 else if (uView == LVS_ICON)
3810 FIXME("uView == LVS_ICON: not implemented\n");
3817 * Arranges listview items in icon display mode.
3820 * [I] HWND : window handle
3821 * [I] INT : alignment code
3827 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
3829 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3830 BOOL bResult = FALSE;
3832 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3837 FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
3840 FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
3843 FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
3845 case LVA_SNAPTOGRID:
3846 FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
3854 /* << LISTVIEW_CreateDragImage >> */
3859 * Removes all listview items and subitems.
3862 * [I] HWND : window handle
3868 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
3870 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3871 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3872 UINT uView = lStyle & LVS_TYPEMASK;
3873 LISTVIEW_ITEM *lpItem;
3874 LISTVIEW_SUBITEM *lpSubItem;
3877 BOOL bResult = FALSE;
3880 TRACE("(hwnd=%x,)\n", hwnd);
3882 LISTVIEW_RemoveAllSelections(hwnd);
3883 infoPtr->nSelectionMark=-1;
3884 infoPtr->nFocusedItem=-1;
3885 /* But we are supposed to leave nHotItem as is! */
3887 if (lStyle & LVS_OWNERDATA)
3889 infoPtr->hdpaItems->nItemCount = 0;
3890 InvalidateRect(hwnd, NULL, TRUE);
3894 if (GETITEMCOUNT(infoPtr) > 0)
3898 /* send LVN_DELETEALLITEMS notification */
3899 /* verify if subsequent LVN_DELETEITEM notifications should be
3901 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3903 bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &nmlv);
3905 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3907 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3908 if (hdpaSubItems != NULL)
3910 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3912 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3913 if (lpSubItem != NULL)
3915 /* free subitem string */
3916 if (is_textW(lpSubItem->pszText))
3917 COMCTL32_Free(lpSubItem->pszText);
3920 COMCTL32_Free(lpSubItem);
3924 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3929 /* send LVN_DELETEITEM notification */
3931 nmlv.lParam = lpItem->lParam;
3932 listview_notify(hwnd, LVN_DELETEITEM, &nmlv);
3935 /* free item string */
3936 if (is_textW(lpItem->pszText))
3937 COMCTL32_Free(lpItem->pszText);
3940 COMCTL32_Free(lpItem);
3943 DPA_Destroy(hdpaSubItems);
3947 /* reinitialize listview memory */
3948 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3950 /* align items (set position of each item) */
3951 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3953 if (lStyle & LVS_ALIGNLEFT)
3955 LISTVIEW_AlignLeft(hwnd);
3959 LISTVIEW_AlignTop(hwnd);
3963 LISTVIEW_UpdateScroll(hwnd);
3965 /* invalidate client area (optimization needed) */
3966 InvalidateRect(hwnd, NULL, TRUE);
3974 * Removes a column from the listview control.
3977 * [I] HWND : window handle
3978 * [I] INT : column index
3984 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
3986 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3987 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3988 UINT uOwnerData = GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA;
3989 BOOL bResult = FALSE;
3991 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
3994 bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
3996 /* Need to reset the item width when deleting a column */
3997 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
3999 /* reset scroll parameters */
4000 if (uView == LVS_REPORT)
4002 /* update scrollbar(s) */
4003 LISTVIEW_UpdateScroll(hwnd);
4005 /* refresh client area */
4006 InvalidateRect(hwnd, NULL, FALSE);
4015 * Removes an item from the listview control.
4018 * [I] HWND : window handle
4019 * [I] INT : item index
4025 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
4027 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4028 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4029 UINT uView = lStyle & LVS_TYPEMASK;
4030 LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID);
4032 BOOL bResult = FALSE;
4034 LISTVIEW_ITEM *lpItem;
4035 LISTVIEW_SUBITEM *lpSubItem;
4039 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
4042 /* First, send LVN_DELETEITEM notification. */
4043 memset(&nmlv, 0, sizeof (NMLISTVIEW));
4044 nmlv.hdr.hwndFrom = hwnd;
4045 nmlv.hdr.idFrom = lCtrlId;
4046 nmlv.hdr.code = LVN_DELETEITEM;
4048 SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
4052 /* remove it from the selection range */
4053 ZeroMemory(&item,sizeof(item));
4054 item.stateMask = LVIS_SELECTED;
4055 LISTVIEW_SetItemState(hwnd,nItem,&item);
4057 if (lStyle & LVS_OWNERDATA)
4059 infoPtr->hdpaItems->nItemCount --;
4060 InvalidateRect(hwnd, NULL, TRUE);
4064 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4066 /* initialize memory */
4067 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4069 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4070 if (hdpaSubItems != NULL)
4072 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4074 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4075 if (lpSubItem != NULL)
4077 /* free item string */
4078 if (is_textW(lpSubItem->pszText))
4079 COMCTL32_Free(lpSubItem->pszText);
4082 COMCTL32_Free(lpSubItem);
4086 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4089 /* free item string */
4090 if (is_textW(lpItem->pszText))
4091 COMCTL32_Free(lpItem->pszText);
4094 COMCTL32_Free(lpItem);
4097 bResult = DPA_Destroy(hdpaSubItems);
4100 LISTVIEW_ShiftIndices(hwnd,nItem,-1);
4102 /* align items (set position of each item) */
4103 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4105 if (lStyle & LVS_ALIGNLEFT)
4106 LISTVIEW_AlignLeft(hwnd);
4108 LISTVIEW_AlignTop(hwnd);
4111 LISTVIEW_UpdateScroll(hwnd);
4113 /* refresh client area */
4114 InvalidateRect(hwnd, NULL, TRUE);
4123 * Return edit control handle of current edit label
4126 * [I] HWND : window handle
4132 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4134 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4135 return infoPtr->hwndEdit;
4141 * Callback implementation for editlabel control
4144 * [I] HWND : window handle
4145 * [I] LPSTR : modified text
4146 * [I] DWORD : item index
4152 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem)
4154 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4155 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4156 NMLVDISPINFOW dispInfo;
4157 LISTVIEW_ITEM *lpItem;
4159 LISTVIEW_ITEM lvItemRef;
4162 if (!(lStyle & LVS_OWNERDATA))
4164 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4167 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4172 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4173 ZeroMemory(&item,sizeof(item));
4176 item.mask = LVIF_PARAM | LVIF_STATE;
4177 ListView_GetItemW(hwnd, &item);
4178 lvItemRef.state = item.state;
4179 lvItemRef.iImage = item.iImage;
4180 lvItemRef.lParam = item.lParam;
4181 lpItem = &lvItemRef;
4184 ZeroMemory(&dispInfo, sizeof(dispInfo));
4185 dispInfo.item.mask = 0;
4186 dispInfo.item.iItem = nItem;
4187 dispInfo.item.state = lpItem->state;
4188 dispInfo.item.stateMask = 0;
4189 dispInfo.item.pszText = pszText;
4190 dispInfo.item.cchTextMax = pszText ? lstrlenW(pszText) : 0;
4191 dispInfo.item.iImage = lpItem->iImage;
4192 dispInfo.item.lParam = lpItem->lParam;
4193 infoPtr->hwndEdit = 0;
4195 /* Do we need to update the Item Text */
4196 if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, TRUE))
4197 if ((lpItem->pszText != LPSTR_TEXTCALLBACKW)&&(!(lStyle & LVS_OWNERDATA)))
4198 Str_SetPtrW(&lpItem->pszText, pszText);
4205 * Begin in place editing of specified list view item
4208 * [I] HWND : window handle
4209 * [I] INT : item index
4210 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4216 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW)
4218 NMLVDISPINFOW dispInfo;
4220 LISTVIEW_ITEM *lpItem;
4222 HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE);
4223 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4225 WCHAR szDispText[DISP_TEXT_SIZE];
4227 LISTVIEW_ITEM lvItemRef;
4228 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4230 if (~GetWindowLongW(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4233 /* Is the EditBox still there, if so remove it */
4234 if(infoPtr->hwndEdit != 0)
4237 LISTVIEW_SetSelection(hwnd, nItem);
4238 LISTVIEW_SetItemFocus(hwnd, nItem);
4240 if (!(lStyle & LVS_OWNERDATA))
4242 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4245 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4251 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4252 ZeroMemory(&item, sizeof(item));
4255 item.mask = LVIF_PARAM | LVIF_STATE;
4256 ListView_GetItemW(hwnd, &item);
4257 lvItemRef.iImage = item.iImage;
4258 lvItemRef.state = item.state;
4259 lvItemRef.lParam = item.lParam;
4260 lpItem = &lvItemRef;
4263 /* get information needed for drawing the item */
4264 ZeroMemory(&lvItem, sizeof(lvItem));
4265 lvItem.mask = LVIF_TEXT;
4266 lvItem.iItem = nItem;
4267 lvItem.iSubItem = 0;
4268 lvItem.cchTextMax = DISP_TEXT_SIZE;
4269 lvItem.pszText = szDispText;
4270 *lvItem.pszText = '\0';
4271 LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW);
4273 ZeroMemory(&dispInfo, sizeof(dispInfo));
4274 dispInfo.item.mask = 0;
4275 dispInfo.item.iItem = nItem;
4276 dispInfo.item.state = lpItem->state;
4277 dispInfo.item.stateMask = 0;
4278 dispInfo.item.pszText = lvItem.pszText;
4279 dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4280 dispInfo.item.iImage = lpItem->iImage;
4281 dispInfo.item.lParam = lpItem->lParam;
4283 if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW))
4286 rect.left = LVIR_LABEL;
4287 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4290 /* FIXME: if !isW, should we create a ASCII edit label instead
4291 * with a ASCII callback which should send ASCII notification msgs???
4293 if (!(hedit = CreateEditLabelW(szDispText , WS_VISIBLE,
4294 rect.left-2, rect.top-1, 0,
4295 rect.bottom - rect.top+2,
4296 hwnd, hinst, LISTVIEW_EndEditLabelW, nItem)))
4299 infoPtr->hwndEdit = hedit;
4301 SendMessageW(hedit, EM_SETSEL, 0, -1);
4309 * Ensures the specified item is visible, scrolling into view if necessary.
4312 * [I] HWND : window handle
4313 * [I] INT : item index
4314 * [I] BOOL : partially or entirely visible
4320 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4322 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4323 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4324 INT nScrollPosHeight = 0;
4325 INT nScrollPosWidth = 0;
4326 SCROLLINFO scrollInfo;
4328 BOOL bRedraw = FALSE;
4330 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4331 scrollInfo.cbSize = sizeof(SCROLLINFO);
4332 scrollInfo.fMask = SIF_POS;
4334 /* ALWAYS bPartial == FALSE, FOR NOW! */
4336 rcItem.left = LVIR_BOUNDS;
4337 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4339 if (rcItem.left < infoPtr->rcList.left)
4341 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4345 if (uView == LVS_LIST)
4347 nScrollPosWidth = infoPtr->nItemWidth;
4348 rcItem.left += infoPtr->rcList.left;
4350 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4352 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4353 rcItem.left += infoPtr->rcList.left;
4356 /* When in LVS_REPORT view, the scroll position should
4358 if (nScrollPosWidth != 0)
4360 if (rcItem.left % nScrollPosWidth == 0)
4361 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4363 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4365 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4369 else if (rcItem.right > infoPtr->rcList.right)
4371 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4375 if (uView == LVS_LIST)
4377 rcItem.right -= infoPtr->rcList.right;
4378 nScrollPosWidth = infoPtr->nItemWidth;
4380 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4382 rcItem.right -= infoPtr->rcList.right;
4383 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4386 /* When in LVS_REPORT view, the scroll position should
4388 if (nScrollPosWidth != 0)
4390 if (rcItem.right % nScrollPosWidth == 0)
4391 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4393 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4395 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4400 if (rcItem.top < infoPtr->rcList.top)
4404 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4406 if (uView == LVS_REPORT)
4408 rcItem.top -= infoPtr->rcList.top;
4409 nScrollPosHeight = infoPtr->nItemHeight;
4411 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4413 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4414 rcItem.top += infoPtr->rcList.top;
4417 if (rcItem.top % nScrollPosHeight == 0)
4418 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4420 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4422 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4425 else if (rcItem.bottom > infoPtr->rcList.bottom)
4429 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4431 if (uView == LVS_REPORT)
4433 rcItem.bottom -= infoPtr->rcList.bottom;
4434 nScrollPosHeight = infoPtr->nItemHeight;
4436 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4438 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4439 rcItem.bottom -= infoPtr->rcList.bottom;
4442 if (rcItem.bottom % nScrollPosHeight == 0)
4443 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4445 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4447 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4453 InvalidateRect(hwnd,NULL,TRUE);
4459 * Retrieves the nearest item, given a position and a direction.
4462 * [I] HWND : window handle
4463 * [I] POINT : start position
4464 * [I] UINT : direction
4467 * Item index if successdful, -1 otherwise.
4469 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4471 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4472 LVHITTESTINFO lvHitTestInfo;
4476 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4478 ZeroMemory(&lvHitTestInfo, sizeof(LVHITTESTINFO));
4479 LISTVIEW_GetOrigin(hwnd, &lvHitTestInfo.pt);
4480 lvHitTestInfo.pt.x += pt.x;
4481 lvHitTestInfo.pt.y += pt.y;
4485 if (vkDirection == VK_DOWN)
4486 lvHitTestInfo.pt.y += infoPtr->nItemHeight;
4487 else if (vkDirection == VK_UP)
4488 lvHitTestInfo.pt.y -= infoPtr->nItemHeight;
4489 else if (vkDirection == VK_LEFT)
4490 lvHitTestInfo.pt.x -= infoPtr->nItemWidth;
4491 else if (vkDirection == VK_RIGHT)
4492 lvHitTestInfo.pt.x += infoPtr->nItemWidth;
4494 if (PtInRect(&rcView, lvHitTestInfo.pt) == FALSE)
4497 nItem = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
4500 while (nItem == -1);
4508 * Searches for an item with specific characteristics.
4511 * [I] hwnd : window handle
4512 * [I] nStart : base item index
4513 * [I] lpFindInfo : item information to look for
4516 * SUCCESS : index of item
4519 static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart,
4520 LPLVFINDINFOW lpFindInfo)
4522 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4524 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4528 INT nLast = GETITEMCOUNT(infoPtr);
4530 if ((nItem >= -1) && (lpFindInfo != NULL))
4532 ZeroMemory(&lvItem, sizeof(lvItem));
4534 if (lpFindInfo->flags & LVFI_PARAM)
4536 lvItem.mask |= LVIF_PARAM;
4539 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4541 lvItem.mask |= LVIF_TEXT;
4542 lvItem.pszText = szDispText;
4543 lvItem.cchTextMax = DISP_TEXT_SIZE;
4546 if (lpFindInfo->flags & LVFI_WRAP)
4549 if (lpFindInfo->flags & LVFI_NEARESTXY)
4551 ptItem.x = lpFindInfo->pt.x;
4552 ptItem.y = lpFindInfo->pt.y;
4557 while (nItem < nLast)
4559 if (lpFindInfo->flags & LVFI_NEARESTXY)
4561 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4562 lpFindInfo->vkDirection);
4565 /* get position of the new item index */
4566 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4577 lvItem.iItem = nItem;
4578 lvItem.iSubItem = 0;
4579 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
4581 if (lvItem.mask & LVIF_TEXT)
4583 if (lpFindInfo->flags & LVFI_PARTIAL)
4585 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4590 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4595 if (lvItem.mask & LVIF_PARAM)
4597 if (lpFindInfo->lParam != lvItem.lParam)
4623 * Searches for an item with specific characteristics.
4626 * [I] hwnd : window handle
4627 * [I] nStart : base item index
4628 * [I] lpFindInfo : item information to look for
4631 * SUCCESS : index of item
4634 static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart,
4635 LPLVFINDINFOA lpFindInfo)
4637 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4641 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4642 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4643 res = LISTVIEW_FindItemW(hwnd, nStart, &fiw);
4644 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4650 * Retrieves the background color of the listview control.
4653 * [I] HWND : window handle
4656 * COLORREF associated with the background.
4658 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4660 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4662 return infoPtr->clrBk;
4667 * Retrieves the background image of the listview control.
4670 * [I] HWND : window handle
4671 * [O] LPLVMKBIMAGE : background image attributes
4677 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4679 /* FIXME (listview, "empty stub!\n"); */
4685 * Retrieves the callback mask.
4688 * [I] HWND : window handle
4693 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4695 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4697 return infoPtr->uCallbackMask;
4702 * Retrieves column attributes.
4705 * [I] HWND : window handle
4706 * [I] INT : column index
4707 * [IO] LPLVCOLUMNW : column information
4708 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4709 * otherwise it is in fact a LPLVCOLUMNA
4715 static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
4717 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4719 BOOL bResult = FALSE;
4721 if (lpColumn != NULL)
4723 /* initialize memory */
4724 ZeroMemory(&hdi, sizeof(hdi));
4726 if (lpColumn->mask & LVCF_FMT)
4727 hdi.mask |= HDI_FORMAT;
4729 if (lpColumn->mask & LVCF_WIDTH)
4730 hdi.mask |= HDI_WIDTH;
4732 if (lpColumn->mask & LVCF_TEXT)
4734 hdi.mask |= HDI_TEXT;
4735 hdi.cchTextMax = lpColumn->cchTextMax;
4736 hdi.pszText = lpColumn->pszText;
4739 if (lpColumn->mask & LVCF_IMAGE)
4740 hdi.mask |= HDI_IMAGE;
4742 if (lpColumn->mask & LVCF_ORDER)
4743 hdi.mask |= HDI_ORDER;
4746 bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
4748 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4750 if (bResult != FALSE)
4752 if (lpColumn->mask & LVCF_FMT)
4756 if (hdi.fmt & HDF_LEFT)
4757 lpColumn->fmt |= LVCFMT_LEFT;
4758 else if (hdi.fmt & HDF_RIGHT)
4759 lpColumn->fmt |= LVCFMT_RIGHT;
4760 else if (hdi.fmt & HDF_CENTER)
4761 lpColumn->fmt |= LVCFMT_CENTER;
4763 if (hdi.fmt & HDF_IMAGE)
4764 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4766 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4767 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4770 if (lpColumn->mask & LVCF_WIDTH)
4771 lpColumn->cx = hdi.cxy;
4773 if (lpColumn->mask & LVCF_IMAGE)
4774 lpColumn->iImage = hdi.iImage;
4776 if (lpColumn->mask & LVCF_ORDER)
4777 lpColumn->iOrder = hdi.iOrder;
4785 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
4787 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
4794 for (i = 0; i < iCount; i++)
4802 * Retrieves the column width.
4805 * [I] HWND : window handle
4806 * [I] int : column index
4809 * SUCCESS : column width
4812 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
4814 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4815 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4816 INT nColumnWidth = 0;
4819 if (uView == LVS_LIST)
4821 nColumnWidth = infoPtr->nItemWidth;
4823 else if (uView == LVS_REPORT)
4825 /* get column width from header */
4826 ZeroMemory(&hdi, sizeof(hdi));
4827 hdi.mask = HDI_WIDTH;
4828 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
4829 nColumnWidth = hdi.cxy;
4832 return nColumnWidth;
4837 * In list or report display mode, retrieves the number of items that can fit
4838 * vertically in the visible area. In icon or small icon display mode,
4839 * retrieves the total number of visible items.
4842 * [I] HWND : window handle
4845 * Number of fully visible items.
4847 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
4849 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4850 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4853 if (uView == LVS_LIST)
4855 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4857 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
4858 LISTVIEW_GetCountPerColumn(hwnd);
4861 else if (uView == LVS_REPORT)
4863 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
4867 nItemCount = GETITEMCOUNT(infoPtr);
4873 /* LISTVIEW_GetEditControl */
4877 * Retrieves the extended listview style.
4880 * [I] HWND : window handle
4883 * SUCCESS : previous style
4886 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
4888 LISTVIEW_INFO *infoPtr;
4890 /* make sure we can get the listview info */
4891 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
4894 return (infoPtr->dwExStyle);
4899 * Retrieves the handle to the header control.
4902 * [I] HWND : window handle
4907 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
4909 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4911 return infoPtr->hwndHeader;
4914 /* LISTVIEW_GetHotCursor */
4918 * Returns the time that the mouse cursor must hover over an item
4919 * before it is selected.
4922 * [I] HWND : window handle
4925 * Returns the previously set hover time or (DWORD)-1 to indicate that the
4926 * hover time is set to the default hover time.
4928 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
4930 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4932 return infoPtr->dwHoverTime;
4937 * Retrieves an image list handle.
4940 * [I] HWND : window handle
4941 * [I] INT : image list identifier
4944 * SUCCESS : image list handle
4947 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
4949 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4950 HIMAGELIST himl = NULL;
4955 himl = infoPtr->himlNormal;
4958 himl = infoPtr->himlSmall;
4961 himl = infoPtr->himlState;
4965 return (LRESULT)himl;
4968 /* LISTVIEW_GetISearchString */
4972 * Retrieves item attributes.
4975 * [I] hwnd : window handle
4976 * [IO] lpLVItem : item info
4977 * [I] internal : if true then we will use tricks that avoid copies
4978 * but are not compatible with the regular interface
4979 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
4980 * if FALSE, the lpLVItem is a LPLVITEMA.
4986 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
4988 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4989 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4990 NMLVDISPINFOW dispInfo;
4991 LISTVIEW_SUBITEM *lpSubItem;
4992 LISTVIEW_ITEM *lpItem;
4995 INT* piImage = (INT*)&null;
4996 LPWSTR* ppszText= (LPWSTR*)&null;
4997 LPARAM* plParam = (LPARAM*)&null;
4999 if (internal && !isW)
5001 ERR("We can't have internal non-Unicode GetItem!\n");
5005 /* In the following:
5006 * lpLVItem describes the information requested by the user
5007 * lpItem/lpSubItem is what we have
5008 * dispInfo is a structure we use to request the missing
5009 * information from the application
5012 TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n",
5013 hwnd, debuglvitem_t(lpLVItem, isW), internal, isW);
5015 if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5016 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5019 ZeroMemory(&dispInfo, sizeof(dispInfo));
5021 if (lStyle & LVS_OWNERDATA)
5023 if (lpLVItem->mask & ~LVIF_STATE)
5025 memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5026 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5027 memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5028 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5031 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5033 lpLVItem->state = 0;
5034 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5035 lpLVItem->state |= LVIS_FOCUSED;
5036 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5037 lpLVItem->state |= LVIS_SELECTED;
5043 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5044 if (hdpaSubItems == NULL) return FALSE;
5046 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5049 ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5050 if (lpLVItem->iSubItem == 0)
5052 piImage=&lpItem->iImage;
5053 ppszText=&lpItem->pszText;
5054 plParam=&lpItem->lParam;
5055 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5057 dispInfo.item.mask |= LVIF_STATE;
5058 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5063 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5064 if (lpSubItem != NULL)
5066 piImage=&lpSubItem->iImage;
5067 ppszText=&lpSubItem->pszText;
5071 if ((lpLVItem->mask & LVIF_IMAGE) && !(*piImage && *piImage!=I_IMAGECALLBACK))
5073 dispInfo.item.mask |= LVIF_IMAGE;
5076 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5078 dispInfo.item.mask |= LVIF_TEXT;
5079 dispInfo.item.pszText = lpLVItem->pszText;
5080 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5081 if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5082 *dispInfo.item.pszText = '\0';
5083 if (dispInfo.item.pszText && (*ppszText == NULL))
5084 *dispInfo.item.pszText = '\0';
5087 if (dispInfo.item.mask != 0)
5089 /* We don't have all the requested info, query the application */
5090 dispInfo.item.iItem = lpLVItem->iItem;
5091 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5092 dispInfo.item.lParam = lpItem->lParam;
5093 dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5094 TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5097 if (dispInfo.item.mask & LVIF_IMAGE)
5099 lpLVItem->iImage = dispInfo.item.iImage;
5100 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *piImage)
5101 *piImage = dispInfo.item.iImage;
5103 else if (lpLVItem->mask & LVIF_IMAGE)
5105 lpLVItem->iImage = *piImage;
5108 if (dispInfo.item.mask & LVIF_PARAM)
5110 lpLVItem->lParam = dispInfo.item.lParam;
5111 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *plParam)
5112 *plParam = dispInfo.item.lParam;
5114 else if (lpLVItem->mask & LVIF_PARAM)
5115 lpLVItem->lParam = lpItem->lParam;
5117 if (dispInfo.item.mask & LVIF_TEXT)
5119 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5121 if (isW) Str_SetPtrW(ppszText, dispInfo.item.pszText);
5122 else Str_SetPtrA((LPSTR *)ppszText, (LPCSTR)dispInfo.item.pszText);
5125 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5126 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5127 if (lpLVItem->pszText != dispInfo.item.pszText)
5128 textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5131 else if (lpLVItem->mask & LVIF_TEXT)
5133 if (internal) lpLVItem->pszText = *ppszText;
5134 else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5137 if (lpLVItem->iSubItem == 0)
5139 if (dispInfo.item.mask & LVIF_STATE)
5141 lpLVItem->state = lpItem->state;
5142 lpLVItem->state &= ~dispInfo.item.stateMask;
5143 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5145 lpLVItem->state &= ~LVIS_SELECTED;
5146 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5147 LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem))
5148 lpLVItem->state |= LVIS_SELECTED;
5150 else if (lpLVItem->mask & LVIF_STATE)
5152 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5154 lpLVItem->state &= ~LVIS_SELECTED;
5155 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5156 LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5157 lpLVItem->state |= LVIS_SELECTED;
5160 if (lpLVItem->mask & LVIF_PARAM)
5161 lpLVItem->lParam = lpItem->lParam;
5163 if (lpLVItem->mask & LVIF_INDENT)
5164 lpLVItem->iIndent = lpItem->iIndent;
5170 /* LISTVIEW_GetHotCursor */
5174 * Retrieves the index of the hot item.
5177 * [I] HWND : window handle
5180 * SUCCESS : hot item index
5181 * FAILURE : -1 (no hot item)
5183 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5185 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5187 return infoPtr->nHotItem;
5190 /* LISTVIEW_GetHoverTime */
5194 * Retrieves the number of items in the listview control.
5197 * [I] HWND : window handle
5202 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5204 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5206 return GETITEMCOUNT(infoPtr);
5211 * Retrieves the position (upper-left) of the listview control item.
5214 * [I] HWND : window handle
5215 * [I] INT : item index
5216 * [O] LPPOINT : coordinate information
5222 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem,
5223 LPPOINT lpptPosition)
5225 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5226 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5227 BOOL bResult = FALSE;
5229 LISTVIEW_ITEM *lpItem;
5230 INT nCountPerColumn;
5233 TRACE("(hwnd=%x,nItem=%d,lpptPosition=%p)\n", hwnd, nItem, lpptPosition);
5235 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5236 (lpptPosition != NULL))
5238 if (uView == LVS_LIST)
5241 nItem = nItem - ListView_GetTopIndex(hwnd);
5242 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5245 nRow = nItem % nCountPerColumn;
5248 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
5249 lpptPosition->y = 0;
5253 lpptPosition->x = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5254 lpptPosition->y = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5259 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
5260 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
5263 else if (uView == LVS_REPORT)
5265 SCROLLINFO scrollInfo;
5267 lpptPosition->x = REPORT_MARGINX;
5268 lpptPosition->y = ((nItem - ListView_GetTopIndex(hwnd)) *
5269 infoPtr->nItemHeight) + infoPtr->rcList.top;
5271 /* Adjust position by scrollbar offset */
5272 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5273 scrollInfo.cbSize = sizeof(SCROLLINFO);
5274 scrollInfo.fMask = SIF_POS;
5275 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5276 lpptPosition->x -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5280 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5281 if (hdpaSubItems != NULL)
5283 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
5287 lpptPosition->x = lpItem->ptPosition.x;
5288 lpptPosition->y = lpItem->ptPosition.y;
5298 * Retrieves the bounding rectangle for a listview control item.
5301 * [I] HWND : window handle
5302 * [I] INT : item index
5303 * [IO] LPRECT : bounding rectangle coordinates
5304 * lprc->left specifies the portion of the item for which the bounding
5305 * rectangle will be retrieved.
5307 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5308 * including the icon and label.
5309 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5310 * LVIR_LABEL Returns the bounding rectangle of the item text.
5311 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5312 * rectangles, but excludes columns in report view.
5318 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5320 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5321 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5322 BOOL bResult = FALSE;
5333 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5335 if (uView & LVS_REPORT)
5337 ZeroMemory(&lvItem, sizeof(lvItem));
5338 lvItem.mask = LVIF_INDENT;
5339 lvItem.iItem = nItem;
5340 lvItem.iSubItem = 0;
5341 LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
5344 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5345 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5352 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5354 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) != FALSE)
5359 if (uView == LVS_ICON)
5361 if (infoPtr->himlNormal != NULL)
5363 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5366 lprc->left = ptItem.x + ptOrigin.x;
5367 lprc->top = ptItem.y + ptOrigin.y;
5368 lprc->right = lprc->left + infoPtr->iconSize.cx;
5369 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5370 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5374 else if (uView == LVS_SMALLICON)
5376 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5379 lprc->left = ptItem.x + ptOrigin.x;
5380 lprc->top = ptItem.y + ptOrigin.y;
5381 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5383 if (infoPtr->himlState != NULL)
5384 lprc->left += infoPtr->iconSize.cx;
5386 if (infoPtr->himlSmall != NULL)
5387 lprc->right = lprc->left + infoPtr->iconSize.cx;
5389 lprc->right = lprc->left;
5395 lprc->left = ptItem.x;
5396 if (uView & LVS_REPORT)
5397 lprc->left += nIndent;
5398 lprc->top = ptItem.y;
5399 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5401 if (infoPtr->himlState != NULL)
5402 lprc->left += infoPtr->iconSize.cx;
5404 if (infoPtr->himlSmall != NULL)
5405 lprc->right = lprc->left + infoPtr->iconSize.cx;
5407 lprc->right = lprc->left;
5412 if (uView == LVS_ICON)
5414 if (infoPtr->himlNormal != NULL)
5416 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5419 lprc->left = ptItem.x + ptOrigin.x;
5420 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5421 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5422 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5423 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5425 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5426 lprc->right = lprc->left + nLabelWidth;
5431 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5435 hOldFont = SelectObject(hdc, infoPtr->hFont);
5436 GetTextMetricsW(hdc, &tm);
5437 lprc->bottom = lprc->top + tm.tmHeight + HEIGHT_PADDING;
5438 SelectObject(hdc, hOldFont);
5439 ReleaseDC(hwnd, hdc);
5443 else if (uView == LVS_SMALLICON)
5445 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5448 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5449 lprc->top = ptItem.y + ptOrigin.y;
5450 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5452 if (infoPtr->himlState != NULL)
5453 lprc->left += infoPtr->iconSize.cx;
5455 if (infoPtr->himlSmall != NULL)
5456 lprc->left += infoPtr->iconSize.cx;
5458 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5459 nLabelWidth += TRAILING_PADDING;
5460 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5461 lprc->right = lprc->left + nLabelWidth;
5463 lprc->right = nLeftPos + infoPtr->nItemWidth;
5469 if (uView & LVS_REPORT)
5470 nLeftPos = lprc->left = ptItem.x + nIndent;
5472 nLeftPos = lprc->left = ptItem.x;
5473 lprc->top = ptItem.y;
5474 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5476 if (infoPtr->himlState != NULL)
5477 lprc->left += infoPtr->iconSize.cx;
5479 if (infoPtr->himlSmall != NULL)
5480 lprc->left += infoPtr->iconSize.cx;
5482 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5483 nLabelWidth += TRAILING_PADDING;
5484 if (infoPtr->himlSmall)
5485 nLabelWidth += IMAGE_PADDING;
5486 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5487 lprc->right = lprc->left + nLabelWidth;
5489 lprc->right = nLeftPos + infoPtr->nItemWidth;
5494 if (uView == LVS_ICON)
5496 if (infoPtr->himlNormal != NULL)
5498 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5501 lprc->left = ptItem.x + ptOrigin.x;
5502 lprc->top = ptItem.y + ptOrigin.y;
5503 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5504 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5508 else if (uView == LVS_SMALLICON)
5510 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5513 lprc->left = ptItem.x + ptOrigin.x;
5514 lprc->right = lprc->left;
5515 lprc->top = ptItem.y + ptOrigin.y;
5516 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5517 if (infoPtr->himlState != NULL)
5518 lprc->right += infoPtr->iconSize.cx;
5519 if (infoPtr->himlSmall != NULL)
5520 lprc->right += infoPtr->iconSize.cx;
5522 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5523 nLabelWidth += TRAILING_PADDING;
5524 if (infoPtr->himlSmall)
5525 nLabelWidth += IMAGE_PADDING;
5526 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5527 lprc->right += nLabelWidth;
5529 lprc->right = lprc->left + infoPtr->nItemWidth;
5535 lprc->left = ptItem.x;
5536 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5537 lprc->left += nIndent;
5538 lprc->right = lprc->left;
5539 lprc->top = ptItem.y;
5540 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5542 if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
5545 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5546 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5548 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5552 if (infoPtr->himlState != NULL)
5553 lprc->right += infoPtr->iconSize.cx;
5555 if (infoPtr->himlSmall != NULL)
5556 lprc->right += infoPtr->iconSize.cx;
5558 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5559 nLabelWidth += TRAILING_PADDING;
5560 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5561 lprc->right += nLabelWidth;
5563 lprc->right = lprc->left + infoPtr->nItemWidth;
5568 case LVIR_SELECTBOUNDS:
5569 if (uView == LVS_ICON)
5571 if (infoPtr->himlNormal != NULL)
5573 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5576 lprc->left = ptItem.x + ptOrigin.x;
5577 lprc->top = ptItem.y + ptOrigin.y;
5578 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5579 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5583 else if (uView == LVS_SMALLICON)
5585 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5588 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5589 lprc->top = ptItem.y + ptOrigin.y;
5590 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5592 if (infoPtr->himlState != NULL)
5593 lprc->left += infoPtr->iconSize.cx;
5595 lprc->right = lprc->left;
5597 if (infoPtr->himlSmall != NULL)
5598 lprc->right += infoPtr->iconSize.cx;
5600 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5601 nLabelWidth += TRAILING_PADDING;
5602 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5603 lprc->right += nLabelWidth;
5605 lprc->right = nLeftPos + infoPtr->nItemWidth;
5611 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5612 nLeftPos = lprc->left = ptItem.x + nIndent;
5614 nLeftPos = lprc->left = ptItem.x;
5615 lprc->top = ptItem.y;
5616 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5618 if (infoPtr->himlState != NULL)
5619 lprc->left += infoPtr->iconSize.cx;
5621 lprc->right = lprc->left;
5623 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5626 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5627 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5629 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5633 if (infoPtr->himlSmall != NULL)
5634 lprc->right += infoPtr->iconSize.cx;
5636 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5637 nLabelWidth += TRAILING_PADDING;
5638 if (infoPtr->himlSmall)
5639 nLabelWidth += IMAGE_PADDING;
5640 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5641 lprc->right += nLabelWidth;
5643 lprc->right = nLeftPos + infoPtr->nItemWidth;
5655 * Retrieves the width of a label.
5658 * [I] HWND : window handle
5661 * SUCCESS : string width (in pixels)
5664 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
5666 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5667 INT nLabelWidth = 0;
5670 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
5672 ZeroMemory(&lvItem, sizeof(lvItem));
5673 lvItem.mask = LVIF_TEXT;
5674 lvItem.iItem = nItem;
5675 lvItem.cchTextMax = DISP_TEXT_SIZE;
5676 lvItem.pszText = szDispText;
5677 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
5678 nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText);
5685 * Retrieves the spacing between listview control items.
5688 * [I] HWND : window handle
5689 * [I] BOOL : flag for small or large icon
5692 * Horizontal + vertical spacing
5694 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
5696 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5699 if (bSmall == FALSE)
5701 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5705 LONG style = GetWindowLongW(hwnd, GWL_STYLE);
5706 if ((style & LVS_TYPEMASK) == LVS_ICON)
5707 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5709 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5716 * Retrieves the state of a listview control item.
5719 * [I] HWND : window handle
5720 * [I] INT : item index
5721 * [I] UINT : state mask
5724 * State specified by the mask.
5726 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
5728 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5732 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5734 ZeroMemory(&lvItem, sizeof(lvItem));
5735 lvItem.iItem = nItem;
5736 lvItem.stateMask = uMask;
5737 lvItem.mask = LVIF_STATE;
5738 if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
5739 uState = lvItem.state;
5747 * Retrieves the text of a listview control item or subitem.
5750 * [I] hwnd : window handle
5751 * [I] nItem : item index
5752 * [IO] lpLVItem : item information
5753 * [I] isW : TRUE if lpLVItem is Unicode
5756 * SUCCESS : string length
5759 static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5761 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5764 if (lpLVItem != NULL)
5766 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5768 lpLVItem->mask = LVIF_TEXT;
5769 lpLVItem->iItem = nItem;
5770 if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
5771 nLength = textlenT(lpLVItem->pszText, isW);
5780 * Searches for an item based on properties + relationships.
5783 * [I] HWND : window handle
5784 * [I] INT : item index
5785 * [I] INT : relationship flag
5788 * SUCCESS : item index
5791 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
5793 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5794 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5796 LVFINDINFOW lvFindInfo;
5797 INT nCountPerColumn;
5800 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
5802 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5804 if (uFlags & LVNI_CUT)
5807 if (uFlags & LVNI_DROPHILITED)
5808 uMask |= LVIS_DROPHILITED;
5810 if (uFlags & LVNI_FOCUSED)
5811 uMask |= LVIS_FOCUSED;
5813 if (uFlags & LVNI_SELECTED)
5814 uMask |= LVIS_SELECTED;
5816 if (uFlags & LVNI_ABOVE)
5818 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5823 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5829 lvFindInfo.flags = LVFI_NEARESTXY;
5830 lvFindInfo.vkDirection = VK_UP;
5831 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5832 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
5834 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5839 else if (uFlags & LVNI_BELOW)
5841 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5843 while (nItem < GETITEMCOUNT(infoPtr))
5846 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5852 lvFindInfo.flags = LVFI_NEARESTXY;
5853 lvFindInfo.vkDirection = VK_DOWN;
5854 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5855 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
5857 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5862 else if (uFlags & LVNI_TOLEFT)
5864 if (uView == LVS_LIST)
5866 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5867 while (nItem - nCountPerColumn >= 0)
5869 nItem -= nCountPerColumn;
5870 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5874 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5876 lvFindInfo.flags = LVFI_NEARESTXY;
5877 lvFindInfo.vkDirection = VK_LEFT;
5878 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5879 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
5881 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5886 else if (uFlags & LVNI_TORIGHT)
5888 if (uView == LVS_LIST)
5890 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5891 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
5893 nItem += nCountPerColumn;
5894 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5898 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5900 lvFindInfo.flags = LVFI_NEARESTXY;
5901 lvFindInfo.vkDirection = VK_RIGHT;
5902 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5903 while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
5905 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5914 /* search by index */
5915 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
5917 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
5926 /* LISTVIEW_GetNumberOfWorkAreas */
5930 * Retrieves the origin coordinates when in icon or small icon display mode.
5933 * [I] HWND : window handle
5934 * [O] LPPOINT : coordinate information
5940 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
5942 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5943 UINT uView = lStyle & LVS_TYPEMASK;
5944 BOOL bResult = FALSE;
5946 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
5948 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5950 SCROLLINFO scrollInfo;
5951 ZeroMemory(lpptOrigin, sizeof(POINT));
5952 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5953 scrollInfo.cbSize = sizeof(SCROLLINFO);
5955 if (lStyle & WS_HSCROLL)
5957 scrollInfo.fMask = SIF_POS;
5958 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
5959 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5962 if (lStyle & WS_VSCROLL)
5964 scrollInfo.fMask = SIF_POS;
5965 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
5966 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5977 * Retrieves the number of items that are marked as selected.
5980 * [I] HWND : window handle
5983 * Number of items selected.
5985 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
5988 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5989 INT nSelectedCount = 0;
5992 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
5994 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
5998 return nSelectedCount;
6003 * Retrieves item index that marks the start of a multiple selection.
6006 * [I] HWND : window handle
6009 * Index number or -1 if there is no selection mark.
6011 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6013 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6015 return infoPtr->nSelectionMark;
6021 * Retrieves the width of a string.
6024 * [I] hwnd : window handle
6025 * [I] lpszText : text string to process
6026 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6029 * SUCCESS : string width (in pixels)
6032 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
6034 if (is_textT(lpszText, isW))
6036 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6037 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6038 HDC hdc = GetDC(hwnd);
6039 HFONT hOldFont = SelectObject(hdc, hFont);
6041 ZeroMemory(&stringSize, sizeof(SIZE));
6043 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6045 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6046 SelectObject(hdc, hOldFont);
6047 ReleaseDC(hwnd, hdc);
6048 return stringSize.cx;
6055 * Retrieves the text backgound color.
6058 * [I] HWND : window handle
6061 * COLORREF associated with the the background.
6063 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6065 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6067 return infoPtr->clrTextBk;
6072 * Retrieves the text color.
6075 * [I] HWND : window handle
6078 * COLORREF associated with the text.
6080 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6082 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6084 return infoPtr->clrText;
6089 * Determines which section of the item was selected (if any).
6092 * [I] HWND : window handle
6093 * [IO] LPLVHITTESTINFO : hit test information
6094 * [I] subitem : fill out iSubItem.
6097 * SUCCESS : item index
6100 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo,
6103 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6104 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6105 UINT uView = lStyle & LVS_TYPEMASK;
6106 INT i,topindex,bottomindex;
6110 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6111 lpHitTestInfo->pt.y);
6113 topindex = ListView_GetTopIndex(hwnd);
6114 if (uView == LVS_REPORT)
6116 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6117 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6121 bottomindex = GETITEMCOUNT(infoPtr);
6124 for (i = topindex; i < bottomindex; i++)
6126 rcItem.left = LVIR_BOUNDS;
6127 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6129 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6131 rcItem.left = LVIR_ICON;
6132 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6134 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6136 lpHitTestInfo->flags = LVHT_ONITEMICON;
6137 lpHitTestInfo->iItem = i;
6138 if (subitem) lpHitTestInfo->iSubItem = 0;
6143 rcItem.left = LVIR_LABEL;
6144 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6146 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6148 lpHitTestInfo->flags = LVHT_ONITEMLABEL;
6149 lpHitTestInfo->iItem = i;
6150 if (subitem) lpHitTestInfo->iSubItem = 0;
6155 lpHitTestInfo->flags = LVHT_ONITEMSTATEICON;
6156 lpHitTestInfo->iItem = i;
6157 if (subitem) lpHitTestInfo->iSubItem = 0;
6163 lpHitTestInfo->flags = LVHT_NOWHERE;
6170 * Determines which listview item is located at the specified position.
6173 * [I] HWND : window handle
6174 * [IO} LPLVHITTESTINFO : hit test information
6177 * SUCCESS : item index
6180 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6182 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6185 lpHitTestInfo->flags = 0;
6187 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6188 lpHitTestInfo->flags = LVHT_TOLEFT;
6189 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6190 lpHitTestInfo->flags = LVHT_TORIGHT;
6191 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6192 lpHitTestInfo->flags |= LVHT_ABOVE;
6193 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6194 lpHitTestInfo->flags |= LVHT_BELOW;
6196 if (lpHitTestInfo->flags == 0)
6198 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6199 * an app might pass only a structure with space up to iItem!
6200 * (MS Office 97 does that for instance in the file open dialog)
6202 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6210 * Inserts a new column.
6213 * [I] HWND : window handle
6214 * [I] INT : column index
6215 * [I] LPLVCOLUMNW : column information
6218 * SUCCESS : new column index
6221 static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn,
6222 LPLVCOLUMNW lpColumn, BOOL isW)
6224 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6225 INT nNewColumn = -1;
6228 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
6230 if (lpColumn != NULL)
6232 /* initialize memory */
6233 ZeroMemory(&hdi, sizeof(hdi));
6235 if (lpColumn->mask & LVCF_FMT)
6237 /* format member is valid */
6238 hdi.mask |= HDI_FORMAT;
6240 /* set text alignment (leftmost column must be left-aligned) */
6243 hdi.fmt |= HDF_LEFT;
6247 if (lpColumn->fmt & LVCFMT_LEFT)
6249 hdi.fmt |= HDF_LEFT;
6251 else if (lpColumn->fmt & LVCFMT_RIGHT)
6253 hdi.fmt |= HDF_RIGHT;
6255 else if (lpColumn->fmt & LVCFMT_CENTER)
6257 hdi.fmt |= HDF_CENTER;
6261 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6263 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6267 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6272 if (lpColumn->fmt & LVCFMT_IMAGE)
6274 hdi.fmt |= HDF_IMAGE;
6275 hdi.iImage = I_IMAGECALLBACK;
6279 if (lpColumn->mask & LVCF_WIDTH)
6281 hdi.mask |= HDI_WIDTH;
6282 hdi.cxy = lpColumn->cx;
6285 if (lpColumn->mask & LVCF_TEXT)
6287 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6288 hdi.pszText = lpColumn->pszText;
6289 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6290 hdi.fmt |= HDF_STRING;
6293 if (lpColumn->mask & LVCF_IMAGE)
6295 hdi.mask |= HDI_IMAGE;
6296 hdi.iImage = lpColumn->iImage;
6299 if (lpColumn->mask & LVCF_ORDER)
6301 hdi.mask |= HDI_ORDER;
6302 hdi.iOrder = lpColumn->iOrder;
6305 /* insert item in header control */
6306 nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6307 (WPARAM)nColumn, (LPARAM)&hdi);
6309 /* Need to reset the item width when inserting a new column */
6310 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6312 LISTVIEW_UpdateScroll(hwnd);
6313 InvalidateRect(hwnd, NULL, FALSE);
6319 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6320 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6321 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6322 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6323 their own sort proc. when sending LVM_SORTITEMS.
6326 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6328 LVS_SORTXXX must be specified,
6329 LVS_OWNERDRAW is not set,
6330 <item>.pszText is not LPSTR_TEXTCALLBACK.
6332 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6333 are sorted based on item text..."
6335 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6337 LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6338 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6339 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6340 INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6341 /* if we're sorting descending, negate the return value */
6342 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6347 * Inserts a new item in the listview control.
6350 * [I] HWND : window handle
6351 * [I] LPLVITEMW : item information
6352 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6355 * SUCCESS : new item index
6358 static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
6360 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6361 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6362 UINT uView = lStyle & LVS_TYPEMASK;
6366 LISTVIEW_ITEM *lpItem = NULL;
6368 TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
6369 hwnd, debuglvitem_t(lpLVItem, isW), isW);
6371 if (lStyle & LVS_OWNERDATA)
6373 nItem = infoPtr->hdpaItems->nItemCount;
6374 infoPtr->hdpaItems->nItemCount ++;
6378 if (lpLVItem != NULL)
6380 /* make sure it's not a subitem; cannot insert a subitem */
6381 if (lpLVItem->iSubItem == 0)
6383 if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6385 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6386 if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
6388 /* insert item in listview control data structure */
6389 if ( (hdpaSubItems = DPA_Create(8)) )
6391 if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
6393 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6394 && !(lStyle & LVS_OWNERDRAWFIXED)
6395 && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
6397 /* Insert the item in the proper sort order based on the pszText
6398 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6399 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6400 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6401 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6402 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6406 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6413 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6415 /* manage item focus */
6416 if (lpLVItem->mask & LVIF_STATE)
6418 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6419 if (lpLVItem->stateMask & LVIS_SELECTED)
6420 LISTVIEW_SetSelection(hwnd, nItem);
6421 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6422 LISTVIEW_SetItemFocus(hwnd, nItem);
6425 /* send LVN_INSERTITEM notification */
6426 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6428 nmlv.lParam = lpItem->lParam;
6429 listview_notify(hwnd, LVN_INSERTITEM, &nmlv);
6431 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6433 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6434 if (nItemWidth > infoPtr->nItemWidth)
6435 infoPtr->nItemWidth = nItemWidth;
6438 /* align items (set position of each item) */
6439 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6441 if (lStyle & LVS_ALIGNLEFT)
6442 LISTVIEW_AlignLeft(hwnd);
6444 LISTVIEW_AlignTop(hwnd);
6447 LISTVIEW_UpdateScroll(hwnd);
6448 /* refresh client area */
6449 InvalidateRect(hwnd, NULL, FALSE);
6458 /* free memory if unsuccessful */
6459 if ((nItem == -1) && (lpItem != NULL))
6460 COMCTL32_Free(lpItem);
6467 * Redraws a range of items.
6470 * [I] HWND : window handle
6471 * [I] INT : first item
6472 * [I] INT : last item
6478 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6480 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6481 BOOL bResult = FALSE;
6485 if (nFirst <= nLast)
6487 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6489 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6491 for (i = nFirst; i <= nLast; i++)
6493 rcItem.left = LVIR_BOUNDS;
6494 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
6495 InvalidateRect(hwnd, &rcItem, TRUE);
6504 /* LISTVIEW_Scroll */
6508 * Sets the background color.
6511 * [I] HWND : window handle
6512 * [I] COLORREF : background color
6518 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6520 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6522 infoPtr->clrBk = clrBk;
6523 InvalidateRect(hwnd, NULL, TRUE);
6528 /* LISTVIEW_SetBkImage */
6532 * Sets the callback mask. This mask will be used when the parent
6533 * window stores state information (some or all).
6536 * [I] HWND : window handle
6537 * [I] UINT : state mask
6543 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
6545 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6547 infoPtr->uCallbackMask = uMask;
6554 * Sets the attributes of a header item.
6557 * [I] HWND : window handle
6558 * [I] INT : column index
6559 * [I] LPLVCOLUMNW : column attributes
6560 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
6561 * otherwise it is in fact a LPLVCOLUMNA
6567 static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn,
6568 LPLVCOLUMNW lpColumn, BOOL isW)
6570 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6571 BOOL bResult = FALSE;
6572 HDITEMW hdi, hdiget;
6574 if ((lpColumn != NULL) && (nColumn >= 0) &&
6575 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6577 /* initialize memory */
6578 ZeroMemory(&hdi, sizeof(hdi));
6580 if (lpColumn->mask & LVCF_FMT)
6582 /* format member is valid */
6583 hdi.mask |= HDI_FORMAT;
6585 /* get current format first */
6586 hdiget.mask = HDI_FORMAT;
6587 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6588 /* preserve HDF_STRING if present */
6589 hdi.fmt = hdiget.fmt & HDF_STRING;
6591 /* set text alignment (leftmost column must be left-aligned) */
6594 hdi.fmt |= HDF_LEFT;
6598 if (lpColumn->fmt & LVCFMT_LEFT)
6599 hdi.fmt |= HDF_LEFT;
6600 else if (lpColumn->fmt & LVCFMT_RIGHT)
6601 hdi.fmt |= HDF_RIGHT;
6602 else if (lpColumn->fmt & LVCFMT_CENTER)
6603 hdi.fmt |= HDF_CENTER;
6606 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6607 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6609 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6610 hdi.fmt |= HDF_IMAGE;
6612 if (lpColumn->fmt & LVCFMT_IMAGE)
6614 hdi.fmt |= HDF_IMAGE;
6615 hdi.iImage = I_IMAGECALLBACK;
6619 if (lpColumn->mask & LVCF_WIDTH)
6621 hdi.mask |= HDI_WIDTH;
6622 hdi.cxy = lpColumn->cx;
6625 if (lpColumn->mask & LVCF_TEXT)
6627 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6628 hdi.pszText = lpColumn->pszText;
6629 hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6630 hdi.fmt |= HDF_STRING;
6633 if (lpColumn->mask & LVCF_IMAGE)
6635 hdi.mask |= HDI_IMAGE;
6636 hdi.iImage = lpColumn->iImage;
6639 if (lpColumn->mask & LVCF_ORDER)
6641 hdi.mask |= HDI_ORDER;
6642 hdi.iOrder = lpColumn->iOrder;
6645 /* set header item attributes */
6646 bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6654 * Sets the column order array
6657 * [I] HWND : window handle
6658 * [I] INT : number of elements in column order array
6659 * [I] INT : pointer to column order array
6665 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
6667 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
6669 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6680 * Sets the width of a column
6683 * [I] HWND : window handle
6684 * [I] INT : column index
6685 * [I] INT : column width
6691 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
6693 LISTVIEW_INFO *infoPtr;
6696 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6697 UINT uView = lStyle & LVS_TYPEMASK;
6702 WCHAR text_buffer[DISP_TEXT_SIZE];
6703 INT header_item_count;
6708 /* make sure we can get the listview info */
6709 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
6712 if (!infoPtr->hwndHeader) /* make sure we have a header */
6715 /* set column width only if in report or list mode */
6716 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6719 /* take care of invalid cx values */
6720 if((uView == LVS_REPORT) && (cx < -2))
6721 cx = LVSCW_AUTOSIZE;
6722 else if (uView == LVS_LIST && (cx < 1))
6725 /* resize all columns if in LVS_LIST mode */
6726 if(uView == LVS_LIST) {
6727 infoPtr->nItemWidth = cx;
6728 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6732 /* autosize based on listview items width */
6733 if(cx == LVSCW_AUTOSIZE)
6735 /* set the width of the header to the width of the widest item */
6736 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6738 if(cx < LISTVIEW_GetLabelWidth(hwnd, item_index))
6739 cx = LISTVIEW_GetLabelWidth(hwnd, item_index);
6741 } /* autosize based on listview header width */
6742 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6744 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6746 /* if iCol is the last column make it fill the remainder of the controls width */
6747 if(iCol == (header_item_count - 1)) {
6748 /* get the width of every item except the current one */
6749 hdi.mask = HDI_WIDTH;
6752 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6753 Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6757 /* retrieve the layout of the header */
6758 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6760 cx = (rcHeader.right - rcHeader.left) - cx;
6764 /* retrieve header font */
6765 header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6767 /* retrieve header text */
6768 hdi.mask = HDI_TEXT;
6769 hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
6770 hdi.pszText = text_buffer;
6772 Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6774 /* determine the width of the text in the header */
6776 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6778 GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
6780 SelectObject(hdc, old_font); /* restore the old font */
6781 ReleaseDC(hwnd, hdc);
6783 /* set the width of this column to the width of the text */
6788 /* call header to update the column change */
6789 hdi.mask = HDI_WIDTH;
6792 lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6794 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6801 * Sets the extended listview style.
6804 * [I] HWND : window handle
6809 * SUCCESS : previous style
6812 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
6814 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6815 DWORD dwOldStyle = infoPtr->dwExStyle;
6819 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6821 infoPtr->dwExStyle = dwStyle;
6826 /* LISTVIEW_SetHotCursor */
6830 * Sets the hot item index.
6833 * [I] HWND : window handle
6837 * SUCCESS : previous hot item index
6838 * FAILURE : -1 (no hot item)
6840 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
6842 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6843 INT iOldIndex = infoPtr->nHotItem;
6846 infoPtr->nHotItem = iIndex;
6853 * Sets the amount of time the cursor must hover over an item before it is selected.
6856 * [I] HWND : window handle
6857 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6860 * Returns the previous hover time
6862 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
6864 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6865 DWORD oldHoverTime = infoPtr->dwHoverTime;
6867 infoPtr->dwHoverTime = dwHoverTime;
6869 return oldHoverTime;
6872 /* LISTVIEW_SetIconSpacing */
6879 * [I] HWND : window handle
6880 * [I] INT : image list type
6881 * [I] HIMAGELIST : image list handle
6884 * SUCCESS : old image list
6887 static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
6889 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6890 HIMAGELIST himlOld = 0;
6896 himlOld = infoPtr->himlNormal;
6897 infoPtr->himlNormal = himl;
6901 himlOld = infoPtr->himlSmall;
6902 infoPtr->himlSmall = himl;
6906 himlOld = infoPtr->himlState;
6907 infoPtr->himlState = himl;
6908 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6912 oldHeight = infoPtr->nItemHeight;
6913 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
6914 if (infoPtr->nItemHeight != oldHeight)
6915 LISTVIEW_UpdateScroll(hwnd);
6922 * Preallocates memory (does *not* set the actual count of items !)
6925 * [I] HWND : window handle
6926 * [I] INT : item count (projected number of items to allocate)
6927 * [I] DWORD : update flags
6933 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
6935 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6937 TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
6939 if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
6941 int precount,topvisible;
6944 * Internally remove all the selections.
6948 LISTVIEW_SELECTION *selection;
6949 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
6951 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
6954 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
6956 precount = infoPtr->hdpaItems->nItemCount;
6957 topvisible = ListView_GetTopIndex(hwnd) +
6958 LISTVIEW_GetCountPerColumn(hwnd) + 1;
6960 infoPtr->hdpaItems->nItemCount = nItems;
6962 LISTVIEW_UpdateSize(hwnd);
6963 LISTVIEW_UpdateScroll(hwnd);
6964 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
6965 InvalidateRect(hwnd, NULL, TRUE);
6969 FIXME("setitemcount not done for non-ownerdata\n");
6977 * Sets the position of an item.
6980 * [I] HWND : window handle
6981 * [I] INT : item index
6982 * [I] LONG : x coordinate
6983 * [I] LONG : y coordinate
6989 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
6990 LONG nPosX, LONG nPosY)
6992 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6993 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6994 UINT uView = lStyle & LVS_TYPEMASK;
6995 LISTVIEW_ITEM *lpItem;
6997 BOOL bResult = FALSE;
6999 TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7001 if (lStyle & LVS_OWNERDATA)
7004 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7006 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7008 if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7010 if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7013 lpItem->ptPosition.x = nPosX;
7014 lpItem->ptPosition.y = nPosY;
7025 * Sets the state of one or many items.
7028 * [I] HWND : window handle
7029 * [I]INT : item index
7030 * [I] LPLVITEM : item or subitem info
7036 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem)
7038 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7039 BOOL bResult = TRUE;
7042 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
7043 hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
7045 ZeroMemory(&lvItem, sizeof(lvItem));
7046 lvItem.mask = LVIF_STATE;
7047 lvItem.state = lpLVItem->state;
7048 lvItem.stateMask = lpLVItem->stateMask ;
7049 lvItem.iItem = nItem;
7053 /* apply to all items */
7054 for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7055 if (!ListView_SetItemW(hwnd, &lvItem)) bResult = FALSE;
7058 bResult = ListView_SetItemW(hwnd, &lvItem);
7065 * Sets the text of an item or subitem.
7068 * [I] hwnd : window handle
7069 * [I] nItem : item index
7070 * [I] lpLVItem : item or subitem info
7071 * [I] isW : TRUE if input is Unicode
7077 static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7079 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7080 BOOL bResult = FALSE;
7083 TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
7084 hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
7086 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7088 ZeroMemory(&lvItem, sizeof(LVITEMW));
7089 lvItem.mask = LVIF_TEXT;
7090 lvItem.pszText = lpLVItem->pszText;
7091 lvItem.iItem = nItem;
7092 lvItem.iSubItem = lpLVItem->iSubItem;
7093 if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
7094 else bResult = ListView_SetItemA(hwnd, &lvItem);
7102 * Set item index that marks the start of a multiple selection.
7105 * [I] HWND : window handle
7109 * Index number or -1 if there is no selection mark.
7111 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7113 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7114 INT nOldIndex = infoPtr->nSelectionMark;
7116 TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
7118 infoPtr->nSelectionMark = nIndex;
7125 * Sets the text background color.
7128 * [I] HWND : window handle
7129 * [I] COLORREF : text background color
7135 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7137 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7139 TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
7141 infoPtr->clrTextBk = clrTextBk;
7142 InvalidateRect(hwnd, NULL, TRUE);
7149 * Sets the text foreground color.
7152 * [I] HWND : window handle
7153 * [I] COLORREF : text color
7159 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7161 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7163 TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
7165 infoPtr->clrText = clrText;
7166 InvalidateRect(hwnd, NULL, TRUE);
7171 /* LISTVIEW_SetToolTips */
7172 /* LISTVIEW_SetUnicodeFormat */
7173 /* LISTVIEW_SetWorkAreas */
7177 * Callback internally used by LISTVIEW_SortItems()
7180 * [I] LPVOID : first LISTVIEW_ITEM to compare
7181 * [I] LPVOID : second LISTVIEW_ITEM to compare
7182 * [I] LPARAM : HWND of control
7185 * if first comes before second : negative
7186 * if first comes after second : positive
7187 * if first and second are equivalent : zero
7189 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7191 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7192 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
7193 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
7195 /* Forward the call to the client defined callback */
7196 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7201 * Sorts the listview items.
7204 * [I] HWND : window handle
7205 * [I] WPARAM : application-defined value
7206 * [I] LPARAM : pointer to comparision callback
7212 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7214 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7215 UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7216 HDPA hdpaSubItems=NULL;
7217 LISTVIEW_ITEM *pLVItem=NULL;
7218 LPVOID selectionMarkItem;
7221 TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
7223 if (lStyle & LVS_OWNERDATA) return FALSE;
7225 if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
7227 nCount = GETITEMCOUNT(infoPtr);
7228 /* if there are 0 or 1 items, there is no need to sort */
7232 infoPtr->pfnCompare = pfnCompare;
7233 infoPtr->lParamSort = lParamSort;
7234 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7236 /* Adjust selections and indices so that they are the way they should
7237 * be after the sort (otherwise, the list items move around, but
7238 * whatever is at the item's previous original position will be
7241 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7242 for (i=0; i < nCount; i++)
7244 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7245 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7247 if (pLVItem->state & LVIS_SELECTED)
7248 LISTVIEW_AddSelectionRange(hwnd, i, i);
7250 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7251 if (pLVItem->state & LVIS_FOCUSED)
7252 infoPtr->nFocusedItem=i;
7254 if (selectionMarkItem != NULL)
7255 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7256 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7258 /* align the items */
7259 LISTVIEW_AlignTop(hwnd);
7261 /* refresh the display */
7262 InvalidateRect(hwnd, NULL, TRUE);
7267 /* LISTVIEW_SubItemHitTest */
7271 * Updates an items or rearranges the listview control.
7274 * [I] HWND : window handle
7275 * [I] INT : item index
7281 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7283 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7284 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7285 BOOL bResult = FALSE;
7288 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
7290 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7294 /* rearrange with default alignment style */
7295 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7296 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7298 ListView_Arrange(hwnd, 0);
7302 /* get item bounding rectangle */
7303 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
7304 InvalidateRect(hwnd, &rc, TRUE);
7313 * Creates the listview control.
7316 * [I] HWND : window handle
7321 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
7323 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7324 UINT uView = lpcs->style & LVS_TYPEMASK;
7327 TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
7329 /* initialize info pointer */
7330 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7332 /* determine the type of structures to use */
7333 infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT,
7334 (WPARAM)hwnd, (LPARAM)NF_QUERY);
7336 /* initialize color information */
7337 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7338 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7339 infoPtr->clrTextBk = CLR_DEFAULT;
7341 /* set default values */
7342 infoPtr->uCallbackMask = 0;
7343 infoPtr->nFocusedItem = -1;
7344 infoPtr->nSelectionMark = -1;
7345 infoPtr->nHotItem = -1;
7346 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7347 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7348 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7349 infoPtr->hwndEdit = 0;
7350 infoPtr->pedititem = NULL;
7351 infoPtr->nEditLabelItem = -1;
7353 /* get default font (icon title) */
7354 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7355 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7356 infoPtr->hFont = infoPtr->hDefaultFont;
7359 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
7360 WS_CHILD | HDS_HORZ | HDS_BUTTONS,
7361 0, 0, 0, 0, hwnd, (HMENU)0,
7362 lpcs->hInstance, NULL);
7364 /* set header font */
7365 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7368 if (uView == LVS_ICON)
7370 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7371 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7373 else if (uView == LVS_REPORT)
7375 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7377 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7381 /* set HDS_HIDDEN flag to hide the header bar */
7382 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7383 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7387 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7388 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7392 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7393 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7396 /* display unsupported listview window styles */
7397 LISTVIEW_UnsupportedStyles(lpcs->style);
7399 /* allocate memory for the data structure */
7400 infoPtr->hdpaItems = DPA_Create(10);
7402 /* allocate memory for the selection ranges */
7403 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7405 /* initialize size of items */
7406 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7407 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7409 /* initialize the hover time to -1(indicating the default system hover time) */
7410 infoPtr->dwHoverTime = -1;
7417 * Erases the background of the listview control.
7420 * [I] HWND : window handle
7421 * [I] WPARAM : device context handle
7422 * [I] LPARAM : not used
7428 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
7431 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7434 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
7436 if (infoPtr->clrBk == CLR_NONE)
7438 bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
7443 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7444 GetClientRect(hwnd, &rc);
7445 FillRect((HDC)wParam, &rc, hBrush);
7446 DeleteObject(hBrush);
7454 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
7456 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7458 TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
7460 if (infoPtr->clrBk != CLR_NONE)
7462 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7463 FillRect(hdc, rc, hBrush);
7464 DeleteObject(hBrush);
7470 * Retrieves the listview control font.
7473 * [I] HWND : window handle
7478 static LRESULT LISTVIEW_GetFont(HWND hwnd)
7480 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7482 TRACE("(hwnd=%x)\n", hwnd);
7484 return infoPtr->hFont;
7489 * Performs vertical scrolling.
7492 * [I] HWND : window handle
7493 * [I] INT : scroll code
7494 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
7496 * [I] HWND : scrollbar control window handle
7501 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7504 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7505 SCROLLINFO scrollInfo;
7507 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
7508 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
7510 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7512 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7513 scrollInfo.cbSize = sizeof(SCROLLINFO);
7514 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7516 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7518 INT nOldScrollPos = scrollInfo.nPos;
7519 switch (nScrollCode)
7522 if (scrollInfo.nPos > scrollInfo.nMin)
7527 if (scrollInfo.nPos < scrollInfo.nMax)
7532 if (scrollInfo.nPos > scrollInfo.nMin)
7534 if (scrollInfo.nPos >= scrollInfo.nPage)
7535 scrollInfo.nPos -= scrollInfo.nPage;
7537 scrollInfo.nPos = scrollInfo.nMin;
7542 if (scrollInfo.nPos < scrollInfo.nMax)
7544 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7545 scrollInfo.nPos += scrollInfo.nPage;
7547 scrollInfo.nPos = scrollInfo.nMax;
7551 case SB_THUMBPOSITION:
7553 scrollInfo.nPos = nCurrentPos;
7554 if (scrollInfo.nPos > scrollInfo.nMax)
7555 scrollInfo.nPos=scrollInfo.nMax;
7557 if (scrollInfo.nPos < scrollInfo.nMin)
7558 scrollInfo.nPos=scrollInfo.nMin;
7563 if (nOldScrollPos != scrollInfo.nPos)
7565 scrollInfo.fMask = SIF_POS;
7566 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
7567 InvalidateRect(hwnd, NULL, TRUE);
7576 * Performs horizontal scrolling.
7579 * [I] HWND : window handle
7580 * [I] INT : scroll code
7581 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
7583 * [I] HWND : scrollbar control window handle
7588 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7591 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7592 SCROLLINFO scrollInfo;
7594 TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
7595 hwnd, nScrollCode, nCurrentPos, hScrollWnd);
7597 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7599 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7600 scrollInfo.cbSize = sizeof(SCROLLINFO);
7601 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7603 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
7605 INT nOldScrollPos = scrollInfo.nPos;
7607 switch (nScrollCode)
7610 if (scrollInfo.nPos > scrollInfo.nMin)
7615 if (scrollInfo.nPos < scrollInfo.nMax)
7620 if (scrollInfo.nPos > scrollInfo.nMin)
7622 if (scrollInfo.nPos >= scrollInfo.nPage)
7623 scrollInfo.nPos -= scrollInfo.nPage;
7625 scrollInfo.nPos = scrollInfo.nMin;
7630 if (scrollInfo.nPos < scrollInfo.nMax)
7632 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7633 scrollInfo.nPos += scrollInfo.nPage;
7635 scrollInfo.nPos = scrollInfo.nMax;
7639 case SB_THUMBPOSITION:
7641 scrollInfo.nPos = nCurrentPos;
7643 if (scrollInfo.nPos > scrollInfo.nMax)
7644 scrollInfo.nPos=scrollInfo.nMax;
7646 if (scrollInfo.nPos < scrollInfo.nMin)
7647 scrollInfo.nPos=scrollInfo.nMin;
7651 if (nOldScrollPos != scrollInfo.nPos)
7653 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7654 scrollInfo.fMask = SIF_POS;
7655 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
7656 if(uView == LVS_REPORT)
7658 scrollInfo.fMask = SIF_POS;
7659 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
7660 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
7662 InvalidateRect(hwnd, NULL, TRUE);
7669 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
7671 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7672 INT gcWheelDelta = 0;
7673 UINT pulScrollLines = 3;
7674 SCROLLINFO scrollInfo;
7676 TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
7678 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7679 gcWheelDelta -= wheelDelta;
7681 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7682 scrollInfo.cbSize = sizeof(SCROLLINFO);
7683 scrollInfo.fMask = SIF_POS | SIF_RANGE;
7690 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7691 * should be fixed in the future.
7693 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7694 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
7698 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7700 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7702 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
7703 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7704 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7710 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7721 * [I] HWND : window handle
7722 * [I] INT : virtual key
7723 * [I] LONG : key data
7728 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
7730 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7731 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7733 NMLVKEYDOWN nmKeyDown;
7735 TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
7737 /* send LVN_KEYDOWN notification */
7738 nmKeyDown.wVKey = nVirtualKey;
7739 nmKeyDown.flags = 0;
7740 notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
7742 switch (nVirtualKey)
7745 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
7747 hdr_notify(hwnd, NM_RETURN); /* NM_RETURN notification */
7748 hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
7753 if (GETITEMCOUNT(infoPtr) > 0)
7758 if (GETITEMCOUNT(infoPtr) > 0)
7759 nItem = GETITEMCOUNT(infoPtr) - 1;
7763 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
7767 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
7771 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
7775 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
7779 if (uView == LVS_REPORT)
7780 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
7782 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
7783 * LISTVIEW_GetCountPerRow(hwnd);
7784 if(nItem < 0) nItem = 0;
7788 if (uView == LVS_REPORT)
7789 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
7791 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
7792 * LISTVIEW_GetCountPerRow(hwnd);
7793 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
7797 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7799 if (LISTVIEW_KeySelection(hwnd, nItem))
7800 UpdateWindow(hwnd); /* update client area */
7811 * [I] HWND : window handle
7816 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
7818 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7819 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7822 TRACE("(hwnd=%x)\n", hwnd);
7824 /* send NM_KILLFOCUS notification */
7825 hdr_notify(hwnd, NM_KILLFOCUS);
7827 /* set window focus flag */
7828 infoPtr->bFocus = FALSE;
7830 /* NEED drawing optimization ; redraw the selected items */
7831 if (uView & LVS_REPORT)
7833 nTop = LISTVIEW_GetTopIndex(hwnd);
7835 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7840 nBottom = GETITEMCOUNT(infoPtr);
7842 for (i = nTop; i<nBottom; i++)
7844 if (LISTVIEW_IsSelected(hwnd,i))
7847 rcItem.left = LVIR_BOUNDS;
7848 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
7849 InvalidateRect(hwnd, &rcItem, FALSE);
7858 * Processes double click messages (left mouse button).
7861 * [I] HWND : window handle
7862 * [I] WORD : key flag
7863 * [I] WORD : x coordinate
7864 * [I] WORD : y coordinate
7869 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
7872 LVHITTESTINFO htInfo;
7875 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
7877 htInfo.pt.x = wPosX;
7878 htInfo.pt.y = wPosY;
7880 /* send NM_DBLCLK notification */
7881 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7882 if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -1)
7884 nmlv.iItem = htInfo.iItem;
7885 nmlv.iSubItem = htInfo.iSubItem;
7892 nmlv.ptAction.x = wPosX;
7893 nmlv.ptAction.y = wPosY;
7894 listview_notify(hwnd, NM_DBLCLK, &nmlv);
7897 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7898 if(nmlv.iItem != -1)
7899 hdr_notify(hwnd, LVN_ITEMACTIVATE);
7906 * Processes mouse down messages (left mouse button).
7909 * [I] HWND : window handle
7910 * [I] WORD : key flag
7911 * [I] WORD : x coordinate
7912 * [I] WORD : y coordinate
7917 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
7920 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7921 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7922 static BOOL bGroupSelect = TRUE;
7926 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
7928 /* send NM_RELEASEDCAPTURE notification */
7929 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
7931 if (infoPtr->bFocus == FALSE)
7934 /* set left button down flag */
7935 infoPtr->bLButtonDown = TRUE;
7937 ptPosition.x = wPosX;
7938 ptPosition.y = wPosY;
7939 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
7940 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7942 if (lStyle & LVS_SINGLESEL)
7944 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
7945 && infoPtr->nEditLabelItem == -1)
7946 infoPtr->nEditLabelItem = nItem;
7948 LISTVIEW_SetSelection(hwnd, nItem);
7952 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7955 LISTVIEW_AddGroupSelection(hwnd, nItem);
7957 LISTVIEW_AddSelection(hwnd, nItem);
7959 else if (wKey & MK_CONTROL)
7961 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
7963 else if (wKey & MK_SHIFT)
7965 LISTVIEW_SetGroupSelection(hwnd, nItem);
7970 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
7972 /* set selection (clears other pre-existing selections) */
7973 LISTVIEW_SetSelection(hwnd, nItem);
7975 if (was_selected && infoPtr->nEditLabelItem == -1)
7976 infoPtr->nEditLabelItem = nItem;
7982 /* remove all selections */
7983 LISTVIEW_RemoveAllSelections(hwnd);
7986 /* redraw if we could have possibly selected something */
7987 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
7994 * Processes mouse up messages (left mouse button).
7997 * [I] HWND : window handle
7998 * [I] WORD : key flag
7999 * [I] WORD : x coordinate
8000 * [I] WORD : y coordinate
8005 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8008 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8010 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8012 if (infoPtr->bLButtonDown != FALSE)
8014 LVHITTESTINFO lvHitTestInfo;
8017 lvHitTestInfo.pt.x = wPosX;
8018 lvHitTestInfo.pt.y = wPosY;
8020 /* send NM_CLICK notification */
8021 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8022 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8024 nmlv.iItem = lvHitTestInfo.iItem;
8025 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8032 nmlv.ptAction.x = wPosX;
8033 nmlv.ptAction.y = wPosY;
8034 listview_notify(hwnd, NM_CLICK, &nmlv);
8036 /* set left button flag */
8037 infoPtr->bLButtonDown = FALSE;
8039 if(infoPtr->nEditLabelItem != -1)
8041 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem)
8042 LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
8043 infoPtr->nEditLabelItem = -1;
8052 * Creates the listview control (called before WM_CREATE).
8055 * [I] HWND : window handle
8056 * [I] WPARAM : unhandled
8057 * [I] LPARAM : widow creation info
8062 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8064 LISTVIEW_INFO *infoPtr;
8066 TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8068 /* allocate memory for info structure */
8069 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8070 if (infoPtr == NULL)
8072 ERR("could not allocate info memory!\n");
8076 SetWindowLongW(hwnd, 0, (LONG)infoPtr);
8077 if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr)
8079 ERR("pointer assignment error!\n");
8083 return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam);
8088 * Destroys the listview control (called after WM_DESTROY).
8091 * [I] HWND : window handle
8096 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8098 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8100 TRACE("(hwnd=%x)\n", hwnd);
8102 /* delete all items */
8103 LISTVIEW_DeleteAllItems(hwnd);
8105 /* destroy data structure */
8106 DPA_Destroy(infoPtr->hdpaItems);
8107 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8110 infoPtr->hFont = (HFONT)0;
8111 if (infoPtr->hDefaultFont)
8113 DeleteObject(infoPtr->hDefaultFont);
8116 /* free listview info pointer*/
8117 COMCTL32_Free(infoPtr);
8119 SetWindowLongW(hwnd, 0, 0);
8125 * Handles notifications from children.
8128 * [I] HWND : window handle
8129 * [I] INT : control identifier
8130 * [I] LPNMHDR : notification information
8135 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8137 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8139 TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
8141 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8143 /* handle notification from header control */
8144 if (lpnmh->code == HDN_ENDTRACKW)
8146 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8147 InvalidateRect(hwnd, NULL, TRUE);
8149 else if(lpnmh->code == HDN_ITEMCLICKW)
8151 /* Handle sorting by Header Column */
8154 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8156 nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
8157 listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv);
8159 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8161 /* Idealy this should be done in HDN_ENDTRACKA
8162 * but since SetItemBounds in Header.c is called after
8163 * the notification is sent, it is neccessary to handle the
8164 * update of the scroll bar here (Header.c works fine as it is,
8165 * no need to disturb it)
8167 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8168 LISTVIEW_UpdateScroll(hwnd);
8169 InvalidateRect(hwnd, NULL, TRUE);
8179 * Determines the type of structure to use.
8182 * [I] HWND : window handle of the sender
8183 * [I] HWND : listview window handle
8184 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8189 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8191 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8193 TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
8195 if (nCommand == NF_REQUERY)
8196 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
8197 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8203 * Paints/Repaints the listview control.
8206 * [I] HWND : window handle
8207 * [I] HDC : device context handle
8212 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8216 TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc);
8220 hdc = BeginPaint(hwnd, &ps);
8221 LISTVIEW_Refresh(hwnd, hdc);
8222 EndPaint(hwnd, &ps);
8226 LISTVIEW_Refresh(hwnd, hdc);
8234 * Processes double click messages (right mouse button).
8237 * [I] HWND : window handle
8238 * [I] WORD : key flag
8239 * [I] WORD : x coordinate
8240 * [I] WORD : y coordinate
8245 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8248 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8250 /* send NM_RELEASEDCAPTURE notification */
8251 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8253 /* send NM_RDBLCLK notification */
8254 hdr_notify(hwnd, NM_RDBLCLK);
8261 * Processes mouse down messages (right mouse button).
8264 * [I] HWND : window handle
8265 * [I] WORD : key flag
8266 * [I] WORD : x coordinate
8267 * [I] WORD : y coordinate
8272 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8275 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8279 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8281 /* send NM_RELEASEDCAPTURE notification */
8282 hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8284 /* make sure the listview control window has the focus */
8285 if (infoPtr->bFocus == FALSE)
8288 /* set right button down flag */
8289 infoPtr->bRButtonDown = TRUE;
8291 /* determine the index of the selected item */
8292 ptPosition.x = wPosX;
8293 ptPosition.y = wPosY;
8294 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8295 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8297 LISTVIEW_SetItemFocus(hwnd,nItem);
8298 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8299 !LISTVIEW_IsSelected(hwnd,nItem))
8300 LISTVIEW_SetSelection(hwnd, nItem);
8304 LISTVIEW_RemoveAllSelections(hwnd);
8312 * Processes mouse up messages (right mouse button).
8315 * [I] HWND : window handle
8316 * [I] WORD : key flag
8317 * [I] WORD : x coordinate
8318 * [I] WORD : y coordinate
8323 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8326 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8328 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8330 if (infoPtr->bRButtonDown)
8332 LVHITTESTINFO lvHitTestInfo;
8336 lvHitTestInfo.pt.x = wPosX;
8337 lvHitTestInfo.pt.y = wPosY;
8339 /* Send NM_RClICK notification */
8340 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8341 if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8343 nmlv.iItem = lvHitTestInfo.iItem;
8344 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8351 nmlv.ptAction.x = wPosX;
8352 nmlv.ptAction.y = wPosY;
8353 listview_notify(hwnd, NM_RCLICK, &nmlv);
8358 /* set button flag */
8359 infoPtr->bRButtonDown = FALSE;
8361 /* Change to screen coordinate for WM_CONTEXTMENU */
8362 ClientToScreen(hwnd, &pt);
8364 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8365 SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
8376 * [I] HWND : window handle
8377 * [I] HWND : window handle of previously focused window
8382 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
8384 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8386 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
8388 /* send NM_SETFOCUS notification */
8389 hdr_notify(hwnd, NM_SETFOCUS);
8391 /* set window focus flag */
8392 infoPtr->bFocus = TRUE;
8404 * [I] HWND : window handle
8405 * [I] HFONT : font handle
8406 * [I] WORD : redraw flag
8411 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
8413 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8414 UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8416 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
8418 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8420 if (uView == LVS_REPORT)
8422 /* set header font */
8423 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
8424 MAKELPARAM(fRedraw, 0));
8427 /* invalidate listview control client area */
8428 InvalidateRect(hwnd, NULL, TRUE);
8430 if (fRedraw != FALSE)
8438 * Message handling for WM_SETREDRAW.
8439 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8442 * [I] HWND : window handle
8443 * [I] bRedraw: state of redraw flag
8446 * DefWinProc return value
8448 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
8450 LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0);
8452 RedrawWindow(hwnd, NULL, 0,
8453 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8459 * Resizes the listview control. This function processes WM_SIZE
8460 * messages. At this time, the width and height are not used.
8463 * [I] HWND : window handle
8464 * [I] WORD : new width
8465 * [I] WORD : new height
8470 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
8472 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8473 UINT uView = lStyle & LVS_TYPEMASK;
8475 TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
8477 LISTVIEW_UpdateSize(hwnd);
8479 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8481 if (lStyle & LVS_ALIGNLEFT)
8482 LISTVIEW_AlignLeft(hwnd);
8484 LISTVIEW_AlignTop(hwnd);
8487 LISTVIEW_UpdateScroll(hwnd);
8489 /* invalidate client area + erase background */
8490 InvalidateRect(hwnd, NULL, TRUE);
8497 * Sets the size information.
8500 * [I] HWND : window handle
8505 static VOID LISTVIEW_UpdateSize(HWND hwnd)
8507 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8508 LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8509 UINT uView = lStyle & LVS_TYPEMASK;
8512 TRACE("(hwnd=%x)\n", hwnd);
8514 GetClientRect(hwnd, &rcList);
8515 infoPtr->rcList.left = 0;
8516 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8517 infoPtr->rcList.top = 0;
8518 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8520 if (uView == LVS_LIST)
8522 if (lStyle & WS_HSCROLL)
8524 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8525 if (infoPtr->rcList.bottom > nHScrollHeight)
8526 infoPtr->rcList.bottom -= nHScrollHeight;
8529 else if (uView == LVS_REPORT)
8536 Header_Layout(infoPtr->hwndHeader, &hl);
8538 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8540 if (!(LVS_NOCOLUMNHEADER & lStyle))
8541 infoPtr->rcList.top = max(wp.cy, 0);
8547 * Processes WM_STYLECHANGED messages.
8550 * [I] HWND : window handle
8551 * [I] WPARAM : window style type (normal or extended)
8552 * [I] LPSTYLESTRUCT : window style information
8557 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
8560 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8561 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8562 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8563 RECT rcList = infoPtr->rcList;
8565 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
8566 hwnd, wStyleType, lpss);
8568 if (wStyleType == GWL_STYLE)
8570 if (uOldView == LVS_REPORT)
8571 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8573 if ((lpss->styleOld & WS_HSCROLL) != 0)
8574 ShowScrollBar(hwnd, SB_HORZ, FALSE);
8576 if ((lpss->styleOld & WS_VSCROLL) != 0)
8577 ShowScrollBar(hwnd, SB_VERT, FALSE);
8579 if (uNewView == LVS_ICON)
8581 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8582 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8583 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8584 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8585 if (lpss->styleNew & LVS_ALIGNLEFT)
8586 LISTVIEW_AlignLeft(hwnd);
8588 LISTVIEW_AlignTop(hwnd);
8590 else if (uNewView == LVS_REPORT)
8597 Header_Layout(infoPtr->hwndHeader, &hl);
8598 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
8600 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8601 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8603 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8604 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8605 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8606 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8608 else if (uNewView == LVS_LIST)
8610 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8611 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8612 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8613 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8617 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8618 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8619 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8620 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8621 if (lpss->styleNew & LVS_ALIGNLEFT)
8622 LISTVIEW_AlignLeft(hwnd);
8624 LISTVIEW_AlignTop(hwnd);
8627 /* update the size of the client area */
8628 LISTVIEW_UpdateSize(hwnd);
8630 /* add scrollbars if needed */
8631 LISTVIEW_UpdateScroll(hwnd);
8633 /* invalidate client area + erase background */
8634 InvalidateRect(hwnd, NULL, TRUE);
8636 /* print the list of unsupported window styles */
8637 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8640 /* If they change the view and we have an active edit control
8641 we will need to kill the control since the redraw will
8642 misplace the edit control.
8644 if (infoPtr->hwndEdit &&
8645 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8646 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8648 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8656 * Window procedure of the listview control.
8659 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
8662 TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
8663 if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
8664 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
8667 case LVM_APPROXIMATEVIEWRECT:
8668 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
8669 LOWORD(lParam), HIWORD(lParam));
8671 return LISTVIEW_Arrange(hwnd, (INT)wParam);
8673 /* case LVM_CREATEDRAGIMAGE: */
8675 case LVM_DELETEALLITEMS:
8676 return LISTVIEW_DeleteAllItems(hwnd);
8678 case LVM_DELETECOLUMN:
8679 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
8681 case LVM_DELETEITEM:
8682 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
8684 case LVM_EDITLABELW:
8685 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
8687 case LVM_EDITLABELA:
8688 return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
8690 case LVM_ENSUREVISIBLE:
8691 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
8694 return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
8697 return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
8699 case LVM_GETBKCOLOR:
8700 return LISTVIEW_GetBkColor(hwnd);
8702 /* case LVM_GETBKIMAGE: */
8704 case LVM_GETCALLBACKMASK:
8705 return LISTVIEW_GetCallbackMask(hwnd);
8707 case LVM_GETCOLUMNA:
8708 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8710 case LVM_GETCOLUMNW:
8711 return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8713 case LVM_GETCOLUMNORDERARRAY:
8714 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
8716 case LVM_GETCOLUMNWIDTH:
8717 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
8719 case LVM_GETCOUNTPERPAGE:
8720 return LISTVIEW_GetCountPerPage(hwnd);
8722 case LVM_GETEDITCONTROL:
8723 return LISTVIEW_GetEditControl(hwnd);
8725 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8726 return LISTVIEW_GetExtendedListViewStyle(hwnd);
8729 return LISTVIEW_GetHeader(hwnd);
8731 /* case LVM_GETHOTCURSOR: */
8733 case LVM_GETHOTITEM:
8734 return LISTVIEW_GetHotItem(hwnd);
8736 case LVM_GETHOVERTIME:
8737 return LISTVIEW_GetHoverTime(hwnd);
8739 case LVM_GETIMAGELIST:
8740 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
8742 /* case LVM_GETISEARCHSTRING: */
8745 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
8748 return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
8750 case LVM_GETITEMCOUNT:
8751 return LISTVIEW_GetItemCount(hwnd);
8753 case LVM_GETITEMPOSITION:
8754 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
8756 case LVM_GETITEMRECT:
8757 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
8759 case LVM_GETITEMSPACING:
8760 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
8762 case LVM_GETITEMSTATE:
8763 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
8765 case LVM_GETITEMTEXTA:
8766 LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8769 case LVM_GETITEMTEXTW:
8770 LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8773 case LVM_GETNEXTITEM:
8774 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
8776 /* case LVM_GETNUMBEROFWORKAREAS: */
8779 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
8781 case LVM_GETSELECTEDCOUNT:
8782 return LISTVIEW_GetSelectedCount(hwnd);
8784 case LVM_GETSELECTIONMARK:
8785 return LISTVIEW_GetSelectionMark(hwnd);
8787 case LVM_GETSTRINGWIDTHA:
8788 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
8790 case LVM_GETSTRINGWIDTHW:
8791 return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
8793 /* case LVM_GETSUBITEMRECT: */
8795 case LVM_GETTEXTBKCOLOR:
8796 return LISTVIEW_GetTextBkColor(hwnd);
8798 case LVM_GETTEXTCOLOR:
8799 return LISTVIEW_GetTextColor(hwnd);
8801 /* case LVM_GETTOOLTIPS: */
8803 case LVM_GETTOPINDEX:
8804 return LISTVIEW_GetTopIndex(hwnd);
8806 /* case LVM_GETUNICODEFORMAT: */
8808 case LVM_GETVIEWRECT:
8809 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
8811 /* case LVM_GETWORKAREAS: */
8814 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
8816 case LVM_INSERTCOLUMNA:
8817 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8819 case LVM_INSERTCOLUMNW:
8820 return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8822 case LVM_INSERTITEMA:
8823 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
8825 case LVM_INSERTITEMW:
8826 return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
8828 case LVM_REDRAWITEMS:
8829 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
8831 /* case LVM_SCROLL: */
8832 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
8834 case LVM_SETBKCOLOR:
8835 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
8837 /* case LVM_SETBKIMAGE: */
8839 case LVM_SETCALLBACKMASK:
8840 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
8842 case LVM_SETCOLUMNA:
8843 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
8845 case LVM_SETCOLUMNW:
8846 return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
8848 case LVM_SETCOLUMNORDERARRAY:
8849 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
8851 case LVM_SETCOLUMNWIDTH:
8852 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
8854 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8855 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
8857 /* case LVM_SETHOTCURSOR: */
8859 case LVM_SETHOTITEM:
8860 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
8862 case LVM_SETHOVERTIME:
8863 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
8865 /* case LVM_SETICONSPACING: */
8867 case LVM_SETIMAGELIST:
8868 return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
8871 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
8874 return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
8876 case LVM_SETITEMCOUNT:
8877 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
8879 case LVM_SETITEMPOSITION:
8880 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
8881 (INT)HIWORD(lParam));
8883 case LVM_SETITEMPOSITION32:
8884 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
8885 ((POINT*)lParam)->y);
8887 case LVM_SETITEMSTATE:
8888 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam);
8890 case LVM_SETITEMTEXTA:
8891 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
8893 case LVM_SETITEMTEXTW:
8894 return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
8896 case LVM_SETSELECTIONMARK:
8897 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
8899 case LVM_SETTEXTBKCOLOR:
8900 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
8902 case LVM_SETTEXTCOLOR:
8903 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
8905 /* case LVM_SETTOOLTIPS: */
8906 /* case LVM_SETUNICODEFORMAT: */
8907 /* case LVM_SETWORKAREAS: */
8910 return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
8912 /* case LVM_SUBITEMHITTEST: */
8915 return LISTVIEW_Update(hwnd, (INT)wParam);
8918 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
8921 return LISTVIEW_Command(hwnd, wParam, lParam);
8924 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
8927 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
8930 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8933 return LISTVIEW_GetFont(hwnd);
8936 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
8937 (INT)HIWORD(wParam), (HWND)lParam);
8940 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
8943 return LISTVIEW_KillFocus(hwnd);
8945 case WM_LBUTTONDBLCLK:
8946 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
8949 case WM_LBUTTONDOWN:
8950 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
8953 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
8956 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
8959 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
8962 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
8965 return LISTVIEW_NCDestroy(hwnd);
8968 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
8970 case WM_NOTIFYFORMAT:
8971 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
8974 return LISTVIEW_Paint(hwnd, (HDC)wParam);
8976 case WM_RBUTTONDBLCLK:
8977 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
8980 case WM_RBUTTONDOWN:
8981 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
8985 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
8989 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
8992 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
8995 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
8998 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9000 case WM_STYLECHANGED:
9001 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9003 /* case WM_TIMER: */
9006 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
9007 (INT)HIWORD(wParam), (HWND)lParam);
9010 if (wParam & (MK_SHIFT | MK_CONTROL))
9011 return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9012 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
9014 /* case WM_WININICHANGE: */
9017 if (uMsg >= WM_USER)
9019 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
9023 /* call default window procedure */
9024 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9032 * Registers the window class.
9040 VOID LISTVIEW_Register(void)
9044 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9045 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9046 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9047 wndClass.cbClsExtra = 0;
9048 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9049 wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
9050 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9051 wndClass.lpszClassName = WC_LISTVIEWW;
9052 RegisterClassW(&wndClass);
9057 * Unregisters the window class.
9065 VOID LISTVIEW_Unregister(void)
9067 UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
9072 * Handle any WM_COMMAND messages
9078 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9080 switch (HIWORD(wParam))
9085 * Adjust the edit window size
9088 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9089 HDC hdc = GetDC(infoPtr->hwndEdit);
9090 HFONT hFont, hOldFont = 0;
9095 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9096 GetWindowRect(infoPtr->hwndEdit, &rect);
9098 /* Select font to get the right dimension of the string */
9099 hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9102 hOldFont = SelectObject(hdc, hFont);
9105 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9107 TEXTMETRICW textMetric;
9109 /* Add Extra spacing for the next character */
9110 GetTextMetricsW(hdc, &textMetric);
9111 sz.cx += (textMetric.tmMaxCharWidth * 2);
9119 rect.bottom - rect.top,
9120 SWP_DRAWFRAME|SWP_NOMOVE);
9123 SelectObject(hdc, hOldFont);
9125 ReleaseDC(hwnd, hdc);
9131 return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9140 * Subclassed edit control windproc function
9146 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg,
9147 WPARAM wParam, LPARAM lParam)
9149 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9150 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9151 static BOOL bIgnoreKillFocus = FALSE;
9152 BOOL cancel = FALSE;
9157 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9160 if(bIgnoreKillFocus) return TRUE;
9165 WNDPROC editProc = einfo->EditWndProc;
9166 SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9167 COMCTL32_Free(einfo);
9168 infoPtr->pedititem = NULL;
9169 return CallWindowProcW(editProc, hwnd, uMsg, wParam, lParam);
9173 if (VK_ESCAPE == (INT)wParam)
9178 else if (VK_RETURN == (INT)wParam)
9182 return CallWindowProcW(einfo->EditWndProc, hwnd,
9183 uMsg, wParam, lParam);
9186 if (einfo->EditLblCb)
9188 WCHAR *buffer = NULL;
9192 int len = 1 + GetWindowTextLengthW(hwnd);
9196 if ( (buffer = COMCTL32_Alloc(len*sizeof(WCHAR))) )
9197 GetWindowTextW(hwnd, buffer, len);
9200 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9201 /* eg. Using a messagebox */
9202 bIgnoreKillFocus = TRUE;
9203 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9205 if (buffer) COMCTL32_Free(buffer);
9207 einfo->EditLblCb = NULL;
9208 bIgnoreKillFocus = FALSE;
9211 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9218 * Creates a subclassed edit cotrol
9224 HWND CreateEditLabelW(LPCWSTR text, DWORD style, INT x, INT y,
9225 INT width, INT height, HWND parent, HINSTANCE hinst,
9226 EditlblCallbackW EditLblCb, DWORD param)
9228 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
9229 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9234 TEXTMETRICW textMetric;
9236 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9239 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9240 hdc = GetDC(parent);
9242 /* Select the font to get appropriate metric dimensions */
9243 if(infoPtr->hFont != 0)
9244 hOldFont = SelectObject(hdc, infoPtr->hFont);
9246 /*Get String Lenght in pixels */
9247 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9249 /*Add Extra spacing for the next character */
9250 GetTextMetricsW(hdc, &textMetric);
9251 sz.cx += (textMetric.tmMaxCharWidth * 2);
9253 if(infoPtr->hFont != 0)
9254 SelectObject(hdc, hOldFont);
9256 ReleaseDC(parent, hdc);
9257 if (!(hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height,
9258 parent, 0, hinst, 0)))
9260 COMCTL32_Free(infoPtr->pedititem);
9264 infoPtr->pedititem->param = param;
9265 infoPtr->pedititem->EditLblCb = EditLblCb;
9266 infoPtr->pedititem->EditWndProc = (WNDPROC)SetWindowLongW(hedit,
9267 GWL_WNDPROC, (LONG) EditLblWndProcW);
9269 SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);