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 * shifts all selection indexs starting with the indesx specified
1483 * in the direction specified.
1486 * [I] HWND : window handle
1487 * [I] INT : item index
1488 * [I] INT : amount and direction of shift
1493 static VOID LISTVIEW_ShiftSelections(HWND hwnd, INT nItem, INT direction)
1495 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1496 LISTVIEW_SELECTION selection,*checkselection;
1499 TRACE("Shifting %iu, %i steps\n",nItem,direction);
1501 selection.upper = nItem;
1502 selection.lower = nItem;
1504 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1505 LISTVIEW_CompareSelectionRanges,
1506 0,DPAS_SORTED|DPAS_INSERTAFTER);
1508 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1510 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1511 if ((checkselection->lower >= nItem)&&
1512 (checkselection->lower + direction >= 0))
1513 checkselection->lower += direction;
1514 if ((checkselection->upper >= nItem)&&
1515 (checkselection->upper + direction >=0))
1516 checkselection->upper += direction;
1524 * Adds a block of selections.
1527 * [I] HWND : window handle
1528 * [I] INT : item index
1533 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1535 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1536 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1537 INT nLast = max(infoPtr->nSelectionMark, nItem);
1541 ZeroMemory(&item,sizeof(LVITEMA));
1542 item.stateMask = LVIS_SELECTED;
1543 item.state = LVIS_SELECTED;
1545 for (i = nFirst; i <= nLast; i++);
1547 LISTVIEW_SetItemState(hwnd,i,&item);
1550 LISTVIEW_SetItemFocus(hwnd, nItem);
1551 infoPtr->nSelectionMark = nItem;
1557 * Adds a single selection.
1560 * [I] HWND : window handle
1561 * [I] INT : item index
1566 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1568 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1571 ZeroMemory(&item,sizeof(LVITEMA));
1572 item.state = LVIS_SELECTED;
1573 item.stateMask = LVIS_SELECTED;
1575 LISTVIEW_SetItemState(hwnd,nItem,&item);
1577 LISTVIEW_SetItemFocus(hwnd, nItem);
1578 infoPtr->nSelectionMark = nItem;
1583 * Selects or unselects an item.
1586 * [I] HWND : window handle
1587 * [I] INT : item index
1593 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1595 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1599 ZeroMemory(&item,sizeof(LVITEMA));
1600 item.stateMask = LVIS_SELECTED;
1602 if (LISTVIEW_IsSelected(hwnd,nItem))
1605 LISTVIEW_SetItemState(hwnd,nItem,&item);
1610 item.state = LVIS_SELECTED;
1611 LISTVIEW_SetItemState(hwnd,nItem,&item);
1615 LISTVIEW_SetItemFocus(hwnd, nItem);
1616 infoPtr->nSelectionMark = nItem;
1623 * Selects items based on view coordinates.
1626 * [I] HWND : window handle
1627 * [I] RECT : selection rectangle
1632 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
1634 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1639 ZeroMemory(&item,sizeof(LVITEMA));
1640 item.stateMask = LVIS_SELECTED;
1642 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1644 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
1646 if (PtInRect(&rcSelRect, ptItem) != FALSE)
1648 item.state = LVIS_SELECTED;
1649 LISTVIEW_SetItemState(hwnd,i,&item);
1654 LISTVIEW_SetItemState(hwnd,i,&item);
1661 * Sets a single group selection.
1664 * [I] HWND : window handle
1665 * [I] INT : item index
1670 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
1672 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1673 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1676 ZeroMemory(&item,sizeof(LVITEMA));
1677 item.stateMask = LVIS_SELECTED;
1679 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
1682 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1683 INT nLast = max(infoPtr->nSelectionMark, nItem);
1685 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
1687 if ((i < nFirst) || (i > nLast))
1690 LISTVIEW_SetItemState(hwnd,i,&item);
1694 item.state = LVIS_SELECTED;
1695 LISTVIEW_SetItemState(hwnd,i,&item);
1704 LISTVIEW_GetItemPosition(hwnd, nItem, &ptItem);
1705 LISTVIEW_GetItemPosition(hwnd, infoPtr->nSelectionMark, &ptSelMark);
1706 rcSel.left = min(ptSelMark.x, ptItem.x);
1707 rcSel.top = min(ptSelMark.y, ptItem.y);
1708 rcSel.right = max(ptSelMark.x, ptItem.x) + infoPtr->nItemWidth;
1709 rcSel.bottom = max(ptSelMark.y, ptItem.y) + infoPtr->nItemHeight;
1710 LISTVIEW_SetSelectionRect(hwnd, rcSel);
1713 LISTVIEW_SetItemFocus(hwnd, nItem);
1718 * Manages the item focus.
1721 * [I] HWND : window handle
1722 * [I] INT : item index
1725 * TRUE : focused item changed
1726 * FALSE : focused item has NOT changed
1728 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
1730 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1731 BOOL bResult = FALSE;
1734 if (infoPtr->nFocusedItem != nItem)
1736 if (infoPtr->nFocusedItem >= 0)
1738 INT oldFocus = infoPtr->nFocusedItem;
1740 infoPtr->nFocusedItem = -1;
1741 ZeroMemory(&lvItem, sizeof(LVITEMA));
1742 lvItem.stateMask = LVIS_FOCUSED;
1743 ListView_SetItemState(hwnd, oldFocus, &lvItem);
1747 lvItem.state = LVIS_FOCUSED;
1748 lvItem.stateMask = LVIS_FOCUSED;
1749 ListView_SetItemState(hwnd, nItem, &lvItem);
1751 infoPtr->nFocusedItem = nItem;
1752 ListView_EnsureVisible(hwnd, nItem, FALSE);
1760 * Sets a single selection.
1763 * [I] HWND : window handle
1764 * [I] INT : item index
1769 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
1771 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1774 ZeroMemory(&lvItem, sizeof(LVITEMA));
1775 lvItem.stateMask = LVIS_FOCUSED;
1776 ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
1778 LISTVIEW_RemoveAllSelections(hwnd);
1780 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
1781 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
1782 ListView_SetItemState(hwnd, nItem, &lvItem);
1784 infoPtr->nFocusedItem = nItem;
1785 infoPtr->nSelectionMark = nItem;
1790 * Set selection(s) with keyboard.
1793 * [I] HWND : window handle
1794 * [I] INT : item index
1797 * SUCCESS : TRUE (needs to be repainted)
1798 * FAILURE : FALSE (nothing has changed)
1800 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
1802 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1803 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1804 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
1805 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
1806 BOOL bResult = FALSE;
1808 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
1810 if (lStyle & LVS_SINGLESEL)
1813 LISTVIEW_SetSelection(hwnd, nItem);
1814 ListView_EnsureVisible(hwnd, nItem, FALSE);
1821 LISTVIEW_SetGroupSelection(hwnd, nItem);
1825 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
1830 LISTVIEW_SetSelection(hwnd, nItem);
1831 ListView_EnsureVisible(hwnd, nItem, FALSE);
1841 * Called when the mouse is being actively tracked and has hovered for a specified
1845 * [I] HWND : window handle
1846 * [I] wParam : key indicator
1847 * [I] lParam : mouse position
1850 * 0 if the message was processed, non-zero if there was an error
1853 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
1854 * over the item for a certain period of time.
1857 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
1859 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1862 pt.x = (INT)LOWORD(lParam);
1863 pt.y = (INT)HIWORD(lParam);
1865 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
1866 /* select the item under the cursor */
1867 LISTVIEW_MouseSelection(hwnd, pt);
1875 * Called whenever WM_MOUSEMOVE is recieved.
1878 * [I] HWND : window handle
1879 * [I] wParam : key indicators
1880 * [I] lParam : cursor position
1883 * 0 if the message is processed, non-zero if there was an error
1885 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
1887 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1888 TRACKMOUSEEVENT trackinfo;
1890 /* see if we are supposed to be tracking mouse hovering */
1891 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
1892 /* fill in the trackinfo struct */
1893 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
1894 trackinfo.dwFlags = TME_QUERY;
1895 trackinfo.hwndTrack = hwnd;
1896 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
1898 /* see if we are already tracking this hwnd */
1899 _TrackMouseEvent(&trackinfo);
1901 if(!(trackinfo.dwFlags & TME_HOVER)) {
1902 trackinfo.dwFlags = TME_HOVER;
1904 /* call TRACKMOUSEEVENT so we recieve WM_MOUSEHOVER messages */
1905 _TrackMouseEvent(&trackinfo);
1914 * Selects an item based on coordinates.
1917 * [I] HWND : window handle
1918 * [I] POINT : mouse click ccordinates
1921 * SUCCESS : item index
1924 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
1926 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1928 INT i,topindex,bottomindex;
1929 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1930 UINT uView = lStyle & LVS_TYPEMASK;
1932 topindex = ListView_GetTopIndex(hwnd);
1933 if (uView == LVS_REPORT)
1935 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
1936 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
1940 bottomindex = GETITEMCOUNT(infoPtr);
1943 for (i = topindex; i < bottomindex; i++)
1945 rcItem.left = LVIR_SELECTBOUNDS;
1946 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
1948 if (PtInRect(&rcItem, pt) != FALSE)
1963 * [IO] HDPA : dynamic pointer array handle
1964 * [I] INT : column index (subitem index)
1970 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
1972 BOOL bResult = TRUE;
1976 for (i = 0; i < hdpaItems->nItemCount; i++)
1978 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
1979 if (hdpaSubItems != NULL)
1981 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
1993 * Removes a subitem at a given position.
1996 * [IO] HDPA : dynamic pointer array handle
1997 * [I] INT : subitem index
2003 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2005 LISTVIEW_SUBITEM *lpSubItem;
2008 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2010 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2011 if (lpSubItem != NULL)
2013 if (lpSubItem->iSubItem == nSubItem)
2016 if ((lpSubItem->pszText != NULL) &&
2017 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
2019 COMCTL32_Free(lpSubItem->pszText);
2023 COMCTL32_Free(lpSubItem);
2025 /* free dpa memory */
2026 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2031 else if (lpSubItem->iSubItem > nSubItem)
2043 * Compares the item information.
2046 * [I] LISTVIEW_ITEM *: destination item
2047 * [I] LPLVITEM : source item
2050 * SUCCCESS : TRUE (EQUAL)
2051 * FAILURE : FALSE (NOT EQUAL)
2053 static UINT LISTVIEW_GetItemChanges(LISTVIEW_ITEM *lpItem, LPLVITEMA lpLVItem)
2057 if ((lpItem != NULL) && (lpLVItem != NULL))
2059 if (lpLVItem->mask & LVIF_STATE)
2061 if ((lpItem->state & lpLVItem->stateMask) !=
2062 (lpLVItem->state & lpLVItem->stateMask))
2064 uChanged |= LVIF_STATE;
2068 if (lpLVItem->mask & LVIF_IMAGE)
2070 if (lpItem->iImage != lpLVItem->iImage)
2072 uChanged |= LVIF_IMAGE;
2076 if (lpLVItem->mask & LVIF_PARAM)
2078 if (lpItem->lParam != lpLVItem->lParam)
2080 uChanged |= LVIF_PARAM;
2084 if (lpLVItem->mask & LVIF_INDENT)
2086 if (lpItem->iIndent != lpLVItem->iIndent)
2088 uChanged |= LVIF_INDENT;
2092 if (lpLVItem->mask & LVIF_TEXT)
2094 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA)
2096 if (lpItem->pszText != LPSTR_TEXTCALLBACKA)
2098 uChanged |= LVIF_TEXT;
2103 if (lpItem->pszText == LPSTR_TEXTCALLBACKA)
2105 uChanged |= LVIF_TEXT;
2109 if (lpLVItem->pszText)
2111 if (lpItem->pszText)
2113 if (strcmp(lpLVItem->pszText, lpItem->pszText) != 0)
2115 uChanged |= LVIF_TEXT;
2120 uChanged |= LVIF_TEXT;
2125 if (lpItem->pszText)
2127 uChanged |= LVIF_TEXT;
2139 * Initializes item attributes.
2142 * [I] HWND : window handle
2143 * [O] LISTVIEW_ITEM *: destination item
2144 * [I] LPLVITEM : source item
2150 static BOOL LISTVIEW_InitItem(HWND hwnd, LISTVIEW_ITEM *lpItem,
2153 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2154 BOOL bResult = FALSE;
2156 if ((lpItem != NULL) && (lpLVItem != NULL))
2160 if (lpLVItem->mask & LVIF_STATE)
2162 lpItem->state &= ~lpLVItem->stateMask;
2163 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2166 if (lpLVItem->mask & LVIF_IMAGE)
2168 lpItem->iImage = lpLVItem->iImage;
2171 if (lpLVItem->mask & LVIF_PARAM)
2173 lpItem->lParam = lpLVItem->lParam;
2176 if (lpLVItem->mask & LVIF_INDENT)
2178 lpItem->iIndent = lpLVItem->iIndent;
2181 if (lpLVItem->mask & LVIF_TEXT)
2183 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA)
2185 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2190 if ((lpItem->pszText != NULL) &&
2191 (lpItem->pszText != LPSTR_TEXTCALLBACKA))
2193 COMCTL32_Free(lpItem->pszText);
2196 lpItem->pszText = LPSTR_TEXTCALLBACKA;
2200 if (lpItem->pszText == LPSTR_TEXTCALLBACKA)
2202 lpItem->pszText = NULL;
2205 bResult = Str_SetPtrA(&lpItem->pszText, lpLVItem->pszText);
2215 * Initializes subitem attributes.
2217 * NOTE: The documentation specifies that the operation fails if the user
2218 * tries to set the indent of a subitem.
2221 * [I] HWND : window handle
2222 * [O] LISTVIEW_SUBITEM *: destination subitem
2223 * [I] LPLVITEM : source subitem
2229 static BOOL LISTVIEW_InitSubItem(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2232 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2233 BOOL bResult = FALSE;
2235 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2237 if (!(lpLVItem->mask & LVIF_INDENT))
2241 lpSubItem->iSubItem = lpLVItem->iSubItem;
2243 if (lpLVItem->mask & LVIF_IMAGE)
2245 lpSubItem->iImage = lpLVItem->iImage;
2248 if (lpLVItem->mask & LVIF_TEXT)
2250 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA)
2252 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2257 if ((lpSubItem->pszText != NULL) &&
2258 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
2260 COMCTL32_Free(lpSubItem->pszText);
2263 lpSubItem->pszText = LPSTR_TEXTCALLBACKA;
2267 if (lpSubItem->pszText == LPSTR_TEXTCALLBACKA)
2269 lpSubItem->pszText = NULL;
2272 bResult = Str_SetPtrA(&lpSubItem->pszText, lpLVItem->pszText);
2283 * Adds a subitem at a given position (column index).
2286 * [I] HWND : window handle
2287 * [I] LPLVITEM : new subitem atttributes
2293 static BOOL LISTVIEW_AddSubItem(HWND hwnd, LPLVITEMA lpLVItem)
2295 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2296 LISTVIEW_SUBITEM *lpSubItem = NULL;
2297 BOOL bResult = FALSE;
2299 INT nPosition, nItem;
2300 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2302 if (lStyle & LVS_OWNERDATA)
2305 if (lpLVItem != NULL)
2307 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2308 if (hdpaSubItems != NULL)
2310 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2311 if (lpSubItem != NULL)
2313 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2314 if (LISTVIEW_InitSubItem(hwnd, lpSubItem, lpLVItem) != FALSE)
2316 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2317 lpSubItem->iSubItem);
2318 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2328 /* cleanup if unsuccessful */
2329 if ((bResult == FALSE) && (lpSubItem != NULL))
2331 COMCTL32_Free(lpSubItem);
2339 * Finds the dpa insert position (array index).
2342 * [I] HWND : window handle
2343 * [I] INT : subitem index
2349 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2351 LISTVIEW_SUBITEM *lpSubItem;
2354 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2356 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2357 if (lpSubItem != NULL)
2359 if (lpSubItem->iSubItem > nSubItem)
2366 return hdpaSubItems->nItemCount;
2371 * Retrieves a listview subitem at a given position (column index).
2374 * [I] HWND : window handle
2375 * [I] INT : subitem index
2381 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2383 LISTVIEW_SUBITEM *lpSubItem;
2386 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2388 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2389 if (lpSubItem != NULL)
2391 if (lpSubItem->iSubItem == nSubItem)
2395 else if (lpSubItem->iSubItem > nSubItem)
2407 * Sets item attributes.
2410 * [I] HWND : window handle
2411 * [I] LPLVITEM : new item atttributes
2417 static BOOL LISTVIEW_SetItem(HWND hwnd, LPLVITEMA lpLVItem)
2419 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2420 BOOL bResult = FALSE;
2422 LISTVIEW_ITEM *lpItem;
2424 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
2425 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2427 UINT uView = lStyle & LVS_TYPEMASK;
2431 if (lStyle & LVS_OWNERDATA)
2433 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2437 ZeroMemory(&itm,sizeof(LVITEMA));
2438 itm.mask = LVIF_STATE | LVIF_PARAM;
2439 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2440 itm.iItem = lpLVItem->iItem;
2442 ListView_GetItemA(hwnd,&itm);
2445 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2446 nmlv.hdr.hwndFrom = hwnd;
2447 nmlv.hdr.idFrom = lCtrlId;
2448 nmlv.hdr.code = LVN_ITEMCHANGING;
2449 nmlv.uNewState = lpLVItem->state;
2450 nmlv.uOldState = itm.state;
2451 nmlv.uChanged = LVIF_STATE;
2452 nmlv.lParam = itm.lParam;
2453 nmlv.iItem = lpLVItem->iItem;
2455 if ((itm.state & lpLVItem->stateMask) !=
2456 (lpLVItem->state & lpLVItem->stateMask))
2458 /* send LVN_ITEMCHANGING notification */
2459 if (!ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv))
2461 if (lpLVItem->stateMask & LVIS_FOCUSED)
2463 if (lpLVItem->state & LVIS_FOCUSED)
2464 infoPtr->nFocusedItem = lpLVItem->iItem;
2465 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2466 infoPtr->nFocusedItem = -1;
2468 if (lpLVItem->stateMask & LVIS_SELECTED)
2470 if (lpLVItem->state & LVIS_SELECTED)
2472 if (lStyle & LVS_SINGLESEL)
2474 LISTVIEW_RemoveAllSelections(hwnd);
2476 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2479 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2483 nmlv.hdr.code = LVN_ITEMCHANGED;
2485 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2487 rcItem.left = LVIR_BOUNDS;
2488 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2489 InvalidateRect(hwnd, &rcItem, TRUE);
2497 if (lpLVItem != NULL)
2499 if (lpLVItem->iSubItem == 0)
2501 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2502 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2504 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2507 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2508 nmlv.hdr.hwndFrom = hwnd;
2509 nmlv.hdr.idFrom = lCtrlId;
2510 nmlv.hdr.code = LVN_ITEMCHANGING;
2511 nmlv.lParam = lpItem->lParam;
2512 uChanged = LISTVIEW_GetItemChanges(lpItem, lpLVItem);
2515 if (uChanged & LVIF_STATE)
2517 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2518 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2520 if (nmlv.uNewState & LVIS_SELECTED)
2523 * This is redundant if called through SetSelection
2525 * however is required if the used directly calls SetItem
2526 * to set the selection.
2528 if (lStyle & LVS_SINGLESEL)
2530 LISTVIEW_RemoveAllSelections(hwnd);
2533 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2536 else if (lpLVItem->stateMask & LVIS_SELECTED)
2538 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2541 if (nmlv.uNewState & LVIS_FOCUSED)
2544 * This is a fun hoop to jump to try to catch if
2545 * the user is calling us directly to call focus or if
2546 * this function is being called as a result of a
2547 * SetItemFocus call.
2549 if (infoPtr->nFocusedItem >= 0)
2550 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2554 nmlv.uChanged = uChanged;
2555 nmlv.iItem = lpLVItem->iItem;
2556 nmlv.lParam = lpItem->lParam;
2557 /* send LVN_ITEMCHANGING notification */
2558 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2560 /* copy information */
2561 bResult = LISTVIEW_InitItem(hwnd, lpItem, lpLVItem);
2563 /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2564 based on the width of the items text */
2565 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2567 item_width = LISTVIEW_GetStringWidthA(hwnd, lpItem->pszText);
2569 if(item_width > infoPtr->nItemWidth)
2570 infoPtr->nItemWidth = item_width;
2573 /* send LVN_ITEMCHANGED notification */
2574 nmlv.hdr.code = LVN_ITEMCHANGED;
2575 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2584 rcItem.left = LVIR_BOUNDS;
2585 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2586 InvalidateRect(hwnd, &rcItem, TRUE);
2598 * Sets subitem attributes.
2601 * [I] HWND : window handle
2602 * [I] LPLVITEM : new subitem atttributes
2608 static BOOL LISTVIEW_SetSubItem(HWND hwnd, LPLVITEMA lpLVItem)
2610 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2611 BOOL bResult = FALSE;
2613 LISTVIEW_SUBITEM *lpSubItem;
2614 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2617 if (lStyle & LVS_OWNERDATA)
2620 if (lpLVItem != NULL)
2622 if (lpLVItem->iSubItem > 0)
2624 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2625 if (hdpaSubItems != NULL)
2627 /* set subitem only if column is present */
2628 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2630 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2631 if (lpSubItem != NULL)
2633 bResult = LISTVIEW_InitSubItem(hwnd, lpSubItem, lpLVItem);
2637 bResult = LISTVIEW_AddSubItem(hwnd, lpLVItem);
2640 rcItem.left = LVIR_BOUNDS;
2641 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2642 InvalidateRect(hwnd, &rcItem, FALSE);
2653 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2656 * [I] HWND : window handle
2661 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2663 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2664 UINT uView = lStyle & LVS_TYPEMASK;
2666 SCROLLINFO scrollInfo;
2668 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2669 scrollInfo.cbSize = sizeof(SCROLLINFO);
2670 scrollInfo.fMask = SIF_POS;
2672 if (uView == LVS_LIST)
2674 if (lStyle & WS_HSCROLL)
2676 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
2678 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2682 else if (uView == LVS_REPORT)
2684 if (lStyle & WS_VSCROLL)
2686 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
2688 nItem = scrollInfo.nPos;
2701 * [I] HWND : window handle
2702 * [I] HDC : device context handle
2703 * [I] INT : item index
2704 * [I] INT : subitem index
2705 * [I] RECT * : clipping rectangle
2710 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
2711 RECT rcItem, BOOL Selected)
2713 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2714 CHAR szDispText[DISP_TEXT_SIZE];
2716 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
2719 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
2722 /* get information needed for drawing the item */
2723 ZeroMemory(&lvItem, sizeof(LVITEMA));
2724 lvItem.mask = LVIF_TEXT;
2725 lvItem.iItem = nItem;
2726 lvItem.iSubItem = nSubItem;
2727 lvItem.cchTextMax = DISP_TEXT_SIZE;
2728 lvItem.pszText = szDispText;
2729 LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
2731 /* redraw the background of the item */
2733 if(infoPtr->nColumnCount == (nSubItem + 1))
2734 rcTemp.right = infoPtr->rcList.right;
2736 rcTemp.right+=WIDTH_PADDING;
2738 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
2740 /* set item colors */
2741 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
2743 if (infoPtr->bFocus)
2745 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
2746 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2750 SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
2751 SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
2756 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
2758 SetBkMode(hdc, TRANSPARENT);
2759 textoutOptions &= ~ETO_OPAQUE;
2763 SetBkMode(hdc, OPAQUE);
2764 SetBkColor(hdc, infoPtr->clrTextBk);
2767 SetTextColor(hdc, infoPtr->clrText);
2770 ExtTextOutA(hdc, rcItem.left, rcItem.top, textoutOptions,
2771 &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
2775 /* fill in the gap */
2777 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
2779 CopyRect(&rec,&rcItem);
2780 rec.left = rec.right;
2781 rec.right = rec.left+REPORT_MARGINX;
2782 ExtTextOutA(hdc, rec.left , rec.top, textoutOptions,
2783 &rec, NULL, 0, NULL);
2785 CopyRect(&rec,&rcItem);
2786 rec.right = rec.left;
2787 rec.left = rec.left - REPORT_MARGINX;
2788 ExtTextOutA(hdc, rec.left , rec.top, textoutOptions,
2789 &rec, NULL, 0, NULL);
2799 * [I] HWND : window handle
2800 * [I] HDC : device context handle
2801 * [I] INT : item index
2802 * [I] RECT * : clipping rectangle
2807 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
2809 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2810 CHAR szDispText[DISP_TEXT_SIZE];
2815 DWORD dwTextColor,dwTextX;
2816 BOOL bImage = FALSE;
2818 UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
2821 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
2824 /* get information needed for drawing the item */
2825 ZeroMemory(&lvItem, sizeof(LVITEMA));
2826 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
2827 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
2828 lvItem.iItem = nItem;
2829 lvItem.iSubItem = 0;
2830 lvItem.cchTextMax = DISP_TEXT_SIZE;
2831 lvItem.pszText = szDispText;
2832 LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
2834 /* redraw the background of the item */
2836 if(infoPtr->nColumnCount == (nItem + 1))
2837 rcTemp.right = infoPtr->rcList.right;
2839 rcTemp.right+=WIDTH_PADDING;
2841 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
2844 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
2846 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
2849 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
2853 if (infoPtr->himlState != NULL)
2855 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
2856 if (uStateImage > 0)
2858 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
2859 rcItem.top, ILD_NORMAL);
2862 rcItem.left += infoPtr->iconSize.cx;
2864 SuggestedFocus->left += infoPtr->iconSize.cx;
2869 if (infoPtr->himlSmall != NULL)
2871 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
2874 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
2875 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
2876 rcItem.top, ILD_SELECTED);
2878 else if (lvItem.iImage>=0)
2880 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
2881 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
2882 rcItem.top, ILD_NORMAL);
2885 rcItem.left += infoPtr->iconSize.cx;
2888 SuggestedFocus->left += infoPtr->iconSize.cx;
2892 /* Don't bother painting item being edited */
2893 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
2896 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
2898 /* set item colors */
2899 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
2900 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2901 /* set raster mode */
2902 nMixMode = SetROP2(hdc, R2_XORPEN);
2904 else if ((GetWindowLongA(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
2905 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
2907 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
2908 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
2909 /* set raster mode */
2910 nMixMode = SetROP2(hdc, R2_COPYPEN);
2914 /* set item colors */
2915 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
2917 dwBkColor = GetBkColor(hdc);
2918 iBkMode = SetBkMode(hdc, TRANSPARENT);
2919 textoutOptions &= ~ETO_OPAQUE;
2923 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
2924 iBkMode = SetBkMode(hdc, OPAQUE);
2927 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
2928 /* set raster mode */
2929 nMixMode = SetROP2(hdc, R2_COPYPEN);
2932 nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
2933 if (rcItem.left + nLabelWidth < rcItem.right)
2936 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
2938 rcItem.right += IMAGE_PADDING;
2942 dwTextX = rcItem.left + 1;
2944 dwTextX += IMAGE_PADDING;
2947 ExtTextOutA(hdc, dwTextX, rcItem.top, textoutOptions,
2948 &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
2950 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
2952 /* fill in the gap */
2954 CopyRect(&rec,&rcItem);
2955 rec.left = rec.right;
2956 rec.right = rec.left+REPORT_MARGINX;
2957 ExtTextOutA(hdc, rec.left , rec.top, textoutOptions,
2958 &rec, NULL, 0, NULL);
2962 CopyRect(SuggestedFocus,&rcItem);
2966 SetROP2(hdc, R2_COPYPEN);
2967 SetBkColor(hdc, dwBkColor);
2968 SetTextColor(hdc, dwTextColor);
2970 SetBkMode(hdc, iBkMode);
2976 * Draws an item when in large icon display mode.
2979 * [I] HWND : window handle
2980 * [I] HDC : device context handle
2981 * [I] LISTVIEW_ITEM * : item
2982 * [I] INT : item index
2983 * [I] RECT * : clipping rectangle
2988 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
2989 RECT *SuggestedFocus)
2991 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2992 CHAR szDispText[DISP_TEXT_SIZE];
2993 INT nDrawPosX = rcItem.left;
2994 INT nLabelWidth, rcWidth;
2997 UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
3000 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, \
3001 bottom=%d)\n", hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right,
3004 /* get information needed for drawing the item */
3005 ZeroMemory(&lvItem, sizeof(LVITEMA));
3006 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3007 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3008 lvItem.iItem = nItem;
3009 lvItem.iSubItem = 0;
3010 lvItem.cchTextMax = DISP_TEXT_SIZE;
3011 lvItem.pszText = szDispText;
3012 LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
3014 /* redraw the background of the item */
3016 if(infoPtr->nColumnCount == (nItem + 1))
3017 rcTemp.right = infoPtr->rcList.right;
3019 rcTemp.right+=WIDTH_PADDING;
3021 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3023 if (lvItem.state & LVIS_SELECTED)
3025 /* set item colors */
3026 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
3027 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
3028 /* set raster mode */
3029 SetROP2(hdc, R2_XORPEN);
3033 /* set item colors */
3034 if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3036 SetBkMode(hdc, TRANSPARENT);
3037 textoutOptions &= ~ETO_OPAQUE;
3041 SetBkMode(hdc, OPAQUE);
3042 SetBkColor(hdc, infoPtr->clrTextBk);
3045 SetTextColor(hdc, infoPtr->clrText);
3046 /* set raster mode */
3047 SetROP2(hdc, R2_COPYPEN);
3050 if (infoPtr->himlNormal != NULL)
3052 rcItem.top += ICON_TOP_PADDING;
3053 nDrawPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
3054 if ((lvItem.state & LVIS_SELECTED) && (lvItem.iImage>=0))
3056 ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX,
3057 rcItem.top, ILD_SELECTED);
3059 else if (lvItem.iImage>=0)
3061 ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX,
3062 rcItem.top, ILD_NORMAL);
3066 /* Don't bother painting item being edited */
3067 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED)
3070 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3071 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3072 nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
3073 GetTextMetricsA(hdc, &tm);
3075 /* append an ellipse ('...') if the caption won't fit in the rect */
3076 rcWidth = max(0, rcItem.right - rcItem.left);
3077 if (nLabelWidth > rcWidth)
3079 INT i, len, eos, nCharsFit;
3080 /* give or take a couple, how many average sized chars would fit? */
3081 nCharsFit = tm.tmAveCharWidth > 0 ? (rcWidth/tm.tmAveCharWidth)+2 : 0;
3082 /* place the ellipse accordingly, without overrunning the buffer */
3083 len = strlen(szDispText);
3084 eos = min((nCharsFit > 1 && nCharsFit < len) ? nCharsFit+3 : len+2,
3085 sizeof(szDispText)-1);
3087 nLabelWidth = ListView_GetStringWidthA(hwnd, szDispText);
3088 while ((nLabelWidth > rcWidth) && (eos > 3))
3090 for (i = 1; i < 4; i++)
3091 szDispText[eos-i] = '.';
3092 /* shift the ellipse one char to the left for each iteration */
3093 szDispText[eos--] = '\0';
3094 nLabelWidth = ListView_GetStringWidthA(hwnd, szDispText);
3098 InflateRect(&rcItem, 2*CAPTION_BORDER, 0);
3099 nDrawPosX = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
3102 rcItem.left += nDrawPosX / 2;
3103 rcItem.right = rcItem.left + nLabelWidth + 2*CAPTION_BORDER;
3108 rcItem.right = rcItem.left + infoPtr->iconSpacing.cx - 1;
3112 rcItem.bottom = rcItem.top + tm.tmHeight + HEIGHT_PADDING;
3114 ExtTextOutA(hdc, rcItem.left + CAPTION_BORDER, rcItem.top, textoutOptions,
3115 &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
3118 CopyRect(SuggestedFocus,&rcItem);
3123 * Draws listview items when in report display mode.
3126 * [I] HWND : window handle
3127 * [I] HDC : device context handle
3132 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3134 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3135 SCROLLINFO scrollInfo;
3136 INT nDrawPosY = infoPtr->rcList.top;
3138 RECT rcItem, rcTemp;
3143 DWORD cditemmode = CDRF_DODEFAULT;
3144 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3147 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3148 scrollInfo.cbSize = sizeof(SCROLLINFO);
3149 scrollInfo.fMask = SIF_POS;
3151 nItem = ListView_GetTopIndex(hwnd);
3153 /* add 1 for displaying a partial item at the bottom */
3154 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3155 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3157 /* send cache hint notification */
3158 if (GetWindowLongA(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3162 nmlv.hdr.hwndFrom = hwnd;
3163 nmlv.hdr.idFrom = GetWindowLongA(hwnd,GWL_ID);
3164 nmlv.hdr.code = LVN_ODCACHEHINT;
3168 SendMessageA(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3172 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3173 infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3174 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3176 /* clear the background of any part of the control that doesn't contain items */
3177 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3178 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3180 /* nothing to draw */
3181 if(GETITEMCOUNT(infoPtr) == 0)
3184 /* Get scroll bar info once before loop */
3185 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3186 scrollOffset = scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
3188 for (; nItem < nLast; nItem++)
3190 RECT SuggestedFocusRect;
3193 if (lStyle & LVS_OWNERDRAWFIXED)
3195 UINT uID = GetWindowLongA( hwnd, GWL_ID);
3200 TRACE("Owner Drawn\n");
3201 dis.CtlType = ODT_LISTVIEW;
3204 dis.itemAction = ODA_DRAWENTIRE;
3207 if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3208 if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS;
3210 dis.hwndItem = hwnd;
3213 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3215 dis.rcItem.left = -scrollOffset;
3216 dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3217 dis.rcItem.top = nDrawPosY;
3218 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3220 ZeroMemory(&item,sizeof(LVITEMA));
3222 item.mask = LVIF_PARAM;
3223 ListView_GetItemA(hwnd,&item);
3225 dis.itemData = item.lParam;
3227 if (SendMessageA(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3229 nDrawPosY += infoPtr->nItemHeight;
3238 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3239 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3241 ir.left += REPORT_MARGINX;
3242 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3244 ir.bottom = ir.top + infoPtr->nItemHeight;
3246 CopyRect(&SuggestedFocusRect,&ir);
3249 for (j = 0; j < nColumnCount; j++)
3251 if (cdmode & CDRF_NOTIFYITEMDRAW)
3252 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3254 if (cditemmode & CDRF_SKIPDEFAULT)
3257 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3259 rcItem.left += REPORT_MARGINX;
3260 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3261 rcItem.top = nDrawPosY;
3262 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3264 /* Offset the Scroll Bar Pos */
3265 rcItem.left -= scrollOffset;
3266 rcItem.right -= scrollOffset;
3270 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3271 &SuggestedFocusRect);
3275 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem,
3279 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3280 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3281 CDDS_ITEMPOSTPAINT);
3286 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3289 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3290 rop = SetROP2(hdc, R2_XORPEN);
3292 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3293 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3296 SetROP2(hdc, R2_COPYPEN);
3298 nDrawPosY += infoPtr->nItemHeight;
3304 * Retrieves the number of items that can fit vertically in the client area.
3307 * [I] HWND : window handle
3310 * Number of items per row.
3312 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3314 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3315 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3316 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3317 INT nCountPerRow = 1;
3321 if (uView == LVS_REPORT)
3327 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3328 if (nCountPerRow == 0)
3335 return nCountPerRow;
3340 * Retrieves the number of items that can fit horizontally in the client
3344 * [I] HWND : window handle
3347 * Number of items per column.
3349 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3351 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3352 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3353 INT nCountPerColumn = 1;
3355 if (nListHeight > 0)
3357 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3358 if (nCountPerColumn == 0)
3360 nCountPerColumn = 1;
3364 return nCountPerColumn;
3369 * Retrieves the number of columns needed to display all the items when in
3370 * list display mode.
3373 * [I] HWND : window handle
3376 * Number of columns.
3378 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3380 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3381 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3382 INT nColumnCount = 0;
3384 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3386 if (infoPtr->rcList.right % infoPtr->nItemWidth == 0)
3388 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3392 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth + 1;
3396 return nColumnCount;
3402 * Draws listview items when in list display mode.
3405 * [I] HWND : window handle
3406 * [I] HDC : device context handle
3411 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3413 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3414 RECT rcItem, FocusRect, rcTemp;
3418 INT nCountPerColumn;
3419 INT nItemWidth = infoPtr->nItemWidth;
3420 INT nItemHeight = infoPtr->nItemHeight;
3421 DWORD cditemmode = CDRF_DODEFAULT;
3423 /* get number of fully visible columns */
3424 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3425 infoPtr->nColumnCount = nColumnCount;
3426 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3427 nItem = ListView_GetTopIndex(hwnd);
3429 /* paint the background of the control that doesn't contain any items */
3430 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3431 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3433 /* nothing to draw, return here */
3434 if(GETITEMCOUNT(infoPtr) == 0)
3437 for (i = 0; i < nColumnCount; i++)
3439 for (j = 0; j < nCountPerColumn; j++, nItem++)
3441 if (nItem >= GETITEMCOUNT(infoPtr))
3444 if (cdmode & CDRF_NOTIFYITEMDRAW)
3445 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3447 if (cditemmode & CDRF_SKIPDEFAULT)
3450 rcItem.top = j * nItemHeight;
3451 rcItem.left = i * nItemWidth;
3452 rcItem.bottom = rcItem.top + nItemHeight;
3453 rcItem.right = rcItem.left + nItemWidth;
3454 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3458 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3459 Rectangle(hdc, FocusRect.left, FocusRect.top,
3460 FocusRect.right,FocusRect.bottom);
3462 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3463 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3464 CDDS_ITEMPOSTPAINT);
3472 * Draws listview items when in icon or small icon display mode.
3475 * [I] HWND : window handle
3476 * [I] HDC : device context handle
3481 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3483 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3486 RECT rcItem, SuggestedFocus, rcTemp;
3488 DWORD cditemmode = CDRF_DODEFAULT;
3490 infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3491 /* DrawItem from erasing the incorrect background area */
3493 /* paint the background of the control that doesn't contain any items */
3494 SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3495 LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3497 /* nothing to draw, return here */
3498 if(GETITEMCOUNT(infoPtr) == 0)
3501 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3502 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3504 if (cdmode & CDRF_NOTIFYITEMDRAW)
3505 cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3507 if (cditemmode & CDRF_SKIPDEFAULT)
3510 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3511 ptPosition.x += ptOrigin.x;
3512 ptPosition.y += ptOrigin.y;
3514 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3516 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3518 if (ptPosition.y < infoPtr->rcList.bottom)
3520 if (ptPosition.x < infoPtr->rcList.right)
3522 rcItem.top = ptPosition.y;
3523 rcItem.left = ptPosition.x;
3524 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3525 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3526 if (bSmall == FALSE)
3528 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3532 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3537 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3539 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3540 SuggestedFocus.right,SuggestedFocus.bottom);
3545 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3546 LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3547 CDDS_ITEMPOSTPAINT);
3553 * Draws listview items.
3556 * [I] HWND : window handle
3557 * [I] HDC : device context handle
3562 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3564 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3565 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3571 GetClientRect(hwnd, &rect);
3572 cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3574 if (cdmode == CDRF_SKIPDEFAULT) return;
3577 hOldFont = SelectObject(hdc, infoPtr->hFont);
3579 /* select the dotted pen (for drawing the focus box) */
3580 hPen = CreatePen(PS_ALTERNATE, 1, 0);
3581 hOldPen = SelectObject(hdc, hPen);
3583 /* select transparent brush (for drawing the focus box) */
3584 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3586 if (uView == LVS_LIST)
3588 LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3590 else if (uView == LVS_REPORT)
3592 LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3594 else if (uView == LVS_SMALLICON)
3596 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3598 else if (uView == LVS_ICON)
3600 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3603 /* unselect objects */
3604 SelectObject(hdc, hOldFont);
3605 SelectObject(hdc, hOldPen);
3610 if (cdmode & CDRF_NOTIFYPOSTPAINT)
3611 LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
3617 * Calculates the approximate width and height of a given number of items.
3620 * [I] HWND : window handle
3621 * [I] INT : number of items
3626 * Returns a DWORD. The width in the low word and the height in high word.
3628 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
3629 WORD wWidth, WORD wHeight)
3631 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3632 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3633 INT nItemCountPerColumn = 1;
3634 INT nColumnCount = 0;
3635 DWORD dwViewRect = 0;
3637 if (nItemCount == -1)
3639 nItemCount = GETITEMCOUNT(infoPtr);
3642 if (uView == LVS_LIST)
3644 if (wHeight == 0xFFFF)
3646 /* use current height */
3647 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3650 if (wHeight < infoPtr->nItemHeight)
3652 wHeight = infoPtr->nItemHeight;
3657 if (infoPtr->nItemHeight > 0)
3659 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3660 if (nItemCountPerColumn == 0)
3662 nItemCountPerColumn = 1;
3665 if (nItemCount % nItemCountPerColumn != 0)
3667 nColumnCount = nItemCount / nItemCountPerColumn;
3671 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3676 /* Microsoft padding magic */
3677 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3678 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3680 dwViewRect = MAKELONG(wWidth, wHeight);
3682 else if (uView == LVS_REPORT)
3686 else if (uView == LVS_SMALLICON)
3690 else if (uView == LVS_ICON)
3700 * Arranges listview items in icon display mode.
3703 * [I] HWND : window handle
3704 * [I] INT : alignment code
3710 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
3712 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3713 BOOL bResult = FALSE;
3715 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3728 case LVA_SNAPTOGRID:
3737 /* << LISTVIEW_CreateDragImage >> */
3742 * Removes all listview items and subitems.
3745 * [I] HWND : window handle
3751 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
3753 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3754 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
3755 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3756 UINT uView = lStyle & LVS_TYPEMASK;
3757 LISTVIEW_ITEM *lpItem;
3758 LISTVIEW_SUBITEM *lpSubItem;
3761 BOOL bResult = FALSE;
3766 TRACE("(hwnd=%x,)\n", hwnd);
3767 LISTVIEW_RemoveAllSelections(hwnd);
3769 if (lStyle & LVS_OWNERDATA)
3771 infoPtr->hdpaItems->nItemCount = 0;
3772 InvalidateRect(hwnd, NULL, TRUE);
3776 if (GETITEMCOUNT(infoPtr) > 0)
3778 /* initialize memory */
3779 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3781 /* send LVN_DELETEALLITEMS notification */
3782 nmlv.hdr.hwndFrom = hwnd;
3783 nmlv.hdr.idFrom = lCtrlId;
3784 nmlv.hdr.code = LVN_DELETEALLITEMS;
3787 /* verify if subsequent LVN_DELETEITEM notifications should be
3789 bSuppress = ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
3791 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3793 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3794 if (hdpaSubItems != NULL)
3796 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3798 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3799 if (lpSubItem != NULL)
3801 /* free subitem string */
3802 if ((lpSubItem->pszText != NULL) &&
3803 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
3805 COMCTL32_Free(lpSubItem->pszText);
3809 COMCTL32_Free(lpSubItem);
3813 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3816 if (bSuppress == FALSE)
3818 /* send LVN_DELETEITEM notification */
3819 nmlv.hdr.code = LVN_DELETEITEM;
3821 nmlv.lParam = lpItem->lParam;
3822 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
3825 /* free item string */
3826 if ((lpItem->pszText != NULL) &&
3827 (lpItem->pszText != LPSTR_TEXTCALLBACKA))
3829 COMCTL32_Free(lpItem->pszText);
3833 COMCTL32_Free(lpItem);
3836 DPA_Destroy(hdpaSubItems);
3840 /* reinitialize listview memory */
3841 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3843 /* align items (set position of each item) */
3844 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3846 if (lStyle & LVS_ALIGNLEFT)
3848 LISTVIEW_AlignLeft(hwnd);
3852 LISTVIEW_AlignTop(hwnd);
3856 LISTVIEW_UpdateScroll(hwnd);
3858 /* invalidate client area (optimization needed) */
3859 InvalidateRect(hwnd, NULL, TRUE);
3867 * Removes a column from the listview control.
3870 * [I] HWND : window handle
3871 * [I] INT : column index
3877 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
3879 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3880 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3881 UINT uOwnerData = GetWindowLongA(hwnd, GWL_STYLE) & LVS_OWNERDATA;
3882 BOOL bResult = FALSE;
3884 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
3887 bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
3889 /* Need to reset the item width when deleting a column */
3890 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
3892 /* reset scroll parameters */
3893 if (uView == LVS_REPORT)
3895 /* update scrollbar(s) */
3896 LISTVIEW_UpdateScroll(hwnd);
3898 /* refresh client area */
3899 InvalidateRect(hwnd, NULL, FALSE);
3908 * Removes an item from the listview control.
3911 * [I] HWND : window handle
3912 * [I] INT : item index
3918 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
3920 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3921 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3922 UINT uView = lStyle & LVS_TYPEMASK;
3923 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
3925 BOOL bResult = FALSE;
3927 LISTVIEW_ITEM *lpItem;
3928 LISTVIEW_SUBITEM *lpSubItem;
3932 TRACE("(hwnd=%x,nItem=%d)\n", hwnd, nItem);
3935 /* First, send LVN_DELETEITEM notification. */
3936 memset(&nmlv, 0, sizeof (NMLISTVIEW));
3937 nmlv.hdr.hwndFrom = hwnd;
3938 nmlv.hdr.idFrom = lCtrlId;
3939 nmlv.hdr.code = LVN_DELETEITEM;
3941 SendMessageA(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
3945 /* remove it from the selection range */
3946 ZeroMemory(&item,sizeof(LVITEMA));
3947 item.stateMask = LVIS_SELECTED;
3948 LISTVIEW_SetItemState(hwnd,nItem,&item);
3950 LISTVIEW_ShiftSelections(hwnd,nItem,-1);
3952 if (lStyle & LVS_OWNERDATA)
3954 infoPtr->hdpaItems->nItemCount --;
3955 InvalidateRect(hwnd, NULL, TRUE);
3959 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
3961 /* initialize memory */
3962 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3964 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
3965 if (hdpaSubItems != NULL)
3967 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3969 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3970 if (lpSubItem != NULL)
3972 /* free item string */
3973 if ((lpSubItem->pszText != NULL) &&
3974 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
3976 COMCTL32_Free(lpSubItem->pszText);
3980 COMCTL32_Free(lpSubItem);
3984 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3987 /* free item string */
3988 if ((lpItem->pszText != NULL) &&
3989 (lpItem->pszText != LPSTR_TEXTCALLBACKA))
3991 COMCTL32_Free(lpItem->pszText);
3995 COMCTL32_Free(lpItem);
3998 bResult = DPA_Destroy(hdpaSubItems);
4001 /* align items (set position of each item) */
4002 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4004 if (lStyle & LVS_ALIGNLEFT)
4006 LISTVIEW_AlignLeft(hwnd);
4010 LISTVIEW_AlignTop(hwnd);
4014 /* If this item had focus change focus to next or previous item */
4015 if (GETITEMCOUNT(infoPtr) > 0)
4017 int sItem = nItem < GETITEMCOUNT(infoPtr) ? nItem : nItem - 1;
4018 if (infoPtr->nFocusedItem == nItem)
4019 LISTVIEW_SetItemFocus(hwnd, sItem);
4022 infoPtr->nFocusedItem = -1;
4024 LISTVIEW_UpdateScroll(hwnd);
4026 /* refresh client area */
4027 InvalidateRect(hwnd, NULL, TRUE);
4036 * Return edit control handle of current edit label
4039 * [I] HWND : window handle
4045 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4047 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4048 return infoPtr->hwndEdit;
4054 * Callback implementation for editlabel control
4057 * [I] HWND : window handle
4058 * [I] LPSTR : modified text
4059 * [I] DWORD : item index
4066 static BOOL LISTVIEW_EndEditLabel(HWND hwnd, LPSTR pszText, DWORD nItem)
4068 NMLVDISPINFOA dispInfo;
4069 LISTVIEW_ITEM *lpItem;
4070 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
4071 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4072 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4074 BOOL bUpdateItemText;
4075 LISTVIEW_ITEM lvItemRef;
4078 ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
4080 if (!(lStyle & LVS_OWNERDATA))
4082 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4085 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4090 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4091 ZeroMemory(&item,sizeof(LVITEMA));
4094 item.mask = LVIF_PARAM | LVIF_STATE;
4095 ListView_GetItemA(hwnd,&item);
4096 lvItemRef.state = item.state;
4097 lvItemRef.iImage = item.iImage;
4098 lvItemRef.lParam = item.lParam;
4099 lpItem = &lvItemRef;
4102 dispInfo.hdr.hwndFrom = hwnd;
4103 dispInfo.hdr.idFrom = nCtrlId;
4104 dispInfo.hdr.code = LVN_ENDLABELEDITA;
4105 dispInfo.item.mask = 0;
4106 dispInfo.item.iItem = nItem;
4107 dispInfo.item.state = lpItem->state;
4108 dispInfo.item.stateMask = 0;
4109 dispInfo.item.pszText = pszText;
4110 dispInfo.item.cchTextMax = pszText ? strlen(pszText) : 0;
4111 dispInfo.item.iImage = lpItem->iImage;
4112 dispInfo.item.lParam = lpItem->lParam;
4113 infoPtr->hwndEdit = 0;
4115 bUpdateItemText = ListView_Notify(GetParent(hwnd), nCtrlId, &dispInfo);
4117 /* Do we need to update the Item Text */
4120 if ((lpItem->pszText != LPSTR_TEXTCALLBACKA)&&(!(lStyle & LVS_OWNERDATA)))
4122 Str_SetPtrA(&lpItem->pszText, pszText);
4131 * Begin in place editing of specified list view item
4134 * [I] HWND : window handle
4135 * [I] INT : item index
4142 static HWND LISTVIEW_EditLabelA(HWND hwnd, INT nItem)
4144 NMLVDISPINFOA dispInfo;
4146 LISTVIEW_ITEM *lpItem;
4148 HINSTANCE hinst = GetWindowLongA(hwnd, GWL_HINSTANCE);
4149 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
4150 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4152 CHAR szDispText[DISP_TEXT_SIZE];
4153 LVITEMA lvItem,item;
4154 LISTVIEW_ITEM lvItemRef;
4155 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4157 if (~GetWindowLongA(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4160 /* Is the EditBox still there, if so remove it */
4161 if(infoPtr->hwndEdit != 0)
4166 LISTVIEW_SetSelection(hwnd, nItem);
4167 LISTVIEW_SetItemFocus(hwnd, nItem);
4169 ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
4170 if (!(lStyle & LVS_OWNERDATA))
4172 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4175 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4180 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4181 ZeroMemory(&item,sizeof(LVITEMA));
4184 item.mask = LVIF_PARAM | LVIF_STATE;
4185 ListView_GetItemA(hwnd,&item);
4186 lvItemRef.iImage = item.iImage;
4187 lvItemRef.state = item.state;
4188 lvItemRef.lParam = item.lParam;
4189 lpItem = &lvItemRef;
4192 /* get information needed for drawing the item */
4193 ZeroMemory(&lvItem, sizeof(LVITEMA));
4194 lvItem.mask = LVIF_TEXT;
4195 lvItem.iItem = nItem;
4196 lvItem.iSubItem = 0;
4197 lvItem.cchTextMax = DISP_TEXT_SIZE;
4198 lvItem.pszText = szDispText;
4199 ListView_GetItemA(hwnd, &lvItem);
4201 dispInfo.hdr.hwndFrom = hwnd;
4202 dispInfo.hdr.idFrom = nCtrlId;
4203 dispInfo.hdr.code = LVN_BEGINLABELEDITA;
4204 dispInfo.item.mask = 0;
4205 dispInfo.item.iItem = nItem;
4206 dispInfo.item.state = lpItem->state;
4207 dispInfo.item.stateMask = 0;
4208 dispInfo.item.pszText = lvItem.pszText;
4209 dispInfo.item.cchTextMax = strlen(lvItem.pszText);
4210 dispInfo.item.iImage = lpItem->iImage;
4211 dispInfo.item.lParam = lpItem->lParam;
4213 if (ListView_LVNotify(GetParent(hwnd), nCtrlId, &dispInfo))
4216 rect.left = LVIR_LABEL;
4217 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4220 if (!(hedit = CreateEditLabel(szDispText , WS_VISIBLE,
4221 rect.left-2, rect.top-1, 0,
4222 rect.bottom - rect.top+2,
4223 hwnd, hinst, LISTVIEW_EndEditLabel, nItem)))
4226 infoPtr->hwndEdit = hedit;
4228 SendMessageA(hedit, EM_SETSEL, 0, -1);
4236 * Ensures the specified item is visible, scrolling into view if necessary.
4239 * [I] HWND : window handle
4240 * [I] INT : item index
4241 * [I] BOOL : partially or entirely visible
4247 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4249 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4250 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4251 INT nScrollPosHeight = 0;
4252 INT nScrollPosWidth = 0;
4253 SCROLLINFO scrollInfo;
4255 BOOL bRedraw = FALSE;
4257 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4258 scrollInfo.cbSize = sizeof(SCROLLINFO);
4259 scrollInfo.fMask = SIF_POS;
4261 /* ALWAYS bPartial == FALSE, FOR NOW! */
4263 rcItem.left = LVIR_BOUNDS;
4264 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4266 if (rcItem.left < infoPtr->rcList.left)
4268 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4272 if (uView == LVS_LIST)
4274 nScrollPosWidth = infoPtr->nItemWidth;
4275 rcItem.left += infoPtr->rcList.left;
4277 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4279 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4280 rcItem.left += infoPtr->rcList.left;
4283 /* When in LVS_REPORT view, the scroll position should
4285 if (nScrollPosWidth != 0)
4287 if (rcItem.left % nScrollPosWidth == 0)
4289 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4293 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4296 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4300 else if (rcItem.right > infoPtr->rcList.right)
4302 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4306 if (uView == LVS_LIST)
4308 rcItem.right -= infoPtr->rcList.right;
4309 nScrollPosWidth = infoPtr->nItemWidth;
4311 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4313 rcItem.right -= infoPtr->rcList.right;
4314 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4317 /* When in LVS_REPORT view, the scroll position should
4319 if (nScrollPosWidth != 0)
4321 if (rcItem.right % nScrollPosWidth == 0)
4323 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4327 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4330 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4335 if (rcItem.top < infoPtr->rcList.top)
4339 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4341 if (uView == LVS_REPORT)
4343 rcItem.top -= infoPtr->rcList.top;
4344 nScrollPosHeight = infoPtr->nItemHeight;
4346 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4348 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4349 rcItem.top += infoPtr->rcList.top;
4352 if (rcItem.top % nScrollPosHeight == 0)
4354 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4358 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4361 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4364 else if (rcItem.bottom > infoPtr->rcList.bottom)
4368 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4370 if (uView == LVS_REPORT)
4372 rcItem.bottom -= infoPtr->rcList.bottom;
4373 nScrollPosHeight = infoPtr->nItemHeight;
4375 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4377 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4378 rcItem.bottom -= infoPtr->rcList.bottom;
4381 if (rcItem.bottom % nScrollPosHeight == 0)
4383 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4387 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4390 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4396 InvalidateRect(hwnd,NULL,TRUE);
4402 * Retrieves the nearest item, given a position and a direction.
4405 * [I] HWND : window handle
4406 * [I] POINT : start position
4407 * [I] UINT : direction
4410 * Item index if successdful, -1 otherwise.
4412 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4414 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4415 LVHITTESTINFO lvHitTestInfo;
4419 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4421 ZeroMemory(&lvHitTestInfo, sizeof(LVHITTESTINFO));
4422 LISTVIEW_GetOrigin(hwnd, &lvHitTestInfo.pt);
4423 lvHitTestInfo.pt.x += pt.x;
4424 lvHitTestInfo.pt.y += pt.y;
4428 if (vkDirection == VK_DOWN)
4430 lvHitTestInfo.pt.y += infoPtr->nItemHeight;
4432 else if (vkDirection == VK_UP)
4434 lvHitTestInfo.pt.y -= infoPtr->nItemHeight;
4436 else if (vkDirection == VK_LEFT)
4438 lvHitTestInfo.pt.x -= infoPtr->nItemWidth;
4440 else if (vkDirection == VK_RIGHT)
4442 lvHitTestInfo.pt.x += infoPtr->nItemWidth;
4445 if (PtInRect(&rcView, lvHitTestInfo.pt) == FALSE)
4451 nItem = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
4455 while (nItem == -1);
4463 * Searches for an item with specific characteristics.
4466 * [I] HWND : window handle
4467 * [I] INT : base item index
4468 * [I] LPLVFINDINFO : item information to look for
4471 * SUCCESS : index of item
4474 static LRESULT LISTVIEW_FindItem(HWND hwnd, INT nStart,
4475 LPLVFINDINFO lpFindInfo)
4477 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4479 CHAR szDispText[DISP_TEXT_SIZE];
4483 INT nLast = GETITEMCOUNT(infoPtr);
4485 if ((nItem >= -1) && (lpFindInfo != NULL))
4487 ZeroMemory(&lvItem, sizeof(LVITEMA));
4489 if (lpFindInfo->flags & LVFI_PARAM)
4491 lvItem.mask |= LVIF_PARAM;
4494 if (lpFindInfo->flags & LVFI_STRING)
4496 lvItem.mask |= LVIF_TEXT;
4497 lvItem.pszText = szDispText;
4498 lvItem.cchTextMax = DISP_TEXT_SIZE;
4501 if (lpFindInfo->flags & LVFI_PARTIAL)
4503 lvItem.mask |= LVIF_TEXT;
4504 lvItem.pszText = szDispText;
4505 lvItem.cchTextMax = DISP_TEXT_SIZE;
4508 if (lpFindInfo->flags & LVFI_WRAP)
4513 if (lpFindInfo->flags & LVFI_NEARESTXY)
4515 ptItem.x = lpFindInfo->pt.x;
4516 ptItem.y = lpFindInfo->pt.y;
4521 while (nItem < nLast)
4523 if (lpFindInfo->flags & LVFI_NEARESTXY)
4525 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4526 lpFindInfo->vkDirection);
4529 /* get position of the new item index */
4530 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4541 lvItem.iItem = nItem;
4542 lvItem.iSubItem = 0;
4543 if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
4545 if (lvItem.mask & LVIF_TEXT)
4547 if (lpFindInfo->flags & LVFI_PARTIAL)
4549 if (strstr(lvItem.pszText, lpFindInfo->psz) == NULL)
4554 if (strcmp(lvItem.pszText, lpFindInfo->psz) != 0)
4559 if (lvItem.mask & LVIF_PARAM)
4561 if (lpFindInfo->lParam != lvItem.lParam)
4587 * Retrieves the background color of the listview control.
4590 * [I] HWND : window handle
4593 * COLORREF associated with the background.
4595 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4597 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4599 return infoPtr->clrBk;
4604 * Retrieves the background image of the listview control.
4607 * [I] HWND : window handle
4608 * [O] LPLVMKBIMAGE : background image attributes
4614 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4616 /* FIXME (listview, "empty stub!\n"); */
4622 * Retrieves the callback mask.
4625 * [I] HWND : window handle
4630 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4632 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4634 return infoPtr->uCallbackMask;
4639 * Retrieves column attributes.
4642 * [I] HWND : window handle
4643 * [I] INT : column index
4644 * [IO] LPLVCOLUMNA : column information
4650 static LRESULT LISTVIEW_GetColumnA(HWND hwnd, INT nItem, LPLVCOLUMNA lpColumn)
4652 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4654 BOOL bResult = FALSE;
4656 if (lpColumn != NULL)
4658 /* initialize memory */
4659 ZeroMemory(&hdi, sizeof(HDITEMA));
4661 if (lpColumn->mask & LVCF_FMT)
4663 hdi.mask |= HDI_FORMAT;
4666 if (lpColumn->mask & LVCF_WIDTH)
4668 hdi.mask |= HDI_WIDTH;
4671 if (lpColumn->mask & LVCF_TEXT)
4673 hdi.mask |= HDI_TEXT;
4674 hdi.cchTextMax = lpColumn->cchTextMax;
4675 hdi.pszText = lpColumn->pszText;
4678 if (lpColumn->mask & LVCF_IMAGE)
4680 hdi.mask |= HDI_IMAGE;
4683 if (lpColumn->mask & LVCF_ORDER)
4685 hdi.mask |= HDI_ORDER;
4688 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4689 if (bResult != FALSE)
4691 if (lpColumn->mask & LVCF_FMT)
4695 if (hdi.fmt & HDF_LEFT)
4697 lpColumn->fmt |= LVCFMT_LEFT;
4699 else if (hdi.fmt & HDF_RIGHT)
4701 lpColumn->fmt |= LVCFMT_RIGHT;
4703 else if (hdi.fmt & HDF_CENTER)
4705 lpColumn->fmt |= LVCFMT_CENTER;
4708 if (hdi.fmt & HDF_IMAGE)
4710 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4713 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4715 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4719 if (lpColumn->mask & LVCF_WIDTH)
4721 lpColumn->cx = hdi.cxy;
4724 if (lpColumn->mask & LVCF_IMAGE)
4726 lpColumn->iImage = hdi.iImage;
4729 if (lpColumn->mask & LVCF_ORDER)
4731 lpColumn->iOrder = hdi.iOrder;
4739 /* LISTVIEW_GetColumnW */
4742 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
4744 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); */
4751 for (i = 0; i < iCount; i++)
4759 * Retrieves the column width.
4762 * [I] HWND : window handle
4763 * [I] int : column index
4766 * SUCCESS : column width
4769 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
4771 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4772 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4773 INT nColumnWidth = 0;
4776 if (uView == LVS_LIST)
4778 nColumnWidth = infoPtr->nItemWidth;
4780 else if (uView == LVS_REPORT)
4782 /* get column width from header */
4783 ZeroMemory(&hdi, sizeof(HDITEMA));
4784 hdi.mask = HDI_WIDTH;
4785 if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
4787 nColumnWidth = hdi.cxy;
4791 return nColumnWidth;
4796 * In list or report display mode, retrieves the number of items that can fit
4797 * vertically in the visible area. In icon or small icon display mode,
4798 * retrieves the total number of visible items.
4801 * [I] HWND : window handle
4804 * Number of fully visible items.
4806 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
4808 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4809 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4812 if (uView == LVS_LIST)
4814 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4816 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
4817 LISTVIEW_GetCountPerColumn(hwnd);
4820 else if (uView == LVS_REPORT)
4822 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
4826 nItemCount = GETITEMCOUNT(infoPtr);
4832 /* LISTVIEW_GetEditControl */
4836 * Retrieves the extended listview style.
4839 * [I] HWND : window handle
4842 * SUCCESS : previous style
4845 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
4847 LISTVIEW_INFO *infoPtr;
4849 /* make sure we can get the listview info */
4850 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
4853 return (infoPtr->dwExStyle);
4858 * Retrieves the handle to the header control.
4861 * [I] HWND : window handle
4866 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
4868 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4870 return infoPtr->hwndHeader;
4873 /* LISTVIEW_GetHotCursor */
4877 * Returns the time that the mouse cursor must hover over an item
4878 * before it is selected.
4881 * [I] HWND : window handle
4884 * Returns the previously set hover time or (DWORD)-1 to indicate that the
4885 * hover time is set to the default hover time.
4887 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
4889 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4891 return infoPtr->dwHoverTime;
4896 * Retrieves an image list handle.
4899 * [I] HWND : window handle
4900 * [I] INT : image list identifier
4903 * SUCCESS : image list handle
4906 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
4908 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4909 HIMAGELIST himl = NULL;
4914 himl = infoPtr->himlNormal;
4917 himl = infoPtr->himlSmall;
4920 himl = infoPtr->himlState;
4924 return (LRESULT)himl;
4927 /* LISTVIEW_GetISearchString */
4931 * Retrieves item attributes.
4934 * [I] HWND : window handle
4935 * [IO] LPLVITEMA : item info
4936 * [I] internal : if true then we will use tricks that avoid copies
4937 * but are not compatible with the regular interface
4943 static LRESULT LISTVIEW_GetItemA(HWND hwnd, LPLVITEMA lpLVItem, BOOL internal)
4945 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4946 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
4947 NMLVDISPINFOA dispInfo;
4948 LISTVIEW_SUBITEM *lpSubItem;
4949 LISTVIEW_ITEM *lpItem;
4953 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4954 /* In the following:
4955 * lpLVItem describes the information requested by the user
4956 * lpItem/lpSubItem is what we have
4957 * dispInfo is a structure we use to request the missing
4958 * information from the application
4961 TRACE("(hwnd=%x, lpLVItem=%p)\n", hwnd, lpLVItem);
4963 if ((lpLVItem == NULL) ||
4964 (lpLVItem->iItem < 0) ||
4965 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr))
4969 if (lStyle & LVS_OWNERDATA)
4971 if (lpLVItem->mask & ~LVIF_STATE)
4973 dispInfo.hdr.hwndFrom = hwnd;
4974 dispInfo.hdr.idFrom = lCtrlId;
4975 dispInfo.hdr.code = LVN_GETDISPINFOA;
4976 memcpy(&dispInfo.item,lpLVItem,sizeof(LVITEMA));
4978 ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
4979 memcpy(lpLVItem,&dispInfo.item,sizeof(LVITEMA));
4982 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
4984 lpLVItem->state = 0;
4985 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4986 lpLVItem->state |= LVIS_FOCUSED;
4987 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
4988 lpLVItem->state |= LVIS_SELECTED;
4995 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4996 if (hdpaSubItems == NULL)
4999 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
5003 ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
5004 if (lpLVItem->iSubItem == 0)
5006 piImage=&lpItem->iImage;
5007 ppszText=&lpItem->pszText;
5008 if ((infoPtr->uCallbackMask != 0) && (lpLVItem->mask & LVIF_STATE))
5010 dispInfo.item.mask |= LVIF_STATE;
5011 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5016 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5017 if (lpSubItem != NULL)
5019 piImage=&lpSubItem->iImage;
5020 ppszText=&lpSubItem->pszText;
5029 if ((lpLVItem->mask & LVIF_IMAGE) &&
5030 ((piImage==NULL) || (*piImage == I_IMAGECALLBACK)))
5032 dispInfo.item.mask |= LVIF_IMAGE;
5035 if ((lpLVItem->mask & LVIF_TEXT) &&
5036 ((ppszText==NULL) || (*ppszText == LPSTR_TEXTCALLBACKA)))
5038 dispInfo.item.mask |= LVIF_TEXT;
5039 dispInfo.item.pszText = lpLVItem->pszText;
5040 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5043 if (dispInfo.item.mask != 0)
5045 /* We don't have all the requested info, query the application */
5046 dispInfo.hdr.hwndFrom = hwnd;
5047 dispInfo.hdr.idFrom = lCtrlId;
5048 dispInfo.hdr.code = LVN_GETDISPINFOA;
5049 dispInfo.item.iItem = lpLVItem->iItem;
5050 dispInfo.item.iSubItem = lpLVItem->iSubItem;
5051 dispInfo.item.lParam = lpItem->lParam;
5052 ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
5055 if (dispInfo.item.mask & LVIF_IMAGE)
5057 lpLVItem->iImage = dispInfo.item.iImage;
5059 else if (lpLVItem->mask & LVIF_IMAGE)
5061 lpLVItem->iImage = *piImage;
5064 if (dispInfo.item.mask & LVIF_PARAM)
5066 lpLVItem->lParam = dispInfo.item.lParam;
5068 else if (lpLVItem->mask & LVIF_PARAM)
5070 lpLVItem->lParam = lpItem->lParam;
5073 if (dispInfo.item.mask & LVIF_TEXT)
5075 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (ppszText != NULL))
5077 Str_SetPtrA(ppszText, dispInfo.item.pszText);
5079 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5080 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
5081 if (lpLVItem->pszText!=dispInfo.item.pszText) {
5082 lstrcpynA(lpLVItem->pszText, dispInfo.item.pszText, lpLVItem->cchTextMax);
5085 else if (lpLVItem->mask & LVIF_TEXT)
5089 lpLVItem->pszText=*ppszText;
5091 lstrcpynA(lpLVItem->pszText, *ppszText, lpLVItem->cchTextMax);
5095 if (lpLVItem->iSubItem == 0)
5097 if (dispInfo.item.mask & LVIF_STATE)
5099 lpLVItem->state = lpItem->state;
5100 lpLVItem->state &= ~dispInfo.item.stateMask;
5101 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5103 lpLVItem->state &= ~LVIS_SELECTED;
5104 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5105 (LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem)))
5106 lpLVItem->state |= LVIS_SELECTED;
5108 else if (lpLVItem->mask & LVIF_STATE)
5110 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5112 lpLVItem->state &= ~LVIS_SELECTED;
5113 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5114 (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem)))
5115 lpLVItem->state |= LVIS_SELECTED;
5118 if (lpLVItem->mask & LVIF_PARAM)
5120 lpLVItem->lParam = lpItem->lParam;
5123 if (lpLVItem->mask & LVIF_INDENT)
5125 lpLVItem->iIndent = lpItem->iIndent;
5132 /* LISTVIEW_GetItemW */
5133 /* LISTVIEW_GetHotCursor */
5137 * Retrieves the index of the hot item.
5140 * [I] HWND : window handle
5143 * SUCCESS : hot item index
5144 * FAILURE : -1 (no hot item)
5146 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5148 LISTVIEW_INFO *infoPtr;
5150 /* make sure we can get the listview info */
5151 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
5154 return (infoPtr->nHotItem);
5157 /* LISTVIEW_GetHoverTime */
5161 * Retrieves the number of items in the listview control.
5164 * [I] HWND : window handle
5169 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5171 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5173 return GETITEMCOUNT(infoPtr);
5178 * Retrieves the position (upper-left) of the listview control item.
5181 * [I] HWND : window handle
5182 * [I] INT : item index
5183 * [O] LPPOINT : coordinate information
5189 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem,
5190 LPPOINT lpptPosition)
5192 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5193 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5194 BOOL bResult = FALSE;
5196 LISTVIEW_ITEM *lpItem;
5197 INT nCountPerColumn;
5200 TRACE("(hwnd=%x,nItem=%d,lpptPosition=%p)\n", hwnd, nItem,
5203 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5204 (lpptPosition != NULL))
5206 if (uView == LVS_LIST)
5209 nItem = nItem - ListView_GetTopIndex(hwnd);
5210 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5213 nRow = nItem % nCountPerColumn;
5216 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
5217 lpptPosition->y = 0;
5221 lpptPosition->x = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5222 lpptPosition->y = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5227 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
5228 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
5231 else if (uView == LVS_REPORT)
5233 SCROLLINFO scrollInfo;
5235 lpptPosition->x = REPORT_MARGINX;
5236 lpptPosition->y = ((nItem - ListView_GetTopIndex(hwnd)) *
5237 infoPtr->nItemHeight) + infoPtr->rcList.top;
5239 /* Adjust position by scrollbar offset */
5240 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5241 scrollInfo.cbSize = sizeof(SCROLLINFO);
5242 scrollInfo.fMask = SIF_POS;
5243 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5244 lpptPosition->x -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5248 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5249 if (hdpaSubItems != NULL)
5251 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
5255 lpptPosition->x = lpItem->ptPosition.x;
5256 lpptPosition->y = lpItem->ptPosition.y;
5266 * Retrieves the bounding rectangle for a listview control item.
5269 * [I] HWND : window handle
5270 * [I] INT : item index
5271 * [IO] LPRECT : bounding rectangle coordinates
5272 * lprc->left specifies the portion of the item for which the bounding
5273 * rectangle will be retrieved.
5275 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5276 * including the icon and label.
5277 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5278 * LVIR_LABEL Returns the bounding rectangle of the item text.
5279 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5280 * rectangles, but excludes columns in report view.
5286 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5288 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5289 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5290 BOOL bResult = FALSE;
5301 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5303 if (uView & LVS_REPORT)
5305 ZeroMemory(&lvItem, sizeof(LVITEMA));
5306 lvItem.mask = LVIF_INDENT;
5307 lvItem.iItem = nItem;
5308 lvItem.iSubItem = 0;
5309 LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
5312 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5314 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5322 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5324 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) != FALSE)
5329 if (uView == LVS_ICON)
5331 if (infoPtr->himlNormal != NULL)
5333 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5336 lprc->left = ptItem.x + ptOrigin.x;
5337 lprc->top = ptItem.y + ptOrigin.y;
5338 lprc->right = lprc->left + infoPtr->iconSize.cx;
5339 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5340 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5344 else if (uView == LVS_SMALLICON)
5346 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5349 lprc->left = ptItem.x + ptOrigin.x;
5350 lprc->top = ptItem.y + ptOrigin.y;
5351 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5353 if (infoPtr->himlState != NULL)
5354 lprc->left += infoPtr->iconSize.cx;
5356 if (infoPtr->himlSmall != NULL)
5357 lprc->right = lprc->left + infoPtr->iconSize.cx;
5359 lprc->right = lprc->left;
5365 lprc->left = ptItem.x;
5366 if (uView & LVS_REPORT)
5367 lprc->left += nIndent;
5368 lprc->top = ptItem.y;
5369 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5371 if (infoPtr->himlState != NULL)
5373 lprc->left += infoPtr->iconSize.cx;
5376 if (infoPtr->himlSmall != NULL)
5378 lprc->right = lprc->left + infoPtr->iconSize.cx;
5382 lprc->right = lprc->left;
5388 if (uView == LVS_ICON)
5390 if (infoPtr->himlNormal != NULL)
5392 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5395 lprc->left = ptItem.x + ptOrigin.x;
5396 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5397 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5398 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5399 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5401 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5402 lprc->right = lprc->left + nLabelWidth;
5407 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5411 hOldFont = SelectObject(hdc, infoPtr->hFont);
5412 GetTextMetricsA(hdc, &tm);
5413 lprc->bottom = lprc->top + tm.tmHeight + HEIGHT_PADDING;
5414 SelectObject(hdc, hOldFont);
5415 ReleaseDC(hwnd, hdc);
5419 else if (uView == LVS_SMALLICON)
5421 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5424 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5425 lprc->top = ptItem.y + ptOrigin.y;
5426 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5428 if (infoPtr->himlState != NULL)
5430 lprc->left += infoPtr->iconSize.cx;
5433 if (infoPtr->himlSmall != NULL)
5435 lprc->left += infoPtr->iconSize.cx;
5438 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5439 nLabelWidth += TRAILING_PADDING;
5440 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5442 lprc->right = lprc->left + nLabelWidth;
5446 lprc->right = nLeftPos + infoPtr->nItemWidth;
5453 if (uView & LVS_REPORT)
5454 nLeftPos = lprc->left = ptItem.x + nIndent;
5456 nLeftPos = lprc->left = ptItem.x;
5457 lprc->top = ptItem.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 (infoPtr->himlSmall)
5473 nLabelWidth += IMAGE_PADDING;
5474 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5476 lprc->right = lprc->left + nLabelWidth;
5480 lprc->right = nLeftPos + infoPtr->nItemWidth;
5486 if (uView == LVS_ICON)
5488 if (infoPtr->himlNormal != NULL)
5490 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5493 lprc->left = ptItem.x + ptOrigin.x;
5494 lprc->top = ptItem.y + ptOrigin.y;
5495 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5496 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5500 else if (uView == LVS_SMALLICON)
5502 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5505 lprc->left = ptItem.x + ptOrigin.x;
5506 lprc->right = lprc->left;
5507 lprc->top = ptItem.y + ptOrigin.y;
5508 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5509 if (infoPtr->himlState != NULL)
5510 lprc->right += infoPtr->iconSize.cx;
5511 if (infoPtr->himlSmall != NULL)
5512 lprc->right += infoPtr->iconSize.cx;
5514 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5515 nLabelWidth += TRAILING_PADDING;
5516 if (infoPtr->himlSmall)
5517 nLabelWidth += IMAGE_PADDING;
5518 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5520 lprc->right += nLabelWidth;
5524 lprc->right = lprc->left + infoPtr->nItemWidth;
5531 lprc->left = ptItem.x;
5532 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5533 lprc->left += nIndent;
5534 lprc->right = lprc->left;
5535 lprc->top = ptItem.y;
5536 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5538 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5541 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5542 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5544 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5548 if (infoPtr->himlState != NULL)
5550 lprc->right += infoPtr->iconSize.cx;
5553 if (infoPtr->himlSmall != NULL)
5555 lprc->right += infoPtr->iconSize.cx;
5558 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5559 nLabelWidth += TRAILING_PADDING;
5560 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5562 lprc->right += nLabelWidth;
5566 lprc->right = lprc->left + infoPtr->nItemWidth;
5572 case LVIR_SELECTBOUNDS:
5573 if (uView == LVS_ICON)
5575 if (infoPtr->himlNormal != NULL)
5577 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5580 lprc->left = ptItem.x + ptOrigin.x;
5581 lprc->top = ptItem.y + ptOrigin.y;
5582 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5583 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5587 else if (uView == LVS_SMALLICON)
5589 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5592 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5593 lprc->top = ptItem.y + ptOrigin.y;
5594 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5596 if (infoPtr->himlState != NULL)
5598 lprc->left += infoPtr->iconSize.cx;
5601 lprc->right = lprc->left;
5603 if (infoPtr->himlSmall != NULL)
5605 lprc->right += infoPtr->iconSize.cx;
5608 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5609 nLabelWidth += TRAILING_PADDING;
5610 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5612 lprc->right += nLabelWidth;
5616 lprc->right = nLeftPos + infoPtr->nItemWidth;
5623 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5624 nLeftPos = lprc->left = ptItem.x + nIndent;
5626 nLeftPos = lprc->left = ptItem.x;
5627 lprc->top = ptItem.y;
5628 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5630 if (infoPtr->himlState != NULL)
5632 lprc->left += infoPtr->iconSize.cx;
5635 lprc->right = lprc->left;
5637 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5640 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5641 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5643 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5647 if (infoPtr->himlSmall != NULL)
5649 lprc->right += infoPtr->iconSize.cx;
5652 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5653 nLabelWidth += TRAILING_PADDING;
5654 if (infoPtr->himlSmall)
5655 nLabelWidth += IMAGE_PADDING;
5656 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5658 lprc->right += nLabelWidth;
5662 lprc->right = nLeftPos + infoPtr->nItemWidth;
5675 * Retrieves the width of a label.
5678 * [I] HWND : window handle
5681 * SUCCESS : string width (in pixels)
5684 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
5686 CHAR szDispText[DISP_TEXT_SIZE];
5687 INT nLabelWidth = 0;
5690 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
5692 ZeroMemory(&lvItem, sizeof(LVITEMA));
5693 lvItem.mask = LVIF_TEXT;
5694 lvItem.iItem = nItem;
5695 lvItem.cchTextMax = DISP_TEXT_SIZE;
5696 lvItem.pszText = szDispText;
5697 if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
5699 nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
5707 * Retrieves the spacing between listview control items.
5710 * [I] HWND : window handle
5711 * [I] BOOL : flag for small or large icon
5714 * Horizontal + vertical spacing
5716 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
5718 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5721 if (bSmall == FALSE)
5723 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5727 /* TODO: need to store width of smallicon item */
5728 lResult = MAKELONG(0, infoPtr->nItemHeight);
5736 * Retrieves the state of a listview control item.
5739 * [I] HWND : window handle
5740 * [I] INT : item index
5741 * [I] UINT : state mask
5744 * State specified by the mask.
5746 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
5748 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5752 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5754 ZeroMemory(&lvItem, sizeof(LVITEMA));
5755 lvItem.iItem = nItem;
5756 lvItem.stateMask = uMask;
5757 lvItem.mask = LVIF_STATE;
5758 if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
5760 uState = lvItem.state;
5769 * Retrieves the text of a listview control item or subitem.
5772 * [I] HWND : window handle
5773 * [I] INT : item index
5774 * [IO] LPLVITEMA : item information
5777 * SUCCESS : string length
5780 static LRESULT LISTVIEW_GetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
5782 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5785 if (lpLVItem != NULL)
5787 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5789 lpLVItem->mask = LVIF_TEXT;
5790 lpLVItem->iItem = nItem;
5791 if (LISTVIEW_GetItemA(hwnd, lpLVItem, FALSE) != FALSE)
5793 nLength = lstrlenA(lpLVItem->pszText);
5803 * Searches for an item based on properties + relationships.
5806 * [I] HWND : window handle
5807 * [I] INT : item index
5808 * [I] INT : relationship flag
5811 * SUCCESS : item index
5814 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
5816 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5817 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5819 LVFINDINFO lvFindInfo;
5820 INT nCountPerColumn;
5823 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
5825 ZeroMemory(&lvFindInfo, sizeof(LVFINDINFO));
5827 if (uFlags & LVNI_CUT)
5830 if (uFlags & LVNI_DROPHILITED)
5831 uMask |= LVIS_DROPHILITED;
5833 if (uFlags & LVNI_FOCUSED)
5834 uMask |= LVIS_FOCUSED;
5836 if (uFlags & LVNI_SELECTED)
5837 uMask |= LVIS_SELECTED;
5839 if (uFlags & LVNI_ABOVE)
5841 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5846 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5852 lvFindInfo.flags = LVFI_NEARESTXY;
5853 lvFindInfo.vkDirection = VK_UP;
5854 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5855 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5857 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5862 else if (uFlags & LVNI_BELOW)
5864 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5866 while (nItem < GETITEMCOUNT(infoPtr))
5869 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5875 lvFindInfo.flags = LVFI_NEARESTXY;
5876 lvFindInfo.vkDirection = VK_DOWN;
5877 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5878 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5880 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5885 else if (uFlags & LVNI_TOLEFT)
5887 if (uView == LVS_LIST)
5889 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5890 while (nItem - nCountPerColumn >= 0)
5892 nItem -= nCountPerColumn;
5893 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5897 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5899 lvFindInfo.flags = LVFI_NEARESTXY;
5900 lvFindInfo.vkDirection = VK_LEFT;
5901 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5902 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5904 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5909 else if (uFlags & LVNI_TORIGHT)
5911 if (uView == LVS_LIST)
5913 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5914 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
5916 nItem += nCountPerColumn;
5917 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5921 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5923 lvFindInfo.flags = LVFI_NEARESTXY;
5924 lvFindInfo.vkDirection = VK_RIGHT;
5925 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5926 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5928 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5937 /* search by index */
5938 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
5940 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
5949 /* LISTVIEW_GetNumberOfWorkAreas */
5953 * Retrieves the origin coordinates when in icon or small icon display mode.
5956 * [I] HWND : window handle
5957 * [O] LPPOINT : coordinate information
5963 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
5965 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
5966 UINT uView = lStyle & LVS_TYPEMASK;
5967 BOOL bResult = FALSE;
5969 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
5971 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5973 SCROLLINFO scrollInfo;
5974 ZeroMemory(lpptOrigin, sizeof(POINT));
5975 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5976 scrollInfo.cbSize = sizeof(SCROLLINFO);
5978 if (lStyle & WS_HSCROLL)
5980 scrollInfo.fMask = SIF_POS;
5981 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
5983 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5987 if (lStyle & WS_VSCROLL)
5989 scrollInfo.fMask = SIF_POS;
5990 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
5992 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6004 * Retrieves the number of items that are marked as selected.
6007 * [I] HWND : window handle
6010 * Number of items selected.
6012 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6015 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6016 INT nSelectedCount = 0;
6019 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6021 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6027 return nSelectedCount;
6032 * Retrieves item index that marks the start of a multiple selection.
6035 * [I] HWND : window handle
6038 * Index number or -1 if there is no selection mark.
6040 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6042 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6044 return infoPtr->nSelectionMark;
6049 * Retrieves the width of a string.
6052 * [I] HWND : window handle
6055 * SUCCESS : string width (in pixels)
6058 static LRESULT LISTVIEW_GetStringWidthA(HWND hwnd, LPCSTR lpszText)
6060 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6061 HFONT hFont, hOldFont;
6065 ZeroMemory(&stringSize, sizeof(SIZE));
6066 if (lpszText != NULL && lpszText != LPSTR_TEXTCALLBACKA)
6068 hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6070 hOldFont = SelectObject(hdc, hFont);
6071 GetTextExtentPointA(hdc, lpszText, lstrlenA(lpszText), &stringSize);
6072 SelectObject(hdc, hOldFont);
6073 ReleaseDC(hwnd, hdc);
6076 return stringSize.cx;
6081 * Retrieves the text backgound color.
6084 * [I] HWND : window handle
6087 * COLORREF associated with the the background.
6089 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6091 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6093 return infoPtr->clrTextBk;
6098 * Retrieves the text color.
6101 * [I] HWND : window handle
6104 * COLORREF associated with the text.
6106 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6108 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6110 return infoPtr->clrText;
6115 * Determines which section of the item was selected (if any).
6118 * [I] HWND : window handle
6119 * [IO] LPLVHITTESTINFO : hit test information
6120 * [I] subitem : fill out iSubItem.
6123 * SUCCESS : item index
6126 static INT LISTVIEW_HitTestItem(
6127 HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem
6129 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6131 INT i,topindex,bottomindex;
6132 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6133 UINT uView = lStyle & LVS_TYPEMASK;
6136 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6137 lpHitTestInfo->pt.y);
6139 topindex = ListView_GetTopIndex(hwnd);
6140 if (uView == LVS_REPORT)
6142 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6143 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6147 bottomindex = GETITEMCOUNT(infoPtr);
6150 for (i = topindex; i < bottomindex; i++)
6152 rcItem.left = LVIR_BOUNDS;
6153 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6155 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6157 rcItem.left = LVIR_ICON;
6158 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6160 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6162 lpHitTestInfo->flags = LVHT_ONITEMICON;
6163 lpHitTestInfo->iItem = i;
6164 if (subitem) lpHitTestInfo->iSubItem = 0;
6169 rcItem.left = LVIR_LABEL;
6170 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
6172 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
6174 lpHitTestInfo->flags = LVHT_ONITEMLABEL;
6175 lpHitTestInfo->iItem = i;
6176 if (subitem) lpHitTestInfo->iSubItem = 0;
6181 lpHitTestInfo->flags = LVHT_ONITEMSTATEICON;
6182 lpHitTestInfo->iItem = i;
6183 if (subitem) lpHitTestInfo->iSubItem = 0;
6189 lpHitTestInfo->flags = LVHT_NOWHERE;
6196 * Determines which listview item is located at the specified position.
6199 * [I] HWND : window handle
6200 * [IO} LPLVHITTESTINFO : hit test information
6203 * SUCCESS : item index
6206 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6208 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6211 lpHitTestInfo->flags = 0;
6213 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6215 lpHitTestInfo->flags = LVHT_TOLEFT;
6217 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6219 lpHitTestInfo->flags = LVHT_TORIGHT;
6221 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6223 lpHitTestInfo->flags |= LVHT_ABOVE;
6225 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6227 lpHitTestInfo->flags |= LVHT_BELOW;
6230 if (lpHitTestInfo->flags == 0)
6232 /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6233 * an app might pass only a structure with space up to iItem!
6234 * (MS Office 97 does that for instance in the file open dialog)
6236 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6244 * Inserts a new column.
6247 * [I] HWND : window handle
6248 * [I] INT : column index
6249 * [I] LPLVCOLUMNA : column information
6252 * SUCCESS : new column index
6255 static LRESULT LISTVIEW_InsertColumnA(HWND hwnd, INT nColumn,
6256 LPLVCOLUMNA lpColumn)
6258 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6260 INT nNewColumn = -1;
6262 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn,
6265 if (lpColumn != NULL)
6267 /* initialize memory */
6268 ZeroMemory(&hdi, sizeof(HDITEMA));
6270 if (lpColumn->mask & LVCF_FMT)
6272 /* format member is valid */
6273 hdi.mask |= HDI_FORMAT;
6275 /* set text alignment (leftmost column must be left-aligned) */
6278 hdi.fmt |= HDF_LEFT;
6282 if (lpColumn->fmt & LVCFMT_LEFT)
6284 hdi.fmt |= HDF_LEFT;
6286 else if (lpColumn->fmt & LVCFMT_RIGHT)
6288 hdi.fmt |= HDF_RIGHT;
6290 else if (lpColumn->fmt & LVCFMT_CENTER)
6292 hdi.fmt |= HDF_CENTER;
6296 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6298 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6302 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6307 if (lpColumn->fmt & LVCFMT_IMAGE)
6309 hdi.fmt |= HDF_IMAGE;
6310 hdi.iImage = I_IMAGECALLBACK;
6314 if (lpColumn->mask & LVCF_WIDTH)
6316 hdi.mask |= HDI_WIDTH;
6317 hdi.cxy = lpColumn->cx;
6320 if (lpColumn->mask & LVCF_TEXT)
6322 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6323 hdi.pszText = lpColumn->pszText;
6324 hdi.cchTextMax = ((lpColumn->pszText!=NULL) && (lpColumn->pszText!=LPSTR_TEXTCALLBACKA) ? strlen(lpColumn->pszText) : 0);
6325 hdi.fmt |= HDF_STRING;
6328 if (lpColumn->mask & LVCF_IMAGE)
6330 hdi.mask |= HDI_IMAGE;
6331 hdi.iImage = lpColumn->iImage;
6334 if (lpColumn->mask & LVCF_ORDER)
6336 hdi.mask |= HDI_ORDER;
6337 hdi.iOrder = lpColumn->iOrder;
6340 /* insert item in header control */
6341 nNewColumn = SendMessageA(infoPtr->hwndHeader, HDM_INSERTITEMA,
6342 (WPARAM)nColumn, (LPARAM)&hdi);
6344 /* Need to reset the item width when inserting a new column */
6345 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6347 LISTVIEW_UpdateScroll(hwnd);
6348 InvalidateRect(hwnd, NULL, FALSE);
6354 static LRESULT LISTVIEW_InsertColumnW(HWND hwnd, INT nColumn,
6355 LPLVCOLUMNW lpColumn)
6360 memcpy(&lvca,lpColumn,sizeof(lvca));
6361 if (lpColumn->mask & LVCF_TEXT) {
6362 if (lpColumn->pszText == LPSTR_TEXTCALLBACKW)
6363 lvca.pszText = LPSTR_TEXTCALLBACKA;
6365 lvca.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpColumn->pszText);
6367 lres = LISTVIEW_InsertColumnA(hwnd,nColumn,&lvca);
6368 if (lpColumn->mask & LVCF_TEXT) {
6369 if (lpColumn->pszText != LPSTR_TEXTCALLBACKW)
6370 HeapFree(GetProcessHeap(),0,lvca.pszText);
6375 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6376 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6377 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6378 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6379 their own sort proc. when sending LVM_SORTITEMS.
6382 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6384 LVS_SORTXXX must be specified,
6385 LVS_OWNERDRAW is not set,
6386 <item>.pszText is not LPSTR_TEXTCALLBACK.
6388 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6389 are sorted based on item text..."
6391 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6393 HDPA hdpa_first = (HDPA) first;
6394 HDPA hdpa_second = (HDPA) second;
6395 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
6396 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
6397 LONG lStyle = GetWindowLongA((HWND) lParam, GWL_STYLE);
6398 INT cmpv = lstrcmpA( lv_first->pszText, lv_second->pszText );
6399 /* if we're sorting descending, negate the return value */
6400 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6405 * Inserts a new item in the listview control.
6408 * [I] HWND : window handle
6409 * [I] LPLVITEMA : item information
6412 * SUCCESS : new item index
6415 static LRESULT LISTVIEW_InsertItemA(HWND hwnd, LPLVITEMA lpLVItem)
6417 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6418 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6419 UINT uView = lStyle & LVS_TYPEMASK;
6420 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
6425 LISTVIEW_ITEM *lpItem = NULL;
6427 TRACE("(hwnd=%x,lpLVItem=%p)\n", hwnd, lpLVItem);
6429 if (lStyle & LVS_OWNERDATA)
6431 nItem = infoPtr->hdpaItems->nItemCount;
6432 infoPtr->hdpaItems->nItemCount ++;
6436 if (lpLVItem != NULL)
6438 /* make sure it's not a subitem; cannot insert a subitem */
6439 if (lpLVItem->iSubItem == 0)
6441 lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM));
6444 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6445 if (LISTVIEW_InitItem(hwnd, lpItem, lpLVItem) != FALSE)
6447 /* insert item in listview control data structure */
6448 hdpaSubItems = DPA_Create(8);
6449 if (hdpaSubItems != NULL)
6451 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
6454 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6455 && !(lStyle & LVS_OWNERDRAWFIXED)
6456 && (LPSTR_TEXTCALLBACKA != lpLVItem->pszText) )
6458 /* Insert the item in the proper sort order based on the pszText
6459 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6460 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6461 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6462 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6463 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6467 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6472 LISTVIEW_ShiftSelections(hwnd,nItem,1);
6474 /* manage item focus */
6475 if (lpLVItem->mask & LVIF_STATE)
6477 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6478 if (lpLVItem->stateMask & LVIS_SELECTED)
6480 LISTVIEW_SetSelection(hwnd, nItem);
6482 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6484 LISTVIEW_SetItemFocus(hwnd, nItem);
6488 /* send LVN_INSERTITEM notification */
6489 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6490 nmlv.hdr.hwndFrom = hwnd;
6491 nmlv.hdr.idFrom = lCtrlId;
6492 nmlv.hdr.code = LVN_INSERTITEM;
6494 nmlv.lParam = lpItem->lParam;;
6495 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
6497 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6499 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6500 if (nItemWidth > infoPtr->nItemWidth)
6502 infoPtr->nItemWidth = nItemWidth;
6506 /* align items (set position of each item) */
6507 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6509 if (lStyle & LVS_ALIGNLEFT)
6511 LISTVIEW_AlignLeft(hwnd);
6515 LISTVIEW_AlignTop(hwnd);
6519 LISTVIEW_UpdateScroll(hwnd);
6520 /* refresh client area */
6521 InvalidateRect(hwnd, NULL, FALSE);
6530 /* free memory if unsuccessful */
6531 if ((nItem == -1) && (lpItem != NULL))
6533 COMCTL32_Free(lpItem);
6539 static LRESULT LISTVIEW_InsertItemW(HWND hwnd, LPLVITEMW lpLVItem) {
6543 memcpy(&lvia,lpLVItem,sizeof(LVITEMA));
6544 if (lvia.mask & LVIF_TEXT) {
6545 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
6546 lvia.pszText = LPSTR_TEXTCALLBACKA;
6548 lvia.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpLVItem->pszText);
6550 lres = LISTVIEW_InsertItemA(hwnd, &lvia);
6551 if (lvia.mask & LVIF_TEXT) {
6552 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6553 HeapFree(GetProcessHeap(),0,lvia.pszText);
6558 /* LISTVIEW_InsertItemW */
6562 * Redraws a range of items.
6565 * [I] HWND : window handle
6566 * [I] INT : first item
6567 * [I] INT : last item
6573 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6575 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6576 BOOL bResult = FALSE;
6579 if (nFirst <= nLast)
6581 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6583 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6586 for (i = nFirst; i <= nLast; i++)
6588 rcItem.left = LVIR_BOUNDS;
6589 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
6590 InvalidateRect(hwnd, &rcItem, TRUE);
6599 /* LISTVIEW_Scroll */
6603 * Sets the background color.
6606 * [I] HWND : window handle
6607 * [I] COLORREF : background color
6613 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6615 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6617 infoPtr->clrBk = clrBk;
6618 InvalidateRect(hwnd, NULL, TRUE);
6623 /* LISTVIEW_SetBkImage */
6627 * Sets the callback mask. This mask will be used when the parent
6628 * window stores state information (some or all).
6631 * [I] HWND : window handle
6632 * [I] UINT : state mask
6638 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
6640 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6642 infoPtr->uCallbackMask = uMask;
6649 * Sets the attributes of a header item.
6652 * [I] HWND : window handle
6653 * [I] INT : column index
6654 * [I] LPLVCOLUMNA : column attributes
6660 static LRESULT LISTVIEW_SetColumnA(HWND hwnd, INT nColumn,
6661 LPLVCOLUMNA lpColumn)
6663 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6664 BOOL bResult = FALSE;
6665 HDITEMA hdi, hdiget;
6667 if ((lpColumn != NULL) && (nColumn >= 0) &&
6668 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6670 /* initialize memory */
6671 ZeroMemory(&hdi, sizeof(HDITEMA));
6673 if (lpColumn->mask & LVCF_FMT)
6675 /* format member is valid */
6676 hdi.mask |= HDI_FORMAT;
6678 /* get current format first */
6679 hdiget.mask = HDI_FORMAT;
6680 if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdiget))
6681 /* preserve HDF_STRING if present */
6682 hdi.fmt = hdiget.fmt & HDF_STRING;
6684 /* set text alignment (leftmost column must be left-aligned) */
6687 hdi.fmt |= HDF_LEFT;
6691 if (lpColumn->fmt & LVCFMT_LEFT)
6693 hdi.fmt |= HDF_LEFT;
6695 else if (lpColumn->fmt & LVCFMT_RIGHT)
6697 hdi.fmt |= HDF_RIGHT;
6699 else if (lpColumn->fmt & LVCFMT_CENTER)
6701 hdi.fmt |= HDF_CENTER;
6705 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6707 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6710 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6712 hdi.fmt |= HDF_IMAGE;
6715 if (lpColumn->fmt & LVCFMT_IMAGE)
6717 hdi.fmt |= HDF_IMAGE;
6718 hdi.iImage = I_IMAGECALLBACK;
6722 if (lpColumn->mask & LVCF_WIDTH)
6724 hdi.mask |= HDI_WIDTH;
6725 hdi.cxy = lpColumn->cx;
6728 if (lpColumn->mask & LVCF_TEXT)
6730 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6731 hdi.pszText = lpColumn->pszText;
6732 hdi.cchTextMax = ((lpColumn->pszText!=NULL) && (lpColumn->pszText!=LPSTR_TEXTCALLBACKA) ? strlen(lpColumn->pszText) : 0);
6733 hdi.fmt |= HDF_STRING;
6736 if (lpColumn->mask & LVCF_IMAGE)
6738 hdi.mask |= HDI_IMAGE;
6739 hdi.iImage = lpColumn->iImage;
6742 if (lpColumn->mask & LVCF_ORDER)
6744 hdi.mask |= HDI_ORDER;
6745 hdi.iOrder = lpColumn->iOrder;
6748 /* set header item attributes */
6749 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6755 /* LISTVIEW_SetColumnW */
6759 * Sets the column order array
6762 * [I] HWND : window handle
6763 * [I] INT : number of elements in column order array
6764 * [I] INT : pointer to column order array
6770 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
6772 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); */
6774 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6785 * Sets the width of a column
6788 * [I] HWND : window handle
6789 * [I] INT : column index
6790 * [I] INT : column width
6796 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
6798 LISTVIEW_INFO *infoPtr;
6801 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6802 UINT uView = lStyle & LVS_TYPEMASK;
6807 CHAR text_buffer[DISP_TEXT_SIZE];
6808 INT header_item_count;
6813 /* make sure we can get the listview info */
6814 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6817 if (!infoPtr->hwndHeader) /* make sure we have a header */
6820 /* set column width only if in report or list mode */
6821 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6824 /* take care of invalid cx values */
6825 if((uView == LVS_REPORT) && (cx < -2))
6826 cx = LVSCW_AUTOSIZE;
6827 else if (uView == LVS_LIST && (cx < 1))
6830 /* resize all columns if in LVS_LIST mode */
6831 if(uView == LVS_LIST) {
6832 infoPtr->nItemWidth = cx;
6833 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6837 /* autosize based on listview items width */
6838 if(cx == LVSCW_AUTOSIZE)
6840 /* set the width of the header to the width of the widest item */
6841 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6843 if(cx < LISTVIEW_GetLabelWidth(hwnd, item_index))
6844 cx = LISTVIEW_GetLabelWidth(hwnd, item_index);
6846 } /* autosize based on listview header width */
6847 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6849 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6851 /* if iCol is the last column make it fill the remainder of the controls width */
6852 if(iCol == (header_item_count - 1)) {
6853 /* get the width of every item except the current one */
6854 hdi.mask = HDI_WIDTH;
6857 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6858 Header_GetItemA(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6862 /* retrieve the layout of the header */
6863 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6865 cx = (rcHeader.right - rcHeader.left) - cx;
6869 /* retrieve header font */
6870 header_font = SendMessageA(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6872 /* retrieve header text */
6873 hdi.mask = HDI_TEXT;
6874 hdi.cchTextMax = sizeof(text_buffer);
6875 hdi.pszText = text_buffer;
6877 Header_GetItemA(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6879 /* determine the width of the text in the header */
6881 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6883 GetTextExtentPoint32A(hdc, text_buffer, strlen(text_buffer), &size);
6885 SelectObject(hdc, old_font); /* restore the old font */
6886 ReleaseDC(hwnd, hdc);
6888 /* set the width of this column to the width of the text */
6893 /* call header to update the column change */
6894 hdi.mask = HDI_WIDTH;
6897 lret = Header_SetItemA(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6899 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6906 * Sets the extended listview style.
6909 * [I] HWND : window handle
6914 * SUCCESS : previous style
6917 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
6919 LISTVIEW_INFO *infoPtr;
6922 /* make sure we can get the listview info */
6923 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6926 /* store previous style */
6927 dwOldStyle = infoPtr->dwExStyle;
6931 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6933 infoPtr->dwExStyle = dwStyle;
6935 return (dwOldStyle);
6938 /* LISTVIEW_SetHotCursor */
6942 * Sets the hot item index.
6945 * [I] HWND : window handle
6949 * SUCCESS : previous hot item index
6950 * FAILURE : -1 (no hot item)
6952 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
6954 LISTVIEW_INFO *infoPtr;
6957 /* make sure we can get the listview info */
6958 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6961 /* store previous index */
6962 iOldIndex = infoPtr->nHotItem;
6965 infoPtr->nHotItem = iIndex;
6972 * Sets the amount of time the cursor must hover over an item before it is selected.
6975 * [I] HWND : window handle
6976 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6979 * Returns the previous hover time
6981 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
6983 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6984 DWORD oldHoverTime = infoPtr->dwHoverTime;
6986 infoPtr->dwHoverTime = dwHoverTime;
6988 return oldHoverTime;
6991 /* LISTVIEW_SetIconSpacing */
6998 * [I] HWND : window handle
6999 * [I] INT : image list type
7000 * [I] HIMAGELIST : image list handle
7003 * SUCCESS : old image list
7006 static LRESULT LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7008 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7009 HIMAGELIST himlOld = 0;
7015 himlOld = infoPtr->himlNormal;
7016 infoPtr->himlNormal = himl;
7020 himlOld = infoPtr->himlSmall;
7021 infoPtr->himlSmall = himl;
7025 himlOld = infoPtr->himlState;
7026 infoPtr->himlState = himl;
7027 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7031 oldHeight = infoPtr->nItemHeight;
7032 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7033 if (infoPtr->nItemHeight != oldHeight)
7034 LISTVIEW_UpdateScroll(hwnd);
7036 return (LRESULT)himlOld;
7042 * Sets the attributes of an item.
7045 * [I] HWND : window handle
7046 * [I] LPLVITEM : item information
7052 static LRESULT LISTVIEW_SetItemA(HWND hwnd, LPLVITEMA lpLVItem)
7054 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7055 BOOL bResult = FALSE;
7057 if (lpLVItem != NULL)
7059 if ((lpLVItem->iItem >= 0) && (lpLVItem->iItem < GETITEMCOUNT(infoPtr)))
7061 if (lpLVItem->iSubItem == 0)
7063 bResult = LISTVIEW_SetItem(hwnd, lpLVItem);
7067 bResult = LISTVIEW_SetSubItem(hwnd, lpLVItem);
7076 /* LISTVIEW_SetItemW */
7080 * Preallocates memory (does *not* set the actual count of items !)
7083 * [I] HWND : window handle
7084 * [I] INT : item count (projected number of items to allocate)
7085 * [I] DWORD : update flags
7091 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7093 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7095 if (GetWindowLongA(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7097 int precount,topvisible;
7098 TRACE("LVS_OWNERDATA is set!\n");
7101 * Internally remove all the selections.
7105 LISTVIEW_SELECTION *selection;
7106 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7108 LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7111 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7113 precount = infoPtr->hdpaItems->nItemCount;
7114 topvisible = ListView_GetTopIndex(hwnd) +
7115 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7117 infoPtr->hdpaItems->nItemCount = nItems;
7119 LISTVIEW_UpdateSize(hwnd);
7120 LISTVIEW_UpdateScroll(hwnd);
7121 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7122 InvalidateRect(hwnd, NULL, TRUE);
7126 FIXME("setitemcount not done for non-ownerdata\n");
7134 * Sets the position of an item.
7137 * [I] HWND : window handle
7138 * [I] INT : item index
7139 * [I] LONG : x coordinate
7140 * [I] LONG : y coordinate
7146 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7147 LONG nPosX, LONG nPosY)
7149 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7150 UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7151 UINT uView = lStyle & LVS_TYPEMASK;
7152 LISTVIEW_ITEM *lpItem;
7154 BOOL bResult = FALSE;
7156 TRACE("(hwnd=%x,nItem=%d,X=%ld,Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7158 if (lStyle & LVS_OWNERDATA)
7161 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7163 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7165 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
7166 if (hdpaSubItems != NULL)
7168 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7172 lpItem->ptPosition.x = nPosX;
7173 lpItem->ptPosition.y = nPosY;
7184 * Sets the state of one or many items.
7187 * [I] HWND : window handle
7188 * [I]INT : item index
7189 * [I] LPLVITEM : item or subitem info
7195 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
7197 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7198 BOOL bResult = FALSE;
7205 ZeroMemory(&lvItem, sizeof(LVITEMA));
7206 lvItem.mask = LVIF_STATE;
7207 lvItem.state = lpLVItem->state;
7208 lvItem.stateMask = lpLVItem->stateMask ;
7210 /* apply to all items */
7211 for (i = 0; i< GETITEMCOUNT(infoPtr); i++)
7214 if (ListView_SetItemA(hwnd, &lvItem) == FALSE)
7222 ZeroMemory(&lvItem, sizeof(LVITEMA));
7223 lvItem.mask = LVIF_STATE;
7224 lvItem.state = lpLVItem->state;
7225 lvItem.stateMask = lpLVItem->stateMask;
7226 lvItem.iItem = nItem;
7227 bResult = ListView_SetItemA(hwnd, &lvItem);
7235 * Sets the text of an item or subitem.
7238 * [I] HWND : window handle
7239 * [I] INT : item index
7240 * [I] LPLVITEMA : item or subitem info
7246 static BOOL LISTVIEW_SetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
7248 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7249 BOOL bResult = FALSE;
7252 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7254 ZeroMemory(&lvItem, sizeof(LVITEMA));
7255 lvItem.mask = LVIF_TEXT;
7256 lvItem.pszText = lpLVItem->pszText;
7257 lvItem.iItem = nItem;
7258 lvItem.iSubItem = lpLVItem->iSubItem;
7259 bResult = ListView_SetItemA(hwnd, &lvItem);
7265 /* LISTVIEW_SetItemTextW */
7269 * Set item index that marks the start of a multiple selection.
7272 * [I] HWND : window handle
7276 * Index number or -1 if there is no selection mark.
7278 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7280 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7281 INT nOldIndex = infoPtr->nSelectionMark;
7283 infoPtr->nSelectionMark = nIndex;
7290 * Sets the text background color.
7293 * [I] HWND : window handle
7294 * [I] COLORREF : text background color
7300 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7302 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7304 infoPtr->clrTextBk = clrTextBk;
7305 InvalidateRect(hwnd, NULL, TRUE);
7312 * Sets the text foreground color.
7315 * [I] HWND : window handle
7316 * [I] COLORREF : text color
7322 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7324 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7326 infoPtr->clrText = clrText;
7327 InvalidateRect(hwnd, NULL, TRUE);
7332 /* LISTVIEW_SetToolTips */
7333 /* LISTVIEW_SetUnicodeFormat */
7334 /* LISTVIEW_SetWorkAreas */
7338 * Callback internally used by LISTVIEW_SortItems()
7341 * [I] LPVOID : first LISTVIEW_ITEM to compare
7342 * [I] LPVOID : second LISTVIEW_ITEM to compare
7343 * [I] LPARAM : HWND of control
7346 * if first comes before second : negative
7347 * if first comes after second : positive
7348 * if first and second are equivalent : zero
7350 static INT WINAPI LISTVIEW_CallBackCompare(
7355 /* Forward the call to the client defined callback */
7357 HWND hwnd = (HWND)lParam;
7358 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7359 HDPA hdpa_first = (HDPA) first;
7360 HDPA hdpa_second = (HDPA) second;
7361 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
7362 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
7364 rv = (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7371 * Sorts the listview items.
7374 * [I] HWND : window handle
7375 * [I] WPARAM : application-defined value
7376 * [I] LPARAM : pointer to comparision callback
7382 static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam)
7384 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7386 UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7387 HDPA *hdpaSubItems=NULL;
7388 LISTVIEW_ITEM *pLVItem=NULL;
7391 if (lStyle & LVS_OWNERDATA)
7394 if (!infoPtr || !infoPtr->hdpaItems)
7397 nCount = GETITEMCOUNT(infoPtr);
7398 /* if there are 0 or 1 items, there is no need to sort */
7401 infoPtr->pfnCompare = (PFNLVCOMPARE)lParam;
7402 infoPtr->lParamSort = (LPARAM)wParam;
7404 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7407 /* Adjust selections so that they are the way they should be after
7408 the sort (otherwise, the list items move around, but whatever
7409 is at the item's previous original position will be selected instead) */
7410 for (i=0; i < nCount; i++)
7412 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7413 pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7415 if (pLVItem->state & LVIS_SELECTED)
7416 LISTVIEW_AddSelectionRange(hwnd, i, i);
7418 LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7421 /* align the items */
7422 LISTVIEW_AlignTop(hwnd);
7424 /* refresh the display */
7425 InvalidateRect(hwnd, NULL, TRUE);
7430 /* LISTVIEW_SubItemHitTest */
7434 * Updates an items or rearranges the listview control.
7437 * [I] HWND : window handle
7438 * [I] INT : item index
7444 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7446 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7447 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7448 BOOL bResult = FALSE;
7451 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7455 /* rearrange with default alignment style */
7456 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7457 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7459 ListView_Arrange(hwnd, 0);
7463 /* get item bounding rectangle */
7464 ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
7465 InvalidateRect(hwnd, &rc, TRUE);
7474 * Creates the listview control.
7477 * [I] HWND : window handle
7482 static LRESULT LISTVIEW_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
7484 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7485 LPCREATESTRUCTA lpcs = (LPCREATESTRUCTA)lParam;
7486 UINT uView = lpcs->style & LVS_TYPEMASK;
7489 /* initialize info pointer */
7490 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7492 /* determine the type of structures to use */
7493 infoPtr->notifyFormat = SendMessageA(GetParent(hwnd), WM_NOTIFYFORMAT,
7494 (WPARAM)hwnd, (LPARAM)NF_QUERY);
7495 if (infoPtr->notifyFormat != NFR_ANSI)
7497 FIXME("ANSI notify format is NOT used\n");
7500 /* initialize color information */
7501 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7502 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7503 infoPtr->clrTextBk = CLR_DEFAULT;
7505 /* set default values */
7506 infoPtr->uCallbackMask = 0;
7507 infoPtr->nFocusedItem = -1;
7508 infoPtr->nSelectionMark = -1;
7509 infoPtr->nHotItem = -1;
7510 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7511 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7512 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7513 infoPtr->hwndEdit = 0;
7514 infoPtr->pedititem = NULL;
7515 infoPtr->nEditLabelItem = -1;
7517 /* get default font (icon title) */
7518 SystemParametersInfoA(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7519 infoPtr->hDefaultFont = CreateFontIndirectA(&logFont);
7520 infoPtr->hFont = infoPtr->hDefaultFont;
7523 infoPtr->hwndHeader = CreateWindowA(WC_HEADERA, (LPCSTR)NULL,
7524 WS_CHILD | HDS_HORZ | HDS_BUTTONS,
7525 0, 0, 0, 0, hwnd, (HMENU)0,
7526 lpcs->hInstance, NULL);
7528 /* set header font */
7529 SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7532 if (uView == LVS_ICON)
7534 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7535 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7537 else if (uView == LVS_REPORT)
7539 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7541 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7545 /* set HDS_HIDDEN flag to hide the header bar */
7546 SetWindowLongA(infoPtr->hwndHeader, GWL_STYLE,
7547 GetWindowLongA(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7551 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7552 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7556 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7557 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7560 /* display unsupported listview window styles */
7561 LISTVIEW_UnsupportedStyles(lpcs->style);
7563 /* allocate memory for the data structure */
7564 infoPtr->hdpaItems = DPA_Create(10);
7566 /* allocate memory for the selection ranges */
7567 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7569 /* initialize size of items */
7570 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7571 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7573 /* initialize the hover time to -1(indicating the default system hover time) */
7574 infoPtr->dwHoverTime = -1;
7581 * Erases the background of the listview control.
7584 * [I] HWND : window handle
7585 * [I] WPARAM : device context handle
7586 * [I] LPARAM : not used
7592 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
7595 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7598 if (infoPtr->clrBk == CLR_NONE)
7600 bResult = SendMessageA(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
7605 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7606 GetClientRect(hwnd, &rc);
7607 FillRect((HDC)wParam, &rc, hBrush);
7608 DeleteObject(hBrush);
7616 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
7618 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7620 if (infoPtr->clrBk != CLR_NONE)
7622 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7623 FillRect(hdc, rc, hBrush);
7624 DeleteObject(hBrush);
7630 * Retrieves the listview control font.
7633 * [I] HWND : window handle
7638 static LRESULT LISTVIEW_GetFont(HWND hwnd)
7640 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7642 return infoPtr->hFont;
7647 * Performs vertical scrolling.
7650 * [I] HWND : window handle
7651 * [I] INT : scroll code
7652 * [I] SHORT : current scroll position if scroll code is SB_THIMBPOSITION
7654 * [I] HWND : scrollbar control window handle
7659 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7662 SCROLLINFO scrollInfo;
7664 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7665 SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7667 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7668 scrollInfo.cbSize = sizeof(SCROLLINFO);
7669 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7671 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7673 INT nOldScrollPos = scrollInfo.nPos;
7674 switch (nScrollCode)
7677 if (scrollInfo.nPos > scrollInfo.nMin)
7684 if (scrollInfo.nPos < scrollInfo.nMax)
7691 if (scrollInfo.nPos > scrollInfo.nMin)
7693 if (scrollInfo.nPos >= scrollInfo.nPage)
7695 scrollInfo.nPos -= scrollInfo.nPage;
7699 scrollInfo.nPos = scrollInfo.nMin;
7705 if (scrollInfo.nPos < scrollInfo.nMax)
7707 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7709 scrollInfo.nPos += scrollInfo.nPage;
7713 scrollInfo.nPos = scrollInfo.nMax;
7719 scrollInfo.nPos = nCurrentPos;
7720 if (scrollInfo.nPos > scrollInfo.nMax)
7721 scrollInfo.nPos=scrollInfo.nMax;
7723 if (scrollInfo.nPos < scrollInfo.nMin)
7724 scrollInfo.nPos=scrollInfo.nMin;
7729 if (nOldScrollPos != scrollInfo.nPos)
7731 scrollInfo.fMask = SIF_POS;
7732 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
7733 InvalidateRect(hwnd, NULL, TRUE);
7742 * Performs horizontal scrolling.
7745 * [I] HWND : window handle
7746 * [I] INT : scroll code
7747 * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
7749 * [I] HWND : scrollbar control window handle
7754 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7757 SCROLLINFO scrollInfo;
7759 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7760 SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7763 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7764 scrollInfo.cbSize = sizeof(SCROLLINFO);
7765 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7767 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
7769 INT nOldScrollPos = scrollInfo.nPos;
7771 switch (nScrollCode)
7774 if (scrollInfo.nPos > scrollInfo.nMin)
7781 if (scrollInfo.nPos < scrollInfo.nMax)
7788 if (scrollInfo.nPos > scrollInfo.nMin)
7790 if (scrollInfo.nPos >= scrollInfo.nPage)
7792 scrollInfo.nPos -= scrollInfo.nPage;
7796 scrollInfo.nPos = scrollInfo.nMin;
7802 if (scrollInfo.nPos < scrollInfo.nMax)
7804 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7806 scrollInfo.nPos += scrollInfo.nPage;
7810 scrollInfo.nPos = scrollInfo.nMax;
7816 scrollInfo.nPos = nCurrentPos;
7818 if (scrollInfo.nPos > scrollInfo.nMax)
7819 scrollInfo.nPos=scrollInfo.nMax;
7821 if (scrollInfo.nPos < scrollInfo.nMin)
7822 scrollInfo.nPos=scrollInfo.nMin;
7826 if (nOldScrollPos != scrollInfo.nPos)
7828 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7829 scrollInfo.fMask = SIF_POS;
7830 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
7831 if(uView == LVS_REPORT)
7833 scrollInfo.fMask = SIF_POS;
7834 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
7835 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
7837 InvalidateRect(hwnd, NULL, TRUE);
7844 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
7846 INT gcWheelDelta = 0;
7847 UINT pulScrollLines = 3;
7848 SCROLLINFO scrollInfo;
7850 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7852 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7853 gcWheelDelta -= wheelDelta;
7855 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7856 scrollInfo.cbSize = sizeof(SCROLLINFO);
7857 scrollInfo.fMask = SIF_POS | SIF_RANGE;
7864 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7865 * should be fixed in the future.
7867 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7868 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
7872 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7874 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7876 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
7877 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7878 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7884 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7895 * [I] HWND : window handle
7896 * [I] INT : virtual key
7897 * [I] LONG : key data
7902 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
7904 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7905 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
7906 HWND hwndParent = GetParent(hwnd);
7907 NMLVKEYDOWN nmKeyDown;
7910 BOOL bRedraw = FALSE;
7911 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7912 UINT uView = lStyle & LVS_TYPEMASK;
7914 /* send LVN_KEYDOWN notification */
7915 ZeroMemory(&nmKeyDown, sizeof(NMLVKEYDOWN));
7916 nmKeyDown.hdr.hwndFrom = hwnd;
7917 nmKeyDown.hdr.idFrom = nCtrlId;
7918 nmKeyDown.hdr.code = LVN_KEYDOWN;
7919 nmKeyDown.wVKey = nVirtualKey;
7920 nmKeyDown.flags = 0;
7921 SendMessageA(hwndParent, WM_NOTIFY, (WPARAM)nCtrlId, (LPARAM)&nmKeyDown);
7924 nmh.hwndFrom = hwnd;
7925 nmh.idFrom = nCtrlId;
7927 switch (nVirtualKey)
7930 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
7932 /* send NM_RETURN notification */
7933 nmh.code = NM_RETURN;
7934 ListView_Notify(hwndParent, nCtrlId, &nmh);
7936 /* send LVN_ITEMACTIVATE notification */
7937 nmh.code = LVN_ITEMACTIVATE;
7938 ListView_Notify(hwndParent, nCtrlId, &nmh);
7943 if (GETITEMCOUNT(infoPtr) > 0)
7950 if (GETITEMCOUNT(infoPtr) > 0)
7952 nItem = GETITEMCOUNT(infoPtr) - 1;
7957 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
7961 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
7965 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
7969 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
7973 if (uView == LVS_REPORT)
7975 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
7979 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
7980 * LISTVIEW_GetCountPerRow(hwnd);
7982 if(nItem < 0) nItem = 0;
7986 if (uView == LVS_REPORT)
7988 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
7992 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
7993 * LISTVIEW_GetCountPerRow(hwnd);
7995 if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
7999 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8001 bRedraw = LISTVIEW_KeySelection(hwnd, nItem);
8002 if (bRedraw != FALSE)
8004 /* refresh client area */
8017 * [I] HWND : window handle
8022 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8024 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
8025 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8029 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8030 UINT uView = lStyle & LVS_TYPEMASK;
8032 TRACE("(hwnd=%x)\n", hwnd);
8034 /* send NM_KILLFOCUS notification */
8035 nmh.hwndFrom = hwnd;
8036 nmh.idFrom = nCtrlId;
8037 nmh.code = NM_KILLFOCUS;
8038 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8040 /* set window focus flag */
8041 infoPtr->bFocus = FALSE;
8043 /* NEED drawing optimization ; redraw the selected items */
8044 if (uView & LVS_REPORT)
8046 nTop = LISTVIEW_GetTopIndex(hwnd);
8048 LISTVIEW_GetCountPerColumn(hwnd) + 1;
8053 nBottom = GETITEMCOUNT(infoPtr);
8055 for (i = nTop; i<nBottom; i++)
8057 if (LISTVIEW_IsSelected(hwnd,i))
8059 rcItem.left = LVIR_BOUNDS;
8060 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8061 InvalidateRect(hwnd, &rcItem, FALSE);
8070 * Processes double click messages (left mouse button).
8073 * [I] HWND : window handle
8074 * [I] WORD : key flag
8075 * [I] WORD : x coordinate
8076 * [I] WORD : y coordinate
8081 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8084 LONG nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8085 LVHITTESTINFO htInfo;
8090 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8092 htInfo.pt.x = wPosX;
8093 htInfo.pt.y = wPosY;
8095 /* send NM_DBLCLK notification */
8096 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8097 nmlv.hdr.hwndFrom = hwnd;
8098 nmlv.hdr.idFrom = nCtrlId;
8099 nmlv.hdr.code = NM_DBLCLK;
8100 ret = LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE);
8103 nmlv.iItem = htInfo.iItem;
8104 nmlv.iSubItem = htInfo.iSubItem;
8111 nmlv.ptAction.x = wPosX;
8112 nmlv.ptAction.y = wPosY;
8113 ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8116 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8119 /* send LVN_ITEMACTIVATE notification */
8120 nmh.hwndFrom = hwnd;
8121 nmh.idFrom = nCtrlId;
8122 nmh.code = LVN_ITEMACTIVATE;
8123 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8131 * Processes mouse down messages (left mouse button).
8134 * [I] HWND : window handle
8135 * [I] WORD : key flag
8136 * [I] WORD : x coordinate
8137 * [I] WORD : y coordinate
8142 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8145 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8146 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8147 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8148 static BOOL bGroupSelect = TRUE;
8153 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX,
8156 /* send NM_RELEASEDCAPTURE notification */
8157 nmh.hwndFrom = hwnd;
8158 nmh.idFrom = nCtrlId;
8159 nmh.code = NM_RELEASEDCAPTURE;
8160 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8162 if (infoPtr->bFocus == FALSE)
8167 /* set left button down flag */
8168 infoPtr->bLButtonDown = TRUE;
8170 ptPosition.x = wPosX;
8171 ptPosition.y = wPosY;
8172 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8173 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8175 if (lStyle & LVS_SINGLESEL)
8177 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8178 && infoPtr->nEditLabelItem == -1)
8180 infoPtr->nEditLabelItem = nItem;
8184 LISTVIEW_SetSelection(hwnd, nItem);
8189 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8191 if (bGroupSelect != FALSE)
8193 LISTVIEW_AddGroupSelection(hwnd, nItem);
8197 LISTVIEW_AddSelection(hwnd, nItem);
8200 else if (wKey & MK_CONTROL)
8202 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8204 else if (wKey & MK_SHIFT)
8206 LISTVIEW_SetGroupSelection(hwnd, nItem);
8211 (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8213 /* set selection (clears other pre-existing selections) */
8214 LISTVIEW_SetSelection(hwnd, nItem);
8216 if (was_selected && infoPtr->nEditLabelItem == -1)
8218 infoPtr->nEditLabelItem = nItem;
8225 /* remove all selections */
8226 LISTVIEW_RemoveAllSelections(hwnd);
8229 /* redraw if we could have possibly selected something */
8230 if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8237 * Processes mouse up messages (left mouse button).
8240 * [I] HWND : window handle
8241 * [I] WORD : key flag
8242 * [I] WORD : x coordinate
8243 * [I] WORD : y coordinate
8248 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8251 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8253 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8255 if (infoPtr->bLButtonDown != FALSE)
8257 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8259 LVHITTESTINFO lvHitTestInfo;
8262 lvHitTestInfo.pt.x = wPosX;
8263 lvHitTestInfo.pt.y = wPosY;
8265 /* send NM_CLICK notification */
8266 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8267 nmlv.hdr.hwndFrom = hwnd;
8268 nmlv.hdr.idFrom = nCtrlId;
8269 nmlv.hdr.code = NM_CLICK;
8270 ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
8273 nmlv.iItem = lvHitTestInfo.iItem;
8274 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8281 nmlv.ptAction.x = wPosX;
8282 nmlv.ptAction.y = wPosY;
8283 ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8286 /* set left button flag */
8287 infoPtr->bLButtonDown = FALSE;
8289 if(infoPtr->nEditLabelItem != -1)
8291 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem)
8293 LISTVIEW_EditLabelA(hwnd, lvHitTestInfo.iItem);
8295 infoPtr->nEditLabelItem = -1;
8304 * Creates the listview control (called before WM_CREATE).
8307 * [I] HWND : window handle
8308 * [I] WPARAM : unhandled
8309 * [I] LPARAM : widow creation info
8314 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8316 LISTVIEW_INFO *infoPtr;
8318 TRACE("(hwnd=%x,wParam=%x,lParam=%lx)\n", hwnd, wParam, lParam);
8320 /* allocate memory for info structure */
8321 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8322 SetWindowLongA(hwnd, 0, (LONG)infoPtr);
8323 if (infoPtr == NULL)
8325 ERR("could not allocate info memory!\n");
8329 if ((LISTVIEW_INFO *)GetWindowLongA(hwnd, 0) != infoPtr)
8331 ERR("pointer assignment error!\n");
8335 return DefWindowProcA(hwnd, WM_NCCREATE, wParam, lParam);
8340 * Destroys the listview control (called after WM_DESTROY).
8343 * [I] HWND : window handle
8348 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8350 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8352 TRACE("(hwnd=%x)\n", hwnd);
8354 /* delete all items */
8355 LISTVIEW_DeleteAllItems(hwnd);
8357 /* destroy data structure */
8358 DPA_Destroy(infoPtr->hdpaItems);
8359 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8362 infoPtr->hFont = (HFONT)0;
8363 if (infoPtr->hDefaultFont)
8365 DeleteObject(infoPtr->hDefaultFont);
8368 /* free listview info pointer*/
8369 COMCTL32_Free(infoPtr);
8371 SetWindowLongA(hwnd, 0, 0);
8377 * Handles notifications from children.
8380 * [I] HWND : window handle
8381 * [I] INT : control identifier
8382 * [I] LPNMHDR : notification information
8387 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8389 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8391 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8393 /* handle notification from header control */
8394 if (lpnmh->code == HDN_ENDTRACKA)
8396 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8397 InvalidateRect(hwnd, NULL, TRUE);
8399 else if(lpnmh->code == HDN_ITEMCLICKA)
8401 /* Handle sorting by Header Column */
8403 LPNMHEADERA pnmHeader = (LPNMHEADERA) lpnmh;
8404 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
8406 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8407 nmlv.hdr.hwndFrom = hwnd;
8408 nmlv.hdr.idFrom = lCtrlId;
8409 nmlv.hdr.code = LVN_COLUMNCLICK;
8411 nmlv.iSubItem = pnmHeader->iItem;
8413 ListView_LVNotify(GetParent(hwnd),lCtrlId, &nmlv);
8416 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8418 /* Idealy this should be done in HDN_ENDTRACKA
8419 * but since SetItemBounds in Header.c is called after
8420 * the notification is sent, it is neccessary to handle the
8421 * update of the scroll bar here (Header.c works fine as it is,
8422 * no need to disturb it)
8424 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8425 LISTVIEW_UpdateScroll(hwnd);
8426 InvalidateRect(hwnd, NULL, TRUE);
8436 * Determines the type of structure to use.
8439 * [I] HWND : window handle of the sender
8440 * [I] HWND : listview window handle
8441 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8446 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8448 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8450 if (nCommand == NF_REQUERY)
8452 /* determine the type of structure to use */
8453 infoPtr->notifyFormat = SendMessageA(hwndFrom, WM_NOTIFYFORMAT,
8454 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8455 if (infoPtr->notifyFormat == NFR_UNICODE)
8457 FIXME("NO support for unicode structures");
8466 * Paints/Repaints the listview control.
8469 * [I] HWND : window handle
8470 * [I] HDC : device context handle
8475 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8479 TRACE("(hwnd=%x,hdc=%x)\n", hwnd, hdc);
8483 hdc = BeginPaint(hwnd, &ps);
8484 LISTVIEW_Refresh(hwnd, hdc);
8485 EndPaint(hwnd, &ps);
8489 LISTVIEW_Refresh(hwnd, hdc);
8497 * Processes double click messages (right mouse button).
8500 * [I] HWND : window handle
8501 * [I] WORD : key flag
8502 * [I] WORD : x coordinate
8503 * [I] WORD : y coordinate
8508 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8511 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8514 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8516 /* send NM_RELEASEDCAPTURE notification */
8517 nmh.hwndFrom = hwnd;
8518 nmh.idFrom = nCtrlId;
8519 nmh.code = NM_RELEASEDCAPTURE;
8520 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8522 /* send NM_RDBLCLK notification */
8523 nmh.code = NM_RDBLCLK;
8524 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8531 * Processes mouse down messages (right mouse button).
8534 * [I] HWND : window handle
8535 * [I] WORD : key flag
8536 * [I] WORD : x coordinate
8537 * [I] WORD : y coordinate
8542 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8545 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8546 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8551 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8553 /* send NM_RELEASEDCAPTURE notification */
8554 nmh.hwndFrom = hwnd;
8555 nmh.idFrom = nCtrlId;
8556 nmh.code = NM_RELEASEDCAPTURE;
8557 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8559 /* make sure the listview control window has the focus */
8560 if (infoPtr->bFocus == FALSE)
8565 /* set right button down flag */
8566 infoPtr->bRButtonDown = TRUE;
8568 /* determine the index of the selected item */
8569 ptPosition.x = wPosX;
8570 ptPosition.y = wPosY;
8571 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8572 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8574 LISTVIEW_SetItemFocus(hwnd,nItem);
8575 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8576 !LISTVIEW_IsSelected(hwnd,nItem))
8578 LISTVIEW_SetSelection(hwnd, nItem);
8583 LISTVIEW_RemoveAllSelections(hwnd);
8591 * Processes mouse up messages (right mouse button).
8594 * [I] HWND : window handle
8595 * [I] WORD : key flag
8596 * [I] WORD : x coordinate
8597 * [I] WORD : y coordinate
8602 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8605 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8606 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8608 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8610 if (infoPtr->bRButtonDown != FALSE)
8613 LVHITTESTINFO lvHitTestInfo;
8617 lvHitTestInfo.pt.x = wPosX;
8618 lvHitTestInfo.pt.y = wPosY;
8620 /* Send NM_RClICK notification */
8621 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8622 nmlv.hdr.hwndFrom = hwnd;
8623 nmlv.hdr.idFrom = nCtrlId;
8624 nmlv.hdr.code = NM_RCLICK;
8625 ret = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE);
8628 nmlv.iItem = lvHitTestInfo.iItem;
8629 nmlv.iSubItem = lvHitTestInfo.iSubItem;
8636 nmlv.ptAction.x = wPosX;
8637 nmlv.ptAction.y = wPosY;
8638 ListView_LVNotify(GetParent(hwnd), nCtrlId, &nmlv);
8643 /* set button flag */
8644 infoPtr->bRButtonDown = FALSE;
8646 /* Change to screen coordinate for WM_CONTEXTMENU */
8647 ClientToScreen(hwnd, &pt);
8649 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8650 SendMessageA( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
8661 * [I] HWND : window handle
8662 * [I] HWND : window handle of previously focused window
8667 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
8669 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8670 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8673 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
8675 /* send NM_SETFOCUS notification */
8676 nmh.hwndFrom = hwnd;
8677 nmh.idFrom = nCtrlId;
8678 nmh.code = NM_SETFOCUS;
8679 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8681 /* set window focus flag */
8682 infoPtr->bFocus = TRUE;
8694 * [I] HWND : window handle
8695 * [I] HFONT : font handle
8696 * [I] WORD : redraw flag
8701 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
8703 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8704 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8706 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
8710 infoPtr->hFont = infoPtr->hDefaultFont;
8714 infoPtr->hFont = hFont;
8717 if (uView == LVS_REPORT)
8719 /* set header font */
8720 SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
8721 MAKELPARAM(fRedraw, 0));
8724 /* invalidate listview control client area */
8725 InvalidateRect(hwnd, NULL, TRUE);
8727 if (fRedraw != FALSE)
8737 * Message handling for WM_SETREDRAW.
8738 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8741 * [I] HWND : window handle
8742 * [I] bRedraw: state of redraw flag
8745 * DefWinProc return value
8747 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
8750 lResult = DefWindowProcA(hwnd, WM_SETREDRAW, bRedraw, 0);
8753 RedrawWindow(hwnd, NULL, 0,
8754 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8761 * Resizes the listview control. This function processes WM_SIZE
8762 * messages. At this time, the width and height are not used.
8765 * [I] HWND : window handle
8766 * [I] WORD : new width
8767 * [I] WORD : new height
8772 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
8774 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8775 UINT uView = lStyle & LVS_TYPEMASK;
8777 TRACE("(hwnd=%x, width=%d, height=%d)\n",hwnd, Width, Height);
8779 LISTVIEW_UpdateSize(hwnd);
8781 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8783 if (lStyle & LVS_ALIGNLEFT)
8785 LISTVIEW_AlignLeft(hwnd);
8789 LISTVIEW_AlignTop(hwnd);
8793 LISTVIEW_UpdateScroll(hwnd);
8795 /* invalidate client area + erase background */
8796 InvalidateRect(hwnd, NULL, TRUE);
8803 * Sets the size information.
8806 * [I] HWND : window handle
8811 static VOID LISTVIEW_UpdateSize(HWND hwnd)
8813 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8814 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8815 UINT uView = lStyle & LVS_TYPEMASK;
8818 GetClientRect(hwnd, &rcList);
8819 infoPtr->rcList.left = 0;
8820 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8821 infoPtr->rcList.top = 0;
8822 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8824 if (uView == LVS_LIST)
8826 if (lStyle & WS_HSCROLL)
8828 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8829 if (infoPtr->rcList.bottom > nHScrollHeight)
8831 infoPtr->rcList.bottom -= nHScrollHeight;
8835 else if (uView == LVS_REPORT)
8842 Header_Layout(infoPtr->hwndHeader, &hl);
8844 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8846 if (!(LVS_NOCOLUMNHEADER & lStyle))
8848 infoPtr->rcList.top = max(wp.cy, 0);
8855 * Processes WM_STYLECHANGED messages.
8858 * [I] HWND : window handle
8859 * [I] WPARAM : window style type (normal or extended)
8860 * [I] LPSTYLESTRUCT : window style information
8865 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
8868 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8869 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8870 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8871 RECT rcList = infoPtr->rcList;
8873 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
8874 hwnd, wStyleType, lpss);
8876 if (wStyleType == GWL_STYLE)
8878 if (uOldView == LVS_REPORT)
8880 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8883 if ((lpss->styleOld & WS_HSCROLL) != 0)
8885 ShowScrollBar(hwnd, SB_HORZ, FALSE);
8888 if ((lpss->styleOld & WS_VSCROLL) != 0)
8890 ShowScrollBar(hwnd, SB_VERT, FALSE);
8893 if (uNewView == LVS_ICON)
8895 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8896 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8897 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8898 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8899 if (lpss->styleNew & LVS_ALIGNLEFT)
8901 LISTVIEW_AlignLeft(hwnd);
8905 LISTVIEW_AlignTop(hwnd);
8908 else if (uNewView == LVS_REPORT)
8915 Header_Layout(infoPtr->hwndHeader, &hl);
8916 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
8918 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8919 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8921 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8922 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8923 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8924 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8926 else if (uNewView == LVS_LIST)
8928 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8929 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8930 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8931 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8935 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8936 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8937 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8938 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8939 if (lpss->styleNew & LVS_ALIGNLEFT)
8941 LISTVIEW_AlignLeft(hwnd);
8945 LISTVIEW_AlignTop(hwnd);
8949 /* update the size of the client area */
8950 LISTVIEW_UpdateSize(hwnd);
8952 /* add scrollbars if needed */
8953 LISTVIEW_UpdateScroll(hwnd);
8955 /* invalidate client area + erase background */
8956 InvalidateRect(hwnd, NULL, TRUE);
8958 /* print the list of unsupported window styles */
8959 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8962 /* If they change the view and we have an active edit control
8963 we will need to kill the control since the redraw will
8964 misplace the edit control.
8966 if (infoPtr->hwndEdit &&
8967 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8968 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8970 SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8978 * Window procedure of the listview control.
8981 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
8984 TRACE("hwnd=%x uMsg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
8985 if (!GetWindowLongA(hwnd, 0) && (uMsg != WM_NCCREATE))
8986 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
8989 case LVM_APPROXIMATEVIEWRECT:
8990 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
8991 LOWORD(lParam), HIWORD(lParam));
8993 return LISTVIEW_Arrange(hwnd, (INT)wParam);
8995 /* case LVM_CREATEDRAGIMAGE: */
8997 case LVM_DELETEALLITEMS:
8998 return LISTVIEW_DeleteAllItems(hwnd);
9000 case LVM_DELETECOLUMN:
9001 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9003 case LVM_DELETEITEM:
9004 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9006 case LVM_EDITLABELW:
9007 case LVM_EDITLABELA:
9008 return LISTVIEW_EditLabelA(hwnd, (INT)wParam);
9010 case LVM_ENSUREVISIBLE:
9011 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9014 return LISTVIEW_FindItem(hwnd, (INT)wParam, (LPLVFINDINFO)lParam);
9016 case LVM_GETBKCOLOR:
9017 return LISTVIEW_GetBkColor(hwnd);
9019 /* case LVM_GETBKIMAGE: */
9021 case LVM_GETCALLBACKMASK:
9022 return LISTVIEW_GetCallbackMask(hwnd);
9024 case LVM_GETCOLUMNA:
9025 return LISTVIEW_GetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9027 /* case LVM_GETCOLUMNW: */
9029 case LVM_GETCOLUMNORDERARRAY:
9030 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9032 case LVM_GETCOLUMNWIDTH:
9033 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9035 case LVM_GETCOUNTPERPAGE:
9036 return LISTVIEW_GetCountPerPage(hwnd);
9038 case LVM_GETEDITCONTROL:
9039 return LISTVIEW_GetEditControl(hwnd);
9041 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9042 return LISTVIEW_GetExtendedListViewStyle(hwnd);
9045 return LISTVIEW_GetHeader(hwnd);
9047 /* case LVM_GETHOTCURSOR: */
9049 case LVM_GETHOTITEM:
9050 return LISTVIEW_GetHotItem(hwnd);
9052 case LVM_GETHOVERTIME:
9053 return LISTVIEW_GetHoverTime(hwnd);
9055 case LVM_GETIMAGELIST:
9056 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9058 /* case LVM_GETISEARCHSTRING: */
9061 return LISTVIEW_GetItemA(hwnd, (LPLVITEMA)lParam, FALSE);
9063 /* case LVM_GETITEMW: */
9065 case LVM_GETITEMCOUNT:
9066 return LISTVIEW_GetItemCount(hwnd);
9068 case LVM_GETITEMPOSITION:
9069 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9071 case LVM_GETITEMRECT:
9072 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9074 case LVM_GETITEMSPACING:
9075 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9077 case LVM_GETITEMSTATE:
9078 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9080 case LVM_GETITEMTEXTA:
9081 LISTVIEW_GetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9084 /* case LVM_GETITEMTEXTW: */
9086 case LVM_GETNEXTITEM:
9087 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9089 /* case LVM_GETNUMBEROFWORKAREAS: */
9092 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9094 case LVM_GETSELECTEDCOUNT:
9095 return LISTVIEW_GetSelectedCount(hwnd);
9097 case LVM_GETSELECTIONMARK:
9098 return LISTVIEW_GetSelectionMark(hwnd);
9100 case LVM_GETSTRINGWIDTHA:
9101 return LISTVIEW_GetStringWidthA (hwnd, (LPCSTR)lParam);
9103 /* case LVM_GETSTRINGWIDTHW: */
9104 /* case LVM_GETSUBITEMRECT: */
9106 case LVM_GETTEXTBKCOLOR:
9107 return LISTVIEW_GetTextBkColor(hwnd);
9109 case LVM_GETTEXTCOLOR:
9110 return LISTVIEW_GetTextColor(hwnd);
9112 /* case LVM_GETTOOLTIPS: */
9114 case LVM_GETTOPINDEX:
9115 return LISTVIEW_GetTopIndex(hwnd);
9117 /* case LVM_GETUNICODEFORMAT: */
9119 case LVM_GETVIEWRECT:
9120 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9122 /* case LVM_GETWORKAREAS: */
9125 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9127 case LVM_INSERTCOLUMNA:
9128 return LISTVIEW_InsertColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9130 case LVM_INSERTCOLUMNW:
9131 return LISTVIEW_InsertColumnW(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam);
9133 case LVM_INSERTITEMA:
9134 return LISTVIEW_InsertItemA(hwnd, (LPLVITEMA)lParam);
9136 case LVM_INSERTITEMW:
9137 return LISTVIEW_InsertItemW(hwnd, (LPLVITEMW)lParam);
9139 case LVM_REDRAWITEMS:
9140 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9142 /* case LVM_SCROLL: */
9143 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9145 case LVM_SETBKCOLOR:
9146 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9148 /* case LVM_SETBKIMAGE: */
9150 case LVM_SETCALLBACKMASK:
9151 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9153 case LVM_SETCOLUMNA:
9154 return LISTVIEW_SetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
9156 case LVM_SETCOLUMNW:
9157 FIXME("Unimplemented msg LVM_SETCOLUMNW\n");
9160 case LVM_SETCOLUMNORDERARRAY:
9161 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9163 case LVM_SETCOLUMNWIDTH:
9164 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9166 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9167 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9169 /* case LVM_SETHOTCURSOR: */
9171 case LVM_SETHOTITEM:
9172 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9174 case LVM_SETHOVERTIME:
9175 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9177 /* case LVM_SETICONSPACING: */
9179 case LVM_SETIMAGELIST:
9180 return LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9183 return LISTVIEW_SetItemA(hwnd, (LPLVITEMA)lParam);
9185 /* case LVM_SETITEMW: */
9187 case LVM_SETITEMCOUNT:
9188 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9190 case LVM_SETITEMPOSITION:
9191 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9192 (INT)HIWORD(lParam));
9194 case LVM_SETITEMPOSITION32:
9195 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9196 ((POINT*)lParam)->y);
9198 case LVM_SETITEMSTATE:
9199 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9201 case LVM_SETITEMTEXTA:
9202 return LISTVIEW_SetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
9204 /* case LVM_SETITEMTEXTW: */
9206 case LVM_SETSELECTIONMARK:
9207 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9209 case LVM_SETTEXTBKCOLOR:
9210 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9212 case LVM_SETTEXTCOLOR:
9213 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9215 /* case LVM_SETTOOLTIPS: */
9216 /* case LVM_SETUNICODEFORMAT: */
9217 /* case LVM_SETWORKAREAS: */
9220 return LISTVIEW_SortItems(hwnd, wParam, lParam);
9222 /* case LVM_SUBITEMHITTEST: */
9225 return LISTVIEW_Update(hwnd, (INT)wParam);
9228 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9231 return LISTVIEW_Command(hwnd, wParam, lParam);
9234 return LISTVIEW_Create(hwnd, wParam, lParam);
9237 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9240 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9243 return LISTVIEW_GetFont(hwnd);
9246 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
9247 (INT)HIWORD(wParam), (HWND)lParam);
9250 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9253 return LISTVIEW_KillFocus(hwnd);
9255 case WM_LBUTTONDBLCLK:
9256 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9259 case WM_LBUTTONDOWN:
9260 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9263 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9266 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9269 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9272 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9275 return LISTVIEW_NCDestroy(hwnd);
9278 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9280 case WM_NOTIFYFORMAT:
9281 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9284 return LISTVIEW_Paint(hwnd, (HDC)wParam);
9286 case WM_RBUTTONDBLCLK:
9287 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
9290 case WM_RBUTTONDOWN:
9291 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
9295 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
9299 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9302 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9305 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9308 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9310 case WM_STYLECHANGED:
9311 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9313 /* case WM_TIMER: */
9316 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
9317 (INT)HIWORD(wParam), (HWND)lParam);
9320 if (wParam & (MK_SHIFT | MK_CONTROL))
9321 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
9322 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
9324 /* case WM_WININICHANGE: */
9327 if (uMsg >= WM_USER)
9329 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
9333 /* call default window procedure */
9334 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
9342 * Registers the window class.
9350 VOID LISTVIEW_Register(void)
9354 ZeroMemory(&wndClass, sizeof(WNDCLASSA));
9355 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9356 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9357 wndClass.cbClsExtra = 0;
9358 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9359 wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
9360 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9361 wndClass.lpszClassName = WC_LISTVIEWA;
9362 RegisterClassA(&wndClass);
9367 * Unregisters the window class.
9375 VOID LISTVIEW_Unregister(void)
9377 UnregisterClassA(WC_LISTVIEWA, (HINSTANCE)NULL);
9382 * Handle any WM_COMMAND messages
9388 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9390 switch (HIWORD(wParam))
9395 * Adjust the edit window size
9398 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
9399 HDC hdc = GetDC(infoPtr->hwndEdit);
9400 HFONT hFont, hOldFont = 0;
9405 len = GetWindowTextA(infoPtr->hwndEdit, buffer, 1023);
9406 GetWindowRect(infoPtr->hwndEdit, &rect);
9408 /* Select font to get the right dimension of the string */
9409 hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9412 hOldFont = SelectObject(hdc, hFont);
9415 if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
9417 TEXTMETRICA textMetric;
9419 /* Add Extra spacing for the next character */
9420 GetTextMetricsA(hdc, &textMetric);
9421 sz.cx += (textMetric.tmMaxCharWidth * 2);
9429 rect.bottom - rect.top,
9430 SWP_DRAWFRAME|SWP_NOMOVE);
9434 SelectObject(hdc, hOldFont);
9437 ReleaseDC(hwnd, hdc);
9443 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9452 * Subclassed edit control windproc function
9458 LRESULT CALLBACK EditLblWndProc(HWND hwnd, UINT uMsg,
9459 WPARAM wParam, LPARAM lParam)
9461 BOOL cancel = FALSE;
9462 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(GetParent(hwnd), 0);
9463 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9464 static BOOL bIgnoreKillFocus = FALSE;
9468 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9471 if(bIgnoreKillFocus)
9479 WNDPROC editProc = einfo->EditWndProc;
9480 SetWindowLongA(hwnd, GWL_WNDPROC, (LONG)editProc);
9481 COMCTL32_Free(einfo);
9482 infoPtr->pedititem = NULL;
9483 return CallWindowProcA(editProc, hwnd, uMsg, wParam, lParam);
9487 if (VK_ESCAPE == (INT)wParam)
9493 else if (VK_RETURN == (INT)wParam)
9497 return CallWindowProcA(einfo->EditWndProc, hwnd,
9498 uMsg, wParam, lParam);
9501 if (einfo->EditLblCb)
9503 char *buffer = NULL;
9508 int len = 1 + GetWindowTextLengthA(hwnd);
9512 if (NULL != (buffer = (char *)COMCTL32_Alloc(len*sizeof(char))))
9514 GetWindowTextA(hwnd, buffer, len);
9518 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9519 /* eg. Using a messagebox */
9520 bIgnoreKillFocus = TRUE;
9521 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9524 COMCTL32_Free(buffer);
9526 einfo->EditLblCb = NULL;
9527 bIgnoreKillFocus = FALSE;
9530 SendMessageA(hwnd, WM_CLOSE, 0, 0);
9537 * Creates a subclassed edit cotrol
9543 HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y,
9544 INT width, INT height, HWND parent, HINSTANCE hinst,
9545 EditlblCallback EditLblCb, DWORD param)
9551 TEXTMETRICA textMetric;
9552 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(parent, 0);
9554 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9557 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9558 hdc = GetDC(parent);
9560 /* Select the font to get appropriate metric dimensions */
9561 if(infoPtr->hFont != 0)
9563 hOldFont = SelectObject(hdc, infoPtr->hFont);
9566 /*Get String Lenght in pixels */
9567 GetTextExtentPoint32A(hdc, text, strlen(text), &sz);
9569 /*Add Extra spacing for the next character */
9570 GetTextMetricsA(hdc, &textMetric);
9571 sz.cx += (textMetric.tmMaxCharWidth * 2);
9573 if(infoPtr->hFont != 0)
9575 SelectObject(hdc, hOldFont);
9578 ReleaseDC(parent, hdc);
9579 if (!(hedit = CreateWindowA("Edit", text, style, x, y, sz.cx, height,
9580 parent, 0, hinst, 0)))
9582 COMCTL32_Free(infoPtr->pedititem);
9586 infoPtr->pedititem->param = param;
9587 infoPtr->pedititem->EditLblCb = EditLblCb;
9588 infoPtr->pedititem->EditWndProc = (WNDPROC)SetWindowLongA(hedit,
9589 GWL_WNDPROC, (LONG) EditLblWndProc);
9591 SendMessageA(hedit, WM_SETFONT, infoPtr->hFont, FALSE);