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, \
3039 bottom=%d)\n", 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 else if (lpLVItem->mask & LVIF_TEXT)
5121 lpLVItem->pszText=*ppszText;
5123 lstrcpynA(lpLVItem->pszText, *ppszText, lpLVItem->cchTextMax);
5127 if (lpLVItem->iSubItem == 0)
5129 if (dispInfo.item.mask & LVIF_STATE)
5131 lpLVItem->state = lpItem->state;
5132 lpLVItem->state &= ~dispInfo.item.stateMask;
5133 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5135 lpLVItem->state &= ~LVIS_SELECTED;
5136 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5137 (LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem)))
5138 lpLVItem->state |= LVIS_SELECTED;
5140 else if (lpLVItem->mask & LVIF_STATE)
5142 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5144 lpLVItem->state &= ~LVIS_SELECTED;
5145 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5146 (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem)))
5147 lpLVItem->state |= LVIS_SELECTED;
5150 if (lpLVItem->mask & LVIF_PARAM)
5152 lpLVItem->lParam = lpItem->lParam;
5155 if (lpLVItem->mask & LVIF_INDENT)
5157 lpLVItem->iIndent = lpItem->iIndent;
5164 /* LISTVIEW_GetItemW */
5165 /* LISTVIEW_GetHotCursor */
5169 * Retrieves the index of the hot item.
5172 * [I] HWND : window handle
5175 * SUCCESS : hot item index
5176 * FAILURE : -1 (no hot item)
5178 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5180 LISTVIEW_INFO *infoPtr;
5182 /* make sure we can get the listview info */
5183 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
5186 return (infoPtr->nHotItem);
5189 /* LISTVIEW_GetHoverTime */
5193 * Retrieves the number of items in the listview control.
5196 * [I] HWND : window handle
5201 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5203 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5205 return GETITEMCOUNT(infoPtr);
5210 * Retrieves the position (upper-left) of the listview control item.
5213 * [I] HWND : window handle
5214 * [I] INT : item index
5215 * [O] LPPOINT : coordinate information
5221 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem,
5222 LPPOINT lpptPosition)
5224 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5225 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5226 BOOL bResult = FALSE;
5228 LISTVIEW_ITEM *lpItem;
5229 INT nCountPerColumn;
5232 TRACE("(hwnd=%x,nItem=%d,lpptPosition=%p)\n", hwnd, nItem,
5235 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5236 (lpptPosition != NULL))
5238 if (uView == LVS_LIST)
5241 nItem = nItem - ListView_GetTopIndex(hwnd);
5242 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5245 nRow = nItem % nCountPerColumn;
5248 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
5249 lpptPosition->y = 0;
5253 lpptPosition->x = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5254 lpptPosition->y = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5259 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
5260 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
5263 else if (uView == LVS_REPORT)
5265 SCROLLINFO scrollInfo;
5267 lpptPosition->x = REPORT_MARGINX;
5268 lpptPosition->y = ((nItem - ListView_GetTopIndex(hwnd)) *
5269 infoPtr->nItemHeight) + infoPtr->rcList.top;
5271 /* Adjust position by scrollbar offset */
5272 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5273 scrollInfo.cbSize = sizeof(SCROLLINFO);
5274 scrollInfo.fMask = SIF_POS;
5275 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5276 lpptPosition->x -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5280 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5281 if (hdpaSubItems != NULL)
5283 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
5287 lpptPosition->x = lpItem->ptPosition.x;
5288 lpptPosition->y = lpItem->ptPosition.y;
5298 * Retrieves the bounding rectangle for a listview control item.
5301 * [I] HWND : window handle
5302 * [I] INT : item index
5303 * [IO] LPRECT : bounding rectangle coordinates
5304 * lprc->left specifies the portion of the item for which the bounding
5305 * rectangle will be retrieved.
5307 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5308 * including the icon and label.
5309 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5310 * LVIR_LABEL Returns the bounding rectangle of the item text.
5311 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5312 * rectangles, but excludes columns in report view.
5318 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5320 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5321 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5322 BOOL bResult = FALSE;
5333 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5335 if (uView & LVS_REPORT)
5337 ZeroMemory(&lvItem, sizeof(LVITEMA));
5338 lvItem.mask = LVIF_INDENT;
5339 lvItem.iItem = nItem;
5340 lvItem.iSubItem = 0;
5341 LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
5344 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5346 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5354 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5356 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) != FALSE)
5361 if (uView == LVS_ICON)
5363 if (infoPtr->himlNormal != NULL)
5365 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5368 lprc->left = ptItem.x + ptOrigin.x;
5369 lprc->top = ptItem.y + ptOrigin.y;
5370 lprc->right = lprc->left + infoPtr->iconSize.cx;
5371 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5372 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5376 else if (uView == LVS_SMALLICON)
5378 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5381 lprc->left = ptItem.x + ptOrigin.x;
5382 lprc->top = ptItem.y + ptOrigin.y;
5383 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5385 if (infoPtr->himlState != NULL)
5386 lprc->left += infoPtr->iconSize.cx;
5388 if (infoPtr->himlSmall != NULL)
5389 lprc->right = lprc->left + infoPtr->iconSize.cx;
5391 lprc->right = lprc->left;
5397 lprc->left = ptItem.x;
5398 if (uView & LVS_REPORT)
5399 lprc->left += nIndent;
5400 lprc->top = ptItem.y;
5401 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5403 if (infoPtr->himlState != NULL)
5405 lprc->left += infoPtr->iconSize.cx;
5408 if (infoPtr->himlSmall != NULL)
5410 lprc->right = lprc->left + infoPtr->iconSize.cx;
5414 lprc->right = lprc->left;
5420 if (uView == LVS_ICON)
5422 if (infoPtr->himlNormal != NULL)
5424 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5427 lprc->left = ptItem.x + ptOrigin.x;
5428 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5429 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5430 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5431 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5433 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5434 lprc->right = lprc->left + nLabelWidth;
5439 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5443 hOldFont = SelectObject(hdc, infoPtr->hFont);
5444 GetTextMetricsA(hdc, &tm);
5445 lprc->bottom = lprc->top + tm.tmHeight + HEIGHT_PADDING;
5446 SelectObject(hdc, hOldFont);
5447 ReleaseDC(hwnd, hdc);
5451 else if (uView == LVS_SMALLICON)
5453 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5456 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5457 lprc->top = ptItem.y + ptOrigin.y;
5458 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5460 if (infoPtr->himlState != NULL)
5462 lprc->left += infoPtr->iconSize.cx;
5465 if (infoPtr->himlSmall != NULL)
5467 lprc->left += infoPtr->iconSize.cx;
5470 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5471 nLabelWidth += TRAILING_PADDING;
5472 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5474 lprc->right = lprc->left + nLabelWidth;
5478 lprc->right = nLeftPos + infoPtr->nItemWidth;
5485 if (uView & LVS_REPORT)
5486 nLeftPos = lprc->left = ptItem.x + nIndent;
5488 nLeftPos = lprc->left = ptItem.x;
5489 lprc->top = ptItem.y;
5490 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5492 if (infoPtr->himlState != NULL)
5494 lprc->left += infoPtr->iconSize.cx;
5497 if (infoPtr->himlSmall != NULL)
5499 lprc->left += infoPtr->iconSize.cx;
5502 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5503 nLabelWidth += TRAILING_PADDING;
5504 if (infoPtr->himlSmall)
5505 nLabelWidth += IMAGE_PADDING;
5506 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5508 lprc->right = lprc->left + nLabelWidth;
5512 lprc->right = nLeftPos + infoPtr->nItemWidth;
5518 if (uView == LVS_ICON)
5520 if (infoPtr->himlNormal != NULL)
5522 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5525 lprc->left = ptItem.x + ptOrigin.x;
5526 lprc->top = ptItem.y + ptOrigin.y;
5527 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5528 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5532 else if (uView == LVS_SMALLICON)
5534 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5537 lprc->left = ptItem.x + ptOrigin.x;
5538 lprc->right = lprc->left;
5539 lprc->top = ptItem.y + ptOrigin.y;
5540 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5541 if (infoPtr->himlState != NULL)
5542 lprc->right += infoPtr->iconSize.cx;
5543 if (infoPtr->himlSmall != NULL)
5544 lprc->right += infoPtr->iconSize.cx;
5546 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5547 nLabelWidth += TRAILING_PADDING;
5548 if (infoPtr->himlSmall)
5549 nLabelWidth += IMAGE_PADDING;
5550 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5552 lprc->right += nLabelWidth;
5556 lprc->right = lprc->left + infoPtr->nItemWidth;
5563 lprc->left = ptItem.x;
5564 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5565 lprc->left += nIndent;
5566 lprc->right = lprc->left;
5567 lprc->top = ptItem.y;
5568 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5570 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5573 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5574 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5576 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5580 if (infoPtr->himlState != NULL)
5582 lprc->right += infoPtr->iconSize.cx;
5585 if (infoPtr->himlSmall != NULL)
5587 lprc->right += infoPtr->iconSize.cx;
5590 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5591 nLabelWidth += TRAILING_PADDING;
5592 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5594 lprc->right += nLabelWidth;
5598 lprc->right = lprc->left + infoPtr->nItemWidth;
5604 case LVIR_SELECTBOUNDS:
5605 if (uView == LVS_ICON)
5607 if (infoPtr->himlNormal != NULL)
5609 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5612 lprc->left = ptItem.x + ptOrigin.x;
5613 lprc->top = ptItem.y + ptOrigin.y;
5614 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5615 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5619 else if (uView == LVS_SMALLICON)
5621 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5624 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5625 lprc->top = ptItem.y + ptOrigin.y;
5626 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5628 if (infoPtr->himlState != NULL)
5630 lprc->left += infoPtr->iconSize.cx;
5633 lprc->right = lprc->left;
5635 if (infoPtr->himlSmall != NULL)
5637 lprc->right += infoPtr->iconSize.cx;
5640 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5641 nLabelWidth += TRAILING_PADDING;
5642 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5644 lprc->right += nLabelWidth;
5648 lprc->right = nLeftPos + infoPtr->nItemWidth;
5655 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5656 nLeftPos = lprc->left = ptItem.x + nIndent;
5658 nLeftPos = lprc->left = ptItem.x;
5659 lprc->top = ptItem.y;
5660 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5662 if (infoPtr->himlState != NULL)
5664 lprc->left += infoPtr->iconSize.cx;
5667 lprc->right = lprc->left;
5669 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5672 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5673 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5675 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5679 if (infoPtr->himlSmall != NULL)
5681 lprc->right += infoPtr->iconSize.cx;
5684 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5685 nLabelWidth += TRAILING_PADDING;
5686 if (infoPtr->himlSmall)
5687 nLabelWidth += IMAGE_PADDING;
5688 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5690 lprc->right += nLabelWidth;
5694 lprc->right = nLeftPos + infoPtr->nItemWidth;
5707 * Retrieves the width of a label.
5710 * [I] HWND : window handle
5713 * SUCCESS : string width (in pixels)
5716 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
5718 CHAR szDispText[DISP_TEXT_SIZE];
5719 INT nLabelWidth = 0;
5722 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
5724 ZeroMemory(&lvItem, sizeof(LVITEMA));
5725 lvItem.mask = LVIF_TEXT;
5726 lvItem.iItem = nItem;
5727 lvItem.cchTextMax = DISP_TEXT_SIZE;
5728 lvItem.pszText = szDispText;
5729 if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
5731 nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
5739 * Retrieves the spacing between listview control items.
5742 * [I] HWND : window handle
5743 * [I] BOOL : flag for small or large icon
5746 * Horizontal + vertical spacing
5748 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
5750 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5753 if (bSmall == FALSE)
5755 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5759 /* TODO: need to store width of smallicon item */
5760 lResult = MAKELONG(0, infoPtr->nItemHeight);
5768 * Retrieves the state of a listview control item.
5771 * [I] HWND : window handle
5772 * [I] INT : item index
5773 * [I] UINT : state mask
5776 * State specified by the mask.
5778 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
5780 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5784 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5786 ZeroMemory(&lvItem, sizeof(LVITEMA));
5787 lvItem.iItem = nItem;
5788 lvItem.stateMask = uMask;
5789 lvItem.mask = LVIF_STATE;
5790 if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
5792 uState = lvItem.state;
5801 * Retrieves the text of a listview control item or subitem.
5804 * [I] HWND : window handle
5805 * [I] INT : item index
5806 * [IO] LPLVITEMA : item information
5809 * SUCCESS : string length
5812 static LRESULT LISTVIEW_GetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
5814 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5817 if (lpLVItem != NULL)
5819 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5821 lpLVItem->mask = LVIF_TEXT;
5822 lpLVItem->iItem = nItem;
5823 if (LISTVIEW_GetItemA(hwnd, lpLVItem, FALSE) != FALSE)
5825 nLength = lstrlenA(lpLVItem->pszText);
5835 * Searches for an item based on properties + relationships.
5838 * [I] HWND : window handle
5839 * [I] INT : item index
5840 * [I] INT : relationship flag
5843 * SUCCESS : item index
5846 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
5848 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5849 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5851 LVFINDINFO lvFindInfo;
5852 INT nCountPerColumn;
5855 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
5857 ZeroMemory(&lvFindInfo, sizeof(LVFINDINFO));
5859 if (uFlags & LVNI_CUT)
5862 if (uFlags & LVNI_DROPHILITED)
5863 uMask |= LVIS_DROPHILITED;
5865 if (uFlags & LVNI_FOCUSED)
5866 uMask |= LVIS_FOCUSED;
5868 if (uFlags & LVNI_SELECTED)
5869 uMask |= LVIS_SELECTED;
5871 if (uFlags & LVNI_ABOVE)
5873 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5878 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5884 lvFindInfo.flags = LVFI_NEARESTXY;
5885 lvFindInfo.vkDirection = VK_UP;
5886 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5887 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5889 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5894 else if (uFlags & LVNI_BELOW)
5896 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5898 while (nItem < GETITEMCOUNT(infoPtr))
5901 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5907 lvFindInfo.flags = LVFI_NEARESTXY;
5908 lvFindInfo.vkDirection = VK_DOWN;
5909 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5910 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5912 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5917 else if (uFlags & LVNI_TOLEFT)
5919 if (uView == LVS_LIST)
5921 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5922 while (nItem - nCountPerColumn >= 0)
5924 nItem -= nCountPerColumn;
5925 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5929 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5931 lvFindInfo.flags = LVFI_NEARESTXY;
5932 lvFindInfo.vkDirection = VK_LEFT;
5933 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5934 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5936 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5941 else if (uFlags & LVNI_TORIGHT)
5943 if (uView == LVS_LIST)
5945 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5946 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
5948 nItem += nCountPerColumn;
5949 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5953 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5955 lvFindInfo.flags = LVFI_NEARESTXY;
5956 lvFindInfo.vkDirection = VK_RIGHT;
5957 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5958 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5960 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5969 /* search by index */
5970 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
5972 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
5981 /* LISTVIEW_GetNumberOfWorkAreas */
5985 * Retrieves the origin coordinates when in icon or small icon display mode.
5988 * [I] HWND : window handle
5989 * [O] LPPOINT : coordinate information
5995 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
5997 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
5998 UINT uView = lStyle & LVS_TYPEMASK;
5999 BOOL bResult = FALSE;
6001 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6003 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6005 SCROLLINFO scrollInfo;
6006 ZeroMemory(lpptOrigin, sizeof(POINT));
6007 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6008 scrollInfo.cbSize = sizeof(SCROLLINFO);
6010 if (lStyle & WS_HSCROLL)
6012 scrollInfo.fMask = SIF_POS;
6013 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6015 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6019 if (lStyle & WS_VSCROLL)
6021 scrollInfo.fMask = SIF_POS;
6022 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6024 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6036 * Retrieves the number of items that are marked as selected.
6039 * [I] HWND : window handle
6042 * Number of items selected.
6044 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6047 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6048 INT nSelectedCount = 0;
6051 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6053 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6059 return nSelectedCount;
6064 * Retrieves item index that marks the start of a multiple selection.
6067 * [I] HWND : window handle
6070 * Index number or -1 if there is no selection mark.
6072 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6074 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6076 return infoPtr->nSelectionMark;
6081 * Retrieves the width of a string.
6084 * [I] HWND : window handle
6087 * SUCCESS : string width (in pixels)
6090 static LRESULT LISTVIEW_GetStringWidthA(HWND hwnd, LPCSTR lpszText)
6092 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6093 HFONT hFont, hOldFont;
6097 ZeroMemory(&stringSize, sizeof(SIZE));
6098 if (lpszText != NULL && lpszText != LPSTR_TEXTCALLBACKA)
6100 hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6102 hOldFont = SelectObject(hdc, hFont);
6103 GetTextExtentPointA(hdc, lpszText, lstrlenA(lpszText), &stringSize);
6104 SelectObject(hdc, hOldFont);
6105 ReleaseDC(hwnd, hdc);
6108 return stringSize.cx;
6113 * Retrieves the text backgound color.
6116 * [I] HWND : window handle
6119 * COLORREF associated with the the background.
6121 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6123 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6125 return infoPtr->clrTextBk;
6130 * Retrieves the text color.
6133 * [I] HWND : window handle
6136 * COLORREF associated with the text.
6138 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6140 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6142 return infoPtr->clrText;
6147 * Determines which section of the item was selected (if any).
6150 * [I] HWND : window handle
6151 * [IO] LPLVHITTESTINFO : hit test information
6152 * [I] subitem : fill out iSubItem.
6155 * SUCCESS : item index
6158 static INT LISTVIEW_HitTestItem(
6159 HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem
6161 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6163 INT i,topindex,bottomindex;
6164 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6165 UINT uView = lStyle & LVS_TYPEMASK;
6168 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6169 lpHitTestInfo->pt.y);
6171 topindex = ListView_GetTopIndex(hwnd);
6172 if (uView == LVS_REPORT)
6174 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6175 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6179 bottomindex = GETITEMCOUNT(infoPtr);
6182 for (i = topindex; i < bottomindex; i++)
6184 rcItem.left = LVIR_BOUNDS;
6185 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6187 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6189 rcItem.left = LVIR_ICON;
6190 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6192 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6194 lpHitTestInfo->flags = LVHT_ONITEMICON;
6195 lpHitTestInfo->iItem = i;
6196 if (subitem) lpHitTestInfo->iSubItem = 0;
6201 rcItem.left = LVIR_LABEL;
6202 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6204 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6206 lpHitTestInfo->flags = LVHT_ONITEMLABEL;
6207 lpHitTestInfo->iItem = i;
6208 if (subitem) lpHitTestInfo->iSubItem = 0;
6213 lpHitTestInfo->flags = LVHT_ONITEMSTATEICON;
6214 lpHitTestInfo->iItem = i;
6215 if (subitem) lpHitTestInfo->iSubItem = 0;
6221 lpHitTestInfo->flags = LVHT_NOWHERE;
6228 * Determines which listview item is located at the specified position.
6231 * [I] HWND : window handle
6232 * [IO} LPLVHITTESTINFO : hit test information
6235 * SUCCESS : item index
6238 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6240 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6243 lpHitTestInfo->flags = 0;
6245 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6247 lpHitTestInfo->flags = LVHT_TOLEFT;
6249 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6251 lpHitTestInfo->flags = LVHT_TORIGHT;
6253 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6255 lpHitTestInfo->flags |= LVHT_ABOVE;
6257 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6259 lpHitTestInfo->flags |= LVHT_BELOW;
6262 if (lpHitTestInfo->flags == 0)
6264 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6265 * an app might pass only a structure with space up to iItem!
6266 * (MS Office 97 does that for instance in the file open dialog)
6268 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6276 * Inserts a new column.
6279 * [I] HWND : window handle
6280 * [I] INT : column index
6281 * [I] LPLVCOLUMNA : column information
6284 * SUCCESS : new column index
6287 static LRESULT LISTVIEW_InsertColumnA(HWND hwnd, INT nColumn,
6288 LPLVCOLUMNA lpColumn)
6290 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6292 INT nNewColumn = -1;
6294 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn,
6297 if (lpColumn != NULL)
6299 /* initialize memory */
6300 ZeroMemory(&hdi, sizeof(HDITEMA));
6302 if (lpColumn->mask & LVCF_FMT)
6304 /* format member is valid */
6305 hdi.mask |= HDI_FORMAT;
6307 /* set text alignment (leftmost column must be left-aligned) */
6310 hdi.fmt |= HDF_LEFT;
6314 if (lpColumn->fmt & LVCFMT_LEFT)
6316 hdi.fmt |= HDF_LEFT;
6318 else if (lpColumn->fmt & LVCFMT_RIGHT)
6320 hdi.fmt |= HDF_RIGHT;
6322 else if (lpColumn->fmt & LVCFMT_CENTER)
6324 hdi.fmt |= HDF_CENTER;
6328 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6330 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6334 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6339 if (lpColumn->fmt & LVCFMT_IMAGE)
6341 hdi.fmt |= HDF_IMAGE;
6342 hdi.iImage = I_IMAGECALLBACK;
6346 if (lpColumn->mask & LVCF_WIDTH)
6348 hdi.mask |= HDI_WIDTH;
6349 hdi.cxy = lpColumn->cx;
6352 if (lpColumn->mask & LVCF_TEXT)
6354 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6355 hdi.pszText = lpColumn->pszText;
6356 hdi.cchTextMax = ((lpColumn->pszText!=NULL) && (lpColumn->pszText!=LPSTR_TEXTCALLBACKA) ? strlen(lpColumn->pszText) : 0);
6357 hdi.fmt |= HDF_STRING;
6360 if (lpColumn->mask & LVCF_IMAGE)
6362 hdi.mask |= HDI_IMAGE;
6363 hdi.iImage = lpColumn->iImage;
6366 if (lpColumn->mask & LVCF_ORDER)
6368 hdi.mask |= HDI_ORDER;
6369 hdi.iOrder = lpColumn->iOrder;
6372 /* insert item in header control */
6373 nNewColumn = SendMessageA(infoPtr->hwndHeader, HDM_INSERTITEMA,
6374 (WPARAM)nColumn, (LPARAM)&hdi);
6376 /* Need to reset the item width when inserting a new column */
6377 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6379 LISTVIEW_UpdateScroll(hwnd);
6380 InvalidateRect(hwnd, NULL, FALSE);
6386 static LRESULT LISTVIEW_InsertColumnW(HWND hwnd, INT nColumn,
6387 LPLVCOLUMNW lpColumn)
6392 memcpy(&lvca,lpColumn,sizeof(lvca));
6393 if (lpColumn->mask & LVCF_TEXT) {
6394 if (lpColumn->pszText == LPSTR_TEXTCALLBACKW)
6395 lvca.pszText = LPSTR_TEXTCALLBACKA;
6397 lvca.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpColumn->pszText);
6399 lres = LISTVIEW_InsertColumnA(hwnd,nColumn,&lvca);
6400 if (lpColumn->mask & LVCF_TEXT) {
6401 if (lpColumn->pszText != LPSTR_TEXTCALLBACKW)
6402 HeapFree(GetProcessHeap(),0,lvca.pszText);
6407 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6408 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6409 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6410 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6411 their own sort proc. when sending LVM_SORTITEMS.
6414 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6416 LVS_SORTXXX must be specified,
6417 LVS_OWNERDRAW is not set,
6418 <item>.pszText is not LPSTR_TEXTCALLBACK.
6420 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6421 are sorted based on item text..."
6423 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6425 HDPA hdpa_first = (HDPA) first;
6426 HDPA hdpa_second = (HDPA) second;
6427 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
6428 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
6429 LONG lStyle = GetWindowLongA((HWND) lParam, GWL_STYLE);
6430 INT cmpv = lstrcmpA( lv_first->pszText, lv_second->pszText );
6431 /* if we're sorting descending, negate the return value */
6432 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6437 * Inserts a new item in the listview control.
6440 * [I] HWND : window handle
6441 * [I] LPLVITEMA : item information
6444 * SUCCESS : new item index
6447 static LRESULT LISTVIEW_InsertItemA(HWND hwnd, LPLVITEMA lpLVItem)
6449 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6450 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6451 UINT uView = lStyle & LVS_TYPEMASK;
6452 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
6457 LISTVIEW_ITEM *lpItem = NULL;
6459 TRACE("(hwnd=%x,lpLVItem=%p)\n", hwnd, lpLVItem);
6461 if (lStyle & LVS_OWNERDATA)
6463 nItem = infoPtr->hdpaItems->nItemCount;
6464 infoPtr->hdpaItems->nItemCount ++;
6468 if (lpLVItem != NULL)
6470 /* make sure it's not a subitem; cannot insert a subitem */
6471 if (lpLVItem->iSubItem == 0)
6473 lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM));
6476 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6477 if (LISTVIEW_InitItem(hwnd, lpItem, lpLVItem) != FALSE)
6479 /* insert item in listview control data structure */
6480 hdpaSubItems = DPA_Create(8);
6481 if (hdpaSubItems != NULL)
6483 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
6486 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6487 && !(lStyle & LVS_OWNERDRAWFIXED)
6488 && (LPSTR_TEXTCALLBACKA != lpLVItem->pszText) )
6490 /* Insert the item in the proper sort order based on the pszText
6491 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6492 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6493 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6494 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6495 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6499 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6504 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6506 /* manage item focus */
6507 if (lpLVItem->mask & LVIF_STATE)
6509 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6510 if (lpLVItem->stateMask & LVIS_SELECTED)
6512 LISTVIEW_SetSelection(hwnd, nItem);
6514 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6516 LISTVIEW_SetItemFocus(hwnd, nItem);
6520 /* send LVN_INSERTITEM notification */
6521 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6522 nmlv.hdr.hwndFrom = hwnd;
6523 nmlv.hdr.idFrom = lCtrlId;
6524 nmlv.hdr.code = LVN_INSERTITEM;
6526 nmlv.lParam = lpItem->lParam;;
6527 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
6529 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6531 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6532 if (nItemWidth > infoPtr->nItemWidth)
6534 infoPtr->nItemWidth = nItemWidth;
6538 /* align items (set position of each item) */
6539 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6541 if (lStyle & LVS_ALIGNLEFT)
6543 LISTVIEW_AlignLeft(hwnd);
6547 LISTVIEW_AlignTop(hwnd);
6551 LISTVIEW_UpdateScroll(hwnd);
6552 /* refresh client area */
6553 InvalidateRect(hwnd, NULL, FALSE);
6562 /* free memory if unsuccessful */
6563 if ((nItem == -1) && (lpItem != NULL))
6565 COMCTL32_Free(lpItem);
6571 static LRESULT LISTVIEW_InsertItemW(HWND hwnd, LPLVITEMW lpLVItem) {
6575 memcpy(&lvia,lpLVItem,sizeof(LVITEMA));
6576 if (lvia.mask & LVIF_TEXT) {
6577 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
6578 lvia.pszText = LPSTR_TEXTCALLBACKA;
6580 lvia.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpLVItem->pszText);
6582 lres = LISTVIEW_InsertItemA(hwnd, &lvia);
6583 if (lvia.mask & LVIF_TEXT) {
6584 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6585 HeapFree(GetProcessHeap(),0,lvia.pszText);
6590 /* LISTVIEW_InsertItemW */
6594 * Redraws a range of items.
6597 * [I] HWND : window handle
6598 * [I] INT : first item
6599 * [I] INT : last item
6605 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6607 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6608 BOOL bResult = FALSE;
6611 if (nFirst <= nLast)
6613 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6615 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6618 for (i = nFirst; i <= nLast; i++)
6620 rcItem.left = LVIR_BOUNDS;
6621 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
6622 InvalidateRect(hwnd, &rcItem, TRUE);
6631 /* LISTVIEW_Scroll */
6635 * Sets the background color.
6638 * [I] HWND : window handle
6639 * [I] COLORREF : background color
6645 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6647 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6649 infoPtr->clrBk = clrBk;
6650 InvalidateRect(hwnd, NULL, TRUE);
6655 /* LISTVIEW_SetBkImage */
6659 * Sets the callback mask. This mask will be used when the parent
6660 * window stores state information (some or all).
6663 * [I] HWND : window handle
6664 * [I] UINT : state mask
6670 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
6672 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6674 infoPtr->uCallbackMask = uMask;
6681 * Sets the attributes of a header item.
6684 * [I] HWND : window handle
6685 * [I] INT : column index
6686 * [I] LPLVCOLUMNA : column attributes
6692 static LRESULT LISTVIEW_SetColumnA(HWND hwnd, INT nColumn,
6693 LPLVCOLUMNA lpColumn)
6695 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6696 BOOL bResult = FALSE;
6697 HDITEMA hdi, hdiget;
6699 if ((lpColumn != NULL) && (nColumn >= 0) &&
6700 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6702 /* initialize memory */
6703 ZeroMemory(&hdi, sizeof(HDITEMA));
6705 if (lpColumn->mask & LVCF_FMT)
6707 /* format member is valid */
6708 hdi.mask |= HDI_FORMAT;
6710 /* get current format first */
6711 hdiget.mask = HDI_FORMAT;
6712 if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdiget))
6713 /* preserve HDF_STRING if present */
6714 hdi.fmt = hdiget.fmt & HDF_STRING;
6716 /* set text alignment (leftmost column must be left-aligned) */
6719 hdi.fmt |= HDF_LEFT;
6723 if (lpColumn->fmt & LVCFMT_LEFT)
6725 hdi.fmt |= HDF_LEFT;
6727 else if (lpColumn->fmt & LVCFMT_RIGHT)
6729 hdi.fmt |= HDF_RIGHT;
6731 else if (lpColumn->fmt & LVCFMT_CENTER)
6733 hdi.fmt |= HDF_CENTER;
6737 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6739 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6742 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6744 hdi.fmt |= HDF_IMAGE;
6747 if (lpColumn->fmt & LVCFMT_IMAGE)
6749 hdi.fmt |= HDF_IMAGE;
6750 hdi.iImage = I_IMAGECALLBACK;
6754 if (lpColumn->mask & LVCF_WIDTH)
6756 hdi.mask |= HDI_WIDTH;
6757 hdi.cxy = lpColumn->cx;
6760 if (lpColumn->mask & LVCF_TEXT)
6762 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6763 hdi.pszText = lpColumn->pszText;
6764 hdi.cchTextMax = ((lpColumn->pszText!=NULL) && (lpColumn->pszText!=LPSTR_TEXTCALLBACKA) ? strlen(lpColumn->pszText) : 0);
6765 hdi.fmt |= HDF_STRING;
6768 if (lpColumn->mask & LVCF_IMAGE)
6770 hdi.mask |= HDI_IMAGE;
6771 hdi.iImage = lpColumn->iImage;
6774 if (lpColumn->mask & LVCF_ORDER)
6776 hdi.mask |= HDI_ORDER;
6777 hdi.iOrder = lpColumn->iOrder;
6780 /* set header item attributes */
6781 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6787 /* LISTVIEW_SetColumnW */
6791 * Sets the column order array
6794 * [I] HWND : window handle
6795 * [I] INT : number of elements in column order array
6796 * [I] INT : pointer to column order array
6802 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
6804 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); */
6806 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6817 * Sets the width of a column
6820 * [I] HWND : window handle
6821 * [I] INT : column index
6822 * [I] INT : column width
6828 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
6830 LISTVIEW_INFO *infoPtr;
6833 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6834 UINT uView = lStyle & LVS_TYPEMASK;
6839 CHAR text_buffer[DISP_TEXT_SIZE];
6840 INT header_item_count;
6845 /* make sure we can get the listview info */
6846 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6849 if (!infoPtr->hwndHeader) /* make sure we have a header */
6852 /* set column width only if in report or list mode */
6853 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6856 /* take care of invalid cx values */
6857 if((uView == LVS_REPORT) && (cx < -2))
6858 cx = LVSCW_AUTOSIZE;
6859 else if (uView == LVS_LIST && (cx < 1))
6862 /* resize all columns if in LVS_LIST mode */
6863 if(uView == LVS_LIST) {
6864 infoPtr->nItemWidth = cx;
6865 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6869 /* autosize based on listview items width */
6870 if(cx == LVSCW_AUTOSIZE)
6872 /* set the width of the header to the width of the widest item */
6873 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6875 if(cx < LISTVIEW_GetLabelWidth(hwnd, item_index))
6876 cx = LISTVIEW_GetLabelWidth(hwnd, item_index);
6878 } /* autosize based on listview header width */
6879 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6881 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6883 /* if iCol is the last column make it fill the remainder of the controls width */
6884 if(iCol == (header_item_count - 1)) {
6885 /* get the width of every item except the current one */
6886 hdi.mask = HDI_WIDTH;
6889 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6890 Header_GetItemA(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6894 /* retrieve the layout of the header */
6895 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6897 cx = (rcHeader.right - rcHeader.left) - cx;
6901 /* retrieve header font */
6902 header_font = SendMessageA(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6904 /* retrieve header text */
6905 hdi.mask = HDI_TEXT;
6906 hdi.cchTextMax = sizeof(text_buffer);
6907 hdi.pszText = text_buffer;
6909 Header_GetItemA(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6911 /* determine the width of the text in the header */
6913 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6915 GetTextExtentPoint32A(hdc, text_buffer, strlen(text_buffer), &size);
6917 SelectObject(hdc, old_font); /* restore the old font */
6918 ReleaseDC(hwnd, hdc);
6920 /* set the width of this column to the width of the text */
6925 /* call header to update the column change */
6926 hdi.mask = HDI_WIDTH;
6929 lret = Header_SetItemA(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6931 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6938 * Sets the extended listview style.
6941 * [I] HWND : window handle
6946 * SUCCESS : previous style
6949 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
6951 LISTVIEW_INFO *infoPtr;
6954 /* make sure we can get the listview info */
6955 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6958 /* store previous style */
6959 dwOldStyle = infoPtr->dwExStyle;
6963 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6965 infoPtr->dwExStyle = dwStyle;
6967 return (dwOldStyle);
6970 /* LISTVIEW_SetHotCursor */
6974 * Sets the hot item index.
6977 * [I] HWND : window handle
6981 * SUCCESS : previous hot item index
6982 * FAILURE : -1 (no hot item)
6984 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
6986 LISTVIEW_INFO *infoPtr;
6989 /* make sure we can get the listview info */
6990 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6993 /* store previous index */
6994 iOldIndex = infoPtr->nHotItem;
6997 infoPtr->nHotItem = iIndex;
7004 * Sets the amount of time the cursor must hover over an item before it is selected.
7007 * [I] HWND : window handle
7008 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7011 * Returns the previous hover time
7013 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7015 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7016 DWORD oldHoverTime = infoPtr->dwHoverTime;
7018 infoPtr->dwHoverTime = dwHoverTime;
7020 return oldHoverTime;
7023 /* LISTVIEW_SetIconSpacing */
7030 * [I] HWND : window handle
7031 * [I] INT : image list type
7032 * [I] HIMAGELIST : image list handle
7035 * SUCCESS : old image list
7038 static LRESULT LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7040 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7041 HIMAGELIST himlOld = 0;
7047 himlOld = infoPtr->himlNormal;
7048 infoPtr->himlNormal = himl;
7052 himlOld = infoPtr->himlSmall;
7053 infoPtr->himlSmall = himl;
7057 himlOld = infoPtr->himlState;
7058 infoPtr->himlState = himl;
7059 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7063 oldHeight = infoPtr->nItemHeight;
7064 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7065 if (infoPtr->nItemHeight != oldHeight)
7066 LISTVIEW_UpdateScroll(hwnd);
7068 return (LRESULT)himlOld;
7074 * Sets the attributes of an item.
7077 * [I] HWND : window handle
7078 * [I] LPLVITEM : item information
7084 static LRESULT LISTVIEW_SetItemA(HWND hwnd, LPLVITEMA lpLVItem)
7086 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7087 BOOL bResult = FALSE;
7089 if (lpLVItem != NULL)
7091 if ((lpLVItem->iItem >= 0) && (lpLVItem->iItem < GETITEMCOUNT(infoPtr)))
7093 if (lpLVItem->iSubItem == 0)
7095 bResult = LISTVIEW_SetItem(hwnd, lpLVItem);
7099 bResult = LISTVIEW_SetSubItem(hwnd, lpLVItem);
7108 /* LISTVIEW_SetItemW */
7112 * Preallocates memory (does *not* set the actual count of items !)
7115 * [I] HWND : window handle
7116 * [I] INT : item count (projected number of items to allocate)
7117 * [I] DWORD : update flags
7123 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7125 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7127 if (GetWindowLongA(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7129 int precount,topvisible;
7130 TRACE("LVS_OWNERDATA is set!\n");
7133 * Internally remove all the selections.
7137 LISTVIEW_SELECTION *selection;
7138 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7140 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7143 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7145 precount = infoPtr->hdpaItems->nItemCount;
7146 topvisible = ListView_GetTopIndex(hwnd) +
7147 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7149 infoPtr->hdpaItems->nItemCount = nItems;
7151 LISTVIEW_UpdateSize(hwnd);
7152 LISTVIEW_UpdateScroll(hwnd);
7153 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7154 InvalidateRect(hwnd, NULL, TRUE);
7158 FIXME("setitemcount not done for non-ownerdata\n");
7166 * Sets the position of an item.
7169 * [I] HWND : window handle
7170 * [I] INT : item index
7171 * [I] LONG : x coordinate
7172 * [I] LONG : y coordinate
7178 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7179 LONG nPosX, LONG nPosY)
7181 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7182 UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7183 UINT uView = lStyle & LVS_TYPEMASK;
7184 LISTVIEW_ITEM *lpItem;
7186 BOOL bResult = FALSE;
7188 TRACE("(hwnd=%x,nItem=%d,X=%ld,Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7190 if (lStyle & LVS_OWNERDATA)
7193 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7195 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7197 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
7198 if (hdpaSubItems != NULL)
7200 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7204 lpItem->ptPosition.x = nPosX;
7205 lpItem->ptPosition.y = nPosY;
7216 * Sets the state of one or many items.
7219 * [I] HWND : window handle
7220 * [I]INT : item index
7221 * [I] LPLVITEM : item or subitem info
7227 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
7229 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7230 BOOL bResult = FALSE;
7237 ZeroMemory(&lvItem, sizeof(LVITEMA));
7238 lvItem.mask = LVIF_STATE;
7239 lvItem.state = lpLVItem->state;
7240 lvItem.stateMask = lpLVItem->stateMask ;
7242 /* apply to all items */
7243 for (i = 0; i< GETITEMCOUNT(infoPtr); i++)
7246 if (ListView_SetItemA(hwnd, &lvItem) == FALSE)
7254 ZeroMemory(&lvItem, sizeof(LVITEMA));
7255 lvItem.mask = LVIF_STATE;
7256 lvItem.state = lpLVItem->state;
7257 lvItem.stateMask = lpLVItem->stateMask;
7258 lvItem.iItem = nItem;
7259 bResult = ListView_SetItemA(hwnd, &lvItem);
7267 * Sets the text of an item or subitem.
7270 * [I] HWND : window handle
7271 * [I] INT : item index
7272 * [I] LPLVITEMA : item or subitem info
7278 static BOOL LISTVIEW_SetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
7280 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7281 BOOL bResult = FALSE;
7284 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7286 ZeroMemory(&lvItem, sizeof(LVITEMA));
7287 lvItem.mask = LVIF_TEXT;
7288 lvItem.pszText = lpLVItem->pszText;
7289 lvItem.iItem = nItem;
7290 lvItem.iSubItem = lpLVItem->iSubItem;
7291 bResult = ListView_SetItemA(hwnd, &lvItem);
7297 /* LISTVIEW_SetItemTextW */
7301 * Set item index that marks the start of a multiple selection.
7304 * [I] HWND : window handle
7308 * Index number or -1 if there is no selection mark.
7310 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7312 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7313 INT nOldIndex = infoPtr->nSelectionMark;
7315 infoPtr->nSelectionMark = nIndex;
7322 * Sets the text background color.
7325 * [I] HWND : window handle
7326 * [I] COLORREF : text background color
7332 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7334 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7336 infoPtr->clrTextBk = clrTextBk;
7337 InvalidateRect(hwnd, NULL, TRUE);
7344 * Sets the text foreground color.
7347 * [I] HWND : window handle
7348 * [I] COLORREF : text color
7354 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7356 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7358 infoPtr->clrText = clrText;
7359 InvalidateRect(hwnd, NULL, TRUE);
7364 /* LISTVIEW_SetToolTips */
7365 /* LISTVIEW_SetUnicodeFormat */
7366 /* LISTVIEW_SetWorkAreas */
7370 * Callback internally used by LISTVIEW_SortItems()
7373 * [I] LPVOID : first LISTVIEW_ITEM to compare
7374 * [I] LPVOID : second LISTVIEW_ITEM to compare
7375 * [I] LPARAM : HWND of control
7378 * if first comes before second : negative
7379 * if first comes after second : positive
7380 * if first and second are equivalent : zero
7382 static INT WINAPI LISTVIEW_CallBackCompare(
7387 /* Forward the call to the client defined callback */
7389 HWND hwnd = (HWND)lParam;
7390 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7391 HDPA hdpa_first = (HDPA) first;
7392 HDPA hdpa_second = (HDPA) second;
7393 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
7394 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
7396 rv = (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7403 * Sorts the listview items.
7406 * [I] HWND : window handle
7407 * [I] WPARAM : application-defined value
7408 * [I] LPARAM : pointer to comparision callback
7414 static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam)
7416 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7418 UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7419 HDPA hdpaSubItems=NULL;
7420 LISTVIEW_ITEM *pLVItem=NULL;
7421 LPVOID selectionMarkItem;
7423 if (lStyle & LVS_OWNERDATA)
7426 if (!infoPtr || !infoPtr->hdpaItems)
7429 nCount = GETITEMCOUNT(infoPtr);
7430 /* if there are 0 or 1 items, there is no need to sort */
7434 infoPtr->pfnCompare = (PFNLVCOMPARE)lParam;
7435 infoPtr->lParamSort = (LPARAM)wParam;
7436 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7438 /* Adjust selections and indices so that they are the way they should
7439 * be after the sort (otherwise, the list items move around, but
7440 * whatever is at the item's previous original position will be
7443 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7444 for (i=0; i < nCount; i++)
7446 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7447 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7449 if (pLVItem->state & LVIS_SELECTED)
7450 LISTVIEW_AddSelectionRange(hwnd, i, i);
7452 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7453 if (pLVItem->state & LVIS_FOCUSED)
7454 infoPtr->nFocusedItem=i;
7456 if (selectionMarkItem != NULL)
7457 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7458 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7460 /* align the items */
7461 LISTVIEW_AlignTop(hwnd);
7463 /* refresh the display */
7464 InvalidateRect(hwnd, NULL, TRUE);
7469 /* LISTVIEW_SubItemHitTest */
7473 * Updates an items or rearranges the listview control.
7476 * [I] HWND : window handle
7477 * [I] INT : item index
7483 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7485 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7486 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7487 BOOL bResult = FALSE;
7490 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7494 /* rearrange with default alignment style */
7495 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7496 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7498 ListView_Arrange(hwnd, 0);
7502 /* get item bounding rectangle */
7503 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
7504 InvalidateRect(hwnd, &rc, TRUE);
7513 * Creates the listview control.
7516 * [I] HWND : window handle
7521 static LRESULT LISTVIEW_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
7523 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7524 LPCREATESTRUCTA lpcs = (LPCREATESTRUCTA)lParam;
7525 UINT uView = lpcs->style & LVS_TYPEMASK;
7528 /* initialize info pointer */
7529 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7531 /* determine the type of structures to use */
7532 infoPtr->notifyFormat = SendMessageA(GetParent(hwnd), WM_NOTIFYFORMAT,
7533 (WPARAM)hwnd, (LPARAM)NF_QUERY);
7534 if (infoPtr->notifyFormat != NFR_ANSI)
7536 FIXME("ANSI notify format is NOT used\n");
7539 /* initialize color information */
7540 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7541 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7542 infoPtr->clrTextBk = CLR_DEFAULT;
7544 /* set default values */
7545 infoPtr->uCallbackMask = 0;
7546 infoPtr->nFocusedItem = -1;
7547 infoPtr->nSelectionMark = -1;
7548 infoPtr->nHotItem = -1;
7549 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7550 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7551 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7552 infoPtr->hwndEdit = 0;
7553 infoPtr->pedititem = NULL;
7554 infoPtr->nEditLabelItem = -1;
7556 /* get default font (icon title) */
7557 SystemParametersInfoA(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7558 infoPtr->hDefaultFont = CreateFontIndirectA(&logFont);
7559 infoPtr->hFont = infoPtr->hDefaultFont;
7562 infoPtr->hwndHeader = CreateWindowA(WC_HEADERA, (LPCSTR)NULL,
7563 WS_CHILD | HDS_HORZ | HDS_BUTTONS,
7564 0, 0, 0, 0, hwnd, (HMENU)0,
7565 lpcs->hInstance, NULL);
7567 /* set header font */
7568 SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7571 if (uView == LVS_ICON)
7573 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7574 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7576 else if (uView == LVS_REPORT)
7578 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7580 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7584 /* set HDS_HIDDEN flag to hide the header bar */
7585 SetWindowLongA(infoPtr->hwndHeader, GWL_STYLE,
7586 GetWindowLongA(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7590 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7591 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7595 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7596 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7599 /* display unsupported listview window styles */
7600 LISTVIEW_UnsupportedStyles(lpcs->style);
7602 /* allocate memory for the data structure */
7603 infoPtr->hdpaItems = DPA_Create(10);
7605 /* allocate memory for the selection ranges */
7606 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7608 /* initialize size of items */
7609 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7610 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7612 /* initialize the hover time to -1(indicating the default system hover time) */
7613 infoPtr->dwHoverTime = -1;
7620 * Erases the background of the listview control.
7623 * [I] HWND : window handle
7624 * [I] WPARAM : device context handle
7625 * [I] LPARAM : not used
7631 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
7634 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7637 if (infoPtr->clrBk == CLR_NONE)
7639 bResult = SendMessageA(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
7644 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7645 GetClientRect(hwnd, &rc);
7646 FillRect((HDC)wParam, &rc, hBrush);
7647 DeleteObject(hBrush);
7655 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
7657 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7659 if (infoPtr->clrBk != CLR_NONE)
7661 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7662 FillRect(hdc, rc, hBrush);
7663 DeleteObject(hBrush);
7669 * Retrieves the listview control font.
7672 * [I] HWND : window handle
7677 static LRESULT LISTVIEW_GetFont(HWND hwnd)
7679 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7681 return infoPtr->hFont;
7686 * Performs vertical scrolling.
7689 * [I] HWND : window handle
7690 * [I] INT : scroll code
7691 * [I] SHORT : current scroll position if scroll code is SB_THIMBPOSITION
7693 * [I] HWND : scrollbar control window handle
7698 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7701 SCROLLINFO scrollInfo;
7703 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7704 SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7706 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7707 scrollInfo.cbSize = sizeof(SCROLLINFO);
7708 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7710 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7712 INT nOldScrollPos = scrollInfo.nPos;
7713 switch (nScrollCode)
7716 if (scrollInfo.nPos > scrollInfo.nMin)
7723 if (scrollInfo.nPos < scrollInfo.nMax)
7730 if (scrollInfo.nPos > scrollInfo.nMin)
7732 if (scrollInfo.nPos >= scrollInfo.nPage)
7734 scrollInfo.nPos -= scrollInfo.nPage;
7738 scrollInfo.nPos = scrollInfo.nMin;
7744 if (scrollInfo.nPos < scrollInfo.nMax)
7746 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7748 scrollInfo.nPos += scrollInfo.nPage;
7752 scrollInfo.nPos = scrollInfo.nMax;
7758 scrollInfo.nPos = nCurrentPos;
7759 if (scrollInfo.nPos > scrollInfo.nMax)
7760 scrollInfo.nPos=scrollInfo.nMax;
7762 if (scrollInfo.nPos < scrollInfo.nMin)
7763 scrollInfo.nPos=scrollInfo.nMin;
7768 if (nOldScrollPos != scrollInfo.nPos)
7770 scrollInfo.fMask = SIF_POS;
7771 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
7772 InvalidateRect(hwnd, NULL, TRUE);
7781 * Performs horizontal scrolling.
7784 * [I] HWND : window handle
7785 * [I] INT : scroll code
7786 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
7788 * [I] HWND : scrollbar control window handle
7793 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7796 SCROLLINFO scrollInfo;
7798 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7799 SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7802 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7803 scrollInfo.cbSize = sizeof(SCROLLINFO);
7804 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7806 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
7808 INT nOldScrollPos = scrollInfo.nPos;
7810 switch (nScrollCode)
7813 if (scrollInfo.nPos > scrollInfo.nMin)
7820 if (scrollInfo.nPos < scrollInfo.nMax)
7827 if (scrollInfo.nPos > scrollInfo.nMin)
7829 if (scrollInfo.nPos >= scrollInfo.nPage)
7831 scrollInfo.nPos -= scrollInfo.nPage;
7835 scrollInfo.nPos = scrollInfo.nMin;
7841 if (scrollInfo.nPos < scrollInfo.nMax)
7843 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7845 scrollInfo.nPos += scrollInfo.nPage;
7849 scrollInfo.nPos = scrollInfo.nMax;
7855 scrollInfo.nPos = nCurrentPos;
7857 if (scrollInfo.nPos > scrollInfo.nMax)
7858 scrollInfo.nPos=scrollInfo.nMax;
7860 if (scrollInfo.nPos < scrollInfo.nMin)
7861 scrollInfo.nPos=scrollInfo.nMin;
7865 if (nOldScrollPos != scrollInfo.nPos)
7867 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7868 scrollInfo.fMask = SIF_POS;
7869 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
7870 if(uView == LVS_REPORT)
7872 scrollInfo.fMask = SIF_POS;
7873 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
7874 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
7876 InvalidateRect(hwnd, NULL, TRUE);
7883 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
7885 INT gcWheelDelta = 0;
7886 UINT pulScrollLines = 3;
7887 SCROLLINFO scrollInfo;
7889 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7891 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7892 gcWheelDelta -= wheelDelta;
7894 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7895 scrollInfo.cbSize = sizeof(SCROLLINFO);
7896 scrollInfo.fMask = SIF_POS | SIF_RANGE;
7903 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7904 * should be fixed in the future.
7906 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7907 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
7911 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7913 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7915 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
7916 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7917 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7923 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7934 * [I] HWND : window handle
7935 * [I] INT : virtual key
7936 * [I] LONG : key data
7941 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
7943 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7944 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
7945 HWND hwndParent = GetParent(hwnd);
7946 NMLVKEYDOWN nmKeyDown;
7949 BOOL bRedraw = FALSE;
7950 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7951 UINT uView = lStyle & LVS_TYPEMASK;
7953 /* send LVN_KEYDOWN notification */
7954 ZeroMemory(&nmKeyDown, sizeof(NMLVKEYDOWN));
7955 nmKeyDown.hdr.hwndFrom = hwnd;
7956 nmKeyDown.hdr.idFrom = nCtrlId;
7957 nmKeyDown.hdr.code = LVN_KEYDOWN;
7958 nmKeyDown.wVKey = nVirtualKey;
7959 nmKeyDown.flags = 0;
7960 SendMessageA(hwndParent, WM_NOTIFY, (WPARAM)nCtrlId, (LPARAM)&nmKeyDown);
7963 nmh.hwndFrom = hwnd;
7964 nmh.idFrom = nCtrlId;
7966 switch (nVirtualKey)
7969 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
7971 /* send NM_RETURN notification */
7972 nmh.code = NM_RETURN;
7973 ListView_Notify(hwndParent, nCtrlId, &nmh);
7975 /* send LVN_ITEMACTIVATE notification */
7976 nmh.code = LVN_ITEMACTIVATE;
7977 ListView_Notify(hwndParent, nCtrlId, &nmh);
7982 if (GETITEMCOUNT(infoPtr) > 0)
7989 if (GETITEMCOUNT(infoPtr) > 0)
7991 nItem = GETITEMCOUNT(infoPtr) - 1;
7996 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8000 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8004 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8008 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8012 if (uView == LVS_REPORT)
8014 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8018 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8019 * LISTVIEW_GetCountPerRow(hwnd);
8021 if(nItem < 0) nItem = 0;
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 >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8038 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8040 bRedraw = LISTVIEW_KeySelection(hwnd, nItem);
8041 if (bRedraw != FALSE)
8043 /* refresh client area */
8056 * [I] HWND : window handle
8061 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8063 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
8064 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8068 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8069 UINT uView = lStyle & LVS_TYPEMASK;
8071 TRACE("(hwnd=%x)\n", hwnd);
8073 /* send NM_KILLFOCUS notification */
8074 nmh.hwndFrom = hwnd;
8075 nmh.idFrom = nCtrlId;
8076 nmh.code = NM_KILLFOCUS;
8077 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8079 /* set window focus flag */
8080 infoPtr->bFocus = FALSE;
8082 /* NEED drawing optimization ; redraw the selected items */
8083 if (uView & LVS_REPORT)
8085 nTop = LISTVIEW_GetTopIndex(hwnd);
8087 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8092 nBottom = GETITEMCOUNT(infoPtr);
8094 for (i = nTop; i<nBottom; i++)
8096 if (LISTVIEW_IsSelected(hwnd,i))
8098 rcItem.left = LVIR_BOUNDS;
8099 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8100 InvalidateRect(hwnd, &rcItem, FALSE);
8109 * Processes double click messages (left mouse button).
8112 * [I] HWND : window handle
8113 * [I] WORD : key flag
8114 * [I] WORD : x coordinate
8115 * [I] WORD : y coordinate
8120 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8123 LONG nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8124 LVHITTESTINFO htInfo;
8129 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8131 htInfo.pt.x = wPosX;
8132 htInfo.pt.y = wPosY;
8134 /* send NM_DBLCLK notification */
8135 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8136 nmlv.hdr.hwndFrom = hwnd;
8137 nmlv.hdr.idFrom = nCtrlId;
8138 nmlv.hdr.code = NM_DBLCLK;
8139 ret = LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE);
8142 nmlv.iItem = htInfo.iItem;
8143 nmlv.iSubItem = htInfo.iSubItem;
8150 nmlv.ptAction.x = wPosX;
8151 nmlv.ptAction.y = wPosY;
8152 ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8155 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8158 /* send LVN_ITEMACTIVATE notification */
8159 nmh.hwndFrom = hwnd;
8160 nmh.idFrom = nCtrlId;
8161 nmh.code = LVN_ITEMACTIVATE;
8162 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8170 * Processes mouse down messages (left mouse button).
8173 * [I] HWND : window handle
8174 * [I] WORD : key flag
8175 * [I] WORD : x coordinate
8176 * [I] WORD : y coordinate
8181 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8184 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8185 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8186 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8187 static BOOL bGroupSelect = TRUE;
8192 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX,
8195 /* send NM_RELEASEDCAPTURE notification */
8196 nmh.hwndFrom = hwnd;
8197 nmh.idFrom = nCtrlId;
8198 nmh.code = NM_RELEASEDCAPTURE;
8199 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8201 if (infoPtr->bFocus == FALSE)
8206 /* set left button down flag */
8207 infoPtr->bLButtonDown = TRUE;
8209 ptPosition.x = wPosX;
8210 ptPosition.y = wPosY;
8211 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8212 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8214 if (lStyle & LVS_SINGLESEL)
8216 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8217 && infoPtr->nEditLabelItem == -1)
8219 infoPtr->nEditLabelItem = nItem;
8223 LISTVIEW_SetSelection(hwnd, nItem);
8228 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8230 if (bGroupSelect != FALSE)
8232 LISTVIEW_AddGroupSelection(hwnd, nItem);
8236 LISTVIEW_AddSelection(hwnd, nItem);
8239 else if (wKey & MK_CONTROL)
8241 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8243 else if (wKey & MK_SHIFT)
8245 LISTVIEW_SetGroupSelection(hwnd, nItem);
8250 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8252 /* set selection (clears other pre-existing selections) */
8253 LISTVIEW_SetSelection(hwnd, nItem);
8255 if (was_selected && infoPtr->nEditLabelItem == -1)
8257 infoPtr->nEditLabelItem = nItem;
8264 /* remove all selections */
8265 LISTVIEW_RemoveAllSelections(hwnd);
8268 /* redraw if we could have possibly selected something */
8269 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8276 * Processes mouse up messages (left mouse button).
8279 * [I] HWND : window handle
8280 * [I] WORD : key flag
8281 * [I] WORD : x coordinate
8282 * [I] WORD : y coordinate
8287 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8290 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8292 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8294 if (infoPtr->bLButtonDown != FALSE)
8296 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8298 LVHITTESTINFO lvHitTestInfo;
8301 lvHitTestInfo.pt.x = wPosX;
8302 lvHitTestInfo.pt.y = wPosY;
8304 /* send NM_CLICK notification */
8305 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8306 nmlv.hdr.hwndFrom = hwnd;
8307 nmlv.hdr.idFrom = nCtrlId;
8308 nmlv.hdr.code = NM_CLICK;
8309 ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
8312 nmlv.iItem = lvHitTestInfo.iItem;
8313 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8320 nmlv.ptAction.x = wPosX;
8321 nmlv.ptAction.y = wPosY;
8322 ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8325 /* set left button flag */
8326 infoPtr->bLButtonDown = FALSE;
8328 if(infoPtr->nEditLabelItem != -1)
8330 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem)
8332 LISTVIEW_EditLabelA(hwnd, lvHitTestInfo.iItem);
8334 infoPtr->nEditLabelItem = -1;
8343 * Creates the listview control (called before WM_CREATE).
8346 * [I] HWND : window handle
8347 * [I] WPARAM : unhandled
8348 * [I] LPARAM : widow creation info
8353 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8355 LISTVIEW_INFO *infoPtr;
8357 TRACE("(hwnd=%x,wParam=%x,lParam=%lx)\n", hwnd, wParam, lParam);
8359 /* allocate memory for info structure */
8360 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8361 SetWindowLongA(hwnd, 0, (LONG)infoPtr);
8362 if (infoPtr == NULL)
8364 ERR("could not allocate info memory!\n");
8368 if ((LISTVIEW_INFO *)GetWindowLongA(hwnd, 0) != infoPtr)
8370 ERR("pointer assignment error!\n");
8374 return DefWindowProcA(hwnd, WM_NCCREATE, wParam, lParam);
8379 * Destroys the listview control (called after WM_DESTROY).
8382 * [I] HWND : window handle
8387 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8389 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8391 TRACE("(hwnd=%x)\n", hwnd);
8393 /* delete all items */
8394 LISTVIEW_DeleteAllItems(hwnd);
8396 /* destroy data structure */
8397 DPA_Destroy(infoPtr->hdpaItems);
8398 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8401 infoPtr->hFont = (HFONT)0;
8402 if (infoPtr->hDefaultFont)
8404 DeleteObject(infoPtr->hDefaultFont);
8407 /* free listview info pointer*/
8408 COMCTL32_Free(infoPtr);
8410 SetWindowLongA(hwnd, 0, 0);
8416 * Handles notifications from children.
8419 * [I] HWND : window handle
8420 * [I] INT : control identifier
8421 * [I] LPNMHDR : notification information
8426 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8428 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8430 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8432 /* handle notification from header control */
8433 if (lpnmh->code == HDN_ENDTRACKA)
8435 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8436 InvalidateRect(hwnd, NULL, TRUE);
8438 else if(lpnmh->code == HDN_ITEMCLICKA)
8440 /* Handle sorting by Header Column */
8442 LPNMHEADERA pnmHeader = (LPNMHEADERA) lpnmh;
8443 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
8445 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8446 nmlv.hdr.hwndFrom = hwnd;
8447 nmlv.hdr.idFrom = lCtrlId;
8448 nmlv.hdr.code = LVN_COLUMNCLICK;
8450 nmlv.iSubItem = pnmHeader->iItem;
8452 ListView_LVNotify(GetParent(hwnd),lCtrlId, &nmlv);
8455 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8457 /* Idealy this should be done in HDN_ENDTRACKA
8458 * but since SetItemBounds in Header.c is called after
8459 * the notification is sent, it is neccessary to handle the
8460 * update of the scroll bar here (Header.c works fine as it is,
8461 * no need to disturb it)
8463 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8464 LISTVIEW_UpdateScroll(hwnd);
8465 InvalidateRect(hwnd, NULL, TRUE);
8475 * Determines the type of structure to use.
8478 * [I] HWND : window handle of the sender
8479 * [I] HWND : listview window handle
8480 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8485 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8487 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8489 if (nCommand == NF_REQUERY)
8491 /* determine the type of structure to use */
8492 infoPtr->notifyFormat = SendMessageA(hwndFrom, WM_NOTIFYFORMAT,
8493 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8494 if (infoPtr->notifyFormat == NFR_UNICODE)
8496 FIXME("NO support for unicode structures");
8505 * Paints/Repaints the listview control.
8508 * [I] HWND : window handle
8509 * [I] HDC : device context handle
8514 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8518 TRACE("(hwnd=%x,hdc=%x)\n", hwnd, hdc);
8522 hdc = BeginPaint(hwnd, &ps);
8523 LISTVIEW_Refresh(hwnd, hdc);
8524 EndPaint(hwnd, &ps);
8528 LISTVIEW_Refresh(hwnd, hdc);
8536 * Processes double click messages (right mouse button).
8539 * [I] HWND : window handle
8540 * [I] WORD : key flag
8541 * [I] WORD : x coordinate
8542 * [I] WORD : y coordinate
8547 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8550 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8553 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8555 /* send NM_RELEASEDCAPTURE notification */
8556 nmh.hwndFrom = hwnd;
8557 nmh.idFrom = nCtrlId;
8558 nmh.code = NM_RELEASEDCAPTURE;
8559 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8561 /* send NM_RDBLCLK notification */
8562 nmh.code = NM_RDBLCLK;
8563 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8570 * Processes mouse down messages (right mouse button).
8573 * [I] HWND : window handle
8574 * [I] WORD : key flag
8575 * [I] WORD : x coordinate
8576 * [I] WORD : y coordinate
8581 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8584 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8585 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8590 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8592 /* send NM_RELEASEDCAPTURE notification */
8593 nmh.hwndFrom = hwnd;
8594 nmh.idFrom = nCtrlId;
8595 nmh.code = NM_RELEASEDCAPTURE;
8596 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8598 /* make sure the listview control window has the focus */
8599 if (infoPtr->bFocus == FALSE)
8604 /* set right button down flag */
8605 infoPtr->bRButtonDown = TRUE;
8607 /* determine the index of the selected item */
8608 ptPosition.x = wPosX;
8609 ptPosition.y = wPosY;
8610 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8611 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8613 LISTVIEW_SetItemFocus(hwnd,nItem);
8614 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8615 !LISTVIEW_IsSelected(hwnd,nItem))
8617 LISTVIEW_SetSelection(hwnd, nItem);
8622 LISTVIEW_RemoveAllSelections(hwnd);
8630 * Processes mouse up messages (right mouse button).
8633 * [I] HWND : window handle
8634 * [I] WORD : key flag
8635 * [I] WORD : x coordinate
8636 * [I] WORD : y coordinate
8641 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8644 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8645 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8647 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8649 if (infoPtr->bRButtonDown != FALSE)
8652 LVHITTESTINFO lvHitTestInfo;
8656 lvHitTestInfo.pt.x = wPosX;
8657 lvHitTestInfo.pt.y = wPosY;
8659 /* Send NM_RClICK notification */
8660 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8661 nmlv.hdr.hwndFrom = hwnd;
8662 nmlv.hdr.idFrom = nCtrlId;
8663 nmlv.hdr.code = NM_RCLICK;
8664 ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
8667 nmlv.iItem = lvHitTestInfo.iItem;
8668 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8675 nmlv.ptAction.x = wPosX;
8676 nmlv.ptAction.y = wPosY;
8677 ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8682 /* set button flag */
8683 infoPtr->bRButtonDown = FALSE;
8685 /* Change to screen coordinate for WM_CONTEXTMENU */
8686 ClientToScreen(hwnd, &pt);
8688 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8689 SendMessageA( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
8700 * [I] HWND : window handle
8701 * [I] HWND : window handle of previously focused window
8706 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
8708 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8709 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8712 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
8714 /* send NM_SETFOCUS notification */
8715 nmh.hwndFrom = hwnd;
8716 nmh.idFrom = nCtrlId;
8717 nmh.code = NM_SETFOCUS;
8718 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8720 /* set window focus flag */
8721 infoPtr->bFocus = TRUE;
8733 * [I] HWND : window handle
8734 * [I] HFONT : font handle
8735 * [I] WORD : redraw flag
8740 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
8742 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8743 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8745 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
8749 infoPtr->hFont = infoPtr->hDefaultFont;
8753 infoPtr->hFont = hFont;
8756 if (uView == LVS_REPORT)
8758 /* set header font */
8759 SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
8760 MAKELPARAM(fRedraw, 0));
8763 /* invalidate listview control client area */
8764 InvalidateRect(hwnd, NULL, TRUE);
8766 if (fRedraw != FALSE)
8776 * Message handling for WM_SETREDRAW.
8777 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8780 * [I] HWND : window handle
8781 * [I] bRedraw: state of redraw flag
8784 * DefWinProc return value
8786 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
8789 lResult = DefWindowProcA(hwnd, WM_SETREDRAW, bRedraw, 0);
8792 RedrawWindow(hwnd, NULL, 0,
8793 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8800 * Resizes the listview control. This function processes WM_SIZE
8801 * messages. At this time, the width and height are not used.
8804 * [I] HWND : window handle
8805 * [I] WORD : new width
8806 * [I] WORD : new height
8811 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
8813 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8814 UINT uView = lStyle & LVS_TYPEMASK;
8816 TRACE("(hwnd=%x, width=%d, height=%d)\n",hwnd, Width, Height);
8818 LISTVIEW_UpdateSize(hwnd);
8820 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8822 if (lStyle & LVS_ALIGNLEFT)
8824 LISTVIEW_AlignLeft(hwnd);
8828 LISTVIEW_AlignTop(hwnd);
8832 LISTVIEW_UpdateScroll(hwnd);
8834 /* invalidate client area + erase background */
8835 InvalidateRect(hwnd, NULL, TRUE);
8842 * Sets the size information.
8845 * [I] HWND : window handle
8850 static VOID LISTVIEW_UpdateSize(HWND hwnd)
8852 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8853 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8854 UINT uView = lStyle & LVS_TYPEMASK;
8857 GetClientRect(hwnd, &rcList);
8858 infoPtr->rcList.left = 0;
8859 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8860 infoPtr->rcList.top = 0;
8861 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8863 if (uView == LVS_LIST)
8865 if (lStyle & WS_HSCROLL)
8867 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8868 if (infoPtr->rcList.bottom > nHScrollHeight)
8870 infoPtr->rcList.bottom -= nHScrollHeight;
8874 else if (uView == LVS_REPORT)
8881 Header_Layout(infoPtr->hwndHeader, &hl);
8883 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8885 if (!(LVS_NOCOLUMNHEADER & lStyle))
8887 infoPtr->rcList.top = max(wp.cy, 0);
8894 * Processes WM_STYLECHANGED messages.
8897 * [I] HWND : window handle
8898 * [I] WPARAM : window style type (normal or extended)
8899 * [I] LPSTYLESTRUCT : window style information
8904 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
8907 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8908 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8909 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8910 RECT rcList = infoPtr->rcList;
8912 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
8913 hwnd, wStyleType, lpss);
8915 if (wStyleType == GWL_STYLE)
8917 if (uOldView == LVS_REPORT)
8919 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8922 if ((lpss->styleOld & WS_HSCROLL) != 0)
8924 ShowScrollBar(hwnd, SB_HORZ, FALSE);
8927 if ((lpss->styleOld & WS_VSCROLL) != 0)
8929 ShowScrollBar(hwnd, SB_VERT, FALSE);
8932 if (uNewView == LVS_ICON)
8934 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8935 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8936 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8937 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8938 if (lpss->styleNew & LVS_ALIGNLEFT)
8940 LISTVIEW_AlignLeft(hwnd);
8944 LISTVIEW_AlignTop(hwnd);
8947 else if (uNewView == LVS_REPORT)
8954 Header_Layout(infoPtr->hwndHeader, &hl);
8955 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
8957 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8958 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8960 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8961 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8962 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8963 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8965 else if (uNewView == LVS_LIST)
8967 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8968 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8969 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8970 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8974 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8975 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8976 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8977 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8978 if (lpss->styleNew & LVS_ALIGNLEFT)
8980 LISTVIEW_AlignLeft(hwnd);
8984 LISTVIEW_AlignTop(hwnd);
8988 /* update the size of the client area */
8989 LISTVIEW_UpdateSize(hwnd);
8991 /* add scrollbars if needed */
8992 LISTVIEW_UpdateScroll(hwnd);
8994 /* invalidate client area + erase background */
8995 InvalidateRect(hwnd, NULL, TRUE);
8997 /* print the list of unsupported window styles */
8998 LISTVIEW_UnsupportedStyles(lpss->styleNew);
9001 /* If they change the view and we have an active edit control
9002 we will need to kill the control since the redraw will
9003 misplace the edit control.
9005 if (infoPtr->hwndEdit &&
9006 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9007 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9009 SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9017 * Window procedure of the listview control.
9020 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9023 TRACE("hwnd=%x uMsg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
9024 if (!GetWindowLongA(hwnd, 0) && (uMsg != WM_NCCREATE))
9025 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
9028 case LVM_APPROXIMATEVIEWRECT:
9029 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9030 LOWORD(lParam), HIWORD(lParam));
9032 return LISTVIEW_Arrange(hwnd, (INT)wParam);
9034 /* case LVM_CREATEDRAGIMAGE: */
9036 case LVM_DELETEALLITEMS:
9037 return LISTVIEW_DeleteAllItems(hwnd);
9039 case LVM_DELETECOLUMN:
9040 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9042 case LVM_DELETEITEM:
9043 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9045 case LVM_EDITLABELW:
9046 case LVM_EDITLABELA:
9047 return LISTVIEW_EditLabelA(hwnd, (INT)wParam);
9049 case LVM_ENSUREVISIBLE:
9050 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9053 return LISTVIEW_FindItem(hwnd, (INT)wParam, (LPLVFINDINFO)lParam);
9055 case LVM_GETBKCOLOR:
9056 return LISTVIEW_GetBkColor(hwnd);
9058 /* case LVM_GETBKIMAGE: */
9060 case LVM_GETCALLBACKMASK:
9061 return LISTVIEW_GetCallbackMask(hwnd);
9063 case LVM_GETCOLUMNA:
9064 return LISTVIEW_GetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9066 /* case LVM_GETCOLUMNW: */
9068 case LVM_GETCOLUMNORDERARRAY:
9069 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9071 case LVM_GETCOLUMNWIDTH:
9072 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9074 case LVM_GETCOUNTPERPAGE:
9075 return LISTVIEW_GetCountPerPage(hwnd);
9077 case LVM_GETEDITCONTROL:
9078 return LISTVIEW_GetEditControl(hwnd);
9080 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9081 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9084 return LISTVIEW_GetHeader(hwnd);
9086 /* case LVM_GETHOTCURSOR: */
9088 case LVM_GETHOTITEM:
9089 return LISTVIEW_GetHotItem(hwnd);
9091 case LVM_GETHOVERTIME:
9092 return LISTVIEW_GetHoverTime(hwnd);
9094 case LVM_GETIMAGELIST:
9095 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9097 /* case LVM_GETISEARCHSTRING: */
9100 return LISTVIEW_GetItemA(hwnd, (LPLVITEMA)lParam, FALSE);
9102 /* case LVM_GETITEMW: */
9104 case LVM_GETITEMCOUNT:
9105 return LISTVIEW_GetItemCount(hwnd);
9107 case LVM_GETITEMPOSITION:
9108 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9110 case LVM_GETITEMRECT:
9111 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9113 case LVM_GETITEMSPACING:
9114 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9116 case LVM_GETITEMSTATE:
9117 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9119 case LVM_GETITEMTEXTA:
9120 LISTVIEW_GetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9123 /* case LVM_GETITEMTEXTW: */
9125 case LVM_GETNEXTITEM:
9126 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9128 /* case LVM_GETNUMBEROFWORKAREAS: */
9131 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9133 case LVM_GETSELECTEDCOUNT:
9134 return LISTVIEW_GetSelectedCount(hwnd);
9136 case LVM_GETSELECTIONMARK:
9137 return LISTVIEW_GetSelectionMark(hwnd);
9139 case LVM_GETSTRINGWIDTHA:
9140 return LISTVIEW_GetStringWidthA (hwnd, (LPCSTR)lParam);
9142 /* case LVM_GETSTRINGWIDTHW: */
9143 /* case LVM_GETSUBITEMRECT: */
9145 case LVM_GETTEXTBKCOLOR:
9146 return LISTVIEW_GetTextBkColor(hwnd);
9148 case LVM_GETTEXTCOLOR:
9149 return LISTVIEW_GetTextColor(hwnd);
9151 /* case LVM_GETTOOLTIPS: */
9153 case LVM_GETTOPINDEX:
9154 return LISTVIEW_GetTopIndex(hwnd);
9156 /* case LVM_GETUNICODEFORMAT: */
9158 case LVM_GETVIEWRECT:
9159 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9161 /* case LVM_GETWORKAREAS: */
9164 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9166 case LVM_INSERTCOLUMNA:
9167 return LISTVIEW_InsertColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9169 case LVM_INSERTCOLUMNW:
9170 return LISTVIEW_InsertColumnW(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam);
9172 case LVM_INSERTITEMA:
9173 return LISTVIEW_InsertItemA(hwnd, (LPLVITEMA)lParam);
9175 case LVM_INSERTITEMW:
9176 return LISTVIEW_InsertItemW(hwnd, (LPLVITEMW)lParam);
9178 case LVM_REDRAWITEMS:
9179 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9181 /* case LVM_SCROLL: */
9182 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9184 case LVM_SETBKCOLOR:
9185 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9187 /* case LVM_SETBKIMAGE: */
9189 case LVM_SETCALLBACKMASK:
9190 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9192 case LVM_SETCOLUMNA:
9193 return LISTVIEW_SetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9195 case LVM_SETCOLUMNW:
9196 FIXME("Unimplemented msg LVM_SETCOLUMNW\n");
9199 case LVM_SETCOLUMNORDERARRAY:
9200 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9202 case LVM_SETCOLUMNWIDTH:
9203 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9205 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9206 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9208 /* case LVM_SETHOTCURSOR: */
9210 case LVM_SETHOTITEM:
9211 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9213 case LVM_SETHOVERTIME:
9214 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9216 /* case LVM_SETICONSPACING: */
9218 case LVM_SETIMAGELIST:
9219 return LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9222 return LISTVIEW_SetItemA(hwnd, (LPLVITEMA)lParam);
9224 /* case LVM_SETITEMW: */
9226 case LVM_SETITEMCOUNT:
9227 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9229 case LVM_SETITEMPOSITION:
9230 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9231 (INT)HIWORD(lParam));
9233 case LVM_SETITEMPOSITION32:
9234 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9235 ((POINT*)lParam)->y);
9237 case LVM_SETITEMSTATE:
9238 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9240 case LVM_SETITEMTEXTA:
9241 return LISTVIEW_SetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9243 /* case LVM_SETITEMTEXTW: */
9245 case LVM_SETSELECTIONMARK:
9246 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9248 case LVM_SETTEXTBKCOLOR:
9249 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9251 case LVM_SETTEXTCOLOR:
9252 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9254 /* case LVM_SETTOOLTIPS: */
9255 /* case LVM_SETUNICODEFORMAT: */
9256 /* case LVM_SETWORKAREAS: */
9259 return LISTVIEW_SortItems(hwnd, wParam, lParam);
9261 /* case LVM_SUBITEMHITTEST: */
9264 return LISTVIEW_Update(hwnd, (INT)wParam);
9267 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9270 return LISTVIEW_Command(hwnd, wParam, lParam);
9273 return LISTVIEW_Create(hwnd, wParam, lParam);
9276 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9279 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9282 return LISTVIEW_GetFont(hwnd);
9285 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
9286 (INT)HIWORD(wParam), (HWND)lParam);
9289 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9292 return LISTVIEW_KillFocus(hwnd);
9294 case WM_LBUTTONDBLCLK:
9295 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9298 case WM_LBUTTONDOWN:
9299 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9302 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9305 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9308 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9311 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9314 return LISTVIEW_NCDestroy(hwnd);
9317 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9319 case WM_NOTIFYFORMAT:
9320 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9323 return LISTVIEW_Paint(hwnd, (HDC)wParam);
9325 case WM_RBUTTONDBLCLK:
9326 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9329 case WM_RBUTTONDOWN:
9330 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9334 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9338 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9341 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9344 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9347 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9349 case WM_STYLECHANGED:
9350 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9352 /* case WM_TIMER: */
9355 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
9356 (INT)HIWORD(wParam), (HWND)lParam);
9359 if (wParam & (MK_SHIFT | MK_CONTROL))
9360 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
9361 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
9363 /* case WM_WININICHANGE: */
9366 if (uMsg >= WM_USER)
9368 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
9372 /* call default window procedure */
9373 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
9381 * Registers the window class.
9389 VOID LISTVIEW_Register(void)
9393 ZeroMemory(&wndClass, sizeof(WNDCLASSA));
9394 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9395 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9396 wndClass.cbClsExtra = 0;
9397 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9398 wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
9399 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9400 wndClass.lpszClassName = WC_LISTVIEWA;
9401 RegisterClassA(&wndClass);
9406 * Unregisters the window class.
9414 VOID LISTVIEW_Unregister(void)
9416 UnregisterClassA(WC_LISTVIEWA, (HINSTANCE)NULL);
9421 * Handle any WM_COMMAND messages
9427 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9429 switch (HIWORD(wParam))
9434 * Adjust the edit window size
9437 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
9438 HDC hdc = GetDC(infoPtr->hwndEdit);
9439 HFONT hFont, hOldFont = 0;
9444 len = GetWindowTextA(infoPtr->hwndEdit, buffer, 1023);
9445 GetWindowRect(infoPtr->hwndEdit, &rect);
9447 /* Select font to get the right dimension of the string */
9448 hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9451 hOldFont = SelectObject(hdc, hFont);
9454 if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
9456 TEXTMETRICA textMetric;
9458 /* Add Extra spacing for the next character */
9459 GetTextMetricsA(hdc, &textMetric);
9460 sz.cx += (textMetric.tmMaxCharWidth * 2);
9468 rect.bottom - rect.top,
9469 SWP_DRAWFRAME|SWP_NOMOVE);
9473 SelectObject(hdc, hOldFont);
9476 ReleaseDC(hwnd, hdc);
9482 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9491 * Subclassed edit control windproc function
9497 LRESULT CALLBACK EditLblWndProc(HWND hwnd, UINT uMsg,
9498 WPARAM wParam, LPARAM lParam)
9500 BOOL cancel = FALSE;
9501 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(GetParent(hwnd), 0);
9502 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9503 static BOOL bIgnoreKillFocus = FALSE;
9507 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9510 if(bIgnoreKillFocus)
9518 WNDPROC editProc = einfo->EditWndProc;
9519 SetWindowLongA(hwnd, GWL_WNDPROC, (LONG)editProc);
9520 COMCTL32_Free(einfo);
9521 infoPtr->pedititem = NULL;
9522 return CallWindowProcA(editProc, hwnd, uMsg, wParam, lParam);
9526 if (VK_ESCAPE == (INT)wParam)
9532 else if (VK_RETURN == (INT)wParam)
9536 return CallWindowProcA(einfo->EditWndProc, hwnd,
9537 uMsg, wParam, lParam);
9540 if (einfo->EditLblCb)
9542 char *buffer = NULL;
9547 int len = 1 + GetWindowTextLengthA(hwnd);
9551 if (NULL != (buffer = (char *)COMCTL32_Alloc(len*sizeof(char))))
9553 GetWindowTextA(hwnd, buffer, len);
9557 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9558 /* eg. Using a messagebox */
9559 bIgnoreKillFocus = TRUE;
9560 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9563 COMCTL32_Free(buffer);
9565 einfo->EditLblCb = NULL;
9566 bIgnoreKillFocus = FALSE;
9569 SendMessageA(hwnd, WM_CLOSE, 0, 0);
9576 * Creates a subclassed edit cotrol
9582 HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y,
9583 INT width, INT height, HWND parent, HINSTANCE hinst,
9584 EditlblCallback EditLblCb, DWORD param)
9590 TEXTMETRICA textMetric;
9591 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(parent, 0);
9593 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9596 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9597 hdc = GetDC(parent);
9599 /* Select the font to get appropriate metric dimensions */
9600 if(infoPtr->hFont != 0)
9602 hOldFont = SelectObject(hdc, infoPtr->hFont);
9605 /*Get String Lenght in pixels */
9606 GetTextExtentPoint32A(hdc, text, strlen(text), &sz);
9608 /*Add Extra spacing for the next character */
9609 GetTextMetricsA(hdc, &textMetric);
9610 sz.cx += (textMetric.tmMaxCharWidth * 2);
9612 if(infoPtr->hFont != 0)
9614 SelectObject(hdc, hOldFont);
9617 ReleaseDC(parent, hdc);
9618 if (!(hedit = CreateWindowA("Edit", text, style, x, y, sz.cx, height,
9619 parent, 0, hinst, 0)))
9621 COMCTL32_Free(infoPtr->pedititem);
9625 infoPtr->pedititem->param = param;
9626 infoPtr->pedititem->EditLblCb = EditLblCb;
9627 infoPtr->pedititem->EditWndProc = (WNDPROC)SetWindowLongA(hedit,
9628 GWL_WNDPROC, (LONG) EditLblWndProc);
9630 SendMessageA(hedit, WM_SETFONT, infoPtr->hFont, FALSE);