4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
9 * Listview control implementation.
12 * 1. No horizontal scrolling when header is larger than the client area.
13 * 2. Drawing optimizations.
14 * 3. Hot item handling.
17 * LISTVIEW_Notify : most notifications from children (editbox and header)
20 * LISTVIEW_SetItemCount : not completed for non OWNERDATA
23 * LISTVIEW_SetItemW : no unicode support
24 * LISTVIEW_InsertItemW : no unicode support
25 * LISTVIEW_InsertColumnW : no unicode support
26 * LISTVIEW_GetColumnW : no unicode support
27 * LISTVIEW_SetColumnW : no unicode support
29 * Advanced functionality:
30 * LISTVIEW_GetNumberOfWorkAreas : not implemented
31 * LISTVIEW_GetHotCursor : not implemented
32 * LISTVIEW_GetISearchString : not implemented
33 * LISTVIEW_GetBkImage : not implemented
34 * LISTVIEW_SetBkImage : not implemented
35 * LISTVIEW_GetColumnOrderArray : simple hack only
36 * LISTVIEW_SetColumnOrderArray : simple hack only
37 * LISTVIEW_Arrange : empty stub
38 * LISTVIEW_ApproximateViewRect : incomplete
39 * LISTVIEW_Scroll : not implemented
40 * LISTVIEW_Update : not completed
50 #include "debugtools.h"
52 DEFAULT_DEBUG_CHANNEL(listview);
54 /* Some definitions for inline edit control */
55 typedef BOOL (*EditlblCallback)(HWND, LPSTR, DWORD);
57 typedef struct tagEDITLABEL_ITEM
61 EditlblCallback EditLblCb;
64 typedef struct tagLISTVIEW_SUBITEM
72 typedef struct tagLISTVIEW_ITEM
83 typedef struct tagLISTVIEW_SELECTION
89 typedef struct tagLISTVIEW_INFO
94 HIMAGELIST himlNormal;
100 HDPA hdpaSelectionRanges;
115 DWORD dwExStyle; /* extended listview style */
117 PFNLVCOMPARE pfnCompare;
121 EDITLABEL_ITEM *pedititem;
123 INT nColumnCount; /* the number of columns in this control */
125 DWORD lastKeyPressTimestamp; /* Added */
126 WPARAM charCode; /* Added */
127 INT nSearchParamLength; /* Added */
128 CHAR szSearchParam[ MAX_PATH ]; /* Added */
135 /* maximum size of a label */
136 #define DISP_TEXT_SIZE 512
138 /* padding for items in list and small icon display modes */
139 #define WIDTH_PADDING 12
141 /* padding for items in list, report and small icon display modes */
142 #define HEIGHT_PADDING 1
144 /* offset of items in report display mode */
145 #define REPORT_MARGINX 2
147 /* padding for icon in large icon display mode */
148 #define ICON_TOP_PADDING 2
149 #define ICON_BOTTOM_PADDING 2
151 /* padding for label in large icon display mode */
152 #define LABEL_VERT_OFFSET 2
154 /* default label width for items in list and small icon display modes */
155 #define DEFAULT_LABEL_WIDTH 40
157 /* default column width for items in list display mode */
158 #define DEFAULT_COLUMN_WIDTH 96
160 /* Increment size of the horizontal scroll bar */
161 #define LISTVIEW_SCROLL_DIV_SIZE 10
163 /* Padding betwen image and label */
164 #define IMAGE_PADDING 2
166 /* Padding behind the label */
167 #define TRAILING_PADDING 5
169 /* Border for the icon caption */
170 #define CAPTION_BORDER 2
174 #define ListView_LVNotify(hwnd,lCtrlId,plvnm) \
175 (BOOL)SendMessageA((hwnd),WM_NOTIFY,(WPARAM)(INT)lCtrlId,(LPARAM)(LPNMLISTVIEW)(plvnm))
176 #define ListView_Notify(hwnd,lCtrlId,pnmh) \
177 (BOOL)SendMessageA((hwnd),WM_NOTIFY,(WPARAM)(INT)lCtrlId,(LPARAM)(LPNMHDR)(pnmh))
178 /* retrieve the number of items in the listview */
179 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
181 HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y,
182 INT width, INT height, HWND parent, HINSTANCE hinst,
183 EditlblCallback EditLblCb, DWORD param);
186 * forward declarations
188 static LRESULT LISTVIEW_GetItemA(HWND hwnd, LPLVITEMA lpLVItem, BOOL internal);
189 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL);
190 static INT LISTVIEW_GetCountPerRow(HWND);
191 static INT LISTVIEW_GetCountPerColumn(HWND);
192 static VOID LISTVIEW_AlignLeft(HWND);
193 static VOID LISTVIEW_AlignTop(HWND);
194 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
195 static VOID LISTVIEW_AddSelection(HWND, INT);
196 static BOOL LISTVIEW_AddSubItem(HWND, LPLVITEMA);
197 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
198 static INT LISTVIEW_GetItemHeight(HWND);
199 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
200 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
201 static INT LISTVIEW_GetItemWidth(HWND);
202 static INT LISTVIEW_GetLabelWidth(HWND, INT);
203 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
204 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
205 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
206 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
207 static BOOL LISTVIEW_InitItem(HWND, LISTVIEW_ITEM *, LPLVITEMA);
208 static BOOL LISTVIEW_InitSubItem(HWND, LISTVIEW_SUBITEM *, LPLVITEMA);
209 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
210 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
211 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
212 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
213 static BOOL LISTVIEW_SetItem(HWND, LPLVITEMA);
214 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
215 static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG);
216 static VOID LISTVIEW_UpdateScroll(HWND);
217 static VOID LISTVIEW_SetSelection(HWND, INT);
218 static VOID LISTVIEW_UpdateSize(HWND);
219 static BOOL LISTVIEW_SetSubItem(HWND, LPLVITEMA);
220 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
221 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
222 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
223 static HWND LISTVIEW_EditLabelA(HWND hwnd, INT nItem);
224 static BOOL LISTVIEW_EndEditLabel(HWND hwnd, LPSTR pszText, DWORD nItem);
225 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
226 static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam);
227 static LRESULT LISTVIEW_GetStringWidthA(HWND hwnd, LPCSTR lpszText);
228 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
229 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
230 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
231 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem);
232 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem);
233 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
234 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
236 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
237 #define KEY_DELAY 450
241 LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
244 LISTVIEW_INFO *infoPtr;
245 NMLVCUSTOMDRAW nmcdhdr;
248 TRACE("drawstage:%lx hdc:%x\n", dwDrawStage, hdc);
250 infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
252 nmcd= & nmcdhdr.nmcd;
253 nmcd->hdr.hwndFrom = hwnd;
254 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
255 nmcd->hdr.code = NM_CUSTOMDRAW;
256 nmcd->dwDrawStage= dwDrawStage;
258 nmcd->rc.left = rc.left;
259 nmcd->rc.right = rc.right;
260 nmcd->rc.bottom = rc.bottom;
261 nmcd->rc.top = rc.top;
262 nmcd->dwItemSpec = 0;
263 nmcd->uItemState = 0;
264 nmcd->lItemlParam= 0;
265 nmcdhdr.clrText = infoPtr->clrText;
266 nmcdhdr.clrTextBk= infoPtr->clrBk;
268 return (BOOL)SendMessageA (GetParent (hwnd), WM_NOTIFY,
269 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
273 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
274 UINT iItem, UINT iSubItem,
277 LISTVIEW_INFO *infoPtr;
278 NMLVCUSTOMDRAW nmcdhdr;
280 DWORD dwDrawStage,dwItemSpec;
286 infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
288 ZeroMemory(&item,sizeof(LVITEMA));
290 item.mask = LVIF_PARAM;
291 ListView_GetItemA(hwnd,&item);
293 dwDrawStage=CDDS_ITEM | uItemDrawState;
297 if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
298 if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS;
299 if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT;
301 itemRect.left = LVIR_BOUNDS;
302 LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
304 nmcd= & nmcdhdr.nmcd;
305 nmcd->hdr.hwndFrom = hwnd;
306 nmcd->hdr.idFrom = GetWindowLongA( hwnd, GWL_ID);
307 nmcd->hdr.code = NM_CUSTOMDRAW;
308 nmcd->dwDrawStage= dwDrawStage;
310 nmcd->rc.left = itemRect.left;
311 nmcd->rc.right = itemRect.right;
312 nmcd->rc.bottom = itemRect.bottom;
313 nmcd->rc.top = itemRect.top;
314 nmcd->dwItemSpec = dwItemSpec;
315 nmcd->uItemState = uItemState;
316 nmcd->lItemlParam= item.lParam;
317 nmcdhdr.clrText = infoPtr->clrText;
318 nmcdhdr.clrTextBk= infoPtr->clrBk;
319 nmcdhdr.iSubItem =iSubItem;
321 TRACE("drawstage:%lx hdc:%x item:%lx, itemstate:%x, lItemlParam:%lx\n",
322 nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
323 nmcd->uItemState, nmcd->lItemlParam);
325 retval=SendMessageA (GetParent (hwnd), WM_NOTIFY,
326 (WPARAM) GetWindowLongA( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
328 infoPtr->clrText=nmcdhdr.clrText;
329 infoPtr->clrBk =nmcdhdr.clrTextBk;
330 return (BOOL) retval;
334 /*************************************************************************
335 * LISTVIEW_ProcessLetterKeys
337 * Processes keyboard messages generated by pressing the letter keys
339 * What this does is perform a case insensitive search from the
340 * current position with the following quirks:
341 * - If two chars or more are pressed in quick succession we search
342 * for the corresponding string (e.g. 'abc').
343 * - If there is a delay we wipe away the current search string and
344 * restart with just that char.
345 * - If the user keeps pressing the same character, whether slowly or
346 * fast, so that the search string is entirely composed of this
347 * character ('aaaaa' for instance), then we search for first item
348 * that starting with that character.
349 * - If the user types the above character in quick succession, then
350 * we must also search for the corresponding string ('aaaaa'), and
351 * go to that string if there is a match.
359 * - The current implementation has a list of characters it will
360 * accept and it ignores averything else. In particular it will
361 * ignore accentuated characters which seems to match what
362 * Windows does. But I'm not sure it makes sense to follow
364 * - We don't sound a beep when the search fails.
368 * TREEVIEW_ProcessLetterKeys
370 static INT LISTVIEW_ProcessLetterKeys(
371 HWND hwnd, /* handle to the window */
372 WPARAM charCode, /* the character code, the actual character */
373 LPARAM keyData /* key data */
376 LISTVIEW_INFO *infoPtr;
381 CHAR buffer[MAX_PATH];
382 DWORD timestamp,elapsed;
384 /* simple parameter checking */
385 if (!hwnd || !charCode || !keyData)
388 infoPtr=(LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
392 /* only allow the valid WM_CHARs through */
393 if (!isalnum(charCode) &&
394 charCode != '.' && charCode != '`' && charCode != '!' &&
395 charCode != '@' && charCode != '#' && charCode != '$' &&
396 charCode != '%' && charCode != '^' && charCode != '&' &&
397 charCode != '*' && charCode != '(' && charCode != ')' &&
398 charCode != '-' && charCode != '_' && charCode != '+' &&
399 charCode != '=' && charCode != '\\'&& charCode != ']' &&
400 charCode != '}' && charCode != '[' && charCode != '{' &&
401 charCode != '/' && charCode != '?' && charCode != '>' &&
402 charCode != '<' && charCode != ',' && charCode != '~')
405 nSize=GETITEMCOUNT(infoPtr);
406 /* if there's one item or less, there is no where to go */
410 /* compute how much time elapsed since last keypress */
411 timestamp=GetTickCount();
412 if (timestamp > infoPtr->lastKeyPressTimestamp) {
413 elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
415 elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
418 /* update the search parameters */
419 infoPtr->lastKeyPressTimestamp=timestamp;
420 if (elapsed < KEY_DELAY) {
421 if (infoPtr->nSearchParamLength < sizeof(infoPtr->szSearchParam)) {
422 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
424 if (infoPtr->charCode != charCode) {
425 infoPtr->charCode=charCode=0;
428 infoPtr->charCode=charCode;
429 infoPtr->szSearchParam[0]=charCode;
430 infoPtr->nSearchParamLength=1;
431 /* Redundant with the 1 char string */
435 /* and search from the current position */
437 if (infoPtr->nFocusedItem >= 0) {
438 endidx=infoPtr->nFocusedItem;
440 /* if looking for single character match,
441 * then we must always move forward
443 if (infoPtr->nSearchParamLength == 1)
457 ZeroMemory(&item, sizeof(item));
458 item.mask = LVIF_TEXT;
461 item.pszText = buffer;
462 item.cchTextMax = sizeof(buffer);
463 ListView_GetItemA( hwnd, &item );
465 /* check for a match */
466 if (strncasecmp(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
469 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
470 (strncasecmp(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
471 /* This would work but we must keep looking for a longer match */
475 } while (idx != endidx);
478 if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
479 /* refresh client area */
480 InvalidateRect(hwnd, NULL, TRUE);
488 /*************************************************************************
489 * LISTVIEW_UpdateHeaderSize [Internal]
491 * Function to resize the header control
494 * hwnd [I] handle to a window
495 * nNewScrollPos [I] Scroll Pos to Set
502 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
504 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
508 GetWindowRect(infoPtr->hwndHeader, &winRect);
509 point[0].x = winRect.left;
510 point[0].y = winRect.top;
511 point[1].x = winRect.right;
512 point[1].y = winRect.bottom;
514 MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
515 point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
516 point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
518 SetWindowPos(infoPtr->hwndHeader,0,
519 point[0].x,point[0].y,point[1].x,point[1].y,
520 SWP_NOZORDER | SWP_NOACTIVATE);
525 * Update the scrollbars. This functions should be called whenever
526 * the content, size or view changes.
529 * [I] HWND : window handle
534 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
536 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
537 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
538 UINT uView = lStyle & LVS_TYPEMASK;
539 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
540 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
541 SCROLLINFO scrollInfo;
543 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
544 scrollInfo.cbSize = sizeof(SCROLLINFO);
546 if (uView == LVS_LIST)
548 /* update horizontal scrollbar */
550 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
551 INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
552 INT nNumOfItems = GETITEMCOUNT(infoPtr);
554 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
555 if((nNumOfItems % nCountPerColumn) == 0)
559 scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
560 scrollInfo.nPage = nCountPerRow;
561 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
562 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
563 ShowScrollBar(hwnd, SB_VERT, FALSE);
565 else if (uView == LVS_REPORT)
567 /* update vertical scrollbar */
569 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
570 scrollInfo.nPos = ListView_GetTopIndex(hwnd);
571 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
572 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
573 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
575 /* update horizontal scrollbar */
576 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
577 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
578 || GETITEMCOUNT(infoPtr) == 0)
583 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
584 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
585 scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
586 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
588 /* Update the Header Control */
589 scrollInfo.fMask = SIF_POS;
590 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
591 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
598 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
600 INT nViewWidth = rcView.right - rcView.left;
601 INT nViewHeight = rcView.bottom - rcView.top;
603 /* Update Horizontal Scrollbar */
604 scrollInfo.fMask = SIF_POS;
605 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
606 || GETITEMCOUNT(infoPtr) == 0)
610 scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
612 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
613 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
614 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
616 /* Update Vertical Scrollbar */
617 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
618 scrollInfo.fMask = SIF_POS;
619 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
620 || GETITEMCOUNT(infoPtr) == 0)
624 scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
626 scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
627 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
628 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
635 * Prints a message for unsupported window styles.
636 * A kind of TODO list for window styles.
639 * [I] LONG : window style
644 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
646 if ((LVS_TYPEMASK & lStyle) == LVS_EDITLABELS)
648 FIXME(" LVS_EDITLABELS\n");
651 if ((LVS_TYPEMASK & lStyle) == LVS_NOLABELWRAP)
653 FIXME(" LVS_NOLABELWRAP\n");
656 if ((LVS_TYPEMASK & lStyle) == LVS_NOSCROLL)
658 FIXME(" LVS_NOSCROLL\n");
661 if ((LVS_TYPEMASK & lStyle) == LVS_NOSORTHEADER)
663 FIXME(" LVS_NOSORTHEADER\n");
666 if ((LVS_TYPEMASK & lStyle) == LVS_OWNERDRAWFIXED)
668 FIXME(" LVS_OWNERDRAWFIXED\n");
671 if ((LVS_TYPEMASK & lStyle) == LVS_SHAREIMAGELISTS)
673 FIXME(" LVS_SHAREIMAGELISTS\n");
676 if ((LVS_TYPEMASK & lStyle) == LVS_SORTASCENDING)
678 FIXME(" LVS_SORTASCENDING\n");
681 if ((LVS_TYPEMASK & lStyle) == LVS_SORTDESCENDING)
683 FIXME(" LVS_SORTDESCENDING\n");
689 * Aligns the items with the top edge of the window.
692 * [I] HWND : window handle
697 static VOID LISTVIEW_AlignTop(HWND hwnd)
699 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
700 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
701 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
706 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
708 ZeroMemory(&ptItem, sizeof(POINT));
709 ZeroMemory(&rcView, sizeof(RECT));
711 if (nListWidth > infoPtr->nItemWidth)
713 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
715 if (ptItem.x + infoPtr->nItemWidth > nListWidth)
718 ptItem.y += infoPtr->nItemHeight;
721 ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
722 ptItem.x += infoPtr->nItemWidth;
723 rcView.right = max(rcView.right, ptItem.x);
726 rcView.bottom = ptItem.y + infoPtr->nItemHeight;
730 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
732 ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
733 ptItem.y += infoPtr->nItemHeight;
736 rcView.right = infoPtr->nItemWidth;
737 rcView.bottom = ptItem.y;
740 LISTVIEW_SetViewRect(hwnd, &rcView);
746 * Aligns the items with the left edge of the window.
749 * [I] HWND : window handle
754 static VOID LISTVIEW_AlignLeft(HWND hwnd)
756 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
757 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
758 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
763 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
765 ZeroMemory(&ptItem, sizeof(POINT));
766 ZeroMemory(&rcView, sizeof(RECT));
768 if (nListHeight > infoPtr->nItemHeight)
770 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
772 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
775 ptItem.x += infoPtr->nItemWidth;
778 ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
779 ptItem.y += infoPtr->nItemHeight;
780 rcView.bottom = max(rcView.bottom, ptItem.y);
783 rcView.right = ptItem.x + infoPtr->nItemWidth;
787 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
789 ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
790 ptItem.x += infoPtr->nItemWidth;
793 rcView.bottom = infoPtr->nItemHeight;
794 rcView.right = ptItem.x;
797 LISTVIEW_SetViewRect(hwnd, &rcView);
803 * Set the bounding rectangle of all the items.
806 * [I] HWND : window handle
807 * [I] LPRECT : bounding rectangle
813 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
815 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
816 BOOL bResult = FALSE;
818 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
819 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
821 if (lprcView != NULL)
824 infoPtr->rcView.left = lprcView->left;
825 infoPtr->rcView.top = lprcView->top;
826 infoPtr->rcView.right = lprcView->right;
827 infoPtr->rcView.bottom = lprcView->bottom;
835 * Retrieves the bounding rectangle of all the items.
838 * [I] HWND : window handle
839 * [O] LPRECT : bounding rectangle
845 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
847 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
848 BOOL bResult = FALSE;
851 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
853 if (lprcView != NULL)
855 bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
856 if (bResult != FALSE)
858 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
859 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
860 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
861 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
864 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
865 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
873 * Retrieves the subitem pointer associated with the subitem index.
876 * [I] HDPA : DPA handle for a specific item
877 * [I] INT : index of subitem
880 * SUCCESS : subitem pointer
883 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
886 LISTVIEW_SUBITEM *lpSubItem;
889 for (i = 1; i < hdpaSubItems->nItemCount; i++)
891 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
892 if (lpSubItem != NULL)
894 if (lpSubItem->iSubItem == nSubItem)
906 * Calculates the width of an item.
909 * [I] HWND : window handle
910 * [I] LONG : window style
913 * Returns item width.
915 static INT LISTVIEW_GetItemWidth(HWND hwnd)
917 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
918 LONG style = GetWindowLongA(hwnd, GWL_STYLE);
919 UINT uView = style & LVS_TYPEMASK;
920 INT nHeaderItemCount;
926 TRACE("(hwnd=%x)\n", hwnd);
928 if (uView == LVS_ICON)
930 nItemWidth = infoPtr->iconSpacing.cx;
932 else if (uView == LVS_REPORT)
934 /* calculate width of header */
935 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
936 for (i = 0; i < nHeaderItemCount; i++)
938 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
940 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
946 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
948 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
949 nItemWidth = max(nItemWidth, nLabelWidth);
952 /* default label size */
953 if (GETITEMCOUNT(infoPtr) == 0)
955 nItemWidth = DEFAULT_COLUMN_WIDTH;
961 nItemWidth = DEFAULT_LABEL_WIDTH;
966 nItemWidth += WIDTH_PADDING;
968 if (infoPtr->himlSmall != NULL)
970 nItemWidth += infoPtr->iconSize.cx;
973 if (infoPtr->himlState != NULL)
975 nItemWidth += infoPtr->iconSize.cx;
982 /* nItemWidth Cannot be Zero */
990 * Calculates the width of a specific item.
993 * [I] HWND : window handle
997 * Returns the width of an item width a specified string.
999 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1001 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1002 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1003 INT nHeaderItemCount;
1008 TRACE("(hwnd=%x)\n", hwnd);
1010 if (uView == LVS_ICON)
1012 nItemWidth = infoPtr->iconSpacing.cx;
1014 else if (uView == LVS_REPORT)
1016 /* calculate width of header */
1017 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1018 for (i = 0; i < nHeaderItemCount; i++)
1020 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1022 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1028 /* get width of string */
1029 nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1031 /* default label size */
1032 if (GETITEMCOUNT(infoPtr) == 0)
1034 nItemWidth = DEFAULT_COLUMN_WIDTH;
1038 if (nItemWidth == 0)
1040 nItemWidth = DEFAULT_LABEL_WIDTH;
1045 nItemWidth += WIDTH_PADDING;
1047 if (infoPtr->himlSmall != NULL)
1049 nItemWidth += infoPtr->iconSize.cx;
1052 if (infoPtr->himlState != NULL)
1054 nItemWidth += infoPtr->iconSize.cx;
1065 * Calculates the height of an item.
1068 * [I] HWND : window handle
1069 * [I] LONG : window style
1072 * Returns item height.
1074 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1076 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1077 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1078 INT nItemHeight = 0;
1080 if (uView == LVS_ICON)
1082 nItemHeight = infoPtr->iconSpacing.cy;
1087 HDC hdc = GetDC(hwnd);
1088 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1089 GetTextMetricsA(hdc, &tm);
1091 if(infoPtr->himlState || infoPtr->himlSmall)
1092 nItemHeight = max(tm.tmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1094 nItemHeight = tm.tmHeight;
1096 SelectObject(hdc, hOldFont);
1097 ReleaseDC(hwnd, hdc);
1104 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1106 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1107 LISTVIEW_SELECTION *selection;
1108 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1111 TRACE("Selections are:\n");
1112 for (i = 0; i < topSelection; i++)
1114 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1115 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1121 * A compare function for selection ranges
1124 * [I] LPVOID : Item 1;
1125 * [I] LPVOID : Item 2;
1126 * [I] LPARAM : flags
1129 * >0 : if Item 1 > Item 2
1130 * <0 : if Item 2 > Item 1
1131 * 0 : if Item 1 == Item 2
1133 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1136 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1137 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1138 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1139 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1153 * Adds a selection range.
1156 * [I] HWND : window handle
1157 * [I] INT : lower item index
1158 * [I] INT : upper item index
1163 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1165 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1166 LISTVIEW_SELECTION *selection;
1167 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1168 BOOL lowerzero=FALSE;
1170 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1171 selection->lower = lItem;
1172 selection->upper = uItem;
1174 TRACE("Add range %i - %i\n",lItem,uItem);
1177 LISTVIEW_SELECTION *checkselection,*checkselection2;
1178 INT index,mergeindex;
1180 /* find overlapping selections */
1181 /* we want to catch adjacent ranges so expand our range by 1 */
1184 if (selection->lower == 0)
1189 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1190 LISTVIEW_CompareSelectionRanges,
1192 selection->upper --;
1196 selection->lower ++;
1200 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1201 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1202 checkselection->upper);
1204 checkselection->lower = min(selection->lower,checkselection->lower);
1205 checkselection->upper = max(selection->upper,checkselection->upper);
1207 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1208 checkselection->upper);
1210 COMCTL32_Free(selection);
1212 /* merge now common selection ranges in the lower group*/
1215 checkselection->upper ++;
1216 if (checkselection->lower == 0)
1219 checkselection->lower --;
1221 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1222 checkselection->upper);
1224 /* not sorted yet */
1225 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1226 LISTVIEW_CompareSelectionRanges, 0,
1229 checkselection->upper --;
1233 checkselection->lower ++;
1235 if (mergeindex >=0 && mergeindex != index)
1237 TRACE("Merge with index %i\n",mergeindex);
1238 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1240 checkselection->lower = min(checkselection->lower,
1241 checkselection2->lower);
1242 checkselection->upper = max(checkselection->upper,
1243 checkselection2->upper);
1244 COMCTL32_Free(checkselection2);
1245 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1249 while (mergeindex > -1 && mergeindex <index);
1251 /* merge now common selection ranges in the upper group*/
1254 checkselection->upper ++;
1255 if (checkselection->lower == 0)
1258 checkselection->lower --;
1260 TRACE("search upper range %i (%lu - %lu)\n",index,
1261 checkselection->lower, checkselection->upper);
1263 /* not sorted yet */
1264 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1266 LISTVIEW_CompareSelectionRanges, 0,
1269 checkselection->upper --;
1273 checkselection->lower ++;
1275 if (mergeindex >=0 && mergeindex !=index)
1277 TRACE("Merge with index %i\n",mergeindex);
1278 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1280 checkselection->lower = min(checkselection->lower,
1281 checkselection2->lower);
1282 checkselection->upper = max(checkselection->upper,
1283 checkselection2->upper);
1284 COMCTL32_Free(checkselection2);
1285 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1288 while (mergeindex > -1);
1293 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1294 LISTVIEW_CompareSelectionRanges, 0,
1297 TRACE("Insert before index %i\n",index);
1300 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1305 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1310 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1311 LISTVIEW_PrintSelectionRanges(hwnd);
1316 * check if a specified index is selected.
1319 * [I] HWND : window handle
1320 * [I] INT : item index
1325 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1327 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1328 LISTVIEW_SELECTION selection;
1331 selection.upper = nItem;
1332 selection.lower = nItem;
1334 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1335 LISTVIEW_CompareSelectionRanges,
1345 * Removes all selection ranges
1348 * HWND: window handle
1354 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1356 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1357 LISTVIEW_SELECTION *selection;
1361 TRACE("(0x%x)\n",hwnd);
1363 ZeroMemory(&item,sizeof(LVITEMA));
1364 item.stateMask = LVIS_SELECTED;
1368 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1371 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1372 for (i = selection->lower; i<=selection->upper; i++)
1373 LISTVIEW_SetItemState(hwnd,i,&item);
1374 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1377 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1385 * Removes a range selections.
1388 * [I] HWND : window handle
1389 * [I] INT : lower item index
1390 * [I] INT : upper item index
1395 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1397 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1398 LISTVIEW_SELECTION removeselection,*checkselection;
1401 removeselection.lower = lItem;
1402 removeselection.upper = uItem;
1404 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1405 LISTVIEW_PrintSelectionRanges(hwnd);
1407 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1408 LISTVIEW_CompareSelectionRanges,
1415 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1418 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1419 checkselection->upper);
1422 if ((checkselection->upper == removeselection.upper) &&
1423 (checkselection->lower == removeselection.lower))
1425 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1428 /* case 2: engulf */
1429 else if (((checkselection->upper < removeselection.upper) &&
1430 (checkselection->lower > removeselection.lower))||
1431 ((checkselection->upper <= removeselection.upper) &&
1432 (checkselection->lower > removeselection.lower)) ||
1433 ((checkselection->upper < removeselection.upper) &&
1434 (checkselection->lower >= removeselection.lower)))
1437 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1438 /* do it again because others may also get caught */
1440 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1442 /* case 3: overlap upper */
1443 else if ((checkselection->upper < removeselection.upper) &&
1444 (checkselection->lower < removeselection.lower))
1446 checkselection->upper = removeselection.lower - 1;
1448 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1450 /* case 4: overlap lower */
1451 else if ((checkselection->upper > removeselection.upper) &&
1452 (checkselection->lower > removeselection.lower))
1454 checkselection->lower = removeselection.upper + 1;
1456 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1458 /* case 5: fully internal */
1459 else if (checkselection->upper == removeselection.upper)
1460 checkselection->upper = removeselection.lower - 1;
1461 else if (checkselection->lower == removeselection.lower)
1462 checkselection->lower = removeselection.upper + 1;
1465 /* bisect the range */
1466 LISTVIEW_SELECTION *newselection;
1468 newselection = (LISTVIEW_SELECTION *)
1469 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1470 newselection -> lower = checkselection->lower;
1471 newselection -> upper = removeselection.lower - 1;
1472 checkselection -> lower = removeselection.upper + 1;
1473 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1475 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1477 LISTVIEW_PrintSelectionRanges(hwnd);
1482 * Updates the various indices after an item has been inserted or deleted.
1485 * [I] HWND : window handle
1486 * [I] INT : item index
1487 * [I] INT : Direction of shift, +1 or -1.
1492 static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
1494 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1495 LISTVIEW_SELECTION selection,*checkselection;
1498 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1500 selection.upper = nItem;
1501 selection.lower = nItem;
1503 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1504 LISTVIEW_CompareSelectionRanges,
1505 0,DPAS_SORTED|DPAS_INSERTAFTER);
1507 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1509 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1510 if ((checkselection->lower >= nItem)&&
1511 (checkselection->lower + direction >= 0))
1512 checkselection->lower += direction;
1513 if ((checkselection->upper >= nItem)&&
1514 (checkselection->upper + direction >=0))
1515 checkselection->upper += direction;
1519 /* Note that the following will fail if direction != +1 and -1 */
1520 if (infoPtr->nSelectionMark > nItem)
1521 infoPtr->nSelectionMark += direction;
1522 else if (infoPtr->nSelectionMark == nItem)
1525 infoPtr->nSelectionMark += direction;
1526 else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1527 infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1530 if (infoPtr->nFocusedItem > nItem)
1531 infoPtr->nFocusedItem += direction;
1532 else if (infoPtr->nFocusedItem == nItem)
1535 infoPtr->nFocusedItem += direction;
1538 if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1539 infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1540 if (infoPtr->nFocusedItem >= 0)
1541 LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
1544 /* But we are not supposed to modify nHotItem! */
1550 * Adds a block of selections.
1553 * [I] HWND : window handle
1554 * [I] INT : item index
1559 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1561 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1562 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1563 INT nLast = max(infoPtr->nSelectionMark, nItem);
1570 ZeroMemory(&item,sizeof(LVITEMA));
1571 item.stateMask = LVIS_SELECTED;
1572 item.state = LVIS_SELECTED;
1574 for (i = nFirst; i <= nLast; i++)
1576 LISTVIEW_SetItemState(hwnd,i,&item);
1579 LISTVIEW_SetItemFocus(hwnd, nItem);
1580 infoPtr->nSelectionMark = nItem;
1586 * Adds a single selection.
1589 * [I] HWND : window handle
1590 * [I] INT : item index
1595 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1597 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1600 ZeroMemory(&item,sizeof(LVITEMA));
1601 item.state = LVIS_SELECTED;
1602 item.stateMask = LVIS_SELECTED;
1604 LISTVIEW_SetItemState(hwnd,nItem,&item);
1606 LISTVIEW_SetItemFocus(hwnd, nItem);
1607 infoPtr->nSelectionMark = nItem;
1612 * Selects or unselects an item.
1615 * [I] HWND : window handle
1616 * [I] INT : item index
1622 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1624 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1628 ZeroMemory(&item,sizeof(LVITEMA));
1629 item.stateMask = LVIS_SELECTED;
1631 if (LISTVIEW_IsSelected(hwnd,nItem))
1634 LISTVIEW_SetItemState(hwnd,nItem,&item);
1639 item.state = LVIS_SELECTED;
1640 LISTVIEW_SetItemState(hwnd,nItem,&item);
1644 LISTVIEW_SetItemFocus(hwnd, nItem);
1645 infoPtr->nSelectionMark = nItem;
1652 * Selects items based on view coordinates.
1655 * [I] HWND : window handle
1656 * [I] RECT : selection rectangle
1661 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
1663 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1668 ZeroMemory(&item,sizeof(LVITEMA));
1669 item.stateMask = LVIS_SELECTED;
1671 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1673 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
1675 if (PtInRect(&rcSelRect, ptItem) != FALSE)
1677 item.state = LVIS_SELECTED;
1678 LISTVIEW_SetItemState(hwnd,i,&item);
1683 LISTVIEW_SetItemState(hwnd,i,&item);
1690 * Sets a single group selection.
1693 * [I] HWND : window handle
1694 * [I] INT : item index
1699 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
1701 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1702 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1705 ZeroMemory(&item,sizeof(LVITEMA));
1706 item.stateMask = LVIS_SELECTED;
1708 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
1713 if (infoPtr->nSelectionMark == -1)
1715 infoPtr->nSelectionMark = nFirst = nLast = nItem;
1719 nFirst = min(infoPtr->nSelectionMark, nItem);
1720 nLast = max(infoPtr->nSelectionMark, nItem);
1723 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
1725 if ((i < nFirst) || (i > nLast))
1728 LISTVIEW_SetItemState(hwnd,i,&item);
1732 item.state = LVIS_SELECTED;
1733 LISTVIEW_SetItemState(hwnd,i,&item);
1742 LISTVIEW_GetItemPosition(hwnd, nItem, &ptItem);
1743 LISTVIEW_GetItemPosition(hwnd, infoPtr->nSelectionMark, &ptSelMark);
1744 rcSel.left = min(ptSelMark.x, ptItem.x);
1745 rcSel.top = min(ptSelMark.y, ptItem.y);
1746 rcSel.right = max(ptSelMark.x, ptItem.x) + infoPtr->nItemWidth;
1747 rcSel.bottom = max(ptSelMark.y, ptItem.y) + infoPtr->nItemHeight;
1748 LISTVIEW_SetSelectionRect(hwnd, rcSel);
1751 LISTVIEW_SetItemFocus(hwnd, nItem);
1756 * Manages the item focus.
1759 * [I] HWND : window handle
1760 * [I] INT : item index
1763 * TRUE : focused item changed
1764 * FALSE : focused item has NOT changed
1766 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
1768 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1769 BOOL bResult = FALSE;
1772 if (infoPtr->nFocusedItem != nItem)
1774 if (infoPtr->nFocusedItem >= 0)
1776 INT oldFocus = infoPtr->nFocusedItem;
1778 infoPtr->nFocusedItem = -1;
1779 ZeroMemory(&lvItem, sizeof(LVITEMA));
1780 lvItem.stateMask = LVIS_FOCUSED;
1781 ListView_SetItemState(hwnd, oldFocus, &lvItem);
1785 lvItem.state = LVIS_FOCUSED;
1786 lvItem.stateMask = LVIS_FOCUSED;
1787 ListView_SetItemState(hwnd, nItem, &lvItem);
1789 infoPtr->nFocusedItem = nItem;
1790 ListView_EnsureVisible(hwnd, nItem, FALSE);
1798 * Sets a single selection.
1801 * [I] HWND : window handle
1802 * [I] INT : item index
1807 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
1809 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1812 ZeroMemory(&lvItem, sizeof(LVITEMA));
1813 lvItem.stateMask = LVIS_FOCUSED;
1814 ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
1816 LISTVIEW_RemoveAllSelections(hwnd);
1818 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
1819 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
1820 ListView_SetItemState(hwnd, nItem, &lvItem);
1822 infoPtr->nFocusedItem = nItem;
1823 infoPtr->nSelectionMark = nItem;
1828 * Set selection(s) with keyboard.
1831 * [I] HWND : window handle
1832 * [I] INT : item index
1835 * SUCCESS : TRUE (needs to be repainted)
1836 * FAILURE : FALSE (nothing has changed)
1838 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
1840 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1841 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1842 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
1843 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
1844 BOOL bResult = FALSE;
1846 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
1848 if (lStyle & LVS_SINGLESEL)
1851 LISTVIEW_SetSelection(hwnd, nItem);
1852 ListView_EnsureVisible(hwnd, nItem, FALSE);
1859 LISTVIEW_SetGroupSelection(hwnd, nItem);
1863 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
1868 LISTVIEW_SetSelection(hwnd, nItem);
1869 ListView_EnsureVisible(hwnd, nItem, FALSE);
1879 * Called when the mouse is being actively tracked and has hovered for a specified
1883 * [I] HWND : window handle
1884 * [I] wParam : key indicator
1885 * [I] lParam : mouse position
1888 * 0 if the message was processed, non-zero if there was an error
1891 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
1892 * over the item for a certain period of time.
1895 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
1897 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1900 pt.x = (INT)LOWORD(lParam);
1901 pt.y = (INT)HIWORD(lParam);
1903 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
1904 /* select the item under the cursor */
1905 LISTVIEW_MouseSelection(hwnd, pt);
1913 * Called whenever WM_MOUSEMOVE is received.
1916 * [I] HWND : window handle
1917 * [I] wParam : key indicators
1918 * [I] lParam : cursor position
1921 * 0 if the message is processed, non-zero if there was an error
1923 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
1925 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1926 TRACKMOUSEEVENT trackinfo;
1928 /* see if we are supposed to be tracking mouse hovering */
1929 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
1930 /* fill in the trackinfo struct */
1931 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
1932 trackinfo.dwFlags = TME_QUERY;
1933 trackinfo.hwndTrack = hwnd;
1934 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
1936 /* see if we are already tracking this hwnd */
1937 _TrackMouseEvent(&trackinfo);
1939 if(!(trackinfo.dwFlags & TME_HOVER)) {
1940 trackinfo.dwFlags = TME_HOVER;
1942 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
1943 _TrackMouseEvent(&trackinfo);
1952 * Selects an item based on coordinates.
1955 * [I] HWND : window handle
1956 * [I] POINT : mouse click ccordinates
1959 * SUCCESS : item index
1962 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
1964 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1966 INT i,topindex,bottomindex;
1967 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1968 UINT uView = lStyle & LVS_TYPEMASK;
1970 topindex = ListView_GetTopIndex(hwnd);
1971 if (uView == LVS_REPORT)
1973 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
1974 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
1978 bottomindex = GETITEMCOUNT(infoPtr);
1981 for (i = topindex; i < bottomindex; i++)
1983 rcItem.left = LVIR_SELECTBOUNDS;
1984 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
1986 if (PtInRect(&rcItem, pt) != FALSE)
2001 * [IO] HDPA : dynamic pointer array handle
2002 * [I] INT : column index (subitem index)
2008 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2010 BOOL bResult = TRUE;
2014 for (i = 0; i < hdpaItems->nItemCount; i++)
2016 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2017 if (hdpaSubItems != NULL)
2019 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2031 * Removes a subitem at a given position.
2034 * [IO] HDPA : dynamic pointer array handle
2035 * [I] INT : subitem index
2041 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2043 LISTVIEW_SUBITEM *lpSubItem;
2046 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2048 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2049 if (lpSubItem != NULL)
2051 if (lpSubItem->iSubItem == nSubItem)
2054 if ((lpSubItem->pszText != NULL) &&
2055 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
2057 COMCTL32_Free(lpSubItem->pszText);
2061 COMCTL32_Free(lpSubItem);
2063 /* free dpa memory */
2064 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2069 else if (lpSubItem->iSubItem > nSubItem)
2081 * Compares the item information.
2084 * [I] LISTVIEW_ITEM *: destination item
2085 * [I] LPLVITEM : source item
2088 * SUCCCESS : TRUE (EQUAL)
2089 * FAILURE : FALSE (NOT EQUAL)
2091 static UINT LISTVIEW_GetItemChanges(LISTVIEW_ITEM *lpItem, LPLVITEMA lpLVItem)
2095 if ((lpItem != NULL) && (lpLVItem != NULL))
2097 if (lpLVItem->mask & LVIF_STATE)
2099 if ((lpItem->state & lpLVItem->stateMask) !=
2100 (lpLVItem->state & lpLVItem->stateMask))
2102 uChanged |= LVIF_STATE;
2106 if (lpLVItem->mask & LVIF_IMAGE)
2108 if (lpItem->iImage != lpLVItem->iImage)
2110 uChanged |= LVIF_IMAGE;
2114 if (lpLVItem->mask & LVIF_PARAM)
2116 if (lpItem->lParam != lpLVItem->lParam)
2118 uChanged |= LVIF_PARAM;
2122 if (lpLVItem->mask & LVIF_INDENT)
2124 if (lpItem->iIndent != lpLVItem->iIndent)
2126 uChanged |= LVIF_INDENT;
2130 if (lpLVItem->mask & LVIF_TEXT)
2132 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA)
2134 if (lpItem->pszText != LPSTR_TEXTCALLBACKA)
2136 uChanged |= LVIF_TEXT;
2141 if (lpItem->pszText == LPSTR_TEXTCALLBACKA)
2143 uChanged |= LVIF_TEXT;
2147 if (lpLVItem->pszText)
2149 if (lpItem->pszText)
2151 if (strcmp(lpLVItem->pszText, lpItem->pszText) != 0)
2153 uChanged |= LVIF_TEXT;
2158 uChanged |= LVIF_TEXT;
2163 if (lpItem->pszText)
2165 uChanged |= LVIF_TEXT;
2177 * Initializes item attributes.
2180 * [I] HWND : window handle
2181 * [O] LISTVIEW_ITEM *: destination item
2182 * [I] LPLVITEM : source item
2188 static BOOL LISTVIEW_InitItem(HWND hwnd, LISTVIEW_ITEM *lpItem,
2191 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2192 BOOL bResult = FALSE;
2194 if ((lpItem != NULL) && (lpLVItem != NULL))
2198 if (lpLVItem->mask & LVIF_STATE)
2200 lpItem->state &= ~lpLVItem->stateMask;
2201 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2204 if (lpLVItem->mask & LVIF_IMAGE)
2206 lpItem->iImage = lpLVItem->iImage;
2209 if (lpLVItem->mask & LVIF_PARAM)
2211 lpItem->lParam = lpLVItem->lParam;
2214 if (lpLVItem->mask & LVIF_INDENT)
2216 lpItem->iIndent = lpLVItem->iIndent;
2219 if (lpLVItem->mask & LVIF_TEXT)
2221 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA)
2223 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2228 if ((lpItem->pszText != NULL) &&
2229 (lpItem->pszText != LPSTR_TEXTCALLBACKA))
2231 COMCTL32_Free(lpItem->pszText);
2234 lpItem->pszText = LPSTR_TEXTCALLBACKA;
2238 if (lpItem->pszText == LPSTR_TEXTCALLBACKA)
2240 lpItem->pszText = NULL;
2243 bResult = Str_SetPtrA(&lpItem->pszText, lpLVItem->pszText);
2253 * Initializes subitem attributes.
2255 * NOTE: The documentation specifies that the operation fails if the user
2256 * tries to set the indent of a subitem.
2259 * [I] HWND : window handle
2260 * [O] LISTVIEW_SUBITEM *: destination subitem
2261 * [I] LPLVITEM : source subitem
2267 static BOOL LISTVIEW_InitSubItem(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2270 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2271 BOOL bResult = FALSE;
2273 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2275 if (!(lpLVItem->mask & LVIF_INDENT))
2279 lpSubItem->iSubItem = lpLVItem->iSubItem;
2281 if (lpLVItem->mask & LVIF_IMAGE)
2283 lpSubItem->iImage = lpLVItem->iImage;
2286 if (lpLVItem->mask & LVIF_TEXT)
2288 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA)
2290 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2295 if ((lpSubItem->pszText != NULL) &&
2296 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
2298 COMCTL32_Free(lpSubItem->pszText);
2301 lpSubItem->pszText = LPSTR_TEXTCALLBACKA;
2305 if (lpSubItem->pszText == LPSTR_TEXTCALLBACKA)
2307 lpSubItem->pszText = NULL;
2310 bResult = Str_SetPtrA(&lpSubItem->pszText, lpLVItem->pszText);
2321 * Adds a subitem at a given position (column index).
2324 * [I] HWND : window handle
2325 * [I] LPLVITEM : new subitem atttributes
2331 static BOOL LISTVIEW_AddSubItem(HWND hwnd, LPLVITEMA lpLVItem)
2333 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2334 LISTVIEW_SUBITEM *lpSubItem = NULL;
2335 BOOL bResult = FALSE;
2337 INT nPosition, nItem;
2338 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2340 if (lStyle & LVS_OWNERDATA)
2343 if (lpLVItem != NULL)
2345 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2346 if (hdpaSubItems != NULL)
2348 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2349 if (lpSubItem != NULL)
2351 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2352 if (LISTVIEW_InitSubItem(hwnd, lpSubItem, lpLVItem) != FALSE)
2354 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2355 lpSubItem->iSubItem);
2356 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2366 /* cleanup if unsuccessful */
2367 if ((bResult == FALSE) && (lpSubItem != NULL))
2369 COMCTL32_Free(lpSubItem);
2377 * Finds the dpa insert position (array index).
2380 * [I] HWND : window handle
2381 * [I] INT : subitem index
2387 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2389 LISTVIEW_SUBITEM *lpSubItem;
2392 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2394 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2395 if (lpSubItem != NULL)
2397 if (lpSubItem->iSubItem > nSubItem)
2404 return hdpaSubItems->nItemCount;
2409 * Retrieves a listview subitem at a given position (column index).
2412 * [I] HWND : window handle
2413 * [I] INT : subitem index
2419 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2421 LISTVIEW_SUBITEM *lpSubItem;
2424 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2426 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2427 if (lpSubItem != NULL)
2429 if (lpSubItem->iSubItem == nSubItem)
2433 else if (lpSubItem->iSubItem > nSubItem)
2445 * Sets item attributes.
2448 * [I] HWND : window handle
2449 * [I] LPLVITEM : new item atttributes
2455 static BOOL LISTVIEW_SetItem(HWND hwnd, LPLVITEMA lpLVItem)
2457 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2458 BOOL bResult = FALSE;
2460 LISTVIEW_ITEM *lpItem;
2462 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
2463 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2465 UINT uView = lStyle & LVS_TYPEMASK;
2469 if (lStyle & LVS_OWNERDATA)
2471 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2475 ZeroMemory(&itm,sizeof(LVITEMA));
2476 itm.mask = LVIF_STATE | LVIF_PARAM;
2477 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2478 itm.iItem = lpLVItem->iItem;
2480 ListView_GetItemA(hwnd,&itm);
2483 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2484 nmlv.hdr.hwndFrom = hwnd;
2485 nmlv.hdr.idFrom = lCtrlId;
2486 nmlv.hdr.code = LVN_ITEMCHANGING;
2487 nmlv.uNewState = lpLVItem->state;
2488 nmlv.uOldState = itm.state;
2489 nmlv.uChanged = LVIF_STATE;
2490 nmlv.lParam = itm.lParam;
2491 nmlv.iItem = lpLVItem->iItem;
2493 if ((itm.state & lpLVItem->stateMask) !=
2494 (lpLVItem->state & lpLVItem->stateMask))
2496 /* send LVN_ITEMCHANGING notification */
2497 if (!ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv))
2499 if (lpLVItem->stateMask & LVIS_FOCUSED)
2501 if (lpLVItem->state & LVIS_FOCUSED)
2502 infoPtr->nFocusedItem = lpLVItem->iItem;
2503 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2504 infoPtr->nFocusedItem = -1;
2506 if (lpLVItem->stateMask & LVIS_SELECTED)
2508 if (lpLVItem->state & LVIS_SELECTED)
2510 if (lStyle & LVS_SINGLESEL)
2512 LISTVIEW_RemoveAllSelections(hwnd);
2514 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2517 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2521 nmlv.hdr.code = LVN_ITEMCHANGED;
2523 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2525 rcItem.left = LVIR_BOUNDS;
2526 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2527 InvalidateRect(hwnd, &rcItem, TRUE);
2535 if (lpLVItem != NULL)
2537 if (lpLVItem->iSubItem == 0)
2539 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2540 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2542 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2545 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2546 nmlv.hdr.hwndFrom = hwnd;
2547 nmlv.hdr.idFrom = lCtrlId;
2548 nmlv.hdr.code = LVN_ITEMCHANGING;
2549 nmlv.lParam = lpItem->lParam;
2550 uChanged = LISTVIEW_GetItemChanges(lpItem, lpLVItem);
2553 if (uChanged & LVIF_STATE)
2555 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2556 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2558 if (nmlv.uNewState & LVIS_SELECTED)
2561 * This is redundant if called through SetSelection
2563 * however is required if the used directly calls SetItem
2564 * to set the selection.
2566 if (lStyle & LVS_SINGLESEL)
2568 LISTVIEW_RemoveAllSelections(hwnd);
2571 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2574 else if (lpLVItem->stateMask & LVIS_SELECTED)
2576 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2579 if (nmlv.uNewState & LVIS_FOCUSED)
2582 * This is a fun hoop to jump to try to catch if
2583 * the user is calling us directly to call focus or if
2584 * this function is being called as a result of a
2585 * SetItemFocus call.
2587 if (infoPtr->nFocusedItem >= 0)
2588 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2592 nmlv.uChanged = uChanged;
2593 nmlv.iItem = lpLVItem->iItem;
2594 nmlv.lParam = lpItem->lParam;
2595 /* send LVN_ITEMCHANGING notification */
2596 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2598 /* copy information */
2599 bResult = LISTVIEW_InitItem(hwnd, lpItem, lpLVItem);
2601 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2602 based on the width of the items text */
2603 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2605 item_width = LISTVIEW_GetStringWidthA(hwnd, lpItem->pszText);
2607 if(item_width > infoPtr->nItemWidth)
2608 infoPtr->nItemWidth = item_width;
2611 /* send LVN_ITEMCHANGED notification */
2612 nmlv.hdr.code = LVN_ITEMCHANGED;
2613 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2622 rcItem.left = LVIR_BOUNDS;
2623 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2624 InvalidateRect(hwnd, &rcItem, TRUE);
2636 * Sets subitem attributes.
2639 * [I] HWND : window handle
2640 * [I] LPLVITEM : new subitem atttributes
2646 static BOOL LISTVIEW_SetSubItem(HWND hwnd, LPLVITEMA lpLVItem)
2648 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2649 BOOL bResult = FALSE;
2651 LISTVIEW_SUBITEM *lpSubItem;
2652 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2655 if (lStyle & LVS_OWNERDATA)
2658 if (lpLVItem != NULL)
2660 if (lpLVItem->iSubItem > 0)
2662 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2663 if (hdpaSubItems != NULL)
2665 /* set subitem only if column is present */
2666 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2668 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2669 if (lpSubItem != NULL)
2671 bResult = LISTVIEW_InitSubItem(hwnd, lpSubItem, lpLVItem);
2675 bResult = LISTVIEW_AddSubItem(hwnd, lpLVItem);
2678 rcItem.left = LVIR_BOUNDS;
2679 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2680 InvalidateRect(hwnd, &rcItem, FALSE);
2691 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2694 * [I] HWND : window handle
2699 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2701 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2702 UINT uView = lStyle & LVS_TYPEMASK;
2704 SCROLLINFO scrollInfo;
2706 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2707 scrollInfo.cbSize = sizeof(SCROLLINFO);
2708 scrollInfo.fMask = SIF_POS;
2710 if (uView == LVS_LIST)
2712 if (lStyle & WS_HSCROLL)
2714 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
2716 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2720 else if (uView == LVS_REPORT)
2722 if (lStyle & WS_VSCROLL)
2724 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
2726 nItem = scrollInfo.nPos;
2739 * [I] HWND : window handle
2740 * [I] HDC : device context handle
2741 * [I] INT : item index
2742 * [I] INT : subitem index
2743 * [I] RECT * : clipping rectangle
2748 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
2749 RECT rcItem, BOOL Selected)
2751 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2752 CHAR szDispText[DISP_TEXT_SIZE];
2754 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
2757 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
2760 /* get information needed for drawing the item */
2761 ZeroMemory(&lvItem, sizeof(LVITEMA));
2762 lvItem.mask = LVIF_TEXT;
2763 lvItem.iItem = nItem;
2764 lvItem.iSubItem = nSubItem;
2765 lvItem.cchTextMax = DISP_TEXT_SIZE;
2766 lvItem.pszText = szDispText;
2767 LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
2769 /* redraw the background of the item */
2771 if(infoPtr->nColumnCount == (nSubItem + 1))
2772 rcTemp.right = infoPtr->rcList.right;
2774 rcTemp.right+=WIDTH_PADDING;
2776 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
2778 /* set item colors */
2779 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
2781 if (infoPtr->bFocus)
2783 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
2784 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2788 SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
2789 SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
2794 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
2796 SetBkMode(hdc, TRANSPARENT);
2797 textoutOptions &= ~ETO_OPAQUE;
2801 SetBkMode(hdc, OPAQUE);
2802 SetBkColor(hdc, infoPtr->clrTextBk);
2805 SetTextColor(hdc, infoPtr->clrText);
2808 ExtTextOutA(hdc, rcItem.left, rcItem.top, textoutOptions,
2809 &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
2813 /* fill in the gap */
2815 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
2817 CopyRect(&rec,&rcItem);
2818 rec.left = rec.right;
2819 rec.right = rec.left+REPORT_MARGINX;
2820 ExtTextOutA(hdc, rec.left , rec.top, textoutOptions,
2821 &rec, NULL, 0, NULL);
2823 CopyRect(&rec,&rcItem);
2824 rec.right = rec.left;
2825 rec.left = rec.left - REPORT_MARGINX;
2826 ExtTextOutA(hdc, rec.left , rec.top, textoutOptions,
2827 &rec, NULL, 0, NULL);
2837 * [I] HWND : window handle
2838 * [I] HDC : device context handle
2839 * [I] INT : item index
2840 * [I] RECT * : clipping rectangle
2845 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
2847 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2848 CHAR szDispText[DISP_TEXT_SIZE];
2853 DWORD dwTextColor,dwTextX;
2854 BOOL bImage = FALSE;
2856 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
2859 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
2862 /* get information needed for drawing the item */
2863 ZeroMemory(&lvItem, sizeof(LVITEMA));
2864 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
2865 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
2866 lvItem.iItem = nItem;
2867 lvItem.iSubItem = 0;
2868 lvItem.cchTextMax = DISP_TEXT_SIZE;
2869 lvItem.pszText = szDispText;
2870 LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
2872 /* redraw the background of the item */
2874 if(infoPtr->nColumnCount == (nItem + 1))
2875 rcTemp.right = infoPtr->rcList.right;
2877 rcTemp.right+=WIDTH_PADDING;
2879 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
2882 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
2884 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
2887 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
2891 if (infoPtr->himlState != NULL)
2893 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
2894 if (uStateImage > 0)
2896 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
2897 rcItem.top, ILD_NORMAL);
2900 rcItem.left += infoPtr->iconSize.cx;
2902 SuggestedFocus->left += infoPtr->iconSize.cx;
2907 if (infoPtr->himlSmall != NULL)
2909 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
2912 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
2913 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
2914 rcItem.top, ILD_SELECTED);
2916 else if (lvItem.iImage>=0)
2918 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
2919 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
2920 rcItem.top, ILD_NORMAL);
2923 rcItem.left += infoPtr->iconSize.cx;
2926 SuggestedFocus->left += infoPtr->iconSize.cx;
2930 /* Don't bother painting item being edited */
2931 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
2934 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
2936 /* set item colors */
2937 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
2938 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2939 /* set raster mode */
2940 nMixMode = SetROP2(hdc, R2_XORPEN);
2942 else if ((GetWindowLongA(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
2943 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
2945 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
2946 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
2947 /* set raster mode */
2948 nMixMode = SetROP2(hdc, R2_COPYPEN);
2952 /* set item colors */
2953 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
2955 dwBkColor = GetBkColor(hdc);
2956 iBkMode = SetBkMode(hdc, TRANSPARENT);
2957 textoutOptions &= ~ETO_OPAQUE;
2961 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
2962 iBkMode = SetBkMode(hdc, OPAQUE);
2965 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
2966 /* set raster mode */
2967 nMixMode = SetROP2(hdc, R2_COPYPEN);
2970 nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
2971 if (rcItem.left + nLabelWidth < rcItem.right)
2974 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
2976 rcItem.right += IMAGE_PADDING;
2980 dwTextX = rcItem.left + 1;
2982 dwTextX += IMAGE_PADDING;
2985 ExtTextOutA(hdc, dwTextX, rcItem.top, textoutOptions,
2986 &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
2988 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
2990 /* fill in the gap */
2992 CopyRect(&rec,&rcItem);
2993 rec.left = rec.right;
2994 rec.right = rec.left+REPORT_MARGINX;
2995 ExtTextOutA(hdc, rec.left , rec.top, textoutOptions,
2996 &rec, NULL, 0, NULL);
3000 CopyRect(SuggestedFocus,&rcItem);
3004 SetROP2(hdc, R2_COPYPEN);
3005 SetBkColor(hdc, dwBkColor);
3006 SetTextColor(hdc, dwTextColor);
3008 SetBkMode(hdc, iBkMode);
3014 * Draws an item when in large icon display mode.
3017 * [I] HWND : window handle
3018 * [I] HDC : device context handle
3019 * [I] LISTVIEW_ITEM * : item
3020 * [I] INT : item index
3021 * [I] RECT * : clipping rectangle
3026 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3027 RECT *SuggestedFocus)
3029 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3030 CHAR szDispText[DISP_TEXT_SIZE];
3031 INT nDrawPosX = rcItem.left;
3032 INT nLabelWidth, rcWidth;
3035 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
3038 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3039 hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right,
3042 /* get information needed for drawing the item */
3043 ZeroMemory(&lvItem, sizeof(LVITEMA));
3044 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3045 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3046 lvItem.iItem = nItem;
3047 lvItem.iSubItem = 0;
3048 lvItem.cchTextMax = DISP_TEXT_SIZE;
3049 lvItem.pszText = szDispText;
3050 LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
3052 /* redraw the background of the item */
3054 if(infoPtr->nColumnCount == (nItem + 1))
3055 rcTemp.right = infoPtr->rcList.right;
3057 rcTemp.right+=WIDTH_PADDING;
3059 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3061 if (lvItem.state & LVIS_SELECTED)
3063 /* set item colors */
3064 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3065 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3066 /* set raster mode */
3067 SetROP2(hdc, R2_XORPEN);
3071 /* set item colors */
3072 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3074 SetBkMode(hdc, TRANSPARENT);
3075 textoutOptions &= ~ETO_OPAQUE;
3079 SetBkMode(hdc, OPAQUE);
3080 SetBkColor(hdc, infoPtr->clrTextBk);
3083 SetTextColor(hdc, infoPtr->clrText);
3084 /* set raster mode */
3085 SetROP2(hdc, R2_COPYPEN);
3088 if (infoPtr->himlNormal != NULL)
3090 rcItem.top += ICON_TOP_PADDING;
3091 nDrawPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
3092 if ((lvItem.state & LVIS_SELECTED) && (lvItem.iImage>=0))
3094 ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX,
3095 rcItem.top, ILD_SELECTED);
3097 else if (lvItem.iImage>=0)
3099 ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX,
3100 rcItem.top, ILD_NORMAL);
3104 /* Don't bother painting item being edited */
3105 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED)
3108 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3109 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3110 nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
3111 GetTextMetricsA(hdc, &tm);
3113 /* append an ellipse ('...') if the caption won't fit in the rect */
3114 rcWidth = max(0, rcItem.right - rcItem.left);
3115 if (nLabelWidth > rcWidth)
3117 INT i, len, eos, nCharsFit;
3118 /* give or take a couple, how many average sized chars would fit? */
3119 nCharsFit = tm.tmAveCharWidth > 0 ? (rcWidth/tm.tmAveCharWidth)+2 : 0;
3120 /* place the ellipse accordingly, without overrunning the buffer */
3121 len = strlen(szDispText);
3122 eos = min((nCharsFit > 1 && nCharsFit < len) ? nCharsFit+3 : len+2,
3123 sizeof(szDispText)-1);
3125 nLabelWidth = ListView_GetStringWidthA(hwnd, szDispText);
3126 while ((nLabelWidth > rcWidth) && (eos > 3))
3128 for (i = 1; i < 4; i++)
3129 szDispText[eos-i] = '.';
3130 /* shift the ellipse one char to the left for each iteration */
3131 szDispText[eos--] = '\0';
3132 nLabelWidth = ListView_GetStringWidthA(hwnd, szDispText);
3136 InflateRect(&rcItem, 2*CAPTION_BORDER, 0);
3137 nDrawPosX = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
3140 rcItem.left += nDrawPosX / 2;
3141 rcItem.right = rcItem.left + nLabelWidth + 2*CAPTION_BORDER;
3146 rcItem.right = rcItem.left + infoPtr->iconSpacing.cx - 1;
3150 rcItem.bottom = rcItem.top + tm.tmHeight + HEIGHT_PADDING;
3152 ExtTextOutA(hdc, rcItem.left + CAPTION_BORDER, rcItem.top, textoutOptions,
3153 &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
3156 CopyRect(SuggestedFocus,&rcItem);
3161 * Draws listview items when in report display mode.
3164 * [I] HWND : window handle
3165 * [I] HDC : device context handle
3170 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3172 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3173 SCROLLINFO scrollInfo;
3174 INT nDrawPosY = infoPtr->rcList.top;
3176 RECT rcItem, rcTemp;
3181 DWORD cditemmode = CDRF_DODEFAULT;
3182 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3185 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3186 scrollInfo.cbSize = sizeof(SCROLLINFO);
3187 scrollInfo.fMask = SIF_POS;
3189 nItem = ListView_GetTopIndex(hwnd);
3191 /* add 1 for displaying a partial item at the bottom */
3192 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3193 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3195 /* send cache hint notification */
3196 if (GetWindowLongA(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3200 nmlv.hdr.hwndFrom = hwnd;
3201 nmlv.hdr.idFrom = GetWindowLongA(hwnd,GWL_ID);
3202 nmlv.hdr.code = LVN_ODCACHEHINT;
3206 SendMessageA(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3210 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3211 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3212 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3214 /* clear the background of any part of the control that doesn't contain items */
3215 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3216 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3218 /* nothing to draw */
3219 if(GETITEMCOUNT(infoPtr) == 0)
3222 /* Get scroll bar info once before loop */
3223 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3224 scrollOffset = scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
3226 for (; nItem < nLast; nItem++)
3228 RECT SuggestedFocusRect;
3231 if (lStyle & LVS_OWNERDRAWFIXED)
3233 UINT uID = GetWindowLongA( hwnd, GWL_ID);
3238 TRACE("Owner Drawn\n");
3239 dis.CtlType = ODT_LISTVIEW;
3242 dis.itemAction = ODA_DRAWENTIRE;
3245 if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3246 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3248 dis.hwndItem = hwnd;
3251 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3253 dis.rcItem.left = -scrollOffset;
3254 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3255 dis.rcItem.top = nDrawPosY;
3256 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3258 ZeroMemory(&item,sizeof(LVITEMA));
3260 item.mask = LVIF_PARAM;
3261 ListView_GetItemA(hwnd,&item);
3263 dis.itemData = item.lParam;
3265 if (SendMessageA(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3267 nDrawPosY += infoPtr->nItemHeight;
3276 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3277 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3279 ir.left += REPORT_MARGINX;
3280 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3282 ir.bottom = ir.top + infoPtr->nItemHeight;
3284 CopyRect(&SuggestedFocusRect,&ir);
3287 for (j = 0; j < nColumnCount; j++)
3289 if (cdmode & CDRF_NOTIFYITEMDRAW)
3290 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3292 if (cditemmode & CDRF_SKIPDEFAULT)
3295 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3297 rcItem.left += REPORT_MARGINX;
3298 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3299 rcItem.top = nDrawPosY;
3300 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3302 /* Offset the Scroll Bar Pos */
3303 rcItem.left -= scrollOffset;
3304 rcItem.right -= scrollOffset;
3308 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3309 &SuggestedFocusRect);
3313 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem,
3317 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3318 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3319 CDDS_ITEMPOSTPAINT);
3324 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3327 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3328 rop = SetROP2(hdc, R2_XORPEN);
3330 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3331 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3334 SetROP2(hdc, R2_COPYPEN);
3336 nDrawPosY += infoPtr->nItemHeight;
3342 * Retrieves the number of items that can fit vertically in the client area.
3345 * [I] HWND : window handle
3348 * Number of items per row.
3350 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3352 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3353 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3354 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3355 INT nCountPerRow = 1;
3359 if (uView == LVS_REPORT)
3365 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3366 if (nCountPerRow == 0)
3373 return nCountPerRow;
3378 * Retrieves the number of items that can fit horizontally in the client
3382 * [I] HWND : window handle
3385 * Number of items per column.
3387 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3389 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3390 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3391 INT nCountPerColumn = 1;
3393 if (nListHeight > 0)
3395 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3396 if (nCountPerColumn == 0)
3398 nCountPerColumn = 1;
3402 return nCountPerColumn;
3407 * Retrieves the number of columns needed to display all the items when in
3408 * list display mode.
3411 * [I] HWND : window handle
3414 * Number of columns.
3416 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3418 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3419 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3420 INT nColumnCount = 0;
3422 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3424 if (infoPtr->rcList.right % infoPtr->nItemWidth == 0)
3426 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3430 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth + 1;
3434 return nColumnCount;
3440 * Draws listview items when in list display mode.
3443 * [I] HWND : window handle
3444 * [I] HDC : device context handle
3449 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3451 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3452 RECT rcItem, FocusRect, rcTemp;
3456 INT nCountPerColumn;
3457 INT nItemWidth = infoPtr->nItemWidth;
3458 INT nItemHeight = infoPtr->nItemHeight;
3459 DWORD cditemmode = CDRF_DODEFAULT;
3461 /* get number of fully visible columns */
3462 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3463 infoPtr->nColumnCount = nColumnCount;
3464 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3465 nItem = ListView_GetTopIndex(hwnd);
3467 /* paint the background of the control that doesn't contain any items */
3468 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3469 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3471 /* nothing to draw, return here */
3472 if(GETITEMCOUNT(infoPtr) == 0)
3475 for (i = 0; i < nColumnCount; i++)
3477 for (j = 0; j < nCountPerColumn; j++, nItem++)
3479 if (nItem >= GETITEMCOUNT(infoPtr))
3482 if (cdmode & CDRF_NOTIFYITEMDRAW)
3483 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3485 if (cditemmode & CDRF_SKIPDEFAULT)
3488 rcItem.top = j * nItemHeight;
3489 rcItem.left = i * nItemWidth;
3490 rcItem.bottom = rcItem.top + nItemHeight;
3491 rcItem.right = rcItem.left + nItemWidth;
3492 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3496 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3497 Rectangle(hdc, FocusRect.left, FocusRect.top,
3498 FocusRect.right,FocusRect.bottom);
3500 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3501 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3502 CDDS_ITEMPOSTPAINT);
3510 * Draws listview items when in icon or small icon display mode.
3513 * [I] HWND : window handle
3514 * [I] HDC : device context handle
3519 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3521 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3524 RECT rcItem, SuggestedFocus, rcTemp;
3526 DWORD cditemmode = CDRF_DODEFAULT;
3528 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3529 /* DrawItem from erasing the incorrect background area */
3531 /* paint the background of the control that doesn't contain any items */
3532 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3533 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3535 /* nothing to draw, return here */
3536 if(GETITEMCOUNT(infoPtr) == 0)
3539 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3540 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3542 if (cdmode & CDRF_NOTIFYITEMDRAW)
3543 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3545 if (cditemmode & CDRF_SKIPDEFAULT)
3548 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3549 ptPosition.x += ptOrigin.x;
3550 ptPosition.y += ptOrigin.y;
3552 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3554 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3556 if (ptPosition.y < infoPtr->rcList.bottom)
3558 if (ptPosition.x < infoPtr->rcList.right)
3560 rcItem.top = ptPosition.y;
3561 rcItem.left = ptPosition.x;
3562 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3563 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3564 if (bSmall == FALSE)
3566 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3570 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3575 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3577 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3578 SuggestedFocus.right,SuggestedFocus.bottom);
3583 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3584 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3585 CDDS_ITEMPOSTPAINT);
3591 * Draws listview items.
3594 * [I] HWND : window handle
3595 * [I] HDC : device context handle
3600 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3602 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3603 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3609 GetClientRect(hwnd, &rect);
3610 cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3612 if (cdmode == CDRF_SKIPDEFAULT) return;
3615 hOldFont = SelectObject(hdc, infoPtr->hFont);
3617 /* select the dotted pen (for drawing the focus box) */
3618 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3619 hOldPen = SelectObject(hdc, hPen);
3621 /* select transparent brush (for drawing the focus box) */
3622 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3624 if (uView == LVS_LIST)
3626 LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3628 else if (uView == LVS_REPORT)
3630 LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3632 else if (uView == LVS_SMALLICON)
3634 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3636 else if (uView == LVS_ICON)
3638 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3641 /* unselect objects */
3642 SelectObject(hdc, hOldFont);
3643 SelectObject(hdc, hOldPen);
3648 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3649 LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
3655 * Calculates the approximate width and height of a given number of items.
3658 * [I] HWND : window handle
3659 * [I] INT : number of items
3664 * Returns a DWORD. The width in the low word and the height in high word.
3666 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
3667 WORD wWidth, WORD wHeight)
3669 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3670 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3671 INT nItemCountPerColumn = 1;
3672 INT nColumnCount = 0;
3673 DWORD dwViewRect = 0;
3675 if (nItemCount == -1)
3677 nItemCount = GETITEMCOUNT(infoPtr);
3680 if (uView == LVS_LIST)
3682 if (wHeight == 0xFFFF)
3684 /* use current height */
3685 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3688 if (wHeight < infoPtr->nItemHeight)
3690 wHeight = infoPtr->nItemHeight;
3695 if (infoPtr->nItemHeight > 0)
3697 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3698 if (nItemCountPerColumn == 0)
3700 nItemCountPerColumn = 1;
3703 if (nItemCount % nItemCountPerColumn != 0)
3705 nColumnCount = nItemCount / nItemCountPerColumn;
3709 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3714 /* Microsoft padding magic */
3715 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3716 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3718 dwViewRect = MAKELONG(wWidth, wHeight);
3720 else if (uView == LVS_REPORT)
3724 else if (uView == LVS_SMALLICON)
3728 else if (uView == LVS_ICON)
3738 * Arranges listview items in icon display mode.
3741 * [I] HWND : window handle
3742 * [I] INT : alignment code
3748 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
3750 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3751 BOOL bResult = FALSE;
3753 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3766 case LVA_SNAPTOGRID:
3775 /* << LISTVIEW_CreateDragImage >> */
3780 * Removes all listview items and subitems.
3783 * [I] HWND : window handle
3789 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
3791 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3792 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
3793 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3794 UINT uView = lStyle & LVS_TYPEMASK;
3795 LISTVIEW_ITEM *lpItem;
3796 LISTVIEW_SUBITEM *lpSubItem;
3799 BOOL bResult = FALSE;
3804 TRACE("(hwnd=%x,)\n", hwnd);
3806 LISTVIEW_RemoveAllSelections(hwnd);
3807 infoPtr->nSelectionMark=-1;
3808 infoPtr->nFocusedItem=-1;
3809 /* But we are supposed to leave nHotItem as is! */
3811 if (lStyle & LVS_OWNERDATA)
3813 infoPtr->hdpaItems->nItemCount = 0;
3814 InvalidateRect(hwnd, NULL, TRUE);
3818 if (GETITEMCOUNT(infoPtr) > 0)
3820 /* initialize memory */
3821 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3823 /* send LVN_DELETEALLITEMS notification */
3824 nmlv.hdr.hwndFrom = hwnd;
3825 nmlv.hdr.idFrom = lCtrlId;
3826 nmlv.hdr.code = LVN_DELETEALLITEMS;
3829 /* verify if subsequent LVN_DELETEITEM notifications should be
3831 bSuppress = ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
3833 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3835 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3836 if (hdpaSubItems != NULL)
3838 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3840 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3841 if (lpSubItem != NULL)
3843 /* free subitem string */
3844 if ((lpSubItem->pszText != NULL) &&
3845 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
3847 COMCTL32_Free(lpSubItem->pszText);
3851 COMCTL32_Free(lpSubItem);
3855 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3858 if (bSuppress == FALSE)
3860 /* send LVN_DELETEITEM notification */
3861 nmlv.hdr.code = LVN_DELETEITEM;
3863 nmlv.lParam = lpItem->lParam;
3864 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
3867 /* free item string */
3868 if ((lpItem->pszText != NULL) &&
3869 (lpItem->pszText != LPSTR_TEXTCALLBACKA))
3871 COMCTL32_Free(lpItem->pszText);
3875 COMCTL32_Free(lpItem);
3878 DPA_Destroy(hdpaSubItems);
3882 /* reinitialize listview memory */
3883 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3885 /* align items (set position of each item) */
3886 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3888 if (lStyle & LVS_ALIGNLEFT)
3890 LISTVIEW_AlignLeft(hwnd);
3894 LISTVIEW_AlignTop(hwnd);
3898 LISTVIEW_UpdateScroll(hwnd);
3900 /* invalidate client area (optimization needed) */
3901 InvalidateRect(hwnd, NULL, TRUE);
3909 * Removes a column from the listview control.
3912 * [I] HWND : window handle
3913 * [I] INT : column index
3919 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
3921 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3922 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3923 UINT uOwnerData = GetWindowLongA(hwnd, GWL_STYLE) & LVS_OWNERDATA;
3924 BOOL bResult = FALSE;
3926 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
3929 bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
3931 /* Need to reset the item width when deleting a column */
3932 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
3934 /* reset scroll parameters */
3935 if (uView == LVS_REPORT)
3937 /* update scrollbar(s) */
3938 LISTVIEW_UpdateScroll(hwnd);
3940 /* refresh client area */
3941 InvalidateRect(hwnd, NULL, FALSE);
3950 * Removes an item from the listview control.
3953 * [I] HWND : window handle
3954 * [I] INT : item index
3960 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
3962 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3963 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3964 UINT uView = lStyle & LVS_TYPEMASK;
3965 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
3967 BOOL bResult = FALSE;
3969 LISTVIEW_ITEM *lpItem;
3970 LISTVIEW_SUBITEM *lpSubItem;
3974 TRACE("(hwnd=%x,nItem=%d)\n", hwnd, nItem);
3977 /* First, send LVN_DELETEITEM notification. */
3978 memset(&nmlv, 0, sizeof (NMLISTVIEW));
3979 nmlv.hdr.hwndFrom = hwnd;
3980 nmlv.hdr.idFrom = lCtrlId;
3981 nmlv.hdr.code = LVN_DELETEITEM;
3983 SendMessageA(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
3987 /* remove it from the selection range */
3988 ZeroMemory(&item,sizeof(LVITEMA));
3989 item.stateMask = LVIS_SELECTED;
3990 LISTVIEW_SetItemState(hwnd,nItem,&item);
3992 if (lStyle & LVS_OWNERDATA)
3994 infoPtr->hdpaItems->nItemCount --;
3995 InvalidateRect(hwnd, NULL, TRUE);
3999 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4001 /* initialize memory */
4002 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4004 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4005 if (hdpaSubItems != NULL)
4007 for (i = 1; i < hdpaSubItems->nItemCount; i++)
4009 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4010 if (lpSubItem != NULL)
4012 /* free item string */
4013 if ((lpSubItem->pszText != NULL) &&
4014 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
4016 COMCTL32_Free(lpSubItem->pszText);
4020 COMCTL32_Free(lpSubItem);
4024 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4027 /* free item string */
4028 if ((lpItem->pszText != NULL) &&
4029 (lpItem->pszText != LPSTR_TEXTCALLBACKA))
4031 COMCTL32_Free(lpItem->pszText);
4035 COMCTL32_Free(lpItem);
4038 bResult = DPA_Destroy(hdpaSubItems);
4041 LISTVIEW_ShiftIndices(hwnd,nItem,-1);
4043 /* align items (set position of each item) */
4044 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4046 if (lStyle & LVS_ALIGNLEFT)
4048 LISTVIEW_AlignLeft(hwnd);
4052 LISTVIEW_AlignTop(hwnd);
4056 LISTVIEW_UpdateScroll(hwnd);
4058 /* refresh client area */
4059 InvalidateRect(hwnd, NULL, TRUE);
4068 * Return edit control handle of current edit label
4071 * [I] HWND : window handle
4077 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4079 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4080 return infoPtr->hwndEdit;
4086 * Callback implementation for editlabel control
4089 * [I] HWND : window handle
4090 * [I] LPSTR : modified text
4091 * [I] DWORD : item index
4098 static BOOL LISTVIEW_EndEditLabel(HWND hwnd, LPSTR pszText, DWORD nItem)
4100 NMLVDISPINFOA dispInfo;
4101 LISTVIEW_ITEM *lpItem;
4102 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
4103 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4104 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4106 BOOL bUpdateItemText;
4107 LISTVIEW_ITEM lvItemRef;
4110 ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
4112 if (!(lStyle & LVS_OWNERDATA))
4114 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4117 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4122 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4123 ZeroMemory(&item,sizeof(LVITEMA));
4126 item.mask = LVIF_PARAM | LVIF_STATE;
4127 ListView_GetItemA(hwnd,&item);
4128 lvItemRef.state = item.state;
4129 lvItemRef.iImage = item.iImage;
4130 lvItemRef.lParam = item.lParam;
4131 lpItem = &lvItemRef;
4134 dispInfo.hdr.hwndFrom = hwnd;
4135 dispInfo.hdr.idFrom = nCtrlId;
4136 dispInfo.hdr.code = LVN_ENDLABELEDITA;
4137 dispInfo.item.mask = 0;
4138 dispInfo.item.iItem = nItem;
4139 dispInfo.item.state = lpItem->state;
4140 dispInfo.item.stateMask = 0;
4141 dispInfo.item.pszText = pszText;
4142 dispInfo.item.cchTextMax = pszText ? strlen(pszText) : 0;
4143 dispInfo.item.iImage = lpItem->iImage;
4144 dispInfo.item.lParam = lpItem->lParam;
4145 infoPtr->hwndEdit = 0;
4147 bUpdateItemText = ListView_Notify(GetParent(hwnd), nCtrlId, &dispInfo);
4149 /* Do we need to update the Item Text */
4152 if ((lpItem->pszText != LPSTR_TEXTCALLBACKA)&&(!(lStyle & LVS_OWNERDATA)))
4154 Str_SetPtrA(&lpItem->pszText, pszText);
4163 * Begin in place editing of specified list view item
4166 * [I] HWND : window handle
4167 * [I] INT : item index
4174 static HWND LISTVIEW_EditLabelA(HWND hwnd, INT nItem)
4176 NMLVDISPINFOA dispInfo;
4178 LISTVIEW_ITEM *lpItem;
4180 HINSTANCE hinst = GetWindowLongA(hwnd, GWL_HINSTANCE);
4181 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
4182 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4184 CHAR szDispText[DISP_TEXT_SIZE];
4185 LVITEMA lvItem,item;
4186 LISTVIEW_ITEM lvItemRef;
4187 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4189 if (~GetWindowLongA(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4192 /* Is the EditBox still there, if so remove it */
4193 if(infoPtr->hwndEdit != 0)
4198 LISTVIEW_SetSelection(hwnd, nItem);
4199 LISTVIEW_SetItemFocus(hwnd, nItem);
4201 ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
4202 if (!(lStyle & LVS_OWNERDATA))
4204 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4207 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4212 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4213 ZeroMemory(&item,sizeof(LVITEMA));
4216 item.mask = LVIF_PARAM | LVIF_STATE;
4217 ListView_GetItemA(hwnd,&item);
4218 lvItemRef.iImage = item.iImage;
4219 lvItemRef.state = item.state;
4220 lvItemRef.lParam = item.lParam;
4221 lpItem = &lvItemRef;
4224 /* get information needed for drawing the item */
4225 ZeroMemory(&lvItem, sizeof(LVITEMA));
4226 lvItem.mask = LVIF_TEXT;
4227 lvItem.iItem = nItem;
4228 lvItem.iSubItem = 0;
4229 lvItem.cchTextMax = DISP_TEXT_SIZE;
4230 lvItem.pszText = szDispText;
4231 ListView_GetItemA(hwnd, &lvItem);
4233 dispInfo.hdr.hwndFrom = hwnd;
4234 dispInfo.hdr.idFrom = nCtrlId;
4235 dispInfo.hdr.code = LVN_BEGINLABELEDITA;
4236 dispInfo.item.mask = 0;
4237 dispInfo.item.iItem = nItem;
4238 dispInfo.item.state = lpItem->state;
4239 dispInfo.item.stateMask = 0;
4240 dispInfo.item.pszText = lvItem.pszText;
4241 dispInfo.item.cchTextMax = strlen(lvItem.pszText);
4242 dispInfo.item.iImage = lpItem->iImage;
4243 dispInfo.item.lParam = lpItem->lParam;
4245 if (ListView_LVNotify(GetParent(hwnd), nCtrlId, &dispInfo))
4248 rect.left = LVIR_LABEL;
4249 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4252 if (!(hedit = CreateEditLabel(szDispText , WS_VISIBLE,
4253 rect.left-2, rect.top-1, 0,
4254 rect.bottom - rect.top+2,
4255 hwnd, hinst, LISTVIEW_EndEditLabel, nItem)))
4258 infoPtr->hwndEdit = hedit;
4260 SendMessageA(hedit, EM_SETSEL, 0, -1);
4268 * Ensures the specified item is visible, scrolling into view if necessary.
4271 * [I] HWND : window handle
4272 * [I] INT : item index
4273 * [I] BOOL : partially or entirely visible
4279 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4281 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4282 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4283 INT nScrollPosHeight = 0;
4284 INT nScrollPosWidth = 0;
4285 SCROLLINFO scrollInfo;
4287 BOOL bRedraw = FALSE;
4289 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4290 scrollInfo.cbSize = sizeof(SCROLLINFO);
4291 scrollInfo.fMask = SIF_POS;
4293 /* ALWAYS bPartial == FALSE, FOR NOW! */
4295 rcItem.left = LVIR_BOUNDS;
4296 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4298 if (rcItem.left < infoPtr->rcList.left)
4300 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4304 if (uView == LVS_LIST)
4306 nScrollPosWidth = infoPtr->nItemWidth;
4307 rcItem.left += infoPtr->rcList.left;
4309 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4311 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4312 rcItem.left += infoPtr->rcList.left;
4315 /* When in LVS_REPORT view, the scroll position should
4317 if (nScrollPosWidth != 0)
4319 if (rcItem.left % nScrollPosWidth == 0)
4321 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4325 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4328 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4332 else if (rcItem.right > infoPtr->rcList.right)
4334 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4338 if (uView == LVS_LIST)
4340 rcItem.right -= infoPtr->rcList.right;
4341 nScrollPosWidth = infoPtr->nItemWidth;
4343 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4345 rcItem.right -= infoPtr->rcList.right;
4346 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4349 /* When in LVS_REPORT view, the scroll position should
4351 if (nScrollPosWidth != 0)
4353 if (rcItem.right % nScrollPosWidth == 0)
4355 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4359 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4362 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4367 if (rcItem.top < infoPtr->rcList.top)
4371 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4373 if (uView == LVS_REPORT)
4375 rcItem.top -= infoPtr->rcList.top;
4376 nScrollPosHeight = infoPtr->nItemHeight;
4378 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4380 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4381 rcItem.top += infoPtr->rcList.top;
4384 if (rcItem.top % nScrollPosHeight == 0)
4386 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4390 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4393 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4396 else if (rcItem.bottom > infoPtr->rcList.bottom)
4400 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4402 if (uView == LVS_REPORT)
4404 rcItem.bottom -= infoPtr->rcList.bottom;
4405 nScrollPosHeight = infoPtr->nItemHeight;
4407 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4409 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4410 rcItem.bottom -= infoPtr->rcList.bottom;
4413 if (rcItem.bottom % nScrollPosHeight == 0)
4415 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4419 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4422 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4428 InvalidateRect(hwnd,NULL,TRUE);
4434 * Retrieves the nearest item, given a position and a direction.
4437 * [I] HWND : window handle
4438 * [I] POINT : start position
4439 * [I] UINT : direction
4442 * Item index if successdful, -1 otherwise.
4444 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4446 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4447 LVHITTESTINFO lvHitTestInfo;
4451 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4453 ZeroMemory(&lvHitTestInfo, sizeof(LVHITTESTINFO));
4454 LISTVIEW_GetOrigin(hwnd, &lvHitTestInfo.pt);
4455 lvHitTestInfo.pt.x += pt.x;
4456 lvHitTestInfo.pt.y += pt.y;
4460 if (vkDirection == VK_DOWN)
4462 lvHitTestInfo.pt.y += infoPtr->nItemHeight;
4464 else if (vkDirection == VK_UP)
4466 lvHitTestInfo.pt.y -= infoPtr->nItemHeight;
4468 else if (vkDirection == VK_LEFT)
4470 lvHitTestInfo.pt.x -= infoPtr->nItemWidth;
4472 else if (vkDirection == VK_RIGHT)
4474 lvHitTestInfo.pt.x += infoPtr->nItemWidth;
4477 if (PtInRect(&rcView, lvHitTestInfo.pt) == FALSE)
4483 nItem = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
4487 while (nItem == -1);
4495 * Searches for an item with specific characteristics.
4498 * [I] HWND : window handle
4499 * [I] INT : base item index
4500 * [I] LPLVFINDINFO : item information to look for
4503 * SUCCESS : index of item
4506 static LRESULT LISTVIEW_FindItem(HWND hwnd, INT nStart,
4507 LPLVFINDINFO lpFindInfo)
4509 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4511 CHAR szDispText[DISP_TEXT_SIZE];
4515 INT nLast = GETITEMCOUNT(infoPtr);
4517 if ((nItem >= -1) && (lpFindInfo != NULL))
4519 ZeroMemory(&lvItem, sizeof(LVITEMA));
4521 if (lpFindInfo->flags & LVFI_PARAM)
4523 lvItem.mask |= LVIF_PARAM;
4526 if (lpFindInfo->flags & LVFI_STRING)
4528 lvItem.mask |= LVIF_TEXT;
4529 lvItem.pszText = szDispText;
4530 lvItem.cchTextMax = DISP_TEXT_SIZE;
4533 if (lpFindInfo->flags & LVFI_PARTIAL)
4535 lvItem.mask |= LVIF_TEXT;
4536 lvItem.pszText = szDispText;
4537 lvItem.cchTextMax = DISP_TEXT_SIZE;
4540 if (lpFindInfo->flags & LVFI_WRAP)
4545 if (lpFindInfo->flags & LVFI_NEARESTXY)
4547 ptItem.x = lpFindInfo->pt.x;
4548 ptItem.y = lpFindInfo->pt.y;
4553 while (nItem < nLast)
4555 if (lpFindInfo->flags & LVFI_NEARESTXY)
4557 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4558 lpFindInfo->vkDirection);
4561 /* get position of the new item index */
4562 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4573 lvItem.iItem = nItem;
4574 lvItem.iSubItem = 0;
4575 if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
4577 if (lvItem.mask & LVIF_TEXT)
4579 if (lpFindInfo->flags & LVFI_PARTIAL)
4581 if (strstr(lvItem.pszText, lpFindInfo->psz) == NULL)
4586 if (strcmp(lvItem.pszText, lpFindInfo->psz) != 0)
4591 if (lvItem.mask & LVIF_PARAM)
4593 if (lpFindInfo->lParam != lvItem.lParam)
4619 * Retrieves the background color of the listview control.
4622 * [I] HWND : window handle
4625 * COLORREF associated with the background.
4627 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4629 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4631 return infoPtr->clrBk;
4636 * Retrieves the background image of the listview control.
4639 * [I] HWND : window handle
4640 * [O] LPLVMKBIMAGE : background image attributes
4646 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4648 /* FIXME (listview, "empty stub!\n"); */
4654 * Retrieves the callback mask.
4657 * [I] HWND : window handle
4662 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4664 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4666 return infoPtr->uCallbackMask;
4671 * Retrieves column attributes.
4674 * [I] HWND : window handle
4675 * [I] INT : column index
4676 * [IO] LPLVCOLUMNA : column information
4682 static LRESULT LISTVIEW_GetColumnA(HWND hwnd, INT nItem, LPLVCOLUMNA lpColumn)
4684 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4686 BOOL bResult = FALSE;
4688 if (lpColumn != NULL)
4690 /* initialize memory */
4691 ZeroMemory(&hdi, sizeof(HDITEMA));
4693 if (lpColumn->mask & LVCF_FMT)
4695 hdi.mask |= HDI_FORMAT;
4698 if (lpColumn->mask & LVCF_WIDTH)
4700 hdi.mask |= HDI_WIDTH;
4703 if (lpColumn->mask & LVCF_TEXT)
4705 hdi.mask |= HDI_TEXT;
4706 hdi.cchTextMax = lpColumn->cchTextMax;
4707 hdi.pszText = lpColumn->pszText;
4710 if (lpColumn->mask & LVCF_IMAGE)
4712 hdi.mask |= HDI_IMAGE;
4715 if (lpColumn->mask & LVCF_ORDER)
4717 hdi.mask |= HDI_ORDER;
4720 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4721 if (bResult != FALSE)
4723 if (lpColumn->mask & LVCF_FMT)
4727 if (hdi.fmt & HDF_LEFT)
4729 lpColumn->fmt |= LVCFMT_LEFT;
4731 else if (hdi.fmt & HDF_RIGHT)
4733 lpColumn->fmt |= LVCFMT_RIGHT;
4735 else if (hdi.fmt & HDF_CENTER)
4737 lpColumn->fmt |= LVCFMT_CENTER;
4740 if (hdi.fmt & HDF_IMAGE)
4742 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4745 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4747 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4751 if (lpColumn->mask & LVCF_WIDTH)
4753 lpColumn->cx = hdi.cxy;
4756 if (lpColumn->mask & LVCF_IMAGE)
4758 lpColumn->iImage = hdi.iImage;
4761 if (lpColumn->mask & LVCF_ORDER)
4763 lpColumn->iOrder = hdi.iOrder;
4771 /* LISTVIEW_GetColumnW */
4774 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
4776 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); */
4783 for (i = 0; i < iCount; i++)
4791 * Retrieves the column width.
4794 * [I] HWND : window handle
4795 * [I] int : column index
4798 * SUCCESS : column width
4801 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
4803 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4804 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4805 INT nColumnWidth = 0;
4808 if (uView == LVS_LIST)
4810 nColumnWidth = infoPtr->nItemWidth;
4812 else if (uView == LVS_REPORT)
4814 /* get column width from header */
4815 ZeroMemory(&hdi, sizeof(HDITEMA));
4816 hdi.mask = HDI_WIDTH;
4817 if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
4819 nColumnWidth = hdi.cxy;
4823 return nColumnWidth;
4828 * In list or report display mode, retrieves the number of items that can fit
4829 * vertically in the visible area. In icon or small icon display mode,
4830 * retrieves the total number of visible items.
4833 * [I] HWND : window handle
4836 * Number of fully visible items.
4838 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
4840 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4841 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4844 if (uView == LVS_LIST)
4846 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4848 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
4849 LISTVIEW_GetCountPerColumn(hwnd);
4852 else if (uView == LVS_REPORT)
4854 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
4858 nItemCount = GETITEMCOUNT(infoPtr);
4864 /* LISTVIEW_GetEditControl */
4868 * Retrieves the extended listview style.
4871 * [I] HWND : window handle
4874 * SUCCESS : previous style
4877 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
4879 LISTVIEW_INFO *infoPtr;
4881 /* make sure we can get the listview info */
4882 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
4885 return (infoPtr->dwExStyle);
4890 * Retrieves the handle to the header control.
4893 * [I] HWND : window handle
4898 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
4900 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4902 return infoPtr->hwndHeader;
4905 /* LISTVIEW_GetHotCursor */
4909 * Returns the time that the mouse cursor must hover over an item
4910 * before it is selected.
4913 * [I] HWND : window handle
4916 * Returns the previously set hover time or (DWORD)-1 to indicate that the
4917 * hover time is set to the default hover time.
4919 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
4921 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4923 return infoPtr->dwHoverTime;
4928 * Retrieves an image list handle.
4931 * [I] HWND : window handle
4932 * [I] INT : image list identifier
4935 * SUCCESS : image list handle
4938 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
4940 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4941 HIMAGELIST himl = NULL;
4946 himl = infoPtr->himlNormal;
4949 himl = infoPtr->himlSmall;
4952 himl = infoPtr->himlState;
4956 return (LRESULT)himl;
4959 /* LISTVIEW_GetISearchString */
4963 * Retrieves item attributes.
4966 * [I] HWND : window handle
4967 * [IO] LPLVITEMA : item info
4968 * [I] internal : if true then we will use tricks that avoid copies
4969 * but are not compatible with the regular interface
4975 static LRESULT LISTVIEW_GetItemA(HWND hwnd, LPLVITEMA lpLVItem, BOOL internal)
4977 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4978 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
4979 NMLVDISPINFOA dispInfo;
4980 LISTVIEW_SUBITEM *lpSubItem;
4981 LISTVIEW_ITEM *lpItem;
4986 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4987 /* In the following:
4988 * lpLVItem describes the information requested by the user
4989 * lpItem/lpSubItem is what we have
4990 * dispInfo is a structure we use to request the missing
4991 * information from the application
4994 TRACE("(hwnd=%x, lpLVItem=%p)\n", hwnd, lpLVItem);
4996 if ((lpLVItem == NULL) ||
4997 (lpLVItem->iItem < 0) ||
4998 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr))
5002 if (lStyle & LVS_OWNERDATA)
5004 if (lpLVItem->mask & ~LVIF_STATE)
5006 dispInfo.hdr.hwndFrom = hwnd;
5007 dispInfo.hdr.idFrom = lCtrlId;
5008 dispInfo.hdr.code = LVN_GETDISPINFOA;
5009 memcpy(&dispInfo.item,lpLVItem,sizeof(LVITEMA));
5011 ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
5012 memcpy(lpLVItem,&dispInfo.item,sizeof(LVITEMA));
5015 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5017 lpLVItem->state = 0;
5018 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5019 lpLVItem->state |= LVIS_FOCUSED;
5020 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5021 lpLVItem->state |= LVIS_SELECTED;
5028 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5029 if (hdpaSubItems == NULL)
5032 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
5036 ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
5037 if (lpLVItem->iSubItem == 0)
5039 piImage=&lpItem->iImage;
5040 ppszText=&lpItem->pszText;
5041 plParam=&lpItem->lParam;
5042 if ((infoPtr->uCallbackMask != 0) && (lpLVItem->mask & LVIF_STATE))
5044 dispInfo.item.mask |= LVIF_STATE;
5045 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5050 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5051 if (lpSubItem != NULL)
5053 piImage=&lpSubItem->iImage;
5054 ppszText=&lpSubItem->pszText;
5065 if ((lpLVItem->mask & LVIF_IMAGE) &&
5066 ((piImage==NULL) || (*piImage == I_IMAGECALLBACK)))
5068 dispInfo.item.mask |= LVIF_IMAGE;
5071 if ((lpLVItem->mask & LVIF_TEXT) &&
5072 ((ppszText==NULL) || (*ppszText == LPSTR_TEXTCALLBACKA)))
5074 dispInfo.item.mask |= LVIF_TEXT;
5075 dispInfo.item.pszText = lpLVItem->pszText;
5076 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5079 if (dispInfo.item.mask != 0)
5081 /* We don't have all the requested info, query the application */
5082 dispInfo.hdr.hwndFrom = hwnd;
5083 dispInfo.hdr.idFrom = lCtrlId;
5084 dispInfo.hdr.code = LVN_GETDISPINFOA;
5085 dispInfo.item.iItem = lpLVItem->iItem;
5086 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5087 dispInfo.item.lParam = lpItem->lParam;
5088 ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
5091 if (dispInfo.item.mask & LVIF_IMAGE)
5093 lpLVItem->iImage = dispInfo.item.iImage;
5094 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && piImage)
5096 *piImage = dispInfo.item.iImage;
5099 else if (lpLVItem->mask & LVIF_IMAGE)
5101 lpLVItem->iImage = *piImage;
5104 if (dispInfo.item.mask & LVIF_PARAM)
5106 lpLVItem->lParam = dispInfo.item.lParam;
5107 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && plParam)
5109 *plParam = dispInfo.item.lParam;
5112 else if (lpLVItem->mask & LVIF_PARAM)
5114 lpLVItem->lParam = lpItem->lParam;
5117 if (dispInfo.item.mask & LVIF_TEXT)
5119 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (ppszText != NULL))
5121 Str_SetPtrA(ppszText, dispInfo.item.pszText);
5123 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5124 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5125 if (lpLVItem->pszText!=dispInfo.item.pszText) {
5126 lstrcpynA(lpLVItem->pszText, dispInfo.item.pszText, lpLVItem->cchTextMax);
5129 if (ppszText == NULL)
5131 lstrcpynA(lpLVItem->pszText, "", lpLVItem->cchTextMax);
5134 else if (lpLVItem->mask & LVIF_TEXT)
5138 lpLVItem->pszText=*ppszText;
5140 lstrcpynA(lpLVItem->pszText, *ppszText, lpLVItem->cchTextMax);
5144 if (lpLVItem->iSubItem == 0)
5146 if (dispInfo.item.mask & LVIF_STATE)
5148 lpLVItem->state = lpItem->state;
5149 lpLVItem->state &= ~dispInfo.item.stateMask;
5150 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5152 lpLVItem->state &= ~LVIS_SELECTED;
5153 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5154 (LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem)))
5155 lpLVItem->state |= LVIS_SELECTED;
5157 else if (lpLVItem->mask & LVIF_STATE)
5159 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5161 lpLVItem->state &= ~LVIS_SELECTED;
5162 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5163 (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem)))
5164 lpLVItem->state |= LVIS_SELECTED;
5167 if (lpLVItem->mask & LVIF_PARAM)
5169 lpLVItem->lParam = lpItem->lParam;
5172 if (lpLVItem->mask & LVIF_INDENT)
5174 lpLVItem->iIndent = lpItem->iIndent;
5181 /* LISTVIEW_GetItemW */
5182 /* LISTVIEW_GetHotCursor */
5186 * Retrieves the index of the hot item.
5189 * [I] HWND : window handle
5192 * SUCCESS : hot item index
5193 * FAILURE : -1 (no hot item)
5195 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5197 LISTVIEW_INFO *infoPtr;
5199 /* make sure we can get the listview info */
5200 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
5203 return (infoPtr->nHotItem);
5206 /* LISTVIEW_GetHoverTime */
5210 * Retrieves the number of items in the listview control.
5213 * [I] HWND : window handle
5218 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5220 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5222 return GETITEMCOUNT(infoPtr);
5227 * Retrieves the position (upper-left) of the listview control item.
5230 * [I] HWND : window handle
5231 * [I] INT : item index
5232 * [O] LPPOINT : coordinate information
5238 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem,
5239 LPPOINT lpptPosition)
5241 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5242 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5243 BOOL bResult = FALSE;
5245 LISTVIEW_ITEM *lpItem;
5246 INT nCountPerColumn;
5249 TRACE("(hwnd=%x,nItem=%d,lpptPosition=%p)\n", hwnd, nItem,
5252 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5253 (lpptPosition != NULL))
5255 if (uView == LVS_LIST)
5258 nItem = nItem - ListView_GetTopIndex(hwnd);
5259 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5262 nRow = nItem % nCountPerColumn;
5265 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
5266 lpptPosition->y = 0;
5270 lpptPosition->x = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5271 lpptPosition->y = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5276 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
5277 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
5280 else if (uView == LVS_REPORT)
5282 SCROLLINFO scrollInfo;
5284 lpptPosition->x = REPORT_MARGINX;
5285 lpptPosition->y = ((nItem - ListView_GetTopIndex(hwnd)) *
5286 infoPtr->nItemHeight) + infoPtr->rcList.top;
5288 /* Adjust position by scrollbar offset */
5289 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5290 scrollInfo.cbSize = sizeof(SCROLLINFO);
5291 scrollInfo.fMask = SIF_POS;
5292 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5293 lpptPosition->x -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5297 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5298 if (hdpaSubItems != NULL)
5300 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
5304 lpptPosition->x = lpItem->ptPosition.x;
5305 lpptPosition->y = lpItem->ptPosition.y;
5315 * Retrieves the bounding rectangle for a listview control item.
5318 * [I] HWND : window handle
5319 * [I] INT : item index
5320 * [IO] LPRECT : bounding rectangle coordinates
5321 * lprc->left specifies the portion of the item for which the bounding
5322 * rectangle will be retrieved.
5324 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5325 * including the icon and label.
5326 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5327 * LVIR_LABEL Returns the bounding rectangle of the item text.
5328 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5329 * rectangles, but excludes columns in report view.
5335 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5337 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5338 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5339 BOOL bResult = FALSE;
5350 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5352 if (uView & LVS_REPORT)
5354 ZeroMemory(&lvItem, sizeof(LVITEMA));
5355 lvItem.mask = LVIF_INDENT;
5356 lvItem.iItem = nItem;
5357 lvItem.iSubItem = 0;
5358 LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
5361 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5363 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5371 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5373 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) != FALSE)
5378 if (uView == LVS_ICON)
5380 if (infoPtr->himlNormal != NULL)
5382 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5385 lprc->left = ptItem.x + ptOrigin.x;
5386 lprc->top = ptItem.y + ptOrigin.y;
5387 lprc->right = lprc->left + infoPtr->iconSize.cx;
5388 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5389 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5393 else if (uView == LVS_SMALLICON)
5395 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5398 lprc->left = ptItem.x + ptOrigin.x;
5399 lprc->top = ptItem.y + ptOrigin.y;
5400 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5402 if (infoPtr->himlState != NULL)
5403 lprc->left += infoPtr->iconSize.cx;
5405 if (infoPtr->himlSmall != NULL)
5406 lprc->right = lprc->left + infoPtr->iconSize.cx;
5408 lprc->right = lprc->left;
5414 lprc->left = ptItem.x;
5415 if (uView & LVS_REPORT)
5416 lprc->left += nIndent;
5417 lprc->top = ptItem.y;
5418 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5420 if (infoPtr->himlState != NULL)
5422 lprc->left += infoPtr->iconSize.cx;
5425 if (infoPtr->himlSmall != NULL)
5427 lprc->right = lprc->left + infoPtr->iconSize.cx;
5431 lprc->right = lprc->left;
5437 if (uView == LVS_ICON)
5439 if (infoPtr->himlNormal != NULL)
5441 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5444 lprc->left = ptItem.x + ptOrigin.x;
5445 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5446 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5447 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5448 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5450 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5451 lprc->right = lprc->left + nLabelWidth;
5456 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5460 hOldFont = SelectObject(hdc, infoPtr->hFont);
5461 GetTextMetricsA(hdc, &tm);
5462 lprc->bottom = lprc->top + tm.tmHeight + HEIGHT_PADDING;
5463 SelectObject(hdc, hOldFont);
5464 ReleaseDC(hwnd, hdc);
5468 else if (uView == LVS_SMALLICON)
5470 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5473 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5474 lprc->top = ptItem.y + ptOrigin.y;
5475 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5477 if (infoPtr->himlState != NULL)
5479 lprc->left += infoPtr->iconSize.cx;
5482 if (infoPtr->himlSmall != NULL)
5484 lprc->left += infoPtr->iconSize.cx;
5487 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5488 nLabelWidth += TRAILING_PADDING;
5489 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5491 lprc->right = lprc->left + nLabelWidth;
5495 lprc->right = nLeftPos + infoPtr->nItemWidth;
5502 if (uView & LVS_REPORT)
5503 nLeftPos = lprc->left = ptItem.x + nIndent;
5505 nLeftPos = lprc->left = ptItem.x;
5506 lprc->top = ptItem.y;
5507 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5509 if (infoPtr->himlState != NULL)
5511 lprc->left += infoPtr->iconSize.cx;
5514 if (infoPtr->himlSmall != NULL)
5516 lprc->left += infoPtr->iconSize.cx;
5519 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5520 nLabelWidth += TRAILING_PADDING;
5521 if (infoPtr->himlSmall)
5522 nLabelWidth += IMAGE_PADDING;
5523 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5525 lprc->right = lprc->left + nLabelWidth;
5529 lprc->right = nLeftPos + infoPtr->nItemWidth;
5535 if (uView == LVS_ICON)
5537 if (infoPtr->himlNormal != NULL)
5539 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5542 lprc->left = ptItem.x + ptOrigin.x;
5543 lprc->top = ptItem.y + ptOrigin.y;
5544 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5545 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5549 else if (uView == LVS_SMALLICON)
5551 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5554 lprc->left = ptItem.x + ptOrigin.x;
5555 lprc->right = lprc->left;
5556 lprc->top = ptItem.y + ptOrigin.y;
5557 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5558 if (infoPtr->himlState != NULL)
5559 lprc->right += infoPtr->iconSize.cx;
5560 if (infoPtr->himlSmall != NULL)
5561 lprc->right += infoPtr->iconSize.cx;
5563 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5564 nLabelWidth += TRAILING_PADDING;
5565 if (infoPtr->himlSmall)
5566 nLabelWidth += IMAGE_PADDING;
5567 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5569 lprc->right += nLabelWidth;
5573 lprc->right = lprc->left + infoPtr->nItemWidth;
5580 lprc->left = ptItem.x;
5581 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5582 lprc->left += nIndent;
5583 lprc->right = lprc->left;
5584 lprc->top = ptItem.y;
5585 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5587 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5590 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5591 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5593 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5597 if (infoPtr->himlState != NULL)
5599 lprc->right += infoPtr->iconSize.cx;
5602 if (infoPtr->himlSmall != NULL)
5604 lprc->right += infoPtr->iconSize.cx;
5607 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5608 nLabelWidth += TRAILING_PADDING;
5609 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5611 lprc->right += nLabelWidth;
5615 lprc->right = lprc->left + infoPtr->nItemWidth;
5621 case LVIR_SELECTBOUNDS:
5622 if (uView == LVS_ICON)
5624 if (infoPtr->himlNormal != NULL)
5626 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5629 lprc->left = ptItem.x + ptOrigin.x;
5630 lprc->top = ptItem.y + ptOrigin.y;
5631 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5632 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5636 else if (uView == LVS_SMALLICON)
5638 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5641 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5642 lprc->top = ptItem.y + ptOrigin.y;
5643 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5645 if (infoPtr->himlState != NULL)
5647 lprc->left += infoPtr->iconSize.cx;
5650 lprc->right = lprc->left;
5652 if (infoPtr->himlSmall != NULL)
5654 lprc->right += infoPtr->iconSize.cx;
5657 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5658 nLabelWidth += TRAILING_PADDING;
5659 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5661 lprc->right += nLabelWidth;
5665 lprc->right = nLeftPos + infoPtr->nItemWidth;
5672 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5673 nLeftPos = lprc->left = ptItem.x + nIndent;
5675 nLeftPos = lprc->left = ptItem.x;
5676 lprc->top = ptItem.y;
5677 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5679 if (infoPtr->himlState != NULL)
5681 lprc->left += infoPtr->iconSize.cx;
5684 lprc->right = lprc->left;
5686 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5689 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5690 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5692 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5696 if (infoPtr->himlSmall != NULL)
5698 lprc->right += infoPtr->iconSize.cx;
5701 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5702 nLabelWidth += TRAILING_PADDING;
5703 if (infoPtr->himlSmall)
5704 nLabelWidth += IMAGE_PADDING;
5705 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5707 lprc->right += nLabelWidth;
5711 lprc->right = nLeftPos + infoPtr->nItemWidth;
5724 * Retrieves the width of a label.
5727 * [I] HWND : window handle
5730 * SUCCESS : string width (in pixels)
5733 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
5735 CHAR szDispText[DISP_TEXT_SIZE];
5736 INT nLabelWidth = 0;
5739 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
5741 ZeroMemory(&lvItem, sizeof(LVITEMA));
5742 lvItem.mask = LVIF_TEXT;
5743 lvItem.iItem = nItem;
5744 lvItem.cchTextMax = DISP_TEXT_SIZE;
5745 lvItem.pszText = szDispText;
5746 if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
5748 nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
5756 * Retrieves the spacing between listview control items.
5759 * [I] HWND : window handle
5760 * [I] BOOL : flag for small or large icon
5763 * Horizontal + vertical spacing
5765 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
5767 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5770 if (bSmall == FALSE)
5772 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5776 LONG style = GetWindowLongA(hwnd, GWL_STYLE);
5777 if ((style & LVS_TYPEMASK) == LVS_ICON)
5779 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5783 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5791 * Retrieves the state of a listview control item.
5794 * [I] HWND : window handle
5795 * [I] INT : item index
5796 * [I] UINT : state mask
5799 * State specified by the mask.
5801 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
5803 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5807 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5809 ZeroMemory(&lvItem, sizeof(LVITEMA));
5810 lvItem.iItem = nItem;
5811 lvItem.stateMask = uMask;
5812 lvItem.mask = LVIF_STATE;
5813 if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
5815 uState = lvItem.state;
5824 * Retrieves the text of a listview control item or subitem.
5827 * [I] HWND : window handle
5828 * [I] INT : item index
5829 * [IO] LPLVITEMA : item information
5832 * SUCCESS : string length
5835 static LRESULT LISTVIEW_GetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
5837 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5840 if (lpLVItem != NULL)
5842 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5844 lpLVItem->mask = LVIF_TEXT;
5845 lpLVItem->iItem = nItem;
5846 if (LISTVIEW_GetItemA(hwnd, lpLVItem, FALSE) != FALSE)
5848 nLength = lstrlenA(lpLVItem->pszText);
5858 * Searches for an item based on properties + relationships.
5861 * [I] HWND : window handle
5862 * [I] INT : item index
5863 * [I] INT : relationship flag
5866 * SUCCESS : item index
5869 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
5871 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5872 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5874 LVFINDINFO lvFindInfo;
5875 INT nCountPerColumn;
5878 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
5880 ZeroMemory(&lvFindInfo, sizeof(LVFINDINFO));
5882 if (uFlags & LVNI_CUT)
5885 if (uFlags & LVNI_DROPHILITED)
5886 uMask |= LVIS_DROPHILITED;
5888 if (uFlags & LVNI_FOCUSED)
5889 uMask |= LVIS_FOCUSED;
5891 if (uFlags & LVNI_SELECTED)
5892 uMask |= LVIS_SELECTED;
5894 if (uFlags & LVNI_ABOVE)
5896 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5901 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5907 lvFindInfo.flags = LVFI_NEARESTXY;
5908 lvFindInfo.vkDirection = VK_UP;
5909 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5910 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5912 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5917 else if (uFlags & LVNI_BELOW)
5919 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5921 while (nItem < GETITEMCOUNT(infoPtr))
5924 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5930 lvFindInfo.flags = LVFI_NEARESTXY;
5931 lvFindInfo.vkDirection = VK_DOWN;
5932 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5933 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5935 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5940 else if (uFlags & LVNI_TOLEFT)
5942 if (uView == LVS_LIST)
5944 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5945 while (nItem - nCountPerColumn >= 0)
5947 nItem -= nCountPerColumn;
5948 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5952 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5954 lvFindInfo.flags = LVFI_NEARESTXY;
5955 lvFindInfo.vkDirection = VK_LEFT;
5956 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5957 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5959 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5964 else if (uFlags & LVNI_TORIGHT)
5966 if (uView == LVS_LIST)
5968 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5969 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
5971 nItem += nCountPerColumn;
5972 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5976 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5978 lvFindInfo.flags = LVFI_NEARESTXY;
5979 lvFindInfo.vkDirection = VK_RIGHT;
5980 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5981 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5983 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5992 /* search by index */
5993 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
5995 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
6004 /* LISTVIEW_GetNumberOfWorkAreas */
6008 * Retrieves the origin coordinates when in icon or small icon display mode.
6011 * [I] HWND : window handle
6012 * [O] LPPOINT : coordinate information
6018 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6020 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6021 UINT uView = lStyle & LVS_TYPEMASK;
6022 BOOL bResult = FALSE;
6024 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6026 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6028 SCROLLINFO scrollInfo;
6029 ZeroMemory(lpptOrigin, sizeof(POINT));
6030 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6031 scrollInfo.cbSize = sizeof(SCROLLINFO);
6033 if (lStyle & WS_HSCROLL)
6035 scrollInfo.fMask = SIF_POS;
6036 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6038 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6042 if (lStyle & WS_VSCROLL)
6044 scrollInfo.fMask = SIF_POS;
6045 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6047 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6059 * Retrieves the number of items that are marked as selected.
6062 * [I] HWND : window handle
6065 * Number of items selected.
6067 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6070 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6071 INT nSelectedCount = 0;
6074 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6076 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6082 return nSelectedCount;
6087 * Retrieves item index that marks the start of a multiple selection.
6090 * [I] HWND : window handle
6093 * Index number or -1 if there is no selection mark.
6095 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6097 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6099 return infoPtr->nSelectionMark;
6104 * Retrieves the width of a string.
6107 * [I] HWND : window handle
6110 * SUCCESS : string width (in pixels)
6113 static LRESULT LISTVIEW_GetStringWidthA(HWND hwnd, LPCSTR lpszText)
6115 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6116 HFONT hFont, hOldFont;
6120 ZeroMemory(&stringSize, sizeof(SIZE));
6121 if (lpszText != NULL && lpszText != LPSTR_TEXTCALLBACKA)
6123 hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6125 hOldFont = SelectObject(hdc, hFont);
6126 GetTextExtentPointA(hdc, lpszText, lstrlenA(lpszText), &stringSize);
6127 SelectObject(hdc, hOldFont);
6128 ReleaseDC(hwnd, hdc);
6131 return stringSize.cx;
6136 * Retrieves the text backgound color.
6139 * [I] HWND : window handle
6142 * COLORREF associated with the the background.
6144 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6146 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6148 return infoPtr->clrTextBk;
6153 * Retrieves the text color.
6156 * [I] HWND : window handle
6159 * COLORREF associated with the text.
6161 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6163 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6165 return infoPtr->clrText;
6170 * Determines which section of the item was selected (if any).
6173 * [I] HWND : window handle
6174 * [IO] LPLVHITTESTINFO : hit test information
6175 * [I] subitem : fill out iSubItem.
6178 * SUCCESS : item index
6181 static INT LISTVIEW_HitTestItem(
6182 HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem
6184 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6186 INT i,topindex,bottomindex;
6187 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6188 UINT uView = lStyle & LVS_TYPEMASK;
6191 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6192 lpHitTestInfo->pt.y);
6194 topindex = ListView_GetTopIndex(hwnd);
6195 if (uView == LVS_REPORT)
6197 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6198 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6202 bottomindex = GETITEMCOUNT(infoPtr);
6205 for (i = topindex; i < bottomindex; i++)
6207 rcItem.left = LVIR_BOUNDS;
6208 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6210 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6212 rcItem.left = LVIR_ICON;
6213 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6215 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6217 lpHitTestInfo->flags = LVHT_ONITEMICON;
6218 lpHitTestInfo->iItem = i;
6219 if (subitem) lpHitTestInfo->iSubItem = 0;
6224 rcItem.left = LVIR_LABEL;
6225 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6227 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6229 lpHitTestInfo->flags = LVHT_ONITEMLABEL;
6230 lpHitTestInfo->iItem = i;
6231 if (subitem) lpHitTestInfo->iSubItem = 0;
6236 lpHitTestInfo->flags = LVHT_ONITEMSTATEICON;
6237 lpHitTestInfo->iItem = i;
6238 if (subitem) lpHitTestInfo->iSubItem = 0;
6244 lpHitTestInfo->flags = LVHT_NOWHERE;
6251 * Determines which listview item is located at the specified position.
6254 * [I] HWND : window handle
6255 * [IO} LPLVHITTESTINFO : hit test information
6258 * SUCCESS : item index
6261 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6263 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6266 lpHitTestInfo->flags = 0;
6268 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6270 lpHitTestInfo->flags = LVHT_TOLEFT;
6272 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6274 lpHitTestInfo->flags = LVHT_TORIGHT;
6276 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6278 lpHitTestInfo->flags |= LVHT_ABOVE;
6280 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6282 lpHitTestInfo->flags |= LVHT_BELOW;
6285 if (lpHitTestInfo->flags == 0)
6287 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6288 * an app might pass only a structure with space up to iItem!
6289 * (MS Office 97 does that for instance in the file open dialog)
6291 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6299 * Inserts a new column.
6302 * [I] HWND : window handle
6303 * [I] INT : column index
6304 * [I] LPLVCOLUMNA : column information
6307 * SUCCESS : new column index
6310 static LRESULT LISTVIEW_InsertColumnA(HWND hwnd, INT nColumn,
6311 LPLVCOLUMNA lpColumn)
6313 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6315 INT nNewColumn = -1;
6317 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn,
6320 if (lpColumn != NULL)
6322 /* initialize memory */
6323 ZeroMemory(&hdi, sizeof(HDITEMA));
6325 if (lpColumn->mask & LVCF_FMT)
6327 /* format member is valid */
6328 hdi.mask |= HDI_FORMAT;
6330 /* set text alignment (leftmost column must be left-aligned) */
6333 hdi.fmt |= HDF_LEFT;
6337 if (lpColumn->fmt & LVCFMT_LEFT)
6339 hdi.fmt |= HDF_LEFT;
6341 else if (lpColumn->fmt & LVCFMT_RIGHT)
6343 hdi.fmt |= HDF_RIGHT;
6345 else if (lpColumn->fmt & LVCFMT_CENTER)
6347 hdi.fmt |= HDF_CENTER;
6351 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6353 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6357 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6362 if (lpColumn->fmt & LVCFMT_IMAGE)
6364 hdi.fmt |= HDF_IMAGE;
6365 hdi.iImage = I_IMAGECALLBACK;
6369 if (lpColumn->mask & LVCF_WIDTH)
6371 hdi.mask |= HDI_WIDTH;
6372 hdi.cxy = lpColumn->cx;
6375 if (lpColumn->mask & LVCF_TEXT)
6377 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6378 hdi.pszText = lpColumn->pszText;
6379 hdi.cchTextMax = ((lpColumn->pszText!=NULL) && (lpColumn->pszText!=LPSTR_TEXTCALLBACKA) ? strlen(lpColumn->pszText) : 0);
6380 hdi.fmt |= HDF_STRING;
6383 if (lpColumn->mask & LVCF_IMAGE)
6385 hdi.mask |= HDI_IMAGE;
6386 hdi.iImage = lpColumn->iImage;
6389 if (lpColumn->mask & LVCF_ORDER)
6391 hdi.mask |= HDI_ORDER;
6392 hdi.iOrder = lpColumn->iOrder;
6395 /* insert item in header control */
6396 nNewColumn = SendMessageA(infoPtr->hwndHeader, HDM_INSERTITEMA,
6397 (WPARAM)nColumn, (LPARAM)&hdi);
6399 /* Need to reset the item width when inserting a new column */
6400 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6402 LISTVIEW_UpdateScroll(hwnd);
6403 InvalidateRect(hwnd, NULL, FALSE);
6409 static LRESULT LISTVIEW_InsertColumnW(HWND hwnd, INT nColumn,
6410 LPLVCOLUMNW lpColumn)
6415 memcpy(&lvca,lpColumn,sizeof(lvca));
6416 if (lpColumn->mask & LVCF_TEXT) {
6417 if (lpColumn->pszText == LPSTR_TEXTCALLBACKW)
6418 lvca.pszText = LPSTR_TEXTCALLBACKA;
6420 lvca.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpColumn->pszText);
6422 lres = LISTVIEW_InsertColumnA(hwnd,nColumn,&lvca);
6423 if (lpColumn->mask & LVCF_TEXT) {
6424 if (lpColumn->pszText != LPSTR_TEXTCALLBACKW)
6425 HeapFree(GetProcessHeap(),0,lvca.pszText);
6430 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6431 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6432 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6433 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6434 their own sort proc. when sending LVM_SORTITEMS.
6437 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6439 LVS_SORTXXX must be specified,
6440 LVS_OWNERDRAW is not set,
6441 <item>.pszText is not LPSTR_TEXTCALLBACK.
6443 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6444 are sorted based on item text..."
6446 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6448 HDPA hdpa_first = (HDPA) first;
6449 HDPA hdpa_second = (HDPA) second;
6450 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
6451 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
6452 LONG lStyle = GetWindowLongA((HWND) lParam, GWL_STYLE);
6453 INT cmpv = lstrcmpA( lv_first->pszText, lv_second->pszText );
6454 /* if we're sorting descending, negate the return value */
6455 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6460 * Inserts a new item in the listview control.
6463 * [I] HWND : window handle
6464 * [I] LPLVITEMA : item information
6467 * SUCCESS : new item index
6470 static LRESULT LISTVIEW_InsertItemA(HWND hwnd, LPLVITEMA lpLVItem)
6472 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6473 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6474 UINT uView = lStyle & LVS_TYPEMASK;
6475 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
6480 LISTVIEW_ITEM *lpItem = NULL;
6482 TRACE("(hwnd=%x,lpLVItem=%p)\n", hwnd, lpLVItem);
6484 if (lStyle & LVS_OWNERDATA)
6486 nItem = infoPtr->hdpaItems->nItemCount;
6487 infoPtr->hdpaItems->nItemCount ++;
6491 if (lpLVItem != NULL)
6493 /* make sure it's not a subitem; cannot insert a subitem */
6494 if (lpLVItem->iSubItem == 0)
6496 lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM));
6499 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6500 if (LISTVIEW_InitItem(hwnd, lpItem, lpLVItem) != FALSE)
6502 /* insert item in listview control data structure */
6503 hdpaSubItems = DPA_Create(8);
6504 if (hdpaSubItems != NULL)
6506 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
6509 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6510 && !(lStyle & LVS_OWNERDRAWFIXED)
6511 && (LPSTR_TEXTCALLBACKA != lpLVItem->pszText) )
6513 /* Insert the item in the proper sort order based on the pszText
6514 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6515 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6516 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6517 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6518 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6522 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6527 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6529 /* manage item focus */
6530 if (lpLVItem->mask & LVIF_STATE)
6532 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6533 if (lpLVItem->stateMask & LVIS_SELECTED)
6535 LISTVIEW_SetSelection(hwnd, nItem);
6537 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6539 LISTVIEW_SetItemFocus(hwnd, nItem);
6543 /* send LVN_INSERTITEM notification */
6544 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6545 nmlv.hdr.hwndFrom = hwnd;
6546 nmlv.hdr.idFrom = lCtrlId;
6547 nmlv.hdr.code = LVN_INSERTITEM;
6549 nmlv.lParam = lpItem->lParam;;
6550 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
6552 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6554 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6555 if (nItemWidth > infoPtr->nItemWidth)
6557 infoPtr->nItemWidth = nItemWidth;
6561 /* align items (set position of each item) */
6562 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6564 if (lStyle & LVS_ALIGNLEFT)
6566 LISTVIEW_AlignLeft(hwnd);
6570 LISTVIEW_AlignTop(hwnd);
6574 LISTVIEW_UpdateScroll(hwnd);
6575 /* refresh client area */
6576 InvalidateRect(hwnd, NULL, FALSE);
6585 /* free memory if unsuccessful */
6586 if ((nItem == -1) && (lpItem != NULL))
6588 COMCTL32_Free(lpItem);
6594 static LRESULT LISTVIEW_InsertItemW(HWND hwnd, LPLVITEMW lpLVItem) {
6598 memcpy(&lvia,lpLVItem,sizeof(LVITEMA));
6599 if (lvia.mask & LVIF_TEXT) {
6600 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
6601 lvia.pszText = LPSTR_TEXTCALLBACKA;
6603 lvia.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpLVItem->pszText);
6605 lres = LISTVIEW_InsertItemA(hwnd, &lvia);
6606 if (lvia.mask & LVIF_TEXT) {
6607 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6608 HeapFree(GetProcessHeap(),0,lvia.pszText);
6613 /* LISTVIEW_InsertItemW */
6617 * Redraws a range of items.
6620 * [I] HWND : window handle
6621 * [I] INT : first item
6622 * [I] INT : last item
6628 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6630 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6631 BOOL bResult = FALSE;
6634 if (nFirst <= nLast)
6636 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6638 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6641 for (i = nFirst; i <= nLast; i++)
6643 rcItem.left = LVIR_BOUNDS;
6644 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
6645 InvalidateRect(hwnd, &rcItem, TRUE);
6654 /* LISTVIEW_Scroll */
6658 * Sets the background color.
6661 * [I] HWND : window handle
6662 * [I] COLORREF : background color
6668 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6670 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6672 infoPtr->clrBk = clrBk;
6673 InvalidateRect(hwnd, NULL, TRUE);
6678 /* LISTVIEW_SetBkImage */
6682 * Sets the callback mask. This mask will be used when the parent
6683 * window stores state information (some or all).
6686 * [I] HWND : window handle
6687 * [I] UINT : state mask
6693 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
6695 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6697 infoPtr->uCallbackMask = uMask;
6704 * Sets the attributes of a header item.
6707 * [I] HWND : window handle
6708 * [I] INT : column index
6709 * [I] LPLVCOLUMNA : column attributes
6715 static LRESULT LISTVIEW_SetColumnA(HWND hwnd, INT nColumn,
6716 LPLVCOLUMNA lpColumn)
6718 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6719 BOOL bResult = FALSE;
6720 HDITEMA hdi, hdiget;
6722 if ((lpColumn != NULL) && (nColumn >= 0) &&
6723 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6725 /* initialize memory */
6726 ZeroMemory(&hdi, sizeof(HDITEMA));
6728 if (lpColumn->mask & LVCF_FMT)
6730 /* format member is valid */
6731 hdi.mask |= HDI_FORMAT;
6733 /* get current format first */
6734 hdiget.mask = HDI_FORMAT;
6735 if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdiget))
6736 /* preserve HDF_STRING if present */
6737 hdi.fmt = hdiget.fmt & HDF_STRING;
6739 /* set text alignment (leftmost column must be left-aligned) */
6742 hdi.fmt |= HDF_LEFT;
6746 if (lpColumn->fmt & LVCFMT_LEFT)
6748 hdi.fmt |= HDF_LEFT;
6750 else if (lpColumn->fmt & LVCFMT_RIGHT)
6752 hdi.fmt |= HDF_RIGHT;
6754 else if (lpColumn->fmt & LVCFMT_CENTER)
6756 hdi.fmt |= HDF_CENTER;
6760 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6762 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6765 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6767 hdi.fmt |= HDF_IMAGE;
6770 if (lpColumn->fmt & LVCFMT_IMAGE)
6772 hdi.fmt |= HDF_IMAGE;
6773 hdi.iImage = I_IMAGECALLBACK;
6777 if (lpColumn->mask & LVCF_WIDTH)
6779 hdi.mask |= HDI_WIDTH;
6780 hdi.cxy = lpColumn->cx;
6783 if (lpColumn->mask & LVCF_TEXT)
6785 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6786 hdi.pszText = lpColumn->pszText;
6787 hdi.cchTextMax = ((lpColumn->pszText!=NULL) && (lpColumn->pszText!=LPSTR_TEXTCALLBACKA) ? strlen(lpColumn->pszText) : 0);
6788 hdi.fmt |= HDF_STRING;
6791 if (lpColumn->mask & LVCF_IMAGE)
6793 hdi.mask |= HDI_IMAGE;
6794 hdi.iImage = lpColumn->iImage;
6797 if (lpColumn->mask & LVCF_ORDER)
6799 hdi.mask |= HDI_ORDER;
6800 hdi.iOrder = lpColumn->iOrder;
6803 /* set header item attributes */
6804 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6810 /* LISTVIEW_SetColumnW */
6814 * Sets the column order array
6817 * [I] HWND : window handle
6818 * [I] INT : number of elements in column order array
6819 * [I] INT : pointer to column order array
6825 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
6827 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); */
6829 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6840 * Sets the width of a column
6843 * [I] HWND : window handle
6844 * [I] INT : column index
6845 * [I] INT : column width
6851 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
6853 LISTVIEW_INFO *infoPtr;
6856 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6857 UINT uView = lStyle & LVS_TYPEMASK;
6862 CHAR text_buffer[DISP_TEXT_SIZE];
6863 INT header_item_count;
6868 /* make sure we can get the listview info */
6869 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6872 if (!infoPtr->hwndHeader) /* make sure we have a header */
6875 /* set column width only if in report or list mode */
6876 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6879 /* take care of invalid cx values */
6880 if((uView == LVS_REPORT) && (cx < -2))
6881 cx = LVSCW_AUTOSIZE;
6882 else if (uView == LVS_LIST && (cx < 1))
6885 /* resize all columns if in LVS_LIST mode */
6886 if(uView == LVS_LIST) {
6887 infoPtr->nItemWidth = cx;
6888 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6892 /* autosize based on listview items width */
6893 if(cx == LVSCW_AUTOSIZE)
6895 /* set the width of the header to the width of the widest item */
6896 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6898 if(cx < LISTVIEW_GetLabelWidth(hwnd, item_index))
6899 cx = LISTVIEW_GetLabelWidth(hwnd, item_index);
6901 } /* autosize based on listview header width */
6902 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6904 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6906 /* if iCol is the last column make it fill the remainder of the controls width */
6907 if(iCol == (header_item_count - 1)) {
6908 /* get the width of every item except the current one */
6909 hdi.mask = HDI_WIDTH;
6912 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6913 Header_GetItemA(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6917 /* retrieve the layout of the header */
6918 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6920 cx = (rcHeader.right - rcHeader.left) - cx;
6924 /* retrieve header font */
6925 header_font = SendMessageA(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6927 /* retrieve header text */
6928 hdi.mask = HDI_TEXT;
6929 hdi.cchTextMax = sizeof(text_buffer);
6930 hdi.pszText = text_buffer;
6932 Header_GetItemA(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6934 /* determine the width of the text in the header */
6936 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6938 GetTextExtentPoint32A(hdc, text_buffer, strlen(text_buffer), &size);
6940 SelectObject(hdc, old_font); /* restore the old font */
6941 ReleaseDC(hwnd, hdc);
6943 /* set the width of this column to the width of the text */
6948 /* call header to update the column change */
6949 hdi.mask = HDI_WIDTH;
6952 lret = Header_SetItemA(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6954 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6961 * Sets the extended listview style.
6964 * [I] HWND : window handle
6969 * SUCCESS : previous style
6972 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
6974 LISTVIEW_INFO *infoPtr;
6977 /* make sure we can get the listview info */
6978 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6981 /* store previous style */
6982 dwOldStyle = infoPtr->dwExStyle;
6986 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6988 infoPtr->dwExStyle = dwStyle;
6990 return (dwOldStyle);
6993 /* LISTVIEW_SetHotCursor */
6997 * Sets the hot item index.
7000 * [I] HWND : window handle
7004 * SUCCESS : previous hot item index
7005 * FAILURE : -1 (no hot item)
7007 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7009 LISTVIEW_INFO *infoPtr;
7012 /* make sure we can get the listview info */
7013 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
7016 /* store previous index */
7017 iOldIndex = infoPtr->nHotItem;
7020 infoPtr->nHotItem = iIndex;
7027 * Sets the amount of time the cursor must hover over an item before it is selected.
7030 * [I] HWND : window handle
7031 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7034 * Returns the previous hover time
7036 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7038 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7039 DWORD oldHoverTime = infoPtr->dwHoverTime;
7041 infoPtr->dwHoverTime = dwHoverTime;
7043 return oldHoverTime;
7046 /* LISTVIEW_SetIconSpacing */
7053 * [I] HWND : window handle
7054 * [I] INT : image list type
7055 * [I] HIMAGELIST : image list handle
7058 * SUCCESS : old image list
7061 static LRESULT LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7063 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7064 HIMAGELIST himlOld = 0;
7070 himlOld = infoPtr->himlNormal;
7071 infoPtr->himlNormal = himl;
7075 himlOld = infoPtr->himlSmall;
7076 infoPtr->himlSmall = himl;
7080 himlOld = infoPtr->himlState;
7081 infoPtr->himlState = himl;
7082 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7086 oldHeight = infoPtr->nItemHeight;
7087 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7088 if (infoPtr->nItemHeight != oldHeight)
7089 LISTVIEW_UpdateScroll(hwnd);
7091 return (LRESULT)himlOld;
7097 * Sets the attributes of an item.
7100 * [I] HWND : window handle
7101 * [I] LPLVITEM : item information
7107 static LRESULT LISTVIEW_SetItemA(HWND hwnd, LPLVITEMA lpLVItem)
7109 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7110 BOOL bResult = FALSE;
7112 if (lpLVItem != NULL)
7114 if ((lpLVItem->iItem >= 0) && (lpLVItem->iItem < GETITEMCOUNT(infoPtr)))
7116 if (lpLVItem->iSubItem == 0)
7118 bResult = LISTVIEW_SetItem(hwnd, lpLVItem);
7122 bResult = LISTVIEW_SetSubItem(hwnd, lpLVItem);
7131 /* LISTVIEW_SetItemW */
7135 * Preallocates memory (does *not* set the actual count of items !)
7138 * [I] HWND : window handle
7139 * [I] INT : item count (projected number of items to allocate)
7140 * [I] DWORD : update flags
7146 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7148 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7150 if (GetWindowLongA(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7152 int precount,topvisible;
7153 TRACE("LVS_OWNERDATA is set!\n");
7156 * Internally remove all the selections.
7160 LISTVIEW_SELECTION *selection;
7161 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7163 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7166 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7168 precount = infoPtr->hdpaItems->nItemCount;
7169 topvisible = ListView_GetTopIndex(hwnd) +
7170 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7172 infoPtr->hdpaItems->nItemCount = nItems;
7174 LISTVIEW_UpdateSize(hwnd);
7175 LISTVIEW_UpdateScroll(hwnd);
7176 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7177 InvalidateRect(hwnd, NULL, TRUE);
7181 FIXME("setitemcount not done for non-ownerdata\n");
7189 * Sets the position of an item.
7192 * [I] HWND : window handle
7193 * [I] INT : item index
7194 * [I] LONG : x coordinate
7195 * [I] LONG : y coordinate
7201 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7202 LONG nPosX, LONG nPosY)
7204 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7205 UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7206 UINT uView = lStyle & LVS_TYPEMASK;
7207 LISTVIEW_ITEM *lpItem;
7209 BOOL bResult = FALSE;
7211 TRACE("(hwnd=%x,nItem=%d,X=%ld,Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7213 if (lStyle & LVS_OWNERDATA)
7216 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7218 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7220 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
7221 if (hdpaSubItems != NULL)
7223 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7227 lpItem->ptPosition.x = nPosX;
7228 lpItem->ptPosition.y = nPosY;
7239 * Sets the state of one or many items.
7242 * [I] HWND : window handle
7243 * [I]INT : item index
7244 * [I] LPLVITEM : item or subitem info
7250 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
7252 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7253 BOOL bResult = FALSE;
7260 ZeroMemory(&lvItem, sizeof(LVITEMA));
7261 lvItem.mask = LVIF_STATE;
7262 lvItem.state = lpLVItem->state;
7263 lvItem.stateMask = lpLVItem->stateMask ;
7265 /* apply to all items */
7266 for (i = 0; i< GETITEMCOUNT(infoPtr); i++)
7269 if (ListView_SetItemA(hwnd, &lvItem) == FALSE)
7277 ZeroMemory(&lvItem, sizeof(LVITEMA));
7278 lvItem.mask = LVIF_STATE;
7279 lvItem.state = lpLVItem->state;
7280 lvItem.stateMask = lpLVItem->stateMask;
7281 lvItem.iItem = nItem;
7282 bResult = ListView_SetItemA(hwnd, &lvItem);
7290 * Sets the text of an item or subitem.
7293 * [I] HWND : window handle
7294 * [I] INT : item index
7295 * [I] LPLVITEMA : item or subitem info
7301 static BOOL LISTVIEW_SetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
7303 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7304 BOOL bResult = FALSE;
7307 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7309 ZeroMemory(&lvItem, sizeof(LVITEMA));
7310 lvItem.mask = LVIF_TEXT;
7311 lvItem.pszText = lpLVItem->pszText;
7312 lvItem.iItem = nItem;
7313 lvItem.iSubItem = lpLVItem->iSubItem;
7314 bResult = ListView_SetItemA(hwnd, &lvItem);
7320 /* LISTVIEW_SetItemTextW */
7324 * Set item index that marks the start of a multiple selection.
7327 * [I] HWND : window handle
7331 * Index number or -1 if there is no selection mark.
7333 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7335 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7336 INT nOldIndex = infoPtr->nSelectionMark;
7338 infoPtr->nSelectionMark = nIndex;
7345 * Sets the text background color.
7348 * [I] HWND : window handle
7349 * [I] COLORREF : text background color
7355 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7357 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7359 infoPtr->clrTextBk = clrTextBk;
7360 InvalidateRect(hwnd, NULL, TRUE);
7367 * Sets the text foreground color.
7370 * [I] HWND : window handle
7371 * [I] COLORREF : text color
7377 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7379 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7381 infoPtr->clrText = clrText;
7382 InvalidateRect(hwnd, NULL, TRUE);
7387 /* LISTVIEW_SetToolTips */
7388 /* LISTVIEW_SetUnicodeFormat */
7389 /* LISTVIEW_SetWorkAreas */
7393 * Callback internally used by LISTVIEW_SortItems()
7396 * [I] LPVOID : first LISTVIEW_ITEM to compare
7397 * [I] LPVOID : second LISTVIEW_ITEM to compare
7398 * [I] LPARAM : HWND of control
7401 * if first comes before second : negative
7402 * if first comes after second : positive
7403 * if first and second are equivalent : zero
7405 static INT WINAPI LISTVIEW_CallBackCompare(
7410 /* Forward the call to the client defined callback */
7412 HWND hwnd = (HWND)lParam;
7413 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7414 HDPA hdpa_first = (HDPA) first;
7415 HDPA hdpa_second = (HDPA) second;
7416 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
7417 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
7419 rv = (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7426 * Sorts the listview items.
7429 * [I] HWND : window handle
7430 * [I] WPARAM : application-defined value
7431 * [I] LPARAM : pointer to comparision callback
7437 static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam)
7439 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7441 UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7442 HDPA hdpaSubItems=NULL;
7443 LISTVIEW_ITEM *pLVItem=NULL;
7444 LPVOID selectionMarkItem;
7446 if (lStyle & LVS_OWNERDATA)
7449 if (!infoPtr || !infoPtr->hdpaItems)
7452 nCount = GETITEMCOUNT(infoPtr);
7453 /* if there are 0 or 1 items, there is no need to sort */
7457 infoPtr->pfnCompare = (PFNLVCOMPARE)lParam;
7458 infoPtr->lParamSort = (LPARAM)wParam;
7459 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7461 /* Adjust selections and indices so that they are the way they should
7462 * be after the sort (otherwise, the list items move around, but
7463 * whatever is at the item's previous original position will be
7466 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7467 for (i=0; i < nCount; i++)
7469 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7470 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7472 if (pLVItem->state & LVIS_SELECTED)
7473 LISTVIEW_AddSelectionRange(hwnd, i, i);
7475 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7476 if (pLVItem->state & LVIS_FOCUSED)
7477 infoPtr->nFocusedItem=i;
7479 if (selectionMarkItem != NULL)
7480 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7481 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7483 /* align the items */
7484 LISTVIEW_AlignTop(hwnd);
7486 /* refresh the display */
7487 InvalidateRect(hwnd, NULL, TRUE);
7492 /* LISTVIEW_SubItemHitTest */
7496 * Updates an items or rearranges the listview control.
7499 * [I] HWND : window handle
7500 * [I] INT : item index
7506 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7508 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7509 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7510 BOOL bResult = FALSE;
7513 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7517 /* rearrange with default alignment style */
7518 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7519 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7521 ListView_Arrange(hwnd, 0);
7525 /* get item bounding rectangle */
7526 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
7527 InvalidateRect(hwnd, &rc, TRUE);
7536 * Creates the listview control.
7539 * [I] HWND : window handle
7544 static LRESULT LISTVIEW_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
7546 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7547 LPCREATESTRUCTA lpcs = (LPCREATESTRUCTA)lParam;
7548 UINT uView = lpcs->style & LVS_TYPEMASK;
7551 /* initialize info pointer */
7552 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7554 /* determine the type of structures to use */
7555 infoPtr->notifyFormat = SendMessageA(GetParent(hwnd), WM_NOTIFYFORMAT,
7556 (WPARAM)hwnd, (LPARAM)NF_QUERY);
7557 if (infoPtr->notifyFormat != NFR_ANSI)
7559 FIXME("ANSI notify format is NOT used\n");
7562 /* initialize color information */
7563 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7564 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7565 infoPtr->clrTextBk = CLR_DEFAULT;
7567 /* set default values */
7568 infoPtr->uCallbackMask = 0;
7569 infoPtr->nFocusedItem = -1;
7570 infoPtr->nSelectionMark = -1;
7571 infoPtr->nHotItem = -1;
7572 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7573 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7574 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7575 infoPtr->hwndEdit = 0;
7576 infoPtr->pedititem = NULL;
7577 infoPtr->nEditLabelItem = -1;
7579 /* get default font (icon title) */
7580 SystemParametersInfoA(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7581 infoPtr->hDefaultFont = CreateFontIndirectA(&logFont);
7582 infoPtr->hFont = infoPtr->hDefaultFont;
7585 infoPtr->hwndHeader = CreateWindowA(WC_HEADERA, (LPCSTR)NULL,
7586 WS_CHILD | HDS_HORZ | HDS_BUTTONS,
7587 0, 0, 0, 0, hwnd, (HMENU)0,
7588 lpcs->hInstance, NULL);
7590 /* set header font */
7591 SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7594 if (uView == LVS_ICON)
7596 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7597 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7599 else if (uView == LVS_REPORT)
7601 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7603 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7607 /* set HDS_HIDDEN flag to hide the header bar */
7608 SetWindowLongA(infoPtr->hwndHeader, GWL_STYLE,
7609 GetWindowLongA(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7613 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7614 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7618 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7619 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7622 /* display unsupported listview window styles */
7623 LISTVIEW_UnsupportedStyles(lpcs->style);
7625 /* allocate memory for the data structure */
7626 infoPtr->hdpaItems = DPA_Create(10);
7628 /* allocate memory for the selection ranges */
7629 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7631 /* initialize size of items */
7632 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7633 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7635 /* initialize the hover time to -1(indicating the default system hover time) */
7636 infoPtr->dwHoverTime = -1;
7643 * Erases the background of the listview control.
7646 * [I] HWND : window handle
7647 * [I] WPARAM : device context handle
7648 * [I] LPARAM : not used
7654 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
7657 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7660 if (infoPtr->clrBk == CLR_NONE)
7662 bResult = SendMessageA(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
7667 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7668 GetClientRect(hwnd, &rc);
7669 FillRect((HDC)wParam, &rc, hBrush);
7670 DeleteObject(hBrush);
7678 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
7680 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7682 if (infoPtr->clrBk != CLR_NONE)
7684 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7685 FillRect(hdc, rc, hBrush);
7686 DeleteObject(hBrush);
7692 * Retrieves the listview control font.
7695 * [I] HWND : window handle
7700 static LRESULT LISTVIEW_GetFont(HWND hwnd)
7702 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7704 return infoPtr->hFont;
7709 * Performs vertical scrolling.
7712 * [I] HWND : window handle
7713 * [I] INT : scroll code
7714 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
7716 * [I] HWND : scrollbar control window handle
7721 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7724 SCROLLINFO scrollInfo;
7726 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7727 SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7729 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7730 scrollInfo.cbSize = sizeof(SCROLLINFO);
7731 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7733 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7735 INT nOldScrollPos = scrollInfo.nPos;
7736 switch (nScrollCode)
7739 if (scrollInfo.nPos > scrollInfo.nMin)
7746 if (scrollInfo.nPos < scrollInfo.nMax)
7753 if (scrollInfo.nPos > scrollInfo.nMin)
7755 if (scrollInfo.nPos >= scrollInfo.nPage)
7757 scrollInfo.nPos -= scrollInfo.nPage;
7761 scrollInfo.nPos = scrollInfo.nMin;
7767 if (scrollInfo.nPos < scrollInfo.nMax)
7769 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7771 scrollInfo.nPos += scrollInfo.nPage;
7775 scrollInfo.nPos = scrollInfo.nMax;
7780 case SB_THUMBPOSITION:
7782 scrollInfo.nPos = nCurrentPos;
7783 if (scrollInfo.nPos > scrollInfo.nMax)
7784 scrollInfo.nPos=scrollInfo.nMax;
7786 if (scrollInfo.nPos < scrollInfo.nMin)
7787 scrollInfo.nPos=scrollInfo.nMin;
7792 if (nOldScrollPos != scrollInfo.nPos)
7794 scrollInfo.fMask = SIF_POS;
7795 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
7796 InvalidateRect(hwnd, NULL, TRUE);
7805 * Performs horizontal scrolling.
7808 * [I] HWND : window handle
7809 * [I] INT : scroll code
7810 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
7812 * [I] HWND : scrollbar control window handle
7817 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7820 SCROLLINFO scrollInfo;
7822 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7823 SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7826 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7827 scrollInfo.cbSize = sizeof(SCROLLINFO);
7828 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7830 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
7832 INT nOldScrollPos = scrollInfo.nPos;
7834 switch (nScrollCode)
7837 if (scrollInfo.nPos > scrollInfo.nMin)
7844 if (scrollInfo.nPos < scrollInfo.nMax)
7851 if (scrollInfo.nPos > scrollInfo.nMin)
7853 if (scrollInfo.nPos >= scrollInfo.nPage)
7855 scrollInfo.nPos -= scrollInfo.nPage;
7859 scrollInfo.nPos = scrollInfo.nMin;
7865 if (scrollInfo.nPos < scrollInfo.nMax)
7867 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7869 scrollInfo.nPos += scrollInfo.nPage;
7873 scrollInfo.nPos = scrollInfo.nMax;
7878 case SB_THUMBPOSITION:
7880 scrollInfo.nPos = nCurrentPos;
7882 if (scrollInfo.nPos > scrollInfo.nMax)
7883 scrollInfo.nPos=scrollInfo.nMax;
7885 if (scrollInfo.nPos < scrollInfo.nMin)
7886 scrollInfo.nPos=scrollInfo.nMin;
7890 if (nOldScrollPos != scrollInfo.nPos)
7892 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7893 scrollInfo.fMask = SIF_POS;
7894 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
7895 if(uView == LVS_REPORT)
7897 scrollInfo.fMask = SIF_POS;
7898 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
7899 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
7901 InvalidateRect(hwnd, NULL, TRUE);
7908 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
7910 INT gcWheelDelta = 0;
7911 UINT pulScrollLines = 3;
7912 SCROLLINFO scrollInfo;
7914 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7916 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7917 gcWheelDelta -= wheelDelta;
7919 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7920 scrollInfo.cbSize = sizeof(SCROLLINFO);
7921 scrollInfo.fMask = SIF_POS | SIF_RANGE;
7928 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7929 * should be fixed in the future.
7931 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7932 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
7936 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7938 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7940 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
7941 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7942 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7948 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7959 * [I] HWND : window handle
7960 * [I] INT : virtual key
7961 * [I] LONG : key data
7966 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
7968 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7969 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
7970 HWND hwndParent = GetParent(hwnd);
7971 NMLVKEYDOWN nmKeyDown;
7974 BOOL bRedraw = FALSE;
7975 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7976 UINT uView = lStyle & LVS_TYPEMASK;
7978 /* send LVN_KEYDOWN notification */
7979 ZeroMemory(&nmKeyDown, sizeof(NMLVKEYDOWN));
7980 nmKeyDown.hdr.hwndFrom = hwnd;
7981 nmKeyDown.hdr.idFrom = nCtrlId;
7982 nmKeyDown.hdr.code = LVN_KEYDOWN;
7983 nmKeyDown.wVKey = nVirtualKey;
7984 nmKeyDown.flags = 0;
7985 SendMessageA(hwndParent, WM_NOTIFY, (WPARAM)nCtrlId, (LPARAM)&nmKeyDown);
7988 nmh.hwndFrom = hwnd;
7989 nmh.idFrom = nCtrlId;
7991 switch (nVirtualKey)
7994 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
7996 /* send NM_RETURN notification */
7997 nmh.code = NM_RETURN;
7998 ListView_Notify(hwndParent, nCtrlId, &nmh);
8000 /* send LVN_ITEMACTIVATE notification */
8001 nmh.code = LVN_ITEMACTIVATE;
8002 ListView_Notify(hwndParent, nCtrlId, &nmh);
8007 if (GETITEMCOUNT(infoPtr) > 0)
8014 if (GETITEMCOUNT(infoPtr) > 0)
8016 nItem = GETITEMCOUNT(infoPtr) - 1;
8021 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8025 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8029 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8033 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8037 if (uView == LVS_REPORT)
8039 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8043 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8044 * LISTVIEW_GetCountPerRow(hwnd);
8046 if(nItem < 0) nItem = 0;
8050 if (uView == LVS_REPORT)
8052 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8056 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8057 * LISTVIEW_GetCountPerRow(hwnd);
8059 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8063 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8065 bRedraw = LISTVIEW_KeySelection(hwnd, nItem);
8066 if (bRedraw != FALSE)
8068 /* refresh client area */
8081 * [I] HWND : window handle
8086 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8088 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
8089 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8093 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8094 UINT uView = lStyle & LVS_TYPEMASK;
8096 TRACE("(hwnd=%x)\n", hwnd);
8098 /* send NM_KILLFOCUS notification */
8099 nmh.hwndFrom = hwnd;
8100 nmh.idFrom = nCtrlId;
8101 nmh.code = NM_KILLFOCUS;
8102 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8104 /* set window focus flag */
8105 infoPtr->bFocus = FALSE;
8107 /* NEED drawing optimization ; redraw the selected items */
8108 if (uView & LVS_REPORT)
8110 nTop = LISTVIEW_GetTopIndex(hwnd);
8112 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8117 nBottom = GETITEMCOUNT(infoPtr);
8119 for (i = nTop; i<nBottom; i++)
8121 if (LISTVIEW_IsSelected(hwnd,i))
8123 rcItem.left = LVIR_BOUNDS;
8124 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8125 InvalidateRect(hwnd, &rcItem, FALSE);
8134 * Processes double click messages (left mouse button).
8137 * [I] HWND : window handle
8138 * [I] WORD : key flag
8139 * [I] WORD : x coordinate
8140 * [I] WORD : y coordinate
8145 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8148 LONG nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8149 LVHITTESTINFO htInfo;
8154 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8156 htInfo.pt.x = wPosX;
8157 htInfo.pt.y = wPosY;
8159 /* send NM_DBLCLK notification */
8160 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8161 nmlv.hdr.hwndFrom = hwnd;
8162 nmlv.hdr.idFrom = nCtrlId;
8163 nmlv.hdr.code = NM_DBLCLK;
8164 ret = LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE);
8167 nmlv.iItem = htInfo.iItem;
8168 nmlv.iSubItem = htInfo.iSubItem;
8175 nmlv.ptAction.x = wPosX;
8176 nmlv.ptAction.y = wPosY;
8177 ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8180 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8183 /* send LVN_ITEMACTIVATE notification */
8184 nmh.hwndFrom = hwnd;
8185 nmh.idFrom = nCtrlId;
8186 nmh.code = LVN_ITEMACTIVATE;
8187 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8195 * Processes mouse down messages (left mouse button).
8198 * [I] HWND : window handle
8199 * [I] WORD : key flag
8200 * [I] WORD : x coordinate
8201 * [I] WORD : y coordinate
8206 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8209 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8210 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8211 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8212 static BOOL bGroupSelect = TRUE;
8217 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX,
8220 /* send NM_RELEASEDCAPTURE notification */
8221 nmh.hwndFrom = hwnd;
8222 nmh.idFrom = nCtrlId;
8223 nmh.code = NM_RELEASEDCAPTURE;
8224 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8226 if (infoPtr->bFocus == FALSE)
8231 /* set left button down flag */
8232 infoPtr->bLButtonDown = TRUE;
8234 ptPosition.x = wPosX;
8235 ptPosition.y = wPosY;
8236 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8237 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8239 if (lStyle & LVS_SINGLESEL)
8241 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8242 && infoPtr->nEditLabelItem == -1)
8244 infoPtr->nEditLabelItem = nItem;
8248 LISTVIEW_SetSelection(hwnd, nItem);
8253 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8255 if (bGroupSelect != FALSE)
8257 LISTVIEW_AddGroupSelection(hwnd, nItem);
8261 LISTVIEW_AddSelection(hwnd, nItem);
8264 else if (wKey & MK_CONTROL)
8266 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8268 else if (wKey & MK_SHIFT)
8270 LISTVIEW_SetGroupSelection(hwnd, nItem);
8275 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8277 /* set selection (clears other pre-existing selections) */
8278 LISTVIEW_SetSelection(hwnd, nItem);
8280 if (was_selected && infoPtr->nEditLabelItem == -1)
8282 infoPtr->nEditLabelItem = nItem;
8289 /* remove all selections */
8290 LISTVIEW_RemoveAllSelections(hwnd);
8293 /* redraw if we could have possibly selected something */
8294 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8301 * Processes mouse up messages (left mouse button).
8304 * [I] HWND : window handle
8305 * [I] WORD : key flag
8306 * [I] WORD : x coordinate
8307 * [I] WORD : y coordinate
8312 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8315 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8317 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8319 if (infoPtr->bLButtonDown != FALSE)
8321 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8323 LVHITTESTINFO lvHitTestInfo;
8326 lvHitTestInfo.pt.x = wPosX;
8327 lvHitTestInfo.pt.y = wPosY;
8329 /* send NM_CLICK notification */
8330 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8331 nmlv.hdr.hwndFrom = hwnd;
8332 nmlv.hdr.idFrom = nCtrlId;
8333 nmlv.hdr.code = NM_CLICK;
8334 ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
8337 nmlv.iItem = lvHitTestInfo.iItem;
8338 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8345 nmlv.ptAction.x = wPosX;
8346 nmlv.ptAction.y = wPosY;
8347 ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8350 /* set left button flag */
8351 infoPtr->bLButtonDown = FALSE;
8353 if(infoPtr->nEditLabelItem != -1)
8355 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem)
8357 LISTVIEW_EditLabelA(hwnd, lvHitTestInfo.iItem);
8359 infoPtr->nEditLabelItem = -1;
8368 * Creates the listview control (called before WM_CREATE).
8371 * [I] HWND : window handle
8372 * [I] WPARAM : unhandled
8373 * [I] LPARAM : widow creation info
8378 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8380 LISTVIEW_INFO *infoPtr;
8382 TRACE("(hwnd=%x,wParam=%x,lParam=%lx)\n", hwnd, wParam, lParam);
8384 /* allocate memory for info structure */
8385 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8386 SetWindowLongA(hwnd, 0, (LONG)infoPtr);
8387 if (infoPtr == NULL)
8389 ERR("could not allocate info memory!\n");
8393 if ((LISTVIEW_INFO *)GetWindowLongA(hwnd, 0) != infoPtr)
8395 ERR("pointer assignment error!\n");
8399 return DefWindowProcA(hwnd, WM_NCCREATE, wParam, lParam);
8404 * Destroys the listview control (called after WM_DESTROY).
8407 * [I] HWND : window handle
8412 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8414 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8416 TRACE("(hwnd=%x)\n", hwnd);
8418 /* delete all items */
8419 LISTVIEW_DeleteAllItems(hwnd);
8421 /* destroy data structure */
8422 DPA_Destroy(infoPtr->hdpaItems);
8423 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8426 infoPtr->hFont = (HFONT)0;
8427 if (infoPtr->hDefaultFont)
8429 DeleteObject(infoPtr->hDefaultFont);
8432 /* free listview info pointer*/
8433 COMCTL32_Free(infoPtr);
8435 SetWindowLongA(hwnd, 0, 0);
8441 * Handles notifications from children.
8444 * [I] HWND : window handle
8445 * [I] INT : control identifier
8446 * [I] LPNMHDR : notification information
8451 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8453 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8455 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8457 /* handle notification from header control */
8458 if (lpnmh->code == HDN_ENDTRACKA)
8460 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8461 InvalidateRect(hwnd, NULL, TRUE);
8463 else if(lpnmh->code == HDN_ITEMCLICKA)
8465 /* Handle sorting by Header Column */
8467 LPNMHEADERA pnmHeader = (LPNMHEADERA) lpnmh;
8468 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
8470 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8471 nmlv.hdr.hwndFrom = hwnd;
8472 nmlv.hdr.idFrom = lCtrlId;
8473 nmlv.hdr.code = LVN_COLUMNCLICK;
8475 nmlv.iSubItem = pnmHeader->iItem;
8477 ListView_LVNotify(GetParent(hwnd),lCtrlId, &nmlv);
8480 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8482 /* Idealy this should be done in HDN_ENDTRACKA
8483 * but since SetItemBounds in Header.c is called after
8484 * the notification is sent, it is neccessary to handle the
8485 * update of the scroll bar here (Header.c works fine as it is,
8486 * no need to disturb it)
8488 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8489 LISTVIEW_UpdateScroll(hwnd);
8490 InvalidateRect(hwnd, NULL, TRUE);
8500 * Determines the type of structure to use.
8503 * [I] HWND : window handle of the sender
8504 * [I] HWND : listview window handle
8505 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8510 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8512 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8514 if (nCommand == NF_REQUERY)
8516 /* determine the type of structure to use */
8517 infoPtr->notifyFormat = SendMessageA(hwndFrom, WM_NOTIFYFORMAT,
8518 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8519 if (infoPtr->notifyFormat == NFR_UNICODE)
8521 FIXME("NO support for unicode structures\n");
8530 * Paints/Repaints the listview control.
8533 * [I] HWND : window handle
8534 * [I] HDC : device context handle
8539 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8543 TRACE("(hwnd=%x,hdc=%x)\n", hwnd, hdc);
8547 hdc = BeginPaint(hwnd, &ps);
8548 LISTVIEW_Refresh(hwnd, hdc);
8549 EndPaint(hwnd, &ps);
8553 LISTVIEW_Refresh(hwnd, hdc);
8561 * Processes double click messages (right mouse button).
8564 * [I] HWND : window handle
8565 * [I] WORD : key flag
8566 * [I] WORD : x coordinate
8567 * [I] WORD : y coordinate
8572 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8575 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8578 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8580 /* send NM_RELEASEDCAPTURE notification */
8581 nmh.hwndFrom = hwnd;
8582 nmh.idFrom = nCtrlId;
8583 nmh.code = NM_RELEASEDCAPTURE;
8584 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8586 /* send NM_RDBLCLK notification */
8587 nmh.code = NM_RDBLCLK;
8588 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8595 * Processes mouse down messages (right mouse button).
8598 * [I] HWND : window handle
8599 * [I] WORD : key flag
8600 * [I] WORD : x coordinate
8601 * [I] WORD : y coordinate
8606 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8609 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8610 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8615 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8617 /* send NM_RELEASEDCAPTURE notification */
8618 nmh.hwndFrom = hwnd;
8619 nmh.idFrom = nCtrlId;
8620 nmh.code = NM_RELEASEDCAPTURE;
8621 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8623 /* make sure the listview control window has the focus */
8624 if (infoPtr->bFocus == FALSE)
8629 /* set right button down flag */
8630 infoPtr->bRButtonDown = TRUE;
8632 /* determine the index of the selected item */
8633 ptPosition.x = wPosX;
8634 ptPosition.y = wPosY;
8635 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8636 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8638 LISTVIEW_SetItemFocus(hwnd,nItem);
8639 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8640 !LISTVIEW_IsSelected(hwnd,nItem))
8642 LISTVIEW_SetSelection(hwnd, nItem);
8647 LISTVIEW_RemoveAllSelections(hwnd);
8655 * Processes mouse up messages (right mouse button).
8658 * [I] HWND : window handle
8659 * [I] WORD : key flag
8660 * [I] WORD : x coordinate
8661 * [I] WORD : y coordinate
8666 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8669 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8670 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8672 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8674 if (infoPtr->bRButtonDown != FALSE)
8677 LVHITTESTINFO lvHitTestInfo;
8681 lvHitTestInfo.pt.x = wPosX;
8682 lvHitTestInfo.pt.y = wPosY;
8684 /* Send NM_RClICK notification */
8685 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8686 nmlv.hdr.hwndFrom = hwnd;
8687 nmlv.hdr.idFrom = nCtrlId;
8688 nmlv.hdr.code = NM_RCLICK;
8689 ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
8692 nmlv.iItem = lvHitTestInfo.iItem;
8693 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8700 nmlv.ptAction.x = wPosX;
8701 nmlv.ptAction.y = wPosY;
8702 ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8707 /* set button flag */
8708 infoPtr->bRButtonDown = FALSE;
8710 /* Change to screen coordinate for WM_CONTEXTMENU */
8711 ClientToScreen(hwnd, &pt);
8713 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8714 SendMessageA( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
8725 * [I] HWND : window handle
8726 * [I] HWND : window handle of previously focused window
8731 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
8733 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8734 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8737 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
8739 /* send NM_SETFOCUS notification */
8740 nmh.hwndFrom = hwnd;
8741 nmh.idFrom = nCtrlId;
8742 nmh.code = NM_SETFOCUS;
8743 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8745 /* set window focus flag */
8746 infoPtr->bFocus = TRUE;
8758 * [I] HWND : window handle
8759 * [I] HFONT : font handle
8760 * [I] WORD : redraw flag
8765 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
8767 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8768 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8770 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
8774 infoPtr->hFont = infoPtr->hDefaultFont;
8778 infoPtr->hFont = hFont;
8781 if (uView == LVS_REPORT)
8783 /* set header font */
8784 SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
8785 MAKELPARAM(fRedraw, 0));
8788 /* invalidate listview control client area */
8789 InvalidateRect(hwnd, NULL, TRUE);
8791 if (fRedraw != FALSE)
8801 * Message handling for WM_SETREDRAW.
8802 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8805 * [I] HWND : window handle
8806 * [I] bRedraw: state of redraw flag
8809 * DefWinProc return value
8811 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
8814 lResult = DefWindowProcA(hwnd, WM_SETREDRAW, bRedraw, 0);
8817 RedrawWindow(hwnd, NULL, 0,
8818 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8825 * Resizes the listview control. This function processes WM_SIZE
8826 * messages. At this time, the width and height are not used.
8829 * [I] HWND : window handle
8830 * [I] WORD : new width
8831 * [I] WORD : new height
8836 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
8838 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8839 UINT uView = lStyle & LVS_TYPEMASK;
8841 TRACE("(hwnd=%x, width=%d, height=%d)\n",hwnd, Width, Height);
8843 LISTVIEW_UpdateSize(hwnd);
8845 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8847 if (lStyle & LVS_ALIGNLEFT)
8849 LISTVIEW_AlignLeft(hwnd);
8853 LISTVIEW_AlignTop(hwnd);
8857 LISTVIEW_UpdateScroll(hwnd);
8859 /* invalidate client area + erase background */
8860 InvalidateRect(hwnd, NULL, TRUE);
8867 * Sets the size information.
8870 * [I] HWND : window handle
8875 static VOID LISTVIEW_UpdateSize(HWND hwnd)
8877 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8878 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8879 UINT uView = lStyle & LVS_TYPEMASK;
8882 GetClientRect(hwnd, &rcList);
8883 infoPtr->rcList.left = 0;
8884 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8885 infoPtr->rcList.top = 0;
8886 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8888 if (uView == LVS_LIST)
8890 if (lStyle & WS_HSCROLL)
8892 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8893 if (infoPtr->rcList.bottom > nHScrollHeight)
8895 infoPtr->rcList.bottom -= nHScrollHeight;
8899 else if (uView == LVS_REPORT)
8906 Header_Layout(infoPtr->hwndHeader, &hl);
8908 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8910 if (!(LVS_NOCOLUMNHEADER & lStyle))
8912 infoPtr->rcList.top = max(wp.cy, 0);
8919 * Processes WM_STYLECHANGED messages.
8922 * [I] HWND : window handle
8923 * [I] WPARAM : window style type (normal or extended)
8924 * [I] LPSTYLESTRUCT : window style information
8929 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
8932 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8933 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8934 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8935 RECT rcList = infoPtr->rcList;
8937 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
8938 hwnd, wStyleType, lpss);
8940 if (wStyleType == GWL_STYLE)
8942 if (uOldView == LVS_REPORT)
8944 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8947 if ((lpss->styleOld & WS_HSCROLL) != 0)
8949 ShowScrollBar(hwnd, SB_HORZ, FALSE);
8952 if ((lpss->styleOld & WS_VSCROLL) != 0)
8954 ShowScrollBar(hwnd, SB_VERT, FALSE);
8957 if (uNewView == LVS_ICON)
8959 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8960 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8961 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8962 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8963 if (lpss->styleNew & LVS_ALIGNLEFT)
8965 LISTVIEW_AlignLeft(hwnd);
8969 LISTVIEW_AlignTop(hwnd);
8972 else if (uNewView == LVS_REPORT)
8979 Header_Layout(infoPtr->hwndHeader, &hl);
8980 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
8982 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8983 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8985 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8986 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8987 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8988 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8990 else if (uNewView == LVS_LIST)
8992 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8993 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8994 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8995 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8999 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9000 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9001 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9002 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9003 if (lpss->styleNew & LVS_ALIGNLEFT)
9005 LISTVIEW_AlignLeft(hwnd);
9009 LISTVIEW_AlignTop(hwnd);
9013 /* update the size of the client area */
9014 LISTVIEW_UpdateSize(hwnd);
9016 /* add scrollbars if needed */
9017 LISTVIEW_UpdateScroll(hwnd);
9019 /* invalidate client area + erase background */
9020 InvalidateRect(hwnd, NULL, TRUE);
9022 /* print the list of unsupported window styles */
9023 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9026 /* If they change the view and we have an active edit control
9027 we will need to kill the control since the redraw will
9028 misplace the edit control.
9030 if (infoPtr->hwndEdit &&
9031 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9032 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9034 SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9042 * Window procedure of the listview control.
9045 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9048 TRACE("hwnd=%x uMsg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
9049 if (!GetWindowLongA(hwnd, 0) && (uMsg != WM_NCCREATE))
9050 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
9053 case LVM_APPROXIMATEVIEWRECT:
9054 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9055 LOWORD(lParam), HIWORD(lParam));
9057 return LISTVIEW_Arrange(hwnd, (INT)wParam);
9059 /* case LVM_CREATEDRAGIMAGE: */
9061 case LVM_DELETEALLITEMS:
9062 return LISTVIEW_DeleteAllItems(hwnd);
9064 case LVM_DELETECOLUMN:
9065 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9067 case LVM_DELETEITEM:
9068 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9070 case LVM_EDITLABELW:
9071 case LVM_EDITLABELA:
9072 return LISTVIEW_EditLabelA(hwnd, (INT)wParam);
9074 case LVM_ENSUREVISIBLE:
9075 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9078 return LISTVIEW_FindItem(hwnd, (INT)wParam, (LPLVFINDINFO)lParam);
9080 case LVM_GETBKCOLOR:
9081 return LISTVIEW_GetBkColor(hwnd);
9083 /* case LVM_GETBKIMAGE: */
9085 case LVM_GETCALLBACKMASK:
9086 return LISTVIEW_GetCallbackMask(hwnd);
9088 case LVM_GETCOLUMNA:
9089 return LISTVIEW_GetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9091 /* case LVM_GETCOLUMNW: */
9093 case LVM_GETCOLUMNORDERARRAY:
9094 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9096 case LVM_GETCOLUMNWIDTH:
9097 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9099 case LVM_GETCOUNTPERPAGE:
9100 return LISTVIEW_GetCountPerPage(hwnd);
9102 case LVM_GETEDITCONTROL:
9103 return LISTVIEW_GetEditControl(hwnd);
9105 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9106 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9109 return LISTVIEW_GetHeader(hwnd);
9111 /* case LVM_GETHOTCURSOR: */
9113 case LVM_GETHOTITEM:
9114 return LISTVIEW_GetHotItem(hwnd);
9116 case LVM_GETHOVERTIME:
9117 return LISTVIEW_GetHoverTime(hwnd);
9119 case LVM_GETIMAGELIST:
9120 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9122 /* case LVM_GETISEARCHSTRING: */
9125 return LISTVIEW_GetItemA(hwnd, (LPLVITEMA)lParam, FALSE);
9127 /* case LVM_GETITEMW: */
9129 case LVM_GETITEMCOUNT:
9130 return LISTVIEW_GetItemCount(hwnd);
9132 case LVM_GETITEMPOSITION:
9133 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9135 case LVM_GETITEMRECT:
9136 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9138 case LVM_GETITEMSPACING:
9139 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9141 case LVM_GETITEMSTATE:
9142 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9144 case LVM_GETITEMTEXTA:
9145 LISTVIEW_GetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9148 /* case LVM_GETITEMTEXTW: */
9150 case LVM_GETNEXTITEM:
9151 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9153 /* case LVM_GETNUMBEROFWORKAREAS: */
9156 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9158 case LVM_GETSELECTEDCOUNT:
9159 return LISTVIEW_GetSelectedCount(hwnd);
9161 case LVM_GETSELECTIONMARK:
9162 return LISTVIEW_GetSelectionMark(hwnd);
9164 case LVM_GETSTRINGWIDTHA:
9165 return LISTVIEW_GetStringWidthA (hwnd, (LPCSTR)lParam);
9167 /* case LVM_GETSTRINGWIDTHW: */
9168 /* case LVM_GETSUBITEMRECT: */
9170 case LVM_GETTEXTBKCOLOR:
9171 return LISTVIEW_GetTextBkColor(hwnd);
9173 case LVM_GETTEXTCOLOR:
9174 return LISTVIEW_GetTextColor(hwnd);
9176 /* case LVM_GETTOOLTIPS: */
9178 case LVM_GETTOPINDEX:
9179 return LISTVIEW_GetTopIndex(hwnd);
9181 /* case LVM_GETUNICODEFORMAT: */
9183 case LVM_GETVIEWRECT:
9184 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9186 /* case LVM_GETWORKAREAS: */
9189 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9191 case LVM_INSERTCOLUMNA:
9192 return LISTVIEW_InsertColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9194 case LVM_INSERTCOLUMNW:
9195 return LISTVIEW_InsertColumnW(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam);
9197 case LVM_INSERTITEMA:
9198 return LISTVIEW_InsertItemA(hwnd, (LPLVITEMA)lParam);
9200 case LVM_INSERTITEMW:
9201 return LISTVIEW_InsertItemW(hwnd, (LPLVITEMW)lParam);
9203 case LVM_REDRAWITEMS:
9204 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9206 /* case LVM_SCROLL: */
9207 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9209 case LVM_SETBKCOLOR:
9210 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9212 /* case LVM_SETBKIMAGE: */
9214 case LVM_SETCALLBACKMASK:
9215 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9217 case LVM_SETCOLUMNA:
9218 return LISTVIEW_SetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9220 case LVM_SETCOLUMNW:
9221 FIXME("Unimplemented msg LVM_SETCOLUMNW\n");
9224 case LVM_SETCOLUMNORDERARRAY:
9225 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9227 case LVM_SETCOLUMNWIDTH:
9228 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9230 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9231 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9233 /* case LVM_SETHOTCURSOR: */
9235 case LVM_SETHOTITEM:
9236 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9238 case LVM_SETHOVERTIME:
9239 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9241 /* case LVM_SETICONSPACING: */
9243 case LVM_SETIMAGELIST:
9244 return LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9247 return LISTVIEW_SetItemA(hwnd, (LPLVITEMA)lParam);
9249 /* case LVM_SETITEMW: */
9251 case LVM_SETITEMCOUNT:
9252 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9254 case LVM_SETITEMPOSITION:
9255 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9256 (INT)HIWORD(lParam));
9258 case LVM_SETITEMPOSITION32:
9259 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9260 ((POINT*)lParam)->y);
9262 case LVM_SETITEMSTATE:
9263 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9265 case LVM_SETITEMTEXTA:
9266 return LISTVIEW_SetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9268 /* case LVM_SETITEMTEXTW: */
9270 case LVM_SETSELECTIONMARK:
9271 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9273 case LVM_SETTEXTBKCOLOR:
9274 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9276 case LVM_SETTEXTCOLOR:
9277 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9279 /* case LVM_SETTOOLTIPS: */
9280 /* case LVM_SETUNICODEFORMAT: */
9281 /* case LVM_SETWORKAREAS: */
9284 return LISTVIEW_SortItems(hwnd, wParam, lParam);
9286 /* case LVM_SUBITEMHITTEST: */
9289 return LISTVIEW_Update(hwnd, (INT)wParam);
9292 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9295 return LISTVIEW_Command(hwnd, wParam, lParam);
9298 return LISTVIEW_Create(hwnd, wParam, lParam);
9301 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9304 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9307 return LISTVIEW_GetFont(hwnd);
9310 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
9311 (INT)HIWORD(wParam), (HWND)lParam);
9314 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9317 return LISTVIEW_KillFocus(hwnd);
9319 case WM_LBUTTONDBLCLK:
9320 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9323 case WM_LBUTTONDOWN:
9324 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9327 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9330 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9333 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9336 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9339 return LISTVIEW_NCDestroy(hwnd);
9342 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9344 case WM_NOTIFYFORMAT:
9345 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9348 return LISTVIEW_Paint(hwnd, (HDC)wParam);
9350 case WM_RBUTTONDBLCLK:
9351 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9354 case WM_RBUTTONDOWN:
9355 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9359 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9363 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9366 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9369 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9372 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9374 case WM_STYLECHANGED:
9375 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9377 /* case WM_TIMER: */
9380 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
9381 (INT)HIWORD(wParam), (HWND)lParam);
9384 if (wParam & (MK_SHIFT | MK_CONTROL))
9385 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
9386 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
9388 /* case WM_WININICHANGE: */
9391 if (uMsg >= WM_USER)
9393 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
9397 /* call default window procedure */
9398 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
9406 * Registers the window class.
9414 VOID LISTVIEW_Register(void)
9418 ZeroMemory(&wndClass, sizeof(WNDCLASSA));
9419 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9420 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9421 wndClass.cbClsExtra = 0;
9422 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9423 wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
9424 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9425 wndClass.lpszClassName = WC_LISTVIEWA;
9426 RegisterClassA(&wndClass);
9431 * Unregisters the window class.
9439 VOID LISTVIEW_Unregister(void)
9441 UnregisterClassA(WC_LISTVIEWA, (HINSTANCE)NULL);
9446 * Handle any WM_COMMAND messages
9452 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9454 switch (HIWORD(wParam))
9459 * Adjust the edit window size
9462 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
9463 HDC hdc = GetDC(infoPtr->hwndEdit);
9464 HFONT hFont, hOldFont = 0;
9469 len = GetWindowTextA(infoPtr->hwndEdit, buffer, 1023);
9470 GetWindowRect(infoPtr->hwndEdit, &rect);
9472 /* Select font to get the right dimension of the string */
9473 hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9476 hOldFont = SelectObject(hdc, hFont);
9479 if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
9481 TEXTMETRICA textMetric;
9483 /* Add Extra spacing for the next character */
9484 GetTextMetricsA(hdc, &textMetric);
9485 sz.cx += (textMetric.tmMaxCharWidth * 2);
9493 rect.bottom - rect.top,
9494 SWP_DRAWFRAME|SWP_NOMOVE);
9498 SelectObject(hdc, hOldFont);
9501 ReleaseDC(hwnd, hdc);
9507 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9516 * Subclassed edit control windproc function
9522 LRESULT CALLBACK EditLblWndProc(HWND hwnd, UINT uMsg,
9523 WPARAM wParam, LPARAM lParam)
9525 BOOL cancel = FALSE;
9526 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(GetParent(hwnd), 0);
9527 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9528 static BOOL bIgnoreKillFocus = FALSE;
9532 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9535 if(bIgnoreKillFocus)
9543 WNDPROC editProc = einfo->EditWndProc;
9544 SetWindowLongA(hwnd, GWL_WNDPROC, (LONG)editProc);
9545 COMCTL32_Free(einfo);
9546 infoPtr->pedititem = NULL;
9547 return CallWindowProcA(editProc, hwnd, uMsg, wParam, lParam);
9551 if (VK_ESCAPE == (INT)wParam)
9557 else if (VK_RETURN == (INT)wParam)
9561 return CallWindowProcA(einfo->EditWndProc, hwnd,
9562 uMsg, wParam, lParam);
9565 if (einfo->EditLblCb)
9567 char *buffer = NULL;
9572 int len = 1 + GetWindowTextLengthA(hwnd);
9576 if (NULL != (buffer = (char *)COMCTL32_Alloc(len*sizeof(char))))
9578 GetWindowTextA(hwnd, buffer, len);
9582 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9583 /* eg. Using a messagebox */
9584 bIgnoreKillFocus = TRUE;
9585 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9588 COMCTL32_Free(buffer);
9590 einfo->EditLblCb = NULL;
9591 bIgnoreKillFocus = FALSE;
9594 SendMessageA(hwnd, WM_CLOSE, 0, 0);
9601 * Creates a subclassed edit cotrol
9607 HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y,
9608 INT width, INT height, HWND parent, HINSTANCE hinst,
9609 EditlblCallback EditLblCb, DWORD param)
9615 TEXTMETRICA textMetric;
9616 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(parent, 0);
9618 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9621 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9622 hdc = GetDC(parent);
9624 /* Select the font to get appropriate metric dimensions */
9625 if(infoPtr->hFont != 0)
9627 hOldFont = SelectObject(hdc, infoPtr->hFont);
9630 /*Get String Lenght in pixels */
9631 GetTextExtentPoint32A(hdc, text, strlen(text), &sz);
9633 /*Add Extra spacing for the next character */
9634 GetTextMetricsA(hdc, &textMetric);
9635 sz.cx += (textMetric.tmMaxCharWidth * 2);
9637 if(infoPtr->hFont != 0)
9639 SelectObject(hdc, hOldFont);
9642 ReleaseDC(parent, hdc);
9643 if (!(hedit = CreateWindowA("Edit", text, style, x, y, sz.cx, height,
9644 parent, 0, hinst, 0)))
9646 COMCTL32_Free(infoPtr->pedititem);
9650 infoPtr->pedititem->param = param;
9651 infoPtr->pedititem->EditLblCb = EditLblCb;
9652 infoPtr->pedititem->EditWndProc = (WNDPROC)SetWindowLongA(hedit,
9653 GWL_WNDPROC, (LONG) EditLblWndProc);
9655 SendMessageA(hedit, WM_SETFONT, infoPtr->hFont, FALSE);