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;
4985 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4986 /* In the following:
4987 * lpLVItem describes the information requested by the user
4988 * lpItem/lpSubItem is what we have
4989 * dispInfo is a structure we use to request the missing
4990 * information from the application
4993 TRACE("(hwnd=%x, lpLVItem=%p)\n", hwnd, lpLVItem);
4995 if ((lpLVItem == NULL) ||
4996 (lpLVItem->iItem < 0) ||
4997 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr))
5001 if (lStyle & LVS_OWNERDATA)
5003 if (lpLVItem->mask & ~LVIF_STATE)
5005 dispInfo.hdr.hwndFrom = hwnd;
5006 dispInfo.hdr.idFrom = lCtrlId;
5007 dispInfo.hdr.code = LVN_GETDISPINFOA;
5008 memcpy(&dispInfo.item,lpLVItem,sizeof(LVITEMA));
5010 ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
5011 memcpy(lpLVItem,&dispInfo.item,sizeof(LVITEMA));
5014 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5016 lpLVItem->state = 0;
5017 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5018 lpLVItem->state |= LVIS_FOCUSED;
5019 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5020 lpLVItem->state |= LVIS_SELECTED;
5027 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5028 if (hdpaSubItems == NULL)
5031 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
5035 ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
5036 if (lpLVItem->iSubItem == 0)
5038 piImage=&lpItem->iImage;
5039 ppszText=&lpItem->pszText;
5040 if ((infoPtr->uCallbackMask != 0) && (lpLVItem->mask & LVIF_STATE))
5042 dispInfo.item.mask |= LVIF_STATE;
5043 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5048 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5049 if (lpSubItem != NULL)
5051 piImage=&lpSubItem->iImage;
5052 ppszText=&lpSubItem->pszText;
5061 if ((lpLVItem->mask & LVIF_IMAGE) &&
5062 ((piImage==NULL) || (*piImage == I_IMAGECALLBACK)))
5064 dispInfo.item.mask |= LVIF_IMAGE;
5067 if ((lpLVItem->mask & LVIF_TEXT) &&
5068 ((ppszText==NULL) || (*ppszText == LPSTR_TEXTCALLBACKA)))
5070 dispInfo.item.mask |= LVIF_TEXT;
5071 dispInfo.item.pszText = lpLVItem->pszText;
5072 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5075 if (dispInfo.item.mask != 0)
5077 /* We don't have all the requested info, query the application */
5078 dispInfo.hdr.hwndFrom = hwnd;
5079 dispInfo.hdr.idFrom = lCtrlId;
5080 dispInfo.hdr.code = LVN_GETDISPINFOA;
5081 dispInfo.item.iItem = lpLVItem->iItem;
5082 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5083 dispInfo.item.lParam = lpItem->lParam;
5084 ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
5087 if (dispInfo.item.mask & LVIF_IMAGE)
5089 lpLVItem->iImage = dispInfo.item.iImage;
5091 else if (lpLVItem->mask & LVIF_IMAGE)
5093 lpLVItem->iImage = *piImage;
5096 if (dispInfo.item.mask & LVIF_PARAM)
5098 lpLVItem->lParam = dispInfo.item.lParam;
5100 else if (lpLVItem->mask & LVIF_PARAM)
5102 lpLVItem->lParam = lpItem->lParam;
5105 if (dispInfo.item.mask & LVIF_TEXT)
5107 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (ppszText != NULL))
5109 Str_SetPtrA(ppszText, dispInfo.item.pszText);
5111 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5112 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5113 if (lpLVItem->pszText!=dispInfo.item.pszText) {
5114 lstrcpynA(lpLVItem->pszText, dispInfo.item.pszText, lpLVItem->cchTextMax);
5117 if (ppszText == NULL)
5119 lstrcpynA(lpLVItem->pszText, "", lpLVItem->cchTextMax);
5122 else if (lpLVItem->mask & LVIF_TEXT)
5126 lpLVItem->pszText=*ppszText;
5128 lstrcpynA(lpLVItem->pszText, *ppszText, lpLVItem->cchTextMax);
5132 if (lpLVItem->iSubItem == 0)
5134 if (dispInfo.item.mask & LVIF_STATE)
5136 lpLVItem->state = lpItem->state;
5137 lpLVItem->state &= ~dispInfo.item.stateMask;
5138 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5140 lpLVItem->state &= ~LVIS_SELECTED;
5141 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5142 (LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem)))
5143 lpLVItem->state |= LVIS_SELECTED;
5145 else if (lpLVItem->mask & LVIF_STATE)
5147 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5149 lpLVItem->state &= ~LVIS_SELECTED;
5150 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5151 (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem)))
5152 lpLVItem->state |= LVIS_SELECTED;
5155 if (lpLVItem->mask & LVIF_PARAM)
5157 lpLVItem->lParam = lpItem->lParam;
5160 if (lpLVItem->mask & LVIF_INDENT)
5162 lpLVItem->iIndent = lpItem->iIndent;
5169 /* LISTVIEW_GetItemW */
5170 /* LISTVIEW_GetHotCursor */
5174 * Retrieves the index of the hot item.
5177 * [I] HWND : window handle
5180 * SUCCESS : hot item index
5181 * FAILURE : -1 (no hot item)
5183 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5185 LISTVIEW_INFO *infoPtr;
5187 /* make sure we can get the listview info */
5188 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
5191 return (infoPtr->nHotItem);
5194 /* LISTVIEW_GetHoverTime */
5198 * Retrieves the number of items in the listview control.
5201 * [I] HWND : window handle
5206 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5208 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5210 return GETITEMCOUNT(infoPtr);
5215 * Retrieves the position (upper-left) of the listview control item.
5218 * [I] HWND : window handle
5219 * [I] INT : item index
5220 * [O] LPPOINT : coordinate information
5226 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem,
5227 LPPOINT lpptPosition)
5229 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5230 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5231 BOOL bResult = FALSE;
5233 LISTVIEW_ITEM *lpItem;
5234 INT nCountPerColumn;
5237 TRACE("(hwnd=%x,nItem=%d,lpptPosition=%p)\n", hwnd, nItem,
5240 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5241 (lpptPosition != NULL))
5243 if (uView == LVS_LIST)
5246 nItem = nItem - ListView_GetTopIndex(hwnd);
5247 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5250 nRow = nItem % nCountPerColumn;
5253 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
5254 lpptPosition->y = 0;
5258 lpptPosition->x = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5259 lpptPosition->y = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5264 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
5265 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
5268 else if (uView == LVS_REPORT)
5270 SCROLLINFO scrollInfo;
5272 lpptPosition->x = REPORT_MARGINX;
5273 lpptPosition->y = ((nItem - ListView_GetTopIndex(hwnd)) *
5274 infoPtr->nItemHeight) + infoPtr->rcList.top;
5276 /* Adjust position by scrollbar offset */
5277 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5278 scrollInfo.cbSize = sizeof(SCROLLINFO);
5279 scrollInfo.fMask = SIF_POS;
5280 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5281 lpptPosition->x -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5285 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5286 if (hdpaSubItems != NULL)
5288 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
5292 lpptPosition->x = lpItem->ptPosition.x;
5293 lpptPosition->y = lpItem->ptPosition.y;
5303 * Retrieves the bounding rectangle for a listview control item.
5306 * [I] HWND : window handle
5307 * [I] INT : item index
5308 * [IO] LPRECT : bounding rectangle coordinates
5309 * lprc->left specifies the portion of the item for which the bounding
5310 * rectangle will be retrieved.
5312 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5313 * including the icon and label.
5314 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5315 * LVIR_LABEL Returns the bounding rectangle of the item text.
5316 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5317 * rectangles, but excludes columns in report view.
5323 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5325 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5326 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5327 BOOL bResult = FALSE;
5338 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5340 if (uView & LVS_REPORT)
5342 ZeroMemory(&lvItem, sizeof(LVITEMA));
5343 lvItem.mask = LVIF_INDENT;
5344 lvItem.iItem = nItem;
5345 lvItem.iSubItem = 0;
5346 LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
5349 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5351 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5359 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5361 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) != FALSE)
5366 if (uView == LVS_ICON)
5368 if (infoPtr->himlNormal != NULL)
5370 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5373 lprc->left = ptItem.x + ptOrigin.x;
5374 lprc->top = ptItem.y + ptOrigin.y;
5375 lprc->right = lprc->left + infoPtr->iconSize.cx;
5376 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5377 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5381 else if (uView == LVS_SMALLICON)
5383 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5386 lprc->left = ptItem.x + ptOrigin.x;
5387 lprc->top = ptItem.y + ptOrigin.y;
5388 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5390 if (infoPtr->himlState != NULL)
5391 lprc->left += infoPtr->iconSize.cx;
5393 if (infoPtr->himlSmall != NULL)
5394 lprc->right = lprc->left + infoPtr->iconSize.cx;
5396 lprc->right = lprc->left;
5402 lprc->left = ptItem.x;
5403 if (uView & LVS_REPORT)
5404 lprc->left += nIndent;
5405 lprc->top = ptItem.y;
5406 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5408 if (infoPtr->himlState != NULL)
5410 lprc->left += infoPtr->iconSize.cx;
5413 if (infoPtr->himlSmall != NULL)
5415 lprc->right = lprc->left + infoPtr->iconSize.cx;
5419 lprc->right = lprc->left;
5425 if (uView == LVS_ICON)
5427 if (infoPtr->himlNormal != NULL)
5429 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5432 lprc->left = ptItem.x + ptOrigin.x;
5433 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5434 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5435 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5436 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5438 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5439 lprc->right = lprc->left + nLabelWidth;
5444 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5448 hOldFont = SelectObject(hdc, infoPtr->hFont);
5449 GetTextMetricsA(hdc, &tm);
5450 lprc->bottom = lprc->top + tm.tmHeight + HEIGHT_PADDING;
5451 SelectObject(hdc, hOldFont);
5452 ReleaseDC(hwnd, hdc);
5456 else if (uView == LVS_SMALLICON)
5458 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5461 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5462 lprc->top = ptItem.y + ptOrigin.y;
5463 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5465 if (infoPtr->himlState != NULL)
5467 lprc->left += infoPtr->iconSize.cx;
5470 if (infoPtr->himlSmall != NULL)
5472 lprc->left += infoPtr->iconSize.cx;
5475 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5476 nLabelWidth += TRAILING_PADDING;
5477 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5479 lprc->right = lprc->left + nLabelWidth;
5483 lprc->right = nLeftPos + infoPtr->nItemWidth;
5490 if (uView & LVS_REPORT)
5491 nLeftPos = lprc->left = ptItem.x + nIndent;
5493 nLeftPos = lprc->left = ptItem.x;
5494 lprc->top = ptItem.y;
5495 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5497 if (infoPtr->himlState != NULL)
5499 lprc->left += infoPtr->iconSize.cx;
5502 if (infoPtr->himlSmall != NULL)
5504 lprc->left += infoPtr->iconSize.cx;
5507 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5508 nLabelWidth += TRAILING_PADDING;
5509 if (infoPtr->himlSmall)
5510 nLabelWidth += IMAGE_PADDING;
5511 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5513 lprc->right = lprc->left + nLabelWidth;
5517 lprc->right = nLeftPos + infoPtr->nItemWidth;
5523 if (uView == LVS_ICON)
5525 if (infoPtr->himlNormal != NULL)
5527 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5530 lprc->left = ptItem.x + ptOrigin.x;
5531 lprc->top = ptItem.y + ptOrigin.y;
5532 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5533 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5537 else if (uView == LVS_SMALLICON)
5539 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5542 lprc->left = ptItem.x + ptOrigin.x;
5543 lprc->right = lprc->left;
5544 lprc->top = ptItem.y + ptOrigin.y;
5545 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5546 if (infoPtr->himlState != NULL)
5547 lprc->right += infoPtr->iconSize.cx;
5548 if (infoPtr->himlSmall != NULL)
5549 lprc->right += infoPtr->iconSize.cx;
5551 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5552 nLabelWidth += TRAILING_PADDING;
5553 if (infoPtr->himlSmall)
5554 nLabelWidth += IMAGE_PADDING;
5555 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5557 lprc->right += nLabelWidth;
5561 lprc->right = lprc->left + infoPtr->nItemWidth;
5568 lprc->left = ptItem.x;
5569 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5570 lprc->left += nIndent;
5571 lprc->right = lprc->left;
5572 lprc->top = ptItem.y;
5573 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5575 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5578 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5579 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5581 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5585 if (infoPtr->himlState != NULL)
5587 lprc->right += infoPtr->iconSize.cx;
5590 if (infoPtr->himlSmall != NULL)
5592 lprc->right += infoPtr->iconSize.cx;
5595 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5596 nLabelWidth += TRAILING_PADDING;
5597 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5599 lprc->right += nLabelWidth;
5603 lprc->right = lprc->left + infoPtr->nItemWidth;
5609 case LVIR_SELECTBOUNDS:
5610 if (uView == LVS_ICON)
5612 if (infoPtr->himlNormal != NULL)
5614 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5617 lprc->left = ptItem.x + ptOrigin.x;
5618 lprc->top = ptItem.y + ptOrigin.y;
5619 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5620 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5624 else if (uView == LVS_SMALLICON)
5626 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5629 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5630 lprc->top = ptItem.y + ptOrigin.y;
5631 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5633 if (infoPtr->himlState != NULL)
5635 lprc->left += infoPtr->iconSize.cx;
5638 lprc->right = lprc->left;
5640 if (infoPtr->himlSmall != NULL)
5642 lprc->right += infoPtr->iconSize.cx;
5645 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5646 nLabelWidth += TRAILING_PADDING;
5647 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5649 lprc->right += nLabelWidth;
5653 lprc->right = nLeftPos + infoPtr->nItemWidth;
5660 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5661 nLeftPos = lprc->left = ptItem.x + nIndent;
5663 nLeftPos = lprc->left = ptItem.x;
5664 lprc->top = ptItem.y;
5665 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5667 if (infoPtr->himlState != NULL)
5669 lprc->left += infoPtr->iconSize.cx;
5672 lprc->right = lprc->left;
5674 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5677 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5678 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5680 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5684 if (infoPtr->himlSmall != NULL)
5686 lprc->right += infoPtr->iconSize.cx;
5689 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5690 nLabelWidth += TRAILING_PADDING;
5691 if (infoPtr->himlSmall)
5692 nLabelWidth += IMAGE_PADDING;
5693 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5695 lprc->right += nLabelWidth;
5699 lprc->right = nLeftPos + infoPtr->nItemWidth;
5712 * Retrieves the width of a label.
5715 * [I] HWND : window handle
5718 * SUCCESS : string width (in pixels)
5721 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
5723 CHAR szDispText[DISP_TEXT_SIZE];
5724 INT nLabelWidth = 0;
5727 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
5729 ZeroMemory(&lvItem, sizeof(LVITEMA));
5730 lvItem.mask = LVIF_TEXT;
5731 lvItem.iItem = nItem;
5732 lvItem.cchTextMax = DISP_TEXT_SIZE;
5733 lvItem.pszText = szDispText;
5734 if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
5736 nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
5744 * Retrieves the spacing between listview control items.
5747 * [I] HWND : window handle
5748 * [I] BOOL : flag for small or large icon
5751 * Horizontal + vertical spacing
5753 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
5755 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5758 if (bSmall == FALSE)
5760 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5764 LONG style = GetWindowLongA(hwnd, GWL_STYLE);
5765 if ((style & LVS_TYPEMASK) == LVS_ICON)
5767 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5771 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5779 * Retrieves the state of a listview control item.
5782 * [I] HWND : window handle
5783 * [I] INT : item index
5784 * [I] UINT : state mask
5787 * State specified by the mask.
5789 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
5791 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5795 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5797 ZeroMemory(&lvItem, sizeof(LVITEMA));
5798 lvItem.iItem = nItem;
5799 lvItem.stateMask = uMask;
5800 lvItem.mask = LVIF_STATE;
5801 if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
5803 uState = lvItem.state;
5812 * Retrieves the text of a listview control item or subitem.
5815 * [I] HWND : window handle
5816 * [I] INT : item index
5817 * [IO] LPLVITEMA : item information
5820 * SUCCESS : string length
5823 static LRESULT LISTVIEW_GetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
5825 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5828 if (lpLVItem != NULL)
5830 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5832 lpLVItem->mask = LVIF_TEXT;
5833 lpLVItem->iItem = nItem;
5834 if (LISTVIEW_GetItemA(hwnd, lpLVItem, FALSE) != FALSE)
5836 nLength = lstrlenA(lpLVItem->pszText);
5846 * Searches for an item based on properties + relationships.
5849 * [I] HWND : window handle
5850 * [I] INT : item index
5851 * [I] INT : relationship flag
5854 * SUCCESS : item index
5857 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
5859 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5860 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5862 LVFINDINFO lvFindInfo;
5863 INT nCountPerColumn;
5866 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
5868 ZeroMemory(&lvFindInfo, sizeof(LVFINDINFO));
5870 if (uFlags & LVNI_CUT)
5873 if (uFlags & LVNI_DROPHILITED)
5874 uMask |= LVIS_DROPHILITED;
5876 if (uFlags & LVNI_FOCUSED)
5877 uMask |= LVIS_FOCUSED;
5879 if (uFlags & LVNI_SELECTED)
5880 uMask |= LVIS_SELECTED;
5882 if (uFlags & LVNI_ABOVE)
5884 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5889 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5895 lvFindInfo.flags = LVFI_NEARESTXY;
5896 lvFindInfo.vkDirection = VK_UP;
5897 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5898 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5900 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5905 else if (uFlags & LVNI_BELOW)
5907 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5909 while (nItem < GETITEMCOUNT(infoPtr))
5912 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5918 lvFindInfo.flags = LVFI_NEARESTXY;
5919 lvFindInfo.vkDirection = VK_DOWN;
5920 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5921 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5923 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5928 else if (uFlags & LVNI_TOLEFT)
5930 if (uView == LVS_LIST)
5932 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5933 while (nItem - nCountPerColumn >= 0)
5935 nItem -= nCountPerColumn;
5936 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5940 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5942 lvFindInfo.flags = LVFI_NEARESTXY;
5943 lvFindInfo.vkDirection = VK_LEFT;
5944 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5945 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5947 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5952 else if (uFlags & LVNI_TORIGHT)
5954 if (uView == LVS_LIST)
5956 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5957 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
5959 nItem += nCountPerColumn;
5960 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5964 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5966 lvFindInfo.flags = LVFI_NEARESTXY;
5967 lvFindInfo.vkDirection = VK_RIGHT;
5968 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5969 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5971 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5980 /* search by index */
5981 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
5983 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
5992 /* LISTVIEW_GetNumberOfWorkAreas */
5996 * Retrieves the origin coordinates when in icon or small icon display mode.
5999 * [I] HWND : window handle
6000 * [O] LPPOINT : coordinate information
6006 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6008 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6009 UINT uView = lStyle & LVS_TYPEMASK;
6010 BOOL bResult = FALSE;
6012 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6014 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6016 SCROLLINFO scrollInfo;
6017 ZeroMemory(lpptOrigin, sizeof(POINT));
6018 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6019 scrollInfo.cbSize = sizeof(SCROLLINFO);
6021 if (lStyle & WS_HSCROLL)
6023 scrollInfo.fMask = SIF_POS;
6024 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6026 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6030 if (lStyle & WS_VSCROLL)
6032 scrollInfo.fMask = SIF_POS;
6033 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6035 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6047 * Retrieves the number of items that are marked as selected.
6050 * [I] HWND : window handle
6053 * Number of items selected.
6055 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6058 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6059 INT nSelectedCount = 0;
6062 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6064 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6070 return nSelectedCount;
6075 * Retrieves item index that marks the start of a multiple selection.
6078 * [I] HWND : window handle
6081 * Index number or -1 if there is no selection mark.
6083 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6085 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6087 return infoPtr->nSelectionMark;
6092 * Retrieves the width of a string.
6095 * [I] HWND : window handle
6098 * SUCCESS : string width (in pixels)
6101 static LRESULT LISTVIEW_GetStringWidthA(HWND hwnd, LPCSTR lpszText)
6103 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6104 HFONT hFont, hOldFont;
6108 ZeroMemory(&stringSize, sizeof(SIZE));
6109 if (lpszText != NULL && lpszText != LPSTR_TEXTCALLBACKA)
6111 hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6113 hOldFont = SelectObject(hdc, hFont);
6114 GetTextExtentPointA(hdc, lpszText, lstrlenA(lpszText), &stringSize);
6115 SelectObject(hdc, hOldFont);
6116 ReleaseDC(hwnd, hdc);
6119 return stringSize.cx;
6124 * Retrieves the text backgound color.
6127 * [I] HWND : window handle
6130 * COLORREF associated with the the background.
6132 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6134 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6136 return infoPtr->clrTextBk;
6141 * Retrieves the text color.
6144 * [I] HWND : window handle
6147 * COLORREF associated with the text.
6149 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6151 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6153 return infoPtr->clrText;
6158 * Determines which section of the item was selected (if any).
6161 * [I] HWND : window handle
6162 * [IO] LPLVHITTESTINFO : hit test information
6163 * [I] subitem : fill out iSubItem.
6166 * SUCCESS : item index
6169 static INT LISTVIEW_HitTestItem(
6170 HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem
6172 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6174 INT i,topindex,bottomindex;
6175 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6176 UINT uView = lStyle & LVS_TYPEMASK;
6179 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6180 lpHitTestInfo->pt.y);
6182 topindex = ListView_GetTopIndex(hwnd);
6183 if (uView == LVS_REPORT)
6185 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6186 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6190 bottomindex = GETITEMCOUNT(infoPtr);
6193 for (i = topindex; i < bottomindex; i++)
6195 rcItem.left = LVIR_BOUNDS;
6196 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6198 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6200 rcItem.left = LVIR_ICON;
6201 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6203 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6205 lpHitTestInfo->flags = LVHT_ONITEMICON;
6206 lpHitTestInfo->iItem = i;
6207 if (subitem) lpHitTestInfo->iSubItem = 0;
6212 rcItem.left = LVIR_LABEL;
6213 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6215 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6217 lpHitTestInfo->flags = LVHT_ONITEMLABEL;
6218 lpHitTestInfo->iItem = i;
6219 if (subitem) lpHitTestInfo->iSubItem = 0;
6224 lpHitTestInfo->flags = LVHT_ONITEMSTATEICON;
6225 lpHitTestInfo->iItem = i;
6226 if (subitem) lpHitTestInfo->iSubItem = 0;
6232 lpHitTestInfo->flags = LVHT_NOWHERE;
6239 * Determines which listview item is located at the specified position.
6242 * [I] HWND : window handle
6243 * [IO} LPLVHITTESTINFO : hit test information
6246 * SUCCESS : item index
6249 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6251 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6254 lpHitTestInfo->flags = 0;
6256 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6258 lpHitTestInfo->flags = LVHT_TOLEFT;
6260 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6262 lpHitTestInfo->flags = LVHT_TORIGHT;
6264 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6266 lpHitTestInfo->flags |= LVHT_ABOVE;
6268 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6270 lpHitTestInfo->flags |= LVHT_BELOW;
6273 if (lpHitTestInfo->flags == 0)
6275 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6276 * an app might pass only a structure with space up to iItem!
6277 * (MS Office 97 does that for instance in the file open dialog)
6279 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6287 * Inserts a new column.
6290 * [I] HWND : window handle
6291 * [I] INT : column index
6292 * [I] LPLVCOLUMNA : column information
6295 * SUCCESS : new column index
6298 static LRESULT LISTVIEW_InsertColumnA(HWND hwnd, INT nColumn,
6299 LPLVCOLUMNA lpColumn)
6301 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6303 INT nNewColumn = -1;
6305 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn,
6308 if (lpColumn != NULL)
6310 /* initialize memory */
6311 ZeroMemory(&hdi, sizeof(HDITEMA));
6313 if (lpColumn->mask & LVCF_FMT)
6315 /* format member is valid */
6316 hdi.mask |= HDI_FORMAT;
6318 /* set text alignment (leftmost column must be left-aligned) */
6321 hdi.fmt |= HDF_LEFT;
6325 if (lpColumn->fmt & LVCFMT_LEFT)
6327 hdi.fmt |= HDF_LEFT;
6329 else if (lpColumn->fmt & LVCFMT_RIGHT)
6331 hdi.fmt |= HDF_RIGHT;
6333 else if (lpColumn->fmt & LVCFMT_CENTER)
6335 hdi.fmt |= HDF_CENTER;
6339 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6341 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6345 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6350 if (lpColumn->fmt & LVCFMT_IMAGE)
6352 hdi.fmt |= HDF_IMAGE;
6353 hdi.iImage = I_IMAGECALLBACK;
6357 if (lpColumn->mask & LVCF_WIDTH)
6359 hdi.mask |= HDI_WIDTH;
6360 hdi.cxy = lpColumn->cx;
6363 if (lpColumn->mask & LVCF_TEXT)
6365 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6366 hdi.pszText = lpColumn->pszText;
6367 hdi.cchTextMax = ((lpColumn->pszText!=NULL) && (lpColumn->pszText!=LPSTR_TEXTCALLBACKA) ? strlen(lpColumn->pszText) : 0);
6368 hdi.fmt |= HDF_STRING;
6371 if (lpColumn->mask & LVCF_IMAGE)
6373 hdi.mask |= HDI_IMAGE;
6374 hdi.iImage = lpColumn->iImage;
6377 if (lpColumn->mask & LVCF_ORDER)
6379 hdi.mask |= HDI_ORDER;
6380 hdi.iOrder = lpColumn->iOrder;
6383 /* insert item in header control */
6384 nNewColumn = SendMessageA(infoPtr->hwndHeader, HDM_INSERTITEMA,
6385 (WPARAM)nColumn, (LPARAM)&hdi);
6387 /* Need to reset the item width when inserting a new column */
6388 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6390 LISTVIEW_UpdateScroll(hwnd);
6391 InvalidateRect(hwnd, NULL, FALSE);
6397 static LRESULT LISTVIEW_InsertColumnW(HWND hwnd, INT nColumn,
6398 LPLVCOLUMNW lpColumn)
6403 memcpy(&lvca,lpColumn,sizeof(lvca));
6404 if (lpColumn->mask & LVCF_TEXT) {
6405 if (lpColumn->pszText == LPSTR_TEXTCALLBACKW)
6406 lvca.pszText = LPSTR_TEXTCALLBACKA;
6408 lvca.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpColumn->pszText);
6410 lres = LISTVIEW_InsertColumnA(hwnd,nColumn,&lvca);
6411 if (lpColumn->mask & LVCF_TEXT) {
6412 if (lpColumn->pszText != LPSTR_TEXTCALLBACKW)
6413 HeapFree(GetProcessHeap(),0,lvca.pszText);
6418 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6419 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6420 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6421 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6422 their own sort proc. when sending LVM_SORTITEMS.
6425 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6427 LVS_SORTXXX must be specified,
6428 LVS_OWNERDRAW is not set,
6429 <item>.pszText is not LPSTR_TEXTCALLBACK.
6431 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6432 are sorted based on item text..."
6434 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6436 HDPA hdpa_first = (HDPA) first;
6437 HDPA hdpa_second = (HDPA) second;
6438 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
6439 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
6440 LONG lStyle = GetWindowLongA((HWND) lParam, GWL_STYLE);
6441 INT cmpv = lstrcmpA( lv_first->pszText, lv_second->pszText );
6442 /* if we're sorting descending, negate the return value */
6443 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6448 * Inserts a new item in the listview control.
6451 * [I] HWND : window handle
6452 * [I] LPLVITEMA : item information
6455 * SUCCESS : new item index
6458 static LRESULT LISTVIEW_InsertItemA(HWND hwnd, LPLVITEMA lpLVItem)
6460 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6461 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6462 UINT uView = lStyle & LVS_TYPEMASK;
6463 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
6468 LISTVIEW_ITEM *lpItem = NULL;
6470 TRACE("(hwnd=%x,lpLVItem=%p)\n", hwnd, lpLVItem);
6472 if (lStyle & LVS_OWNERDATA)
6474 nItem = infoPtr->hdpaItems->nItemCount;
6475 infoPtr->hdpaItems->nItemCount ++;
6479 if (lpLVItem != NULL)
6481 /* make sure it's not a subitem; cannot insert a subitem */
6482 if (lpLVItem->iSubItem == 0)
6484 lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM));
6487 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6488 if (LISTVIEW_InitItem(hwnd, lpItem, lpLVItem) != FALSE)
6490 /* insert item in listview control data structure */
6491 hdpaSubItems = DPA_Create(8);
6492 if (hdpaSubItems != NULL)
6494 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
6497 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6498 && !(lStyle & LVS_OWNERDRAWFIXED)
6499 && (LPSTR_TEXTCALLBACKA != lpLVItem->pszText) )
6501 /* Insert the item in the proper sort order based on the pszText
6502 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6503 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6504 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6505 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6506 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6510 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6515 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6517 /* manage item focus */
6518 if (lpLVItem->mask & LVIF_STATE)
6520 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6521 if (lpLVItem->stateMask & LVIS_SELECTED)
6523 LISTVIEW_SetSelection(hwnd, nItem);
6525 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6527 LISTVIEW_SetItemFocus(hwnd, nItem);
6531 /* send LVN_INSERTITEM notification */
6532 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6533 nmlv.hdr.hwndFrom = hwnd;
6534 nmlv.hdr.idFrom = lCtrlId;
6535 nmlv.hdr.code = LVN_INSERTITEM;
6537 nmlv.lParam = lpItem->lParam;;
6538 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
6540 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6542 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6543 if (nItemWidth > infoPtr->nItemWidth)
6545 infoPtr->nItemWidth = nItemWidth;
6549 /* align items (set position of each item) */
6550 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6552 if (lStyle & LVS_ALIGNLEFT)
6554 LISTVIEW_AlignLeft(hwnd);
6558 LISTVIEW_AlignTop(hwnd);
6562 LISTVIEW_UpdateScroll(hwnd);
6563 /* refresh client area */
6564 InvalidateRect(hwnd, NULL, FALSE);
6573 /* free memory if unsuccessful */
6574 if ((nItem == -1) && (lpItem != NULL))
6576 COMCTL32_Free(lpItem);
6582 static LRESULT LISTVIEW_InsertItemW(HWND hwnd, LPLVITEMW lpLVItem) {
6586 memcpy(&lvia,lpLVItem,sizeof(LVITEMA));
6587 if (lvia.mask & LVIF_TEXT) {
6588 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
6589 lvia.pszText = LPSTR_TEXTCALLBACKA;
6591 lvia.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpLVItem->pszText);
6593 lres = LISTVIEW_InsertItemA(hwnd, &lvia);
6594 if (lvia.mask & LVIF_TEXT) {
6595 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6596 HeapFree(GetProcessHeap(),0,lvia.pszText);
6601 /* LISTVIEW_InsertItemW */
6605 * Redraws a range of items.
6608 * [I] HWND : window handle
6609 * [I] INT : first item
6610 * [I] INT : last item
6616 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6618 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6619 BOOL bResult = FALSE;
6622 if (nFirst <= nLast)
6624 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6626 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6629 for (i = nFirst; i <= nLast; i++)
6631 rcItem.left = LVIR_BOUNDS;
6632 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
6633 InvalidateRect(hwnd, &rcItem, TRUE);
6642 /* LISTVIEW_Scroll */
6646 * Sets the background color.
6649 * [I] HWND : window handle
6650 * [I] COLORREF : background color
6656 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6658 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6660 infoPtr->clrBk = clrBk;
6661 InvalidateRect(hwnd, NULL, TRUE);
6666 /* LISTVIEW_SetBkImage */
6670 * Sets the callback mask. This mask will be used when the parent
6671 * window stores state information (some or all).
6674 * [I] HWND : window handle
6675 * [I] UINT : state mask
6681 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
6683 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6685 infoPtr->uCallbackMask = uMask;
6692 * Sets the attributes of a header item.
6695 * [I] HWND : window handle
6696 * [I] INT : column index
6697 * [I] LPLVCOLUMNA : column attributes
6703 static LRESULT LISTVIEW_SetColumnA(HWND hwnd, INT nColumn,
6704 LPLVCOLUMNA lpColumn)
6706 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6707 BOOL bResult = FALSE;
6708 HDITEMA hdi, hdiget;
6710 if ((lpColumn != NULL) && (nColumn >= 0) &&
6711 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6713 /* initialize memory */
6714 ZeroMemory(&hdi, sizeof(HDITEMA));
6716 if (lpColumn->mask & LVCF_FMT)
6718 /* format member is valid */
6719 hdi.mask |= HDI_FORMAT;
6721 /* get current format first */
6722 hdiget.mask = HDI_FORMAT;
6723 if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdiget))
6724 /* preserve HDF_STRING if present */
6725 hdi.fmt = hdiget.fmt & HDF_STRING;
6727 /* set text alignment (leftmost column must be left-aligned) */
6730 hdi.fmt |= HDF_LEFT;
6734 if (lpColumn->fmt & LVCFMT_LEFT)
6736 hdi.fmt |= HDF_LEFT;
6738 else if (lpColumn->fmt & LVCFMT_RIGHT)
6740 hdi.fmt |= HDF_RIGHT;
6742 else if (lpColumn->fmt & LVCFMT_CENTER)
6744 hdi.fmt |= HDF_CENTER;
6748 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6750 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6753 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6755 hdi.fmt |= HDF_IMAGE;
6758 if (lpColumn->fmt & LVCFMT_IMAGE)
6760 hdi.fmt |= HDF_IMAGE;
6761 hdi.iImage = I_IMAGECALLBACK;
6765 if (lpColumn->mask & LVCF_WIDTH)
6767 hdi.mask |= HDI_WIDTH;
6768 hdi.cxy = lpColumn->cx;
6771 if (lpColumn->mask & LVCF_TEXT)
6773 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6774 hdi.pszText = lpColumn->pszText;
6775 hdi.cchTextMax = ((lpColumn->pszText!=NULL) && (lpColumn->pszText!=LPSTR_TEXTCALLBACKA) ? strlen(lpColumn->pszText) : 0);
6776 hdi.fmt |= HDF_STRING;
6779 if (lpColumn->mask & LVCF_IMAGE)
6781 hdi.mask |= HDI_IMAGE;
6782 hdi.iImage = lpColumn->iImage;
6785 if (lpColumn->mask & LVCF_ORDER)
6787 hdi.mask |= HDI_ORDER;
6788 hdi.iOrder = lpColumn->iOrder;
6791 /* set header item attributes */
6792 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6798 /* LISTVIEW_SetColumnW */
6802 * Sets the column order array
6805 * [I] HWND : window handle
6806 * [I] INT : number of elements in column order array
6807 * [I] INT : pointer to column order array
6813 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
6815 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); */
6817 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6828 * Sets the width of a column
6831 * [I] HWND : window handle
6832 * [I] INT : column index
6833 * [I] INT : column width
6839 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
6841 LISTVIEW_INFO *infoPtr;
6844 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6845 UINT uView = lStyle & LVS_TYPEMASK;
6850 CHAR text_buffer[DISP_TEXT_SIZE];
6851 INT header_item_count;
6856 /* make sure we can get the listview info */
6857 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6860 if (!infoPtr->hwndHeader) /* make sure we have a header */
6863 /* set column width only if in report or list mode */
6864 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6867 /* take care of invalid cx values */
6868 if((uView == LVS_REPORT) && (cx < -2))
6869 cx = LVSCW_AUTOSIZE;
6870 else if (uView == LVS_LIST && (cx < 1))
6873 /* resize all columns if in LVS_LIST mode */
6874 if(uView == LVS_LIST) {
6875 infoPtr->nItemWidth = cx;
6876 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6880 /* autosize based on listview items width */
6881 if(cx == LVSCW_AUTOSIZE)
6883 /* set the width of the header to the width of the widest item */
6884 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6886 if(cx < LISTVIEW_GetLabelWidth(hwnd, item_index))
6887 cx = LISTVIEW_GetLabelWidth(hwnd, item_index);
6889 } /* autosize based on listview header width */
6890 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6892 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6894 /* if iCol is the last column make it fill the remainder of the controls width */
6895 if(iCol == (header_item_count - 1)) {
6896 /* get the width of every item except the current one */
6897 hdi.mask = HDI_WIDTH;
6900 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6901 Header_GetItemA(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6905 /* retrieve the layout of the header */
6906 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6908 cx = (rcHeader.right - rcHeader.left) - cx;
6912 /* retrieve header font */
6913 header_font = SendMessageA(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6915 /* retrieve header text */
6916 hdi.mask = HDI_TEXT;
6917 hdi.cchTextMax = sizeof(text_buffer);
6918 hdi.pszText = text_buffer;
6920 Header_GetItemA(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6922 /* determine the width of the text in the header */
6924 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6926 GetTextExtentPoint32A(hdc, text_buffer, strlen(text_buffer), &size);
6928 SelectObject(hdc, old_font); /* restore the old font */
6929 ReleaseDC(hwnd, hdc);
6931 /* set the width of this column to the width of the text */
6936 /* call header to update the column change */
6937 hdi.mask = HDI_WIDTH;
6940 lret = Header_SetItemA(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6942 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6949 * Sets the extended listview style.
6952 * [I] HWND : window handle
6957 * SUCCESS : previous style
6960 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
6962 LISTVIEW_INFO *infoPtr;
6965 /* make sure we can get the listview info */
6966 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6969 /* store previous style */
6970 dwOldStyle = infoPtr->dwExStyle;
6974 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6976 infoPtr->dwExStyle = dwStyle;
6978 return (dwOldStyle);
6981 /* LISTVIEW_SetHotCursor */
6985 * Sets the hot item index.
6988 * [I] HWND : window handle
6992 * SUCCESS : previous hot item index
6993 * FAILURE : -1 (no hot item)
6995 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
6997 LISTVIEW_INFO *infoPtr;
7000 /* make sure we can get the listview info */
7001 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
7004 /* store previous index */
7005 iOldIndex = infoPtr->nHotItem;
7008 infoPtr->nHotItem = iIndex;
7015 * Sets the amount of time the cursor must hover over an item before it is selected.
7018 * [I] HWND : window handle
7019 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7022 * Returns the previous hover time
7024 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7026 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7027 DWORD oldHoverTime = infoPtr->dwHoverTime;
7029 infoPtr->dwHoverTime = dwHoverTime;
7031 return oldHoverTime;
7034 /* LISTVIEW_SetIconSpacing */
7041 * [I] HWND : window handle
7042 * [I] INT : image list type
7043 * [I] HIMAGELIST : image list handle
7046 * SUCCESS : old image list
7049 static LRESULT LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7051 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7052 HIMAGELIST himlOld = 0;
7058 himlOld = infoPtr->himlNormal;
7059 infoPtr->himlNormal = himl;
7063 himlOld = infoPtr->himlSmall;
7064 infoPtr->himlSmall = himl;
7068 himlOld = infoPtr->himlState;
7069 infoPtr->himlState = himl;
7070 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7074 oldHeight = infoPtr->nItemHeight;
7075 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7076 if (infoPtr->nItemHeight != oldHeight)
7077 LISTVIEW_UpdateScroll(hwnd);
7079 return (LRESULT)himlOld;
7085 * Sets the attributes of an item.
7088 * [I] HWND : window handle
7089 * [I] LPLVITEM : item information
7095 static LRESULT LISTVIEW_SetItemA(HWND hwnd, LPLVITEMA lpLVItem)
7097 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7098 BOOL bResult = FALSE;
7100 if (lpLVItem != NULL)
7102 if ((lpLVItem->iItem >= 0) && (lpLVItem->iItem < GETITEMCOUNT(infoPtr)))
7104 if (lpLVItem->iSubItem == 0)
7106 bResult = LISTVIEW_SetItem(hwnd, lpLVItem);
7110 bResult = LISTVIEW_SetSubItem(hwnd, lpLVItem);
7119 /* LISTVIEW_SetItemW */
7123 * Preallocates memory (does *not* set the actual count of items !)
7126 * [I] HWND : window handle
7127 * [I] INT : item count (projected number of items to allocate)
7128 * [I] DWORD : update flags
7134 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7136 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7138 if (GetWindowLongA(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7140 int precount,topvisible;
7141 TRACE("LVS_OWNERDATA is set!\n");
7144 * Internally remove all the selections.
7148 LISTVIEW_SELECTION *selection;
7149 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7151 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7154 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7156 precount = infoPtr->hdpaItems->nItemCount;
7157 topvisible = ListView_GetTopIndex(hwnd) +
7158 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7160 infoPtr->hdpaItems->nItemCount = nItems;
7162 LISTVIEW_UpdateSize(hwnd);
7163 LISTVIEW_UpdateScroll(hwnd);
7164 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7165 InvalidateRect(hwnd, NULL, TRUE);
7169 FIXME("setitemcount not done for non-ownerdata\n");
7177 * Sets the position of an item.
7180 * [I] HWND : window handle
7181 * [I] INT : item index
7182 * [I] LONG : x coordinate
7183 * [I] LONG : y coordinate
7189 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7190 LONG nPosX, LONG nPosY)
7192 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7193 UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7194 UINT uView = lStyle & LVS_TYPEMASK;
7195 LISTVIEW_ITEM *lpItem;
7197 BOOL bResult = FALSE;
7199 TRACE("(hwnd=%x,nItem=%d,X=%ld,Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7201 if (lStyle & LVS_OWNERDATA)
7204 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7206 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7208 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
7209 if (hdpaSubItems != NULL)
7211 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7215 lpItem->ptPosition.x = nPosX;
7216 lpItem->ptPosition.y = nPosY;
7227 * Sets the state of one or many items.
7230 * [I] HWND : window handle
7231 * [I]INT : item index
7232 * [I] LPLVITEM : item or subitem info
7238 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
7240 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7241 BOOL bResult = FALSE;
7248 ZeroMemory(&lvItem, sizeof(LVITEMA));
7249 lvItem.mask = LVIF_STATE;
7250 lvItem.state = lpLVItem->state;
7251 lvItem.stateMask = lpLVItem->stateMask ;
7253 /* apply to all items */
7254 for (i = 0; i< GETITEMCOUNT(infoPtr); i++)
7257 if (ListView_SetItemA(hwnd, &lvItem) == FALSE)
7265 ZeroMemory(&lvItem, sizeof(LVITEMA));
7266 lvItem.mask = LVIF_STATE;
7267 lvItem.state = lpLVItem->state;
7268 lvItem.stateMask = lpLVItem->stateMask;
7269 lvItem.iItem = nItem;
7270 bResult = ListView_SetItemA(hwnd, &lvItem);
7278 * Sets the text of an item or subitem.
7281 * [I] HWND : window handle
7282 * [I] INT : item index
7283 * [I] LPLVITEMA : item or subitem info
7289 static BOOL LISTVIEW_SetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
7291 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7292 BOOL bResult = FALSE;
7295 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7297 ZeroMemory(&lvItem, sizeof(LVITEMA));
7298 lvItem.mask = LVIF_TEXT;
7299 lvItem.pszText = lpLVItem->pszText;
7300 lvItem.iItem = nItem;
7301 lvItem.iSubItem = lpLVItem->iSubItem;
7302 bResult = ListView_SetItemA(hwnd, &lvItem);
7308 /* LISTVIEW_SetItemTextW */
7312 * Set item index that marks the start of a multiple selection.
7315 * [I] HWND : window handle
7319 * Index number or -1 if there is no selection mark.
7321 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7323 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7324 INT nOldIndex = infoPtr->nSelectionMark;
7326 infoPtr->nSelectionMark = nIndex;
7333 * Sets the text background color.
7336 * [I] HWND : window handle
7337 * [I] COLORREF : text background color
7343 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7345 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7347 infoPtr->clrTextBk = clrTextBk;
7348 InvalidateRect(hwnd, NULL, TRUE);
7355 * Sets the text foreground color.
7358 * [I] HWND : window handle
7359 * [I] COLORREF : text color
7365 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7367 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7369 infoPtr->clrText = clrText;
7370 InvalidateRect(hwnd, NULL, TRUE);
7375 /* LISTVIEW_SetToolTips */
7376 /* LISTVIEW_SetUnicodeFormat */
7377 /* LISTVIEW_SetWorkAreas */
7381 * Callback internally used by LISTVIEW_SortItems()
7384 * [I] LPVOID : first LISTVIEW_ITEM to compare
7385 * [I] LPVOID : second LISTVIEW_ITEM to compare
7386 * [I] LPARAM : HWND of control
7389 * if first comes before second : negative
7390 * if first comes after second : positive
7391 * if first and second are equivalent : zero
7393 static INT WINAPI LISTVIEW_CallBackCompare(
7398 /* Forward the call to the client defined callback */
7400 HWND hwnd = (HWND)lParam;
7401 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7402 HDPA hdpa_first = (HDPA) first;
7403 HDPA hdpa_second = (HDPA) second;
7404 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
7405 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
7407 rv = (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7414 * Sorts the listview items.
7417 * [I] HWND : window handle
7418 * [I] WPARAM : application-defined value
7419 * [I] LPARAM : pointer to comparision callback
7425 static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam)
7427 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7429 UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7430 HDPA hdpaSubItems=NULL;
7431 LISTVIEW_ITEM *pLVItem=NULL;
7432 LPVOID selectionMarkItem;
7434 if (lStyle & LVS_OWNERDATA)
7437 if (!infoPtr || !infoPtr->hdpaItems)
7440 nCount = GETITEMCOUNT(infoPtr);
7441 /* if there are 0 or 1 items, there is no need to sort */
7445 infoPtr->pfnCompare = (PFNLVCOMPARE)lParam;
7446 infoPtr->lParamSort = (LPARAM)wParam;
7447 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7449 /* Adjust selections and indices so that they are the way they should
7450 * be after the sort (otherwise, the list items move around, but
7451 * whatever is at the item's previous original position will be
7454 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7455 for (i=0; i < nCount; i++)
7457 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7458 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7460 if (pLVItem->state & LVIS_SELECTED)
7461 LISTVIEW_AddSelectionRange(hwnd, i, i);
7463 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7464 if (pLVItem->state & LVIS_FOCUSED)
7465 infoPtr->nFocusedItem=i;
7467 if (selectionMarkItem != NULL)
7468 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7469 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7471 /* align the items */
7472 LISTVIEW_AlignTop(hwnd);
7474 /* refresh the display */
7475 InvalidateRect(hwnd, NULL, TRUE);
7480 /* LISTVIEW_SubItemHitTest */
7484 * Updates an items or rearranges the listview control.
7487 * [I] HWND : window handle
7488 * [I] INT : item index
7494 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7496 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7497 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7498 BOOL bResult = FALSE;
7501 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7505 /* rearrange with default alignment style */
7506 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7507 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7509 ListView_Arrange(hwnd, 0);
7513 /* get item bounding rectangle */
7514 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
7515 InvalidateRect(hwnd, &rc, TRUE);
7524 * Creates the listview control.
7527 * [I] HWND : window handle
7532 static LRESULT LISTVIEW_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
7534 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7535 LPCREATESTRUCTA lpcs = (LPCREATESTRUCTA)lParam;
7536 UINT uView = lpcs->style & LVS_TYPEMASK;
7539 /* initialize info pointer */
7540 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7542 /* determine the type of structures to use */
7543 infoPtr->notifyFormat = SendMessageA(GetParent(hwnd), WM_NOTIFYFORMAT,
7544 (WPARAM)hwnd, (LPARAM)NF_QUERY);
7545 if (infoPtr->notifyFormat != NFR_ANSI)
7547 FIXME("ANSI notify format is NOT used\n");
7550 /* initialize color information */
7551 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7552 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7553 infoPtr->clrTextBk = CLR_DEFAULT;
7555 /* set default values */
7556 infoPtr->uCallbackMask = 0;
7557 infoPtr->nFocusedItem = -1;
7558 infoPtr->nSelectionMark = -1;
7559 infoPtr->nHotItem = -1;
7560 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7561 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7562 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7563 infoPtr->hwndEdit = 0;
7564 infoPtr->pedititem = NULL;
7565 infoPtr->nEditLabelItem = -1;
7567 /* get default font (icon title) */
7568 SystemParametersInfoA(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7569 infoPtr->hDefaultFont = CreateFontIndirectA(&logFont);
7570 infoPtr->hFont = infoPtr->hDefaultFont;
7573 infoPtr->hwndHeader = CreateWindowA(WC_HEADERA, (LPCSTR)NULL,
7574 WS_CHILD | HDS_HORZ | HDS_BUTTONS,
7575 0, 0, 0, 0, hwnd, (HMENU)0,
7576 lpcs->hInstance, NULL);
7578 /* set header font */
7579 SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7582 if (uView == LVS_ICON)
7584 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7585 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7587 else if (uView == LVS_REPORT)
7589 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7591 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7595 /* set HDS_HIDDEN flag to hide the header bar */
7596 SetWindowLongA(infoPtr->hwndHeader, GWL_STYLE,
7597 GetWindowLongA(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7601 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7602 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7606 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7607 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7610 /* display unsupported listview window styles */
7611 LISTVIEW_UnsupportedStyles(lpcs->style);
7613 /* allocate memory for the data structure */
7614 infoPtr->hdpaItems = DPA_Create(10);
7616 /* allocate memory for the selection ranges */
7617 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7619 /* initialize size of items */
7620 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7621 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7623 /* initialize the hover time to -1(indicating the default system hover time) */
7624 infoPtr->dwHoverTime = -1;
7631 * Erases the background of the listview control.
7634 * [I] HWND : window handle
7635 * [I] WPARAM : device context handle
7636 * [I] LPARAM : not used
7642 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
7645 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7648 if (infoPtr->clrBk == CLR_NONE)
7650 bResult = SendMessageA(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
7655 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7656 GetClientRect(hwnd, &rc);
7657 FillRect((HDC)wParam, &rc, hBrush);
7658 DeleteObject(hBrush);
7666 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
7668 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7670 if (infoPtr->clrBk != CLR_NONE)
7672 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7673 FillRect(hdc, rc, hBrush);
7674 DeleteObject(hBrush);
7680 * Retrieves the listview control font.
7683 * [I] HWND : window handle
7688 static LRESULT LISTVIEW_GetFont(HWND hwnd)
7690 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7692 return infoPtr->hFont;
7697 * Performs vertical scrolling.
7700 * [I] HWND : window handle
7701 * [I] INT : scroll code
7702 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
7704 * [I] HWND : scrollbar control window handle
7709 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7712 SCROLLINFO scrollInfo;
7714 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7715 SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7717 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7718 scrollInfo.cbSize = sizeof(SCROLLINFO);
7719 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7721 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7723 INT nOldScrollPos = scrollInfo.nPos;
7724 switch (nScrollCode)
7727 if (scrollInfo.nPos > scrollInfo.nMin)
7734 if (scrollInfo.nPos < scrollInfo.nMax)
7741 if (scrollInfo.nPos > scrollInfo.nMin)
7743 if (scrollInfo.nPos >= scrollInfo.nPage)
7745 scrollInfo.nPos -= scrollInfo.nPage;
7749 scrollInfo.nPos = scrollInfo.nMin;
7755 if (scrollInfo.nPos < scrollInfo.nMax)
7757 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7759 scrollInfo.nPos += scrollInfo.nPage;
7763 scrollInfo.nPos = scrollInfo.nMax;
7768 case SB_THUMBPOSITION:
7770 scrollInfo.nPos = nCurrentPos;
7771 if (scrollInfo.nPos > scrollInfo.nMax)
7772 scrollInfo.nPos=scrollInfo.nMax;
7774 if (scrollInfo.nPos < scrollInfo.nMin)
7775 scrollInfo.nPos=scrollInfo.nMin;
7780 if (nOldScrollPos != scrollInfo.nPos)
7782 scrollInfo.fMask = SIF_POS;
7783 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
7784 InvalidateRect(hwnd, NULL, TRUE);
7793 * Performs horizontal scrolling.
7796 * [I] HWND : window handle
7797 * [I] INT : scroll code
7798 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
7800 * [I] HWND : scrollbar control window handle
7805 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7808 SCROLLINFO scrollInfo;
7810 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7811 SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7814 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7815 scrollInfo.cbSize = sizeof(SCROLLINFO);
7816 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7818 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
7820 INT nOldScrollPos = scrollInfo.nPos;
7822 switch (nScrollCode)
7825 if (scrollInfo.nPos > scrollInfo.nMin)
7832 if (scrollInfo.nPos < scrollInfo.nMax)
7839 if (scrollInfo.nPos > scrollInfo.nMin)
7841 if (scrollInfo.nPos >= scrollInfo.nPage)
7843 scrollInfo.nPos -= scrollInfo.nPage;
7847 scrollInfo.nPos = scrollInfo.nMin;
7853 if (scrollInfo.nPos < scrollInfo.nMax)
7855 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7857 scrollInfo.nPos += scrollInfo.nPage;
7861 scrollInfo.nPos = scrollInfo.nMax;
7866 case SB_THUMBPOSITION:
7868 scrollInfo.nPos = nCurrentPos;
7870 if (scrollInfo.nPos > scrollInfo.nMax)
7871 scrollInfo.nPos=scrollInfo.nMax;
7873 if (scrollInfo.nPos < scrollInfo.nMin)
7874 scrollInfo.nPos=scrollInfo.nMin;
7878 if (nOldScrollPos != scrollInfo.nPos)
7880 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7881 scrollInfo.fMask = SIF_POS;
7882 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
7883 if(uView == LVS_REPORT)
7885 scrollInfo.fMask = SIF_POS;
7886 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
7887 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
7889 InvalidateRect(hwnd, NULL, TRUE);
7896 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
7898 INT gcWheelDelta = 0;
7899 UINT pulScrollLines = 3;
7900 SCROLLINFO scrollInfo;
7902 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7904 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7905 gcWheelDelta -= wheelDelta;
7907 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7908 scrollInfo.cbSize = sizeof(SCROLLINFO);
7909 scrollInfo.fMask = SIF_POS | SIF_RANGE;
7916 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7917 * should be fixed in the future.
7919 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7920 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
7924 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7926 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7928 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
7929 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7930 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7936 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7947 * [I] HWND : window handle
7948 * [I] INT : virtual key
7949 * [I] LONG : key data
7954 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
7956 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7957 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
7958 HWND hwndParent = GetParent(hwnd);
7959 NMLVKEYDOWN nmKeyDown;
7962 BOOL bRedraw = FALSE;
7963 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7964 UINT uView = lStyle & LVS_TYPEMASK;
7966 /* send LVN_KEYDOWN notification */
7967 ZeroMemory(&nmKeyDown, sizeof(NMLVKEYDOWN));
7968 nmKeyDown.hdr.hwndFrom = hwnd;
7969 nmKeyDown.hdr.idFrom = nCtrlId;
7970 nmKeyDown.hdr.code = LVN_KEYDOWN;
7971 nmKeyDown.wVKey = nVirtualKey;
7972 nmKeyDown.flags = 0;
7973 SendMessageA(hwndParent, WM_NOTIFY, (WPARAM)nCtrlId, (LPARAM)&nmKeyDown);
7976 nmh.hwndFrom = hwnd;
7977 nmh.idFrom = nCtrlId;
7979 switch (nVirtualKey)
7982 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
7984 /* send NM_RETURN notification */
7985 nmh.code = NM_RETURN;
7986 ListView_Notify(hwndParent, nCtrlId, &nmh);
7988 /* send LVN_ITEMACTIVATE notification */
7989 nmh.code = LVN_ITEMACTIVATE;
7990 ListView_Notify(hwndParent, nCtrlId, &nmh);
7995 if (GETITEMCOUNT(infoPtr) > 0)
8002 if (GETITEMCOUNT(infoPtr) > 0)
8004 nItem = GETITEMCOUNT(infoPtr) - 1;
8009 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8013 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8017 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8021 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8025 if (uView == LVS_REPORT)
8027 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8031 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8032 * LISTVIEW_GetCountPerRow(hwnd);
8034 if(nItem < 0) nItem = 0;
8038 if (uView == LVS_REPORT)
8040 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8044 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8045 * LISTVIEW_GetCountPerRow(hwnd);
8047 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8051 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8053 bRedraw = LISTVIEW_KeySelection(hwnd, nItem);
8054 if (bRedraw != FALSE)
8056 /* refresh client area */
8069 * [I] HWND : window handle
8074 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8076 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
8077 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8081 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8082 UINT uView = lStyle & LVS_TYPEMASK;
8084 TRACE("(hwnd=%x)\n", hwnd);
8086 /* send NM_KILLFOCUS notification */
8087 nmh.hwndFrom = hwnd;
8088 nmh.idFrom = nCtrlId;
8089 nmh.code = NM_KILLFOCUS;
8090 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8092 /* set window focus flag */
8093 infoPtr->bFocus = FALSE;
8095 /* NEED drawing optimization ; redraw the selected items */
8096 if (uView & LVS_REPORT)
8098 nTop = LISTVIEW_GetTopIndex(hwnd);
8100 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8105 nBottom = GETITEMCOUNT(infoPtr);
8107 for (i = nTop; i<nBottom; i++)
8109 if (LISTVIEW_IsSelected(hwnd,i))
8111 rcItem.left = LVIR_BOUNDS;
8112 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8113 InvalidateRect(hwnd, &rcItem, FALSE);
8122 * Processes double click messages (left mouse button).
8125 * [I] HWND : window handle
8126 * [I] WORD : key flag
8127 * [I] WORD : x coordinate
8128 * [I] WORD : y coordinate
8133 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8136 LONG nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8137 LVHITTESTINFO htInfo;
8142 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8144 htInfo.pt.x = wPosX;
8145 htInfo.pt.y = wPosY;
8147 /* send NM_DBLCLK notification */
8148 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8149 nmlv.hdr.hwndFrom = hwnd;
8150 nmlv.hdr.idFrom = nCtrlId;
8151 nmlv.hdr.code = NM_DBLCLK;
8152 ret = LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE);
8155 nmlv.iItem = htInfo.iItem;
8156 nmlv.iSubItem = htInfo.iSubItem;
8163 nmlv.ptAction.x = wPosX;
8164 nmlv.ptAction.y = wPosY;
8165 ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8168 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8171 /* send LVN_ITEMACTIVATE notification */
8172 nmh.hwndFrom = hwnd;
8173 nmh.idFrom = nCtrlId;
8174 nmh.code = LVN_ITEMACTIVATE;
8175 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8183 * Processes mouse down messages (left mouse button).
8186 * [I] HWND : window handle
8187 * [I] WORD : key flag
8188 * [I] WORD : x coordinate
8189 * [I] WORD : y coordinate
8194 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8197 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8198 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8199 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8200 static BOOL bGroupSelect = TRUE;
8205 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX,
8208 /* send NM_RELEASEDCAPTURE notification */
8209 nmh.hwndFrom = hwnd;
8210 nmh.idFrom = nCtrlId;
8211 nmh.code = NM_RELEASEDCAPTURE;
8212 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8214 if (infoPtr->bFocus == FALSE)
8219 /* set left button down flag */
8220 infoPtr->bLButtonDown = TRUE;
8222 ptPosition.x = wPosX;
8223 ptPosition.y = wPosY;
8224 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8225 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8227 if (lStyle & LVS_SINGLESEL)
8229 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8230 && infoPtr->nEditLabelItem == -1)
8232 infoPtr->nEditLabelItem = nItem;
8236 LISTVIEW_SetSelection(hwnd, nItem);
8241 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8243 if (bGroupSelect != FALSE)
8245 LISTVIEW_AddGroupSelection(hwnd, nItem);
8249 LISTVIEW_AddSelection(hwnd, nItem);
8252 else if (wKey & MK_CONTROL)
8254 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8256 else if (wKey & MK_SHIFT)
8258 LISTVIEW_SetGroupSelection(hwnd, nItem);
8263 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8265 /* set selection (clears other pre-existing selections) */
8266 LISTVIEW_SetSelection(hwnd, nItem);
8268 if (was_selected && infoPtr->nEditLabelItem == -1)
8270 infoPtr->nEditLabelItem = nItem;
8277 /* remove all selections */
8278 LISTVIEW_RemoveAllSelections(hwnd);
8281 /* redraw if we could have possibly selected something */
8282 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8289 * Processes mouse up messages (left mouse button).
8292 * [I] HWND : window handle
8293 * [I] WORD : key flag
8294 * [I] WORD : x coordinate
8295 * [I] WORD : y coordinate
8300 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8303 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8305 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8307 if (infoPtr->bLButtonDown != FALSE)
8309 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8311 LVHITTESTINFO lvHitTestInfo;
8314 lvHitTestInfo.pt.x = wPosX;
8315 lvHitTestInfo.pt.y = wPosY;
8317 /* send NM_CLICK notification */
8318 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8319 nmlv.hdr.hwndFrom = hwnd;
8320 nmlv.hdr.idFrom = nCtrlId;
8321 nmlv.hdr.code = NM_CLICK;
8322 ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
8325 nmlv.iItem = lvHitTestInfo.iItem;
8326 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8333 nmlv.ptAction.x = wPosX;
8334 nmlv.ptAction.y = wPosY;
8335 ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8338 /* set left button flag */
8339 infoPtr->bLButtonDown = FALSE;
8341 if(infoPtr->nEditLabelItem != -1)
8343 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem)
8345 LISTVIEW_EditLabelA(hwnd, lvHitTestInfo.iItem);
8347 infoPtr->nEditLabelItem = -1;
8356 * Creates the listview control (called before WM_CREATE).
8359 * [I] HWND : window handle
8360 * [I] WPARAM : unhandled
8361 * [I] LPARAM : widow creation info
8366 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8368 LISTVIEW_INFO *infoPtr;
8370 TRACE("(hwnd=%x,wParam=%x,lParam=%lx)\n", hwnd, wParam, lParam);
8372 /* allocate memory for info structure */
8373 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8374 SetWindowLongA(hwnd, 0, (LONG)infoPtr);
8375 if (infoPtr == NULL)
8377 ERR("could not allocate info memory!\n");
8381 if ((LISTVIEW_INFO *)GetWindowLongA(hwnd, 0) != infoPtr)
8383 ERR("pointer assignment error!\n");
8387 return DefWindowProcA(hwnd, WM_NCCREATE, wParam, lParam);
8392 * Destroys the listview control (called after WM_DESTROY).
8395 * [I] HWND : window handle
8400 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8402 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8404 TRACE("(hwnd=%x)\n", hwnd);
8406 /* delete all items */
8407 LISTVIEW_DeleteAllItems(hwnd);
8409 /* destroy data structure */
8410 DPA_Destroy(infoPtr->hdpaItems);
8411 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8414 infoPtr->hFont = (HFONT)0;
8415 if (infoPtr->hDefaultFont)
8417 DeleteObject(infoPtr->hDefaultFont);
8420 /* free listview info pointer*/
8421 COMCTL32_Free(infoPtr);
8423 SetWindowLongA(hwnd, 0, 0);
8429 * Handles notifications from children.
8432 * [I] HWND : window handle
8433 * [I] INT : control identifier
8434 * [I] LPNMHDR : notification information
8439 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8441 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8443 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8445 /* handle notification from header control */
8446 if (lpnmh->code == HDN_ENDTRACKA)
8448 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8449 InvalidateRect(hwnd, NULL, TRUE);
8451 else if(lpnmh->code == HDN_ITEMCLICKA)
8453 /* Handle sorting by Header Column */
8455 LPNMHEADERA pnmHeader = (LPNMHEADERA) lpnmh;
8456 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
8458 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8459 nmlv.hdr.hwndFrom = hwnd;
8460 nmlv.hdr.idFrom = lCtrlId;
8461 nmlv.hdr.code = LVN_COLUMNCLICK;
8463 nmlv.iSubItem = pnmHeader->iItem;
8465 ListView_LVNotify(GetParent(hwnd),lCtrlId, &nmlv);
8468 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8470 /* Idealy this should be done in HDN_ENDTRACKA
8471 * but since SetItemBounds in Header.c is called after
8472 * the notification is sent, it is neccessary to handle the
8473 * update of the scroll bar here (Header.c works fine as it is,
8474 * no need to disturb it)
8476 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8477 LISTVIEW_UpdateScroll(hwnd);
8478 InvalidateRect(hwnd, NULL, TRUE);
8488 * Determines the type of structure to use.
8491 * [I] HWND : window handle of the sender
8492 * [I] HWND : listview window handle
8493 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8498 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8500 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8502 if (nCommand == NF_REQUERY)
8504 /* determine the type of structure to use */
8505 infoPtr->notifyFormat = SendMessageA(hwndFrom, WM_NOTIFYFORMAT,
8506 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8507 if (infoPtr->notifyFormat == NFR_UNICODE)
8509 FIXME("NO support for unicode structures\n");
8518 * Paints/Repaints the listview control.
8521 * [I] HWND : window handle
8522 * [I] HDC : device context handle
8527 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8531 TRACE("(hwnd=%x,hdc=%x)\n", hwnd, hdc);
8535 hdc = BeginPaint(hwnd, &ps);
8536 LISTVIEW_Refresh(hwnd, hdc);
8537 EndPaint(hwnd, &ps);
8541 LISTVIEW_Refresh(hwnd, hdc);
8549 * Processes double click messages (right mouse button).
8552 * [I] HWND : window handle
8553 * [I] WORD : key flag
8554 * [I] WORD : x coordinate
8555 * [I] WORD : y coordinate
8560 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8563 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8566 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8568 /* send NM_RELEASEDCAPTURE notification */
8569 nmh.hwndFrom = hwnd;
8570 nmh.idFrom = nCtrlId;
8571 nmh.code = NM_RELEASEDCAPTURE;
8572 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8574 /* send NM_RDBLCLK notification */
8575 nmh.code = NM_RDBLCLK;
8576 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8583 * Processes mouse down messages (right mouse button).
8586 * [I] HWND : window handle
8587 * [I] WORD : key flag
8588 * [I] WORD : x coordinate
8589 * [I] WORD : y coordinate
8594 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8597 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8598 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8603 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8605 /* send NM_RELEASEDCAPTURE notification */
8606 nmh.hwndFrom = hwnd;
8607 nmh.idFrom = nCtrlId;
8608 nmh.code = NM_RELEASEDCAPTURE;
8609 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8611 /* make sure the listview control window has the focus */
8612 if (infoPtr->bFocus == FALSE)
8617 /* set right button down flag */
8618 infoPtr->bRButtonDown = TRUE;
8620 /* determine the index of the selected item */
8621 ptPosition.x = wPosX;
8622 ptPosition.y = wPosY;
8623 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8624 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8626 LISTVIEW_SetItemFocus(hwnd,nItem);
8627 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8628 !LISTVIEW_IsSelected(hwnd,nItem))
8630 LISTVIEW_SetSelection(hwnd, nItem);
8635 LISTVIEW_RemoveAllSelections(hwnd);
8643 * Processes mouse up messages (right mouse button).
8646 * [I] HWND : window handle
8647 * [I] WORD : key flag
8648 * [I] WORD : x coordinate
8649 * [I] WORD : y coordinate
8654 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8657 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8658 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8660 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8662 if (infoPtr->bRButtonDown != FALSE)
8665 LVHITTESTINFO lvHitTestInfo;
8669 lvHitTestInfo.pt.x = wPosX;
8670 lvHitTestInfo.pt.y = wPosY;
8672 /* Send NM_RClICK notification */
8673 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8674 nmlv.hdr.hwndFrom = hwnd;
8675 nmlv.hdr.idFrom = nCtrlId;
8676 nmlv.hdr.code = NM_RCLICK;
8677 ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
8680 nmlv.iItem = lvHitTestInfo.iItem;
8681 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8688 nmlv.ptAction.x = wPosX;
8689 nmlv.ptAction.y = wPosY;
8690 ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8695 /* set button flag */
8696 infoPtr->bRButtonDown = FALSE;
8698 /* Change to screen coordinate for WM_CONTEXTMENU */
8699 ClientToScreen(hwnd, &pt);
8701 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8702 SendMessageA( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
8713 * [I] HWND : window handle
8714 * [I] HWND : window handle of previously focused window
8719 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
8721 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8722 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8725 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
8727 /* send NM_SETFOCUS notification */
8728 nmh.hwndFrom = hwnd;
8729 nmh.idFrom = nCtrlId;
8730 nmh.code = NM_SETFOCUS;
8731 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8733 /* set window focus flag */
8734 infoPtr->bFocus = TRUE;
8746 * [I] HWND : window handle
8747 * [I] HFONT : font handle
8748 * [I] WORD : redraw flag
8753 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
8755 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8756 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8758 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
8762 infoPtr->hFont = infoPtr->hDefaultFont;
8766 infoPtr->hFont = hFont;
8769 if (uView == LVS_REPORT)
8771 /* set header font */
8772 SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
8773 MAKELPARAM(fRedraw, 0));
8776 /* invalidate listview control client area */
8777 InvalidateRect(hwnd, NULL, TRUE);
8779 if (fRedraw != FALSE)
8789 * Message handling for WM_SETREDRAW.
8790 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8793 * [I] HWND : window handle
8794 * [I] bRedraw: state of redraw flag
8797 * DefWinProc return value
8799 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
8802 lResult = DefWindowProcA(hwnd, WM_SETREDRAW, bRedraw, 0);
8805 RedrawWindow(hwnd, NULL, 0,
8806 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8813 * Resizes the listview control. This function processes WM_SIZE
8814 * messages. At this time, the width and height are not used.
8817 * [I] HWND : window handle
8818 * [I] WORD : new width
8819 * [I] WORD : new height
8824 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
8826 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8827 UINT uView = lStyle & LVS_TYPEMASK;
8829 TRACE("(hwnd=%x, width=%d, height=%d)\n",hwnd, Width, Height);
8831 LISTVIEW_UpdateSize(hwnd);
8833 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8835 if (lStyle & LVS_ALIGNLEFT)
8837 LISTVIEW_AlignLeft(hwnd);
8841 LISTVIEW_AlignTop(hwnd);
8845 LISTVIEW_UpdateScroll(hwnd);
8847 /* invalidate client area + erase background */
8848 InvalidateRect(hwnd, NULL, TRUE);
8855 * Sets the size information.
8858 * [I] HWND : window handle
8863 static VOID LISTVIEW_UpdateSize(HWND hwnd)
8865 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8866 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8867 UINT uView = lStyle & LVS_TYPEMASK;
8870 GetClientRect(hwnd, &rcList);
8871 infoPtr->rcList.left = 0;
8872 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8873 infoPtr->rcList.top = 0;
8874 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8876 if (uView == LVS_LIST)
8878 if (lStyle & WS_HSCROLL)
8880 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8881 if (infoPtr->rcList.bottom > nHScrollHeight)
8883 infoPtr->rcList.bottom -= nHScrollHeight;
8887 else if (uView == LVS_REPORT)
8894 Header_Layout(infoPtr->hwndHeader, &hl);
8896 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8898 if (!(LVS_NOCOLUMNHEADER & lStyle))
8900 infoPtr->rcList.top = max(wp.cy, 0);
8907 * Processes WM_STYLECHANGED messages.
8910 * [I] HWND : window handle
8911 * [I] WPARAM : window style type (normal or extended)
8912 * [I] LPSTYLESTRUCT : window style information
8917 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
8920 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8921 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8922 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8923 RECT rcList = infoPtr->rcList;
8925 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
8926 hwnd, wStyleType, lpss);
8928 if (wStyleType == GWL_STYLE)
8930 if (uOldView == LVS_REPORT)
8932 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8935 if ((lpss->styleOld & WS_HSCROLL) != 0)
8937 ShowScrollBar(hwnd, SB_HORZ, FALSE);
8940 if ((lpss->styleOld & WS_VSCROLL) != 0)
8942 ShowScrollBar(hwnd, SB_VERT, FALSE);
8945 if (uNewView == LVS_ICON)
8947 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8948 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8949 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8950 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8951 if (lpss->styleNew & LVS_ALIGNLEFT)
8953 LISTVIEW_AlignLeft(hwnd);
8957 LISTVIEW_AlignTop(hwnd);
8960 else if (uNewView == LVS_REPORT)
8967 Header_Layout(infoPtr->hwndHeader, &hl);
8968 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
8970 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8971 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8973 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8974 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8975 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8976 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8978 else if (uNewView == LVS_LIST)
8980 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8981 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8982 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8983 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8987 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8988 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8989 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8990 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8991 if (lpss->styleNew & LVS_ALIGNLEFT)
8993 LISTVIEW_AlignLeft(hwnd);
8997 LISTVIEW_AlignTop(hwnd);
9001 /* update the size of the client area */
9002 LISTVIEW_UpdateSize(hwnd);
9004 /* add scrollbars if needed */
9005 LISTVIEW_UpdateScroll(hwnd);
9007 /* invalidate client area + erase background */
9008 InvalidateRect(hwnd, NULL, TRUE);
9010 /* print the list of unsupported window styles */
9011 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9014 /* If they change the view and we have an active edit control
9015 we will need to kill the control since the redraw will
9016 misplace the edit control.
9018 if (infoPtr->hwndEdit &&
9019 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9020 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9022 SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9030 * Window procedure of the listview control.
9033 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9036 TRACE("hwnd=%x uMsg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
9037 if (!GetWindowLongA(hwnd, 0) && (uMsg != WM_NCCREATE))
9038 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
9041 case LVM_APPROXIMATEVIEWRECT:
9042 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9043 LOWORD(lParam), HIWORD(lParam));
9045 return LISTVIEW_Arrange(hwnd, (INT)wParam);
9047 /* case LVM_CREATEDRAGIMAGE: */
9049 case LVM_DELETEALLITEMS:
9050 return LISTVIEW_DeleteAllItems(hwnd);
9052 case LVM_DELETECOLUMN:
9053 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9055 case LVM_DELETEITEM:
9056 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9058 case LVM_EDITLABELW:
9059 case LVM_EDITLABELA:
9060 return LISTVIEW_EditLabelA(hwnd, (INT)wParam);
9062 case LVM_ENSUREVISIBLE:
9063 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9066 return LISTVIEW_FindItem(hwnd, (INT)wParam, (LPLVFINDINFO)lParam);
9068 case LVM_GETBKCOLOR:
9069 return LISTVIEW_GetBkColor(hwnd);
9071 /* case LVM_GETBKIMAGE: */
9073 case LVM_GETCALLBACKMASK:
9074 return LISTVIEW_GetCallbackMask(hwnd);
9076 case LVM_GETCOLUMNA:
9077 return LISTVIEW_GetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9079 /* case LVM_GETCOLUMNW: */
9081 case LVM_GETCOLUMNORDERARRAY:
9082 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9084 case LVM_GETCOLUMNWIDTH:
9085 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9087 case LVM_GETCOUNTPERPAGE:
9088 return LISTVIEW_GetCountPerPage(hwnd);
9090 case LVM_GETEDITCONTROL:
9091 return LISTVIEW_GetEditControl(hwnd);
9093 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9094 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9097 return LISTVIEW_GetHeader(hwnd);
9099 /* case LVM_GETHOTCURSOR: */
9101 case LVM_GETHOTITEM:
9102 return LISTVIEW_GetHotItem(hwnd);
9104 case LVM_GETHOVERTIME:
9105 return LISTVIEW_GetHoverTime(hwnd);
9107 case LVM_GETIMAGELIST:
9108 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9110 /* case LVM_GETISEARCHSTRING: */
9113 return LISTVIEW_GetItemA(hwnd, (LPLVITEMA)lParam, FALSE);
9115 /* case LVM_GETITEMW: */
9117 case LVM_GETITEMCOUNT:
9118 return LISTVIEW_GetItemCount(hwnd);
9120 case LVM_GETITEMPOSITION:
9121 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9123 case LVM_GETITEMRECT:
9124 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9126 case LVM_GETITEMSPACING:
9127 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9129 case LVM_GETITEMSTATE:
9130 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9132 case LVM_GETITEMTEXTA:
9133 LISTVIEW_GetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9136 /* case LVM_GETITEMTEXTW: */
9138 case LVM_GETNEXTITEM:
9139 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9141 /* case LVM_GETNUMBEROFWORKAREAS: */
9144 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9146 case LVM_GETSELECTEDCOUNT:
9147 return LISTVIEW_GetSelectedCount(hwnd);
9149 case LVM_GETSELECTIONMARK:
9150 return LISTVIEW_GetSelectionMark(hwnd);
9152 case LVM_GETSTRINGWIDTHA:
9153 return LISTVIEW_GetStringWidthA (hwnd, (LPCSTR)lParam);
9155 /* case LVM_GETSTRINGWIDTHW: */
9156 /* case LVM_GETSUBITEMRECT: */
9158 case LVM_GETTEXTBKCOLOR:
9159 return LISTVIEW_GetTextBkColor(hwnd);
9161 case LVM_GETTEXTCOLOR:
9162 return LISTVIEW_GetTextColor(hwnd);
9164 /* case LVM_GETTOOLTIPS: */
9166 case LVM_GETTOPINDEX:
9167 return LISTVIEW_GetTopIndex(hwnd);
9169 /* case LVM_GETUNICODEFORMAT: */
9171 case LVM_GETVIEWRECT:
9172 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9174 /* case LVM_GETWORKAREAS: */
9177 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9179 case LVM_INSERTCOLUMNA:
9180 return LISTVIEW_InsertColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9182 case LVM_INSERTCOLUMNW:
9183 return LISTVIEW_InsertColumnW(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam);
9185 case LVM_INSERTITEMA:
9186 return LISTVIEW_InsertItemA(hwnd, (LPLVITEMA)lParam);
9188 case LVM_INSERTITEMW:
9189 return LISTVIEW_InsertItemW(hwnd, (LPLVITEMW)lParam);
9191 case LVM_REDRAWITEMS:
9192 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9194 /* case LVM_SCROLL: */
9195 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9197 case LVM_SETBKCOLOR:
9198 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9200 /* case LVM_SETBKIMAGE: */
9202 case LVM_SETCALLBACKMASK:
9203 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9205 case LVM_SETCOLUMNA:
9206 return LISTVIEW_SetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9208 case LVM_SETCOLUMNW:
9209 FIXME("Unimplemented msg LVM_SETCOLUMNW\n");
9212 case LVM_SETCOLUMNORDERARRAY:
9213 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9215 case LVM_SETCOLUMNWIDTH:
9216 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9218 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9219 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9221 /* case LVM_SETHOTCURSOR: */
9223 case LVM_SETHOTITEM:
9224 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9226 case LVM_SETHOVERTIME:
9227 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9229 /* case LVM_SETICONSPACING: */
9231 case LVM_SETIMAGELIST:
9232 return LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9235 return LISTVIEW_SetItemA(hwnd, (LPLVITEMA)lParam);
9237 /* case LVM_SETITEMW: */
9239 case LVM_SETITEMCOUNT:
9240 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9242 case LVM_SETITEMPOSITION:
9243 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9244 (INT)HIWORD(lParam));
9246 case LVM_SETITEMPOSITION32:
9247 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9248 ((POINT*)lParam)->y);
9250 case LVM_SETITEMSTATE:
9251 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9253 case LVM_SETITEMTEXTA:
9254 return LISTVIEW_SetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9256 /* case LVM_SETITEMTEXTW: */
9258 case LVM_SETSELECTIONMARK:
9259 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9261 case LVM_SETTEXTBKCOLOR:
9262 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9264 case LVM_SETTEXTCOLOR:
9265 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9267 /* case LVM_SETTOOLTIPS: */
9268 /* case LVM_SETUNICODEFORMAT: */
9269 /* case LVM_SETWORKAREAS: */
9272 return LISTVIEW_SortItems(hwnd, wParam, lParam);
9274 /* case LVM_SUBITEMHITTEST: */
9277 return LISTVIEW_Update(hwnd, (INT)wParam);
9280 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9283 return LISTVIEW_Command(hwnd, wParam, lParam);
9286 return LISTVIEW_Create(hwnd, wParam, lParam);
9289 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9292 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9295 return LISTVIEW_GetFont(hwnd);
9298 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
9299 (INT)HIWORD(wParam), (HWND)lParam);
9302 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9305 return LISTVIEW_KillFocus(hwnd);
9307 case WM_LBUTTONDBLCLK:
9308 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9311 case WM_LBUTTONDOWN:
9312 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9315 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9318 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9321 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9324 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9327 return LISTVIEW_NCDestroy(hwnd);
9330 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9332 case WM_NOTIFYFORMAT:
9333 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9336 return LISTVIEW_Paint(hwnd, (HDC)wParam);
9338 case WM_RBUTTONDBLCLK:
9339 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9342 case WM_RBUTTONDOWN:
9343 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9347 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9351 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9354 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9357 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9360 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9362 case WM_STYLECHANGED:
9363 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9365 /* case WM_TIMER: */
9368 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
9369 (INT)HIWORD(wParam), (HWND)lParam);
9372 if (wParam & (MK_SHIFT | MK_CONTROL))
9373 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
9374 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
9376 /* case WM_WININICHANGE: */
9379 if (uMsg >= WM_USER)
9381 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
9385 /* call default window procedure */
9386 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
9394 * Registers the window class.
9402 VOID LISTVIEW_Register(void)
9406 ZeroMemory(&wndClass, sizeof(WNDCLASSA));
9407 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9408 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9409 wndClass.cbClsExtra = 0;
9410 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9411 wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
9412 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9413 wndClass.lpszClassName = WC_LISTVIEWA;
9414 RegisterClassA(&wndClass);
9419 * Unregisters the window class.
9427 VOID LISTVIEW_Unregister(void)
9429 UnregisterClassA(WC_LISTVIEWA, (HINSTANCE)NULL);
9434 * Handle any WM_COMMAND messages
9440 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9442 switch (HIWORD(wParam))
9447 * Adjust the edit window size
9450 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
9451 HDC hdc = GetDC(infoPtr->hwndEdit);
9452 HFONT hFont, hOldFont = 0;
9457 len = GetWindowTextA(infoPtr->hwndEdit, buffer, 1023);
9458 GetWindowRect(infoPtr->hwndEdit, &rect);
9460 /* Select font to get the right dimension of the string */
9461 hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9464 hOldFont = SelectObject(hdc, hFont);
9467 if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
9469 TEXTMETRICA textMetric;
9471 /* Add Extra spacing for the next character */
9472 GetTextMetricsA(hdc, &textMetric);
9473 sz.cx += (textMetric.tmMaxCharWidth * 2);
9481 rect.bottom - rect.top,
9482 SWP_DRAWFRAME|SWP_NOMOVE);
9486 SelectObject(hdc, hOldFont);
9489 ReleaseDC(hwnd, hdc);
9495 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9504 * Subclassed edit control windproc function
9510 LRESULT CALLBACK EditLblWndProc(HWND hwnd, UINT uMsg,
9511 WPARAM wParam, LPARAM lParam)
9513 BOOL cancel = FALSE;
9514 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(GetParent(hwnd), 0);
9515 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9516 static BOOL bIgnoreKillFocus = FALSE;
9520 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9523 if(bIgnoreKillFocus)
9531 WNDPROC editProc = einfo->EditWndProc;
9532 SetWindowLongA(hwnd, GWL_WNDPROC, (LONG)editProc);
9533 COMCTL32_Free(einfo);
9534 infoPtr->pedititem = NULL;
9535 return CallWindowProcA(editProc, hwnd, uMsg, wParam, lParam);
9539 if (VK_ESCAPE == (INT)wParam)
9545 else if (VK_RETURN == (INT)wParam)
9549 return CallWindowProcA(einfo->EditWndProc, hwnd,
9550 uMsg, wParam, lParam);
9553 if (einfo->EditLblCb)
9555 char *buffer = NULL;
9560 int len = 1 + GetWindowTextLengthA(hwnd);
9564 if (NULL != (buffer = (char *)COMCTL32_Alloc(len*sizeof(char))))
9566 GetWindowTextA(hwnd, buffer, len);
9570 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9571 /* eg. Using a messagebox */
9572 bIgnoreKillFocus = TRUE;
9573 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9576 COMCTL32_Free(buffer);
9578 einfo->EditLblCb = NULL;
9579 bIgnoreKillFocus = FALSE;
9582 SendMessageA(hwnd, WM_CLOSE, 0, 0);
9589 * Creates a subclassed edit cotrol
9595 HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y,
9596 INT width, INT height, HWND parent, HINSTANCE hinst,
9597 EditlblCallback EditLblCb, DWORD param)
9603 TEXTMETRICA textMetric;
9604 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(parent, 0);
9606 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9609 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9610 hdc = GetDC(parent);
9612 /* Select the font to get appropriate metric dimensions */
9613 if(infoPtr->hFont != 0)
9615 hOldFont = SelectObject(hdc, infoPtr->hFont);
9618 /*Get String Lenght in pixels */
9619 GetTextExtentPoint32A(hdc, text, strlen(text), &sz);
9621 /*Add Extra spacing for the next character */
9622 GetTextMetricsA(hdc, &textMetric);
9623 sz.cx += (textMetric.tmMaxCharWidth * 2);
9625 if(infoPtr->hFont != 0)
9627 SelectObject(hdc, hOldFont);
9630 ReleaseDC(parent, hdc);
9631 if (!(hedit = CreateWindowA("Edit", text, style, x, y, sz.cx, height,
9632 parent, 0, hinst, 0)))
9634 COMCTL32_Free(infoPtr->pedititem);
9638 infoPtr->pedititem->param = param;
9639 infoPtr->pedititem->EditLblCb = EditLblCb;
9640 infoPtr->pedititem->EditWndProc = (WNDPROC)SetWindowLongA(hedit,
9641 GWL_WNDPROC, (LONG) EditLblWndProc);
9643 SendMessageA(hedit, WM_SETFONT, infoPtr->hFont, FALSE);