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
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_GetColumnOrderArray : simple hack only
35 * LISTVIEW_SetColumnOrderArray : simple hack only
36 * LISTVIEW_Arrange : empty stub
37 * LISTVIEW_ApproximateViewRect : incomplete
38 * LISTVIEW_Scroll : not implemented
39 * LISTVIEW_RedrawItems : empty stub
40 * LISTVIEW_Update : not completed
49 #include "debugtools.h"
51 DEFAULT_DEBUG_CHANNEL(listview);
53 /* Some definitions for inline edit control */
54 typedef BOOL (*EditlblCallback)(HWND, LPSTR, DWORD);
56 typedef struct tagEDITLABEL_ITEM
60 EditlblCallback EditLblCb;
63 typedef struct tagLISTVIEW_SUBITEM
71 typedef struct tagLISTVIEW_ITEM
82 typedef struct tagLISTVIEW_SELECTION
88 typedef struct tagLISTVIEW_INFO
93 HIMAGELIST himlNormal;
99 HDPA hdpaSelectionRanges;
114 DWORD dwExStyle; /* extended listview style */
116 PFNLVCOMPARE pfnCompare;
120 EDITLABEL_ITEM *pedititem;
123 WPARAM charCode; /* Added */
124 CHAR szSearchParam[ MAX_PATH ]; /* Added */
125 DWORD timeSinceLastKeyPress; /* Added */
126 INT nSearchParamLength; /* Added */
133 /* maximum size of a label */
134 #define DISP_TEXT_SIZE 512
136 /* padding for items in list and small icon display modes */
137 #define WIDTH_PADDING 12
139 /* padding for items in list, report and small icon display modes */
140 #define HEIGHT_PADDING 1
142 /* offset of items in report display mode */
143 #define REPORT_MARGINX 2
145 /* padding for icon in large icon display mode */
146 #define ICON_TOP_PADDING 2
147 #define ICON_BOTTOM_PADDING 2
149 /* padding for label in large icon display mode */
150 #define LABEL_VERT_OFFSET 2
152 /* default label width for items in list and small icon display modes */
153 #define DEFAULT_LABEL_WIDTH 40
155 /* default column width for items in list display mode */
156 #define DEFAULT_COLUMN_WIDTH 96
158 /* Increment size of the horizontal scroll bar */
159 #define LISTVIEW_SCROLL_DIV_SIZE 10
161 /* Padding betwen image and label */
162 #define IMAGE_PADDING 2
164 /* Padding behind the label */
165 #define TRAILING_PADDING 5
167 /* Border for the icon caption */
168 #define CAPTION_BORDER 2
172 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
173 #define ListView_LVNotify(hwnd,lCtrlId,plvnm) \
174 (BOOL)SendMessageA((hwnd),WM_NOTIFY,(WPARAM)(INT)lCtrlId,(LPARAM)(LPNMLISTVIEW)(plvnm))
175 #define ListView_Notify(hwnd,lCtrlId,pnmh) \
176 (BOOL)SendMessageA((hwnd),WM_NOTIFY,(WPARAM)(INT)lCtrlId,(LPARAM)(LPNMHDR)(pnmh))
177 /* retrieve the number of items in the listview */
178 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
180 HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y,
181 INT width, INT height, HWND parent, HINSTANCE hinst,
182 EditlblCallback EditLblCb, DWORD param);
185 * forward declarations
187 static LRESULT LISTVIEW_GetItemA(HWND hwnd, LPLVITEMA lpLVItem, BOOL internal);
188 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO);
189 static INT LISTVIEW_GetCountPerRow(HWND);
190 static INT LISTVIEW_GetCountPerColumn(HWND);
191 static VOID LISTVIEW_AlignLeft(HWND);
192 static VOID LISTVIEW_AlignTop(HWND);
193 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
194 static VOID LISTVIEW_AddSelection(HWND, INT);
195 static BOOL LISTVIEW_AddSubItem(HWND, LPLVITEMA);
196 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
197 static INT LISTVIEW_GetItemHeight(HWND);
198 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
199 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
200 static INT LISTVIEW_GetItemWidth(HWND);
201 static INT LISTVIEW_GetLabelWidth(HWND, INT);
202 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
203 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
204 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
205 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
206 static BOOL LISTVIEW_InitItem(HWND, LISTVIEW_ITEM *, LPLVITEMA);
207 static BOOL LISTVIEW_InitSubItem(HWND, LISTVIEW_SUBITEM *, LPLVITEMA);
208 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
209 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
210 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
211 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
212 static BOOL LISTVIEW_SetItem(HWND, LPLVITEMA);
213 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
214 static BOOL LISTVIEW_SetItemPosition(HWND, INT, INT, INT);
215 static VOID LISTVIEW_UpdateScroll(HWND);
216 static VOID LISTVIEW_SetSelection(HWND, INT);
217 static VOID LISTVIEW_UpdateSize(HWND);
218 static BOOL LISTVIEW_SetSubItem(HWND, LPLVITEMA);
219 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
220 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
221 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
222 static HWND LISTVIEW_EditLabelA(HWND hwnd, INT nItem);
223 static BOOL LISTVIEW_EndEditLabel(HWND hwnd, LPSTR pszText, DWORD nItem);
224 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
225 static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam);
226 static LRESULT LISTVIEW_GetStringWidthA(HWND hwnd, LPCSTR lpszText);
227 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
228 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
229 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
230 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem);
232 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
233 #define KEY_DELAY 900
234 #define LISTVIEW_InitLvItemStruct(item,idx,TEXT) \
235 ZeroMemory(&(item), sizeof(LVITEMA)); \
236 (item).mask = LVIF_TEXT; \
237 (item).iItem = (idx); \
238 (item).iSubItem = 0; \
239 (item).pszText = (TEXT); \
240 (item).cchTextMax = MAX_PATH
242 /*************************************************************************
244 * Processes keyboard messages generated by pressing the letter keys on the keyboard.
245 * Assumes the list is sorted alphabetically, without regard to case.
248 * [I] HWND: handle to the window
249 * [I] WPARAM: the character code, the actual character
250 * [I] LPARAM: key data
260 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData )
262 LISTVIEW_INFO *infoPtr = NULL;
267 BOOL bFoundMatchingFiles = FALSE;
269 CHAR TEXT[ MAX_PATH ];
270 CHAR szCharCode[ 2 ];
271 DWORD timeSinceLastKeyPress = 0;
273 szCharCode[0] = charCode;
276 /* simple parameter checking */
277 if ( !hwnd || !charCode || !keyData )
280 infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
285 /* only allow the valid WM_CHARs through */
286 if ( isalnum( charCode ) || charCode == '.' || charCode == '`' || charCode == '!'
287 || charCode == '@' || charCode == '#' || charCode == '$' || charCode == '%'
288 || charCode == '^' || charCode == '&' || charCode == '*' || charCode == '('
289 || charCode == ')' || charCode == '-' || charCode == '_' || charCode == '+'
290 || charCode == '=' || charCode == '\\'|| charCode == ']' || charCode == '}'
291 || charCode == '[' || charCode == '{' || charCode == '/' || charCode == '?'
292 || charCode == '>' || charCode == '<' || charCode == ',' || charCode == '~')
294 timeSinceLastKeyPress = GetTickCount();
296 nSize = GETITEMCOUNT( infoPtr );
297 /* if there are 0 items, there is no where to go */
301 * If the last charCode equals the current charCode then look
302 * to the next element in list to see if it matches the previous
305 if ( infoPtr->charCode == charCode )
307 if ( timeSinceLastKeyPress - infoPtr->timeSinceLastKeyPress < KEY_DELAY )
308 { /* append new character to search string */
309 strcat( infoPtr->szSearchParam, szCharCode );
310 infoPtr->nSearchParamLength++;
312 /* loop from start of list view */
313 for( idx = infoPtr->nFocusedItem; idx < nSize; idx++ )
315 LISTVIEW_InitLvItemStruct( item, idx, TEXT );
316 ListView_GetItemA( hwnd, &item );
319 if ( strncasecmp( item.pszText, infoPtr->szSearchParam,
320 infoPtr->nSearchParamLength ) == 0 )
327 else if ( infoPtr->timeSinceLastKeyPress > timeSinceLastKeyPress )
328 { /* The DWORD went over it's boundery?? Ergo assuming too slow??. */
329 for ( idx = 0; idx < nSize; idx++ )
331 LISTVIEW_InitLvItemStruct( item, idx, TEXT );
332 ListView_GetItemA( hwnd, &item );
334 if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
340 strcpy( infoPtr->szSearchParam, szCharCode );
341 infoPtr->nSearchParamLength = 1;
344 { /* Save szCharCode for use in later searches */
345 strcpy( infoPtr->szSearchParam, szCharCode );
346 infoPtr->nSearchParamLength = 1;
348 LISTVIEW_InitLvItemStruct( item, infoPtr->nFocusedItem + 1, TEXT );
349 ListView_GetItemA( hwnd, &item );
351 if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
352 nItem = infoPtr->nFocusedItem + 1;
355 * Ok so there are no more folders that match
356 * now we look for files.
358 for ( idx = infoPtr->nFocusedItem + 1; idx < nSize; idx ++ )
360 LISTVIEW_InitLvItemStruct( item, idx, TEXT );
361 ListView_GetItemA( hwnd, &item );
363 if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
366 bFoundMatchingFiles = TRUE;
370 if ( !bFoundMatchingFiles )
371 { /* go back to first instance */
372 for ( idx = 0; idx < nSize; idx ++ )
374 LISTVIEW_InitLvItemStruct( item,idx, TEXT );
375 ListView_GetItemA( hwnd, &item );
377 if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
386 } /*END: if ( infoPtr->charCode == charCode )*/
388 else /* different keypressed */
390 /* could be that they are spelling the file/directory for us */
391 if ( timeSinceLastKeyPress - infoPtr->timeSinceLastKeyPress > KEY_DELAY )
393 * Too slow, move to the first instance of the
396 for ( idx = 0; idx < nSize; idx++ )
398 LISTVIEW_InitLvItemStruct( item,idx, TEXT );
399 ListView_GetItemA( hwnd, &item );
401 if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
407 strcpy( infoPtr->szSearchParam, szCharCode );
408 infoPtr->nSearchParamLength = 1;
410 else if ( infoPtr->timeSinceLastKeyPress > timeSinceLastKeyPress )
411 { /* The DWORD went over it's boundery?? Ergo assuming too slow??. */
412 for ( idx = 0; idx < nSize; idx++ )
414 LISTVIEW_InitLvItemStruct( item,idx, TEXT );
415 ListView_GetItemA( hwnd, &item );
417 if ( strncasecmp( &( item.pszText[ 0 ] ), szCharCode, 1 ) == 0 )
423 strcpy( infoPtr->szSearchParam, szCharCode );
424 infoPtr->nSearchParamLength = 1;
426 else /* Search for the string the user is typing */
428 /* append new character to search string */
429 strcat( infoPtr->szSearchParam, szCharCode );
430 infoPtr->nSearchParamLength++;
432 /* loop from start of list view */
433 for( idx = 0; idx < nSize; idx++ )
435 LISTVIEW_InitLvItemStruct( item, idx, TEXT );
436 ListView_GetItemA( hwnd, &item );
439 if ( strncasecmp( item.pszText, infoPtr->szSearchParam,
440 infoPtr->nSearchParamLength ) == 0 )
452 bRedraw = LISTVIEW_KeySelection(hwnd, nItem );
453 if (bRedraw != FALSE)
455 /* refresh client area */
456 InvalidateRect(hwnd, NULL, TRUE);
460 /* Store the WM_CHAR for next time */
461 infoPtr->charCode = charCode;
464 infoPtr->timeSinceLastKeyPress = timeSinceLastKeyPress;
470 /*************************************************************************
471 * LISTVIEW_UpdateHeaderSize [Internal]
473 * Function to resize the header control
476 * hwnd [I] handle to a window
477 * nNewScrollPos [I] Scroll Pos to Set
484 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
486 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
490 GetWindowRect(infoPtr->hwndHeader, &winRect);
491 point[0].x = winRect.left;
492 point[0].y = winRect.top;
493 point[1].x = winRect.right;
494 point[1].y = winRect.bottom;
496 MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
497 point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
498 point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
500 SetWindowPos(infoPtr->hwndHeader,0,
501 point[0].x,point[0].y,point[1].x,point[1].y,
502 SWP_NOZORDER | SWP_NOACTIVATE);
507 * Update the scrollbars. This functions should be called whenever
508 * the content, size or view changes.
511 * [I] HWND : window handle
516 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
518 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
519 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
520 UINT uView = lStyle & LVS_TYPEMASK;
521 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
522 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
523 SCROLLINFO scrollInfo;
525 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
526 scrollInfo.cbSize = sizeof(SCROLLINFO);
528 if (uView == LVS_LIST)
530 /* update horizontal scrollbar */
532 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
533 INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
534 INT nNumOfItems = GETITEMCOUNT(infoPtr);
536 scrollInfo.nMax = nNumOfItems / nCountPerColumn;
537 if((nNumOfItems % nCountPerColumn) == 0)
541 scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
542 scrollInfo.nPage = nCountPerRow;
543 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
544 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
546 else if (uView == LVS_REPORT)
548 /* update vertical scrollbar */
550 scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
551 scrollInfo.nPos = ListView_GetTopIndex(hwnd);
552 scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
553 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
554 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
556 /* update horizontal scrollbar */
557 nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
558 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
559 || GETITEMCOUNT(infoPtr) == 0)
564 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ;
565 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
566 scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
567 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
569 /* Update the Header Control */
570 scrollInfo.fMask = SIF_POS;
571 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
572 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
579 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
581 INT nViewWidth = rcView.right - rcView.left;
582 INT nViewHeight = rcView.bottom - rcView.top;
584 /* Update Horizontal Scrollbar */
585 scrollInfo.fMask = SIF_POS;
586 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
587 || GETITEMCOUNT(infoPtr) == 0)
591 scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
593 scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
594 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
595 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
597 /* Update Vertical Scrollbar */
598 nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
599 scrollInfo.fMask = SIF_POS;
600 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
601 || GETITEMCOUNT(infoPtr) == 0)
605 scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
607 scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
608 scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
609 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
616 * Prints a message for unsupported window styles.
617 * A kind of TODO list for window styles.
620 * [I] LONG : window style
625 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
627 if ((LVS_TYPEMASK & lStyle) == LVS_EDITLABELS)
629 FIXME(" LVS_EDITLABELS\n");
632 if ((LVS_TYPEMASK & lStyle) == LVS_NOLABELWRAP)
634 FIXME(" LVS_NOLABELWRAP\n");
637 if ((LVS_TYPEMASK & lStyle) == LVS_NOSCROLL)
639 FIXME(" LVS_NOSCROLL\n");
642 if ((LVS_TYPEMASK & lStyle) == LVS_NOSORTHEADER)
644 FIXME(" LVS_NOSORTHEADER\n");
647 if ((LVS_TYPEMASK & lStyle) == LVS_OWNERDRAWFIXED)
649 FIXME(" LVS_OWNERDRAWFIXED\n");
652 if ((LVS_TYPEMASK & lStyle) == LVS_SHAREIMAGELISTS)
654 FIXME(" LVS_SHAREIMAGELISTS\n");
657 if ((LVS_TYPEMASK & lStyle) == LVS_SORTASCENDING)
659 FIXME(" LVS_SORTASCENDING\n");
662 if ((LVS_TYPEMASK & lStyle) == LVS_SORTDESCENDING)
664 FIXME(" LVS_SORTDESCENDING\n");
670 * Aligns the items with the top edge of the window.
673 * [I] HWND : window handle
678 static VOID LISTVIEW_AlignTop(HWND hwnd)
680 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
681 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
682 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
687 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
689 ZeroMemory(&ptItem, sizeof(POINT));
690 ZeroMemory(&rcView, sizeof(RECT));
692 if (nListWidth > infoPtr->nItemWidth)
694 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
696 if (ptItem.x + infoPtr->nItemWidth > nListWidth)
699 ptItem.y += infoPtr->nItemHeight;
702 ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
703 ptItem.x += infoPtr->nItemWidth;
704 rcView.right = max(rcView.right, ptItem.x);
707 rcView.bottom = ptItem.y + infoPtr->nItemHeight;
711 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
713 ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
714 ptItem.y += infoPtr->nItemHeight;
717 rcView.right = infoPtr->nItemWidth;
718 rcView.bottom = ptItem.y;
721 LISTVIEW_SetViewRect(hwnd, &rcView);
727 * Aligns the items with the left edge of the window.
730 * [I] HWND : window handle
735 static VOID LISTVIEW_AlignLeft(HWND hwnd)
737 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
738 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
739 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
744 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
746 ZeroMemory(&ptItem, sizeof(POINT));
747 ZeroMemory(&rcView, sizeof(RECT));
749 if (nListHeight > infoPtr->nItemHeight)
751 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
753 if (ptItem.y + infoPtr->nItemHeight > nListHeight)
756 ptItem.x += infoPtr->nItemWidth;
759 ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
760 ptItem.y += infoPtr->nItemHeight;
761 rcView.bottom = max(rcView.bottom, ptItem.y);
764 rcView.right = ptItem.x + infoPtr->nItemWidth;
768 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
770 ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
771 ptItem.x += infoPtr->nItemWidth;
774 rcView.bottom = infoPtr->nItemHeight;
775 rcView.right = ptItem.x;
778 LISTVIEW_SetViewRect(hwnd, &rcView);
784 * Set the bounding rectangle of all the items.
787 * [I] HWND : window handle
788 * [I] LPRECT : bounding rectangle
794 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
796 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
797 BOOL bResult = FALSE;
799 TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
800 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
802 if (lprcView != NULL)
805 infoPtr->rcView.left = lprcView->left;
806 infoPtr->rcView.top = lprcView->top;
807 infoPtr->rcView.right = lprcView->right;
808 infoPtr->rcView.bottom = lprcView->bottom;
816 * Retrieves the bounding rectangle of all the items.
819 * [I] HWND : window handle
820 * [O] LPRECT : bounding rectangle
826 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
828 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
829 BOOL bResult = FALSE;
832 TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
834 if (lprcView != NULL)
836 bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
837 if (bResult != FALSE)
839 lprcView->left = infoPtr->rcView.left + ptOrigin.x;
840 lprcView->top = infoPtr->rcView.top + ptOrigin.y;
841 lprcView->right = infoPtr->rcView.right + ptOrigin.x;
842 lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
845 TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
846 lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
854 * Retrieves the subitem pointer associated with the subitem index.
857 * [I] HDPA : DPA handle for a specific item
858 * [I] INT : index of subitem
861 * SUCCESS : subitem pointer
864 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
867 LISTVIEW_SUBITEM *lpSubItem;
870 for (i = 1; i < hdpaSubItems->nItemCount; i++)
872 lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
873 if (lpSubItem != NULL)
875 if (lpSubItem->iSubItem == nSubItem)
887 * Calculates the width of an item.
890 * [I] HWND : window handle
891 * [I] LONG : window style
894 * Returns item width.
896 static INT LISTVIEW_GetItemWidth(HWND hwnd)
898 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
899 LONG style = GetWindowLongA(hwnd, GWL_STYLE);
900 UINT uView = style & LVS_TYPEMASK;
901 INT nHeaderItemCount;
907 TRACE("(hwnd=%x)\n", hwnd);
909 if (uView == LVS_ICON)
911 nItemWidth = infoPtr->iconSpacing.cx;
913 else if (uView == LVS_REPORT && (!(LVS_NOCOLUMNHEADER & style)) )
915 /* calculate width of header */
916 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
917 for (i = 0; i < nHeaderItemCount; i++)
919 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
921 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
927 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
929 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
930 nItemWidth = max(nItemWidth, nLabelWidth);
933 /* default label size */
934 if (GETITEMCOUNT(infoPtr) == 0)
936 nItemWidth = DEFAULT_COLUMN_WIDTH;
942 nItemWidth = DEFAULT_LABEL_WIDTH;
947 nItemWidth += WIDTH_PADDING;
949 if (infoPtr->himlSmall != NULL)
951 nItemWidth += infoPtr->iconSize.cx;
954 if (infoPtr->himlState != NULL)
956 nItemWidth += infoPtr->iconSize.cx;
963 /* nItemWidth Cannot be Zero */
971 * Calculates the width of a specific item.
974 * [I] HWND : window handle
978 * Returns the width of an item width a specified string.
980 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
982 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
983 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
984 INT nHeaderItemCount;
989 TRACE("(hwnd=%x)\n", hwnd);
991 if (uView == LVS_ICON)
993 nItemWidth = infoPtr->iconSpacing.cx;
995 else if (uView == LVS_REPORT)
997 /* calculate width of header */
998 nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
999 for (i = 0; i < nHeaderItemCount; i++)
1001 if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1003 nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1009 /* get width of string */
1010 nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1012 /* default label size */
1013 if (GETITEMCOUNT(infoPtr) == 0)
1015 nItemWidth = DEFAULT_COLUMN_WIDTH;
1019 if (nItemWidth == 0)
1021 nItemWidth = DEFAULT_LABEL_WIDTH;
1026 nItemWidth += WIDTH_PADDING;
1028 if (infoPtr->himlSmall != NULL)
1030 nItemWidth += infoPtr->iconSize.cx;
1033 if (infoPtr->himlState != NULL)
1035 nItemWidth += infoPtr->iconSize.cx;
1046 * Calculates the height of an item.
1049 * [I] HWND : window handle
1050 * [I] LONG : window style
1053 * Returns item height.
1055 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1057 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1058 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1059 INT nItemHeight = 0;
1061 if (uView == LVS_ICON)
1063 nItemHeight = infoPtr->iconSpacing.cy;
1068 HDC hdc = GetDC(hwnd);
1069 HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1070 GetTextMetricsA(hdc, &tm);
1072 if(infoPtr->himlState || infoPtr->himlSmall)
1073 nItemHeight = max(tm.tmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1075 nItemHeight = tm.tmHeight;
1077 SelectObject(hdc, hOldFont);
1078 ReleaseDC(hwnd, hdc);
1085 static void LISTVIEW_PrintSelectionRanges(hwnd)
1087 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1088 LISTVIEW_SELECTION *selection;
1089 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1092 TRACE("Selections are:\n");
1093 for (i = 0; i < topSelection; i++)
1095 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1096 TRACE(" %lu - %lu\n",selection->lower,selection->upper);
1102 * A compare function for selection ranges
1105 * [I] LPVOID : Item 1;
1106 * [I] LPVOID : Item 2;
1107 * [I] LPARAM : flags
1110 * >0 : if Item 1 > Item 2
1111 * <0 : if Item 2 > Item 1
1112 * 0 : if Item 1 == Item 2
1114 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1117 int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1118 int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1119 int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1120 int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1134 * Adds a selection range.
1137 * [I] HWND : window handle
1138 * [I] INT : lower item index
1139 * [I] INT : upper item index
1144 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1146 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1147 LISTVIEW_SELECTION *selection;
1148 INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1149 BOOL lowerzero=FALSE;
1151 selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1152 selection->lower = lItem;
1153 selection->upper = uItem;
1155 TRACE("Add range %i - %i\n",lItem,uItem);
1158 LISTVIEW_SELECTION *checkselection,*checkselection2;
1159 INT index,mergeindex;
1161 /* find overlapping selections */
1162 /* we want to catch adjacent ranges so expand our range by 1 */
1165 if (selection->lower == 0)
1170 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1171 LISTVIEW_CompareSelectionRanges,
1173 selection->upper --;
1177 selection->lower ++;
1181 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1182 TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1183 checkselection->upper);
1185 checkselection->lower = min(selection->lower,checkselection->lower);
1186 checkselection->upper = max(selection->upper,checkselection->upper);
1188 TRACE("New range (%lu - %lu)\n", checkselection->lower,
1189 checkselection->upper);
1191 COMCTL32_Free(selection);
1193 /* merge now common selection ranges in the lower group*/
1196 checkselection->upper ++;
1197 if (checkselection->lower == 0)
1200 checkselection->lower --;
1202 TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1203 checkselection->upper);
1205 /* not sorted yet */
1206 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1207 LISTVIEW_CompareSelectionRanges, 0,
1210 checkselection->upper --;
1214 checkselection->lower ++;
1216 if (mergeindex >=0 && mergeindex != index)
1218 TRACE("Merge with index %i\n",mergeindex);
1219 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1221 checkselection->lower = min(checkselection->lower,
1222 checkselection2->lower);
1223 checkselection->upper = max(checkselection->upper,
1224 checkselection2->upper);
1225 COMCTL32_Free(checkselection2);
1226 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1230 while (mergeindex > -1 && mergeindex <index);
1232 /* merge now common selection ranges in the upper group*/
1235 checkselection->upper ++;
1236 if (checkselection->lower == 0)
1239 checkselection->lower --;
1241 TRACE("search upper range %i (%lu - %lu)\n",index,
1242 checkselection->lower, checkselection->upper);
1244 /* not sorted yet */
1245 mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1247 LISTVIEW_CompareSelectionRanges, 0,
1250 checkselection->upper --;
1254 checkselection->lower ++;
1256 if (mergeindex >=0 && mergeindex !=index)
1258 TRACE("Merge with index %i\n",mergeindex);
1259 checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1261 checkselection->lower = min(checkselection->lower,
1262 checkselection2->lower);
1263 checkselection->upper = max(checkselection->upper,
1264 checkselection2->upper);
1265 COMCTL32_Free(checkselection2);
1266 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1269 while (mergeindex > -1);
1274 index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1275 LISTVIEW_CompareSelectionRanges, 0,
1278 TRACE("Insert before index %i\n",index);
1281 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1286 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1291 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1292 LISTVIEW_PrintSelectionRanges(hwnd);
1297 * check if a specified index is selected.
1300 * [I] HWND : window handle
1301 * [I] INT : item index
1306 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1308 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1309 LISTVIEW_SELECTION selection;
1312 selection.upper = nItem;
1313 selection.lower = nItem;
1315 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1316 LISTVIEW_CompareSelectionRanges,
1326 * Removes all selection ranges
1329 * HWND: window handle
1335 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1337 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1338 LISTVIEW_SELECTION *selection;
1342 TRACE("(0x%x)\n",hwnd);
1344 ZeroMemory(&item,sizeof(LVITEMA));
1345 item.stateMask = LVIS_SELECTED;
1349 selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1352 TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1353 for (i = selection->lower; i<=selection->upper; i++)
1354 LISTVIEW_SetItemState(hwnd,i,&item);
1357 while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1366 * Removes a range selections.
1369 * [I] HWND : window handle
1370 * [I] INT : lower item index
1371 * [I] INT : upper item index
1376 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1378 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1379 LISTVIEW_SELECTION removeselection,*checkselection;
1382 removeselection.lower = lItem;
1383 removeselection.upper = uItem;
1385 TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1386 LISTVIEW_PrintSelectionRanges(hwnd);
1388 index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1389 LISTVIEW_CompareSelectionRanges,
1396 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1399 TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1400 checkselection->upper);
1403 if ((checkselection->upper == removeselection.upper) &&
1404 (checkselection->lower == removeselection.lower))
1406 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1409 /* case 2: engulf */
1410 else if (((checkselection->upper < removeselection.upper) &&
1411 (checkselection->lower > removeselection.lower))||
1412 ((checkselection->upper <= removeselection.upper) &&
1413 (checkselection->lower > removeselection.lower)) ||
1414 ((checkselection->upper < removeselection.upper) &&
1415 (checkselection->lower >= removeselection.lower)))
1418 DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1419 /* do it again because others may also get caught */
1421 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1423 /* case 3: overlap upper */
1424 else if ((checkselection->upper < removeselection.upper) &&
1425 (checkselection->lower < removeselection.lower))
1427 checkselection->upper = removeselection.lower - 1;
1429 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1431 /* case 4: overlap lower */
1432 else if ((checkselection->upper > removeselection.upper) &&
1433 (checkselection->lower > removeselection.lower))
1435 checkselection->lower = removeselection.upper + 1;
1437 LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1439 /* case 5: fully internal */
1440 else if (checkselection->upper == removeselection.upper)
1441 checkselection->upper = removeselection.lower - 1;
1442 else if (checkselection->lower == removeselection.lower)
1443 checkselection->lower = removeselection.upper + 1;
1446 /* bisect the range */
1447 LISTVIEW_SELECTION *newselection;
1449 newselection = (LISTVIEW_SELECTION *)
1450 COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1451 newselection -> lower = checkselection->lower;
1452 newselection -> upper = removeselection.lower - 1;
1453 checkselection -> lower = removeselection.upper + 1;
1454 DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1456 DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1458 LISTVIEW_PrintSelectionRanges(hwnd);
1463 * shifts all selection indexs starting with the indesx specified
1464 * in the direction specified.
1467 * [I] HWND : window handle
1468 * [I] INT : item index
1469 * [I] INT : amount and direction of shift
1474 static VOID LISTVIEW_ShiftSelections(HWND hwnd, INT nItem, INT direction)
1476 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1477 LISTVIEW_SELECTION selection,*checkselection;
1480 selection.upper = nItem;
1481 selection.lower = nItem;
1483 index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1484 LISTVIEW_CompareSelectionRanges,
1485 0,DPAS_SORTED|DPAS_INSERTAFTER);
1487 while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1489 checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1490 if (checkselection->lower >= nItem)
1491 checkselection->lower += direction;
1492 if (checkselection->upper >= nItem)
1493 checkselection->upper += direction;
1501 * Adds a block of selections.
1504 * [I] HWND : window handle
1505 * [I] INT : item index
1510 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1512 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1513 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1514 INT nLast = max(infoPtr->nSelectionMark, nItem);
1518 ZeroMemory(&item,sizeof(LVITEMA));
1519 item.stateMask = LVIS_SELECTED;
1520 item.state = LVIS_SELECTED;
1522 for (i = nFirst; i <= nLast; i++);
1524 LISTVIEW_SetItemState(hwnd,i,&item);
1527 LISTVIEW_SetItemFocus(hwnd, nItem);
1528 infoPtr->nSelectionMark = nItem;
1534 * Adds a single selection.
1537 * [I] HWND : window handle
1538 * [I] INT : item index
1543 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1545 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1548 ZeroMemory(&item,sizeof(LVITEMA));
1549 item.state = LVIS_SELECTED;
1550 item.stateMask = LVIS_SELECTED;
1552 LISTVIEW_SetItemState(hwnd,nItem,&item);
1554 LISTVIEW_SetItemFocus(hwnd, nItem);
1555 infoPtr->nSelectionMark = nItem;
1560 * Selects or unselects an item.
1563 * [I] HWND : window handle
1564 * [I] INT : item index
1570 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1572 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1576 ZeroMemory(&item,sizeof(LVITEMA));
1577 item.stateMask = LVIS_SELECTED;
1579 if (LISTVIEW_IsSelected(hwnd,nItem))
1582 LISTVIEW_SetItemState(hwnd,nItem,&item);
1587 item.state = LVIS_SELECTED;
1588 LISTVIEW_SetItemState(hwnd,nItem,&item);
1592 LISTVIEW_SetItemFocus(hwnd, nItem);
1593 infoPtr->nSelectionMark = nItem;
1600 * Selects items based on view coorddiantes.
1603 * [I] HWND : window handle
1604 * [I] RECT : selection rectangle
1609 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
1611 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1616 ZeroMemory(&item,sizeof(LVITEMA));
1617 item.stateMask = LVIS_SELECTED;
1619 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1621 LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
1623 if (PtInRect(&rcSelRect, ptItem) != FALSE)
1625 item.state = LVIS_SELECTED;
1626 LISTVIEW_SetItemState(hwnd,i,&item);
1631 LISTVIEW_SetItemState(hwnd,i,&item);
1638 * Sets a single group selection.
1641 * [I] HWND : window handle
1642 * [I] INT : item index
1647 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
1649 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1650 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1653 ZeroMemory(&item,sizeof(LVITEMA));
1654 item.stateMask = LVIS_SELECTED;
1656 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
1659 INT nFirst = min(infoPtr->nSelectionMark, nItem);
1660 INT nLast = max(infoPtr->nSelectionMark, nItem);
1662 for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
1664 if ((i < nFirst) || (i > nLast))
1667 LISTVIEW_SetItemState(hwnd,i,&item);
1671 item.state = LVIS_SELECTED;
1672 LISTVIEW_SetItemState(hwnd,i,&item);
1681 LISTVIEW_GetItemPosition(hwnd, nItem, &ptItem);
1682 LISTVIEW_GetItemPosition(hwnd, infoPtr->nSelectionMark, &ptSelMark);
1683 rcSel.left = min(ptSelMark.x, ptItem.x);
1684 rcSel.top = min(ptSelMark.y, ptItem.y);
1685 rcSel.right = max(ptSelMark.x, ptItem.x) + infoPtr->nItemWidth;
1686 rcSel.bottom = max(ptSelMark.y, ptItem.y) + infoPtr->nItemHeight;
1687 LISTVIEW_SetSelectionRect(hwnd, rcSel);
1690 LISTVIEW_SetItemFocus(hwnd, nItem);
1695 * Manages the item focus.
1698 * [I] HWND : window handle
1699 * [I] INT : item index
1702 * TRUE : focused item changed
1703 * FALSE : focused item has NOT changed
1705 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
1707 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1708 BOOL bResult = FALSE;
1711 if (infoPtr->nFocusedItem != nItem)
1713 if (infoPtr->nFocusedItem >= 0)
1715 INT oldFocus = infoPtr->nFocusedItem;
1717 infoPtr->nFocusedItem = -1;
1718 ZeroMemory(&lvItem, sizeof(LVITEMA));
1719 lvItem.stateMask = LVIS_FOCUSED;
1720 ListView_SetItemState(hwnd, oldFocus, &lvItem);
1724 lvItem.state = LVIS_FOCUSED;
1725 lvItem.stateMask = LVIS_FOCUSED;
1726 ListView_SetItemState(hwnd, nItem, &lvItem);
1728 infoPtr->nFocusedItem = nItem;
1729 ListView_EnsureVisible(hwnd, nItem, FALSE);
1737 * Sets a single selection.
1740 * [I] HWND : window handle
1741 * [I] INT : item index
1746 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
1748 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1751 ZeroMemory(&lvItem, sizeof(LVITEMA));
1752 lvItem.stateMask = LVIS_FOCUSED;
1753 ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
1755 LISTVIEW_RemoveAllSelections(hwnd);
1757 lvItem.state = LVIS_FOCUSED|LVIS_SELECTED;
1758 lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
1759 ListView_SetItemState(hwnd, nItem, &lvItem);
1761 infoPtr->nFocusedItem = nItem;
1762 infoPtr->nSelectionMark = nItem;
1767 * Set selection(s) with keyboard.
1770 * [I] HWND : window handle
1771 * [I] INT : item index
1774 * SUCCESS : TRUE (needs to be repainted)
1775 * FAILURE : FALSE (nothing has changed)
1777 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
1779 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1780 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1781 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
1782 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
1783 BOOL bResult = FALSE;
1785 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
1787 if (lStyle & LVS_SINGLESEL)
1790 LISTVIEW_SetSelection(hwnd, nItem);
1791 ListView_EnsureVisible(hwnd, nItem, FALSE);
1798 LISTVIEW_SetGroupSelection(hwnd, nItem);
1802 bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
1807 LISTVIEW_SetSelection(hwnd, nItem);
1808 ListView_EnsureVisible(hwnd, nItem, FALSE);
1818 * Called when the mouse is being actively tracked and has hovered for a specified
1822 * [I] HWND : window handle
1823 * [I] wParam : key indicator
1824 * [I] lParam : mouse position
1827 * 0 if the message was processed, non-zero if there was an error
1830 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
1831 * over the item for a certain period of time.
1834 static LRESULT LISTVIEW_MouseHover(hwnd, wParam, lParam)
1836 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1839 pt.x = (INT)LOWORD(lParam);
1840 pt.y = (INT)HIWORD(lParam);
1842 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
1843 /* select the item under the cursor */
1844 LISTVIEW_MouseSelection(hwnd, pt);
1852 * Called whenever WM_MOUSEMOVE is recieved.
1855 * [I] HWND : window handle
1856 * [I] wParam : key indicators
1857 * [I] lParam : cursor position
1860 * 0 if the message is processed, non-zero if there was an error
1862 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
1864 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1865 TRACKMOUSEEVENT trackinfo;
1867 /* see if we are supposed to be tracking mouse hovering */
1868 if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
1869 /* fill in the trackinfo struct */
1870 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
1871 trackinfo.dwFlags = TME_QUERY;
1872 trackinfo.hwndTrack = hwnd;
1873 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
1875 /* see if we are already tracking this hwnd */
1876 _TrackMouseEvent(&trackinfo);
1878 if(!(trackinfo.dwFlags & TME_HOVER)) {
1879 trackinfo.dwFlags = TME_HOVER;
1881 /* call TRACKMOUSEEVENT so we recieve WM_MOUSEHOVER messages */
1882 _TrackMouseEvent(&trackinfo);
1891 * Selects an item based on coordinates.
1894 * [I] HWND : window handle
1895 * [I] POINT : mouse click ccordinates
1898 * SUCCESS : item index
1901 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
1903 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1905 INT i,topindex,bottomindex;
1906 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1907 UINT uView = lStyle & LVS_TYPEMASK;
1909 topindex = ListView_GetTopIndex(hwnd);
1910 if (uView == LVS_REPORT)
1912 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
1913 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
1917 bottomindex = GETITEMCOUNT(infoPtr);
1920 for (i = topindex; i < bottomindex; i++)
1922 rcItem.left = LVIR_SELECTBOUNDS;
1923 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
1925 if (PtInRect(&rcItem, pt) != FALSE)
1940 * [IO] HDPA : dynamic pointer array handle
1941 * [I] INT : column index (subitem index)
1947 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
1949 BOOL bResult = TRUE;
1953 for (i = 0; i < hdpaItems->nItemCount; i++)
1955 hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
1956 if (hdpaSubItems != NULL)
1958 if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
1970 * Removes a subitem at a given position.
1973 * [IO] HDPA : dynamic pointer array handle
1974 * [I] INT : subitem index
1980 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
1982 LISTVIEW_SUBITEM *lpSubItem;
1985 for (i = 1; i < hdpaSubItems->nItemCount; i++)
1987 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
1988 if (lpSubItem != NULL)
1990 if (lpSubItem->iSubItem == nSubItem)
1993 if ((lpSubItem->pszText != NULL) &&
1994 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
1996 COMCTL32_Free(lpSubItem->pszText);
2000 COMCTL32_Free(lpSubItem);
2002 /* free dpa memory */
2003 if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2008 else if (lpSubItem->iSubItem > nSubItem)
2020 * Compares the item information.
2023 * [I] LISTVIEW_ITEM *: destination item
2024 * [I] LPLVITEM : source item
2027 * SUCCCESS : TRUE (EQUAL)
2028 * FAILURE : FALSE (NOT EQUAL)
2030 static UINT LISTVIEW_GetItemChanges(LISTVIEW_ITEM *lpItem, LPLVITEMA lpLVItem)
2034 if ((lpItem != NULL) && (lpLVItem != NULL))
2036 if (lpLVItem->mask & LVIF_STATE)
2038 if ((lpItem->state & lpLVItem->stateMask) !=
2039 (lpLVItem->state & lpLVItem->stateMask))
2041 uChanged |= LVIF_STATE;
2045 if (lpLVItem->mask & LVIF_IMAGE)
2047 if (lpItem->iImage != lpLVItem->iImage)
2049 uChanged |= LVIF_IMAGE;
2053 if (lpLVItem->mask & LVIF_PARAM)
2055 if (lpItem->lParam != lpLVItem->lParam)
2057 uChanged |= LVIF_PARAM;
2061 if (lpLVItem->mask & LVIF_INDENT)
2063 if (lpItem->iIndent != lpLVItem->iIndent)
2065 uChanged |= LVIF_INDENT;
2069 if (lpLVItem->mask & LVIF_TEXT)
2071 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA)
2073 if (lpItem->pszText != LPSTR_TEXTCALLBACKA)
2075 uChanged |= LVIF_TEXT;
2080 if (lpItem->pszText == LPSTR_TEXTCALLBACKA)
2082 uChanged |= LVIF_TEXT;
2086 if (lpLVItem->pszText)
2088 if (lpItem->pszText)
2090 if (strcmp(lpLVItem->pszText, lpItem->pszText) != 0)
2092 uChanged |= LVIF_TEXT;
2097 uChanged |= LVIF_TEXT;
2102 if (lpItem->pszText)
2104 uChanged |= LVIF_TEXT;
2116 * Initializes item attributes.
2119 * [I] HWND : window handle
2120 * [O] LISTVIEW_ITEM *: destination item
2121 * [I] LPLVITEM : source item
2127 static BOOL LISTVIEW_InitItem(HWND hwnd, LISTVIEW_ITEM *lpItem,
2130 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2131 BOOL bResult = FALSE;
2133 if ((lpItem != NULL) && (lpLVItem != NULL))
2137 if (lpLVItem->mask & LVIF_STATE)
2139 lpItem->state &= ~lpLVItem->stateMask;
2140 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2143 if (lpLVItem->mask & LVIF_IMAGE)
2145 lpItem->iImage = lpLVItem->iImage;
2148 if (lpLVItem->mask & LVIF_PARAM)
2150 lpItem->lParam = lpLVItem->lParam;
2153 if (lpLVItem->mask & LVIF_INDENT)
2155 lpItem->iIndent = lpLVItem->iIndent;
2158 if (lpLVItem->mask & LVIF_TEXT)
2160 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA)
2162 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2167 if ((lpItem->pszText != NULL) &&
2168 (lpItem->pszText != LPSTR_TEXTCALLBACKA))
2170 COMCTL32_Free(lpItem->pszText);
2173 lpItem->pszText = LPSTR_TEXTCALLBACKA;
2177 if (lpItem->pszText == LPSTR_TEXTCALLBACKA)
2179 lpItem->pszText = NULL;
2182 bResult = Str_SetPtrA(&lpItem->pszText, lpLVItem->pszText);
2192 * Initializes subitem attributes.
2194 * NOTE: The documentation specifies that the operation fails if the user
2195 * tries to set the indent of a subitem.
2198 * [I] HWND : window handle
2199 * [O] LISTVIEW_SUBITEM *: destination subitem
2200 * [I] LPLVITEM : source subitem
2206 static BOOL LISTVIEW_InitSubItem(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2209 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2210 BOOL bResult = FALSE;
2212 if ((lpSubItem != NULL) && (lpLVItem != NULL))
2214 if (!(lpLVItem->mask & LVIF_INDENT))
2218 lpSubItem->iSubItem = lpLVItem->iSubItem;
2220 if (lpLVItem->mask & LVIF_IMAGE)
2222 lpSubItem->iImage = lpLVItem->iImage;
2225 if (lpLVItem->mask & LVIF_TEXT)
2227 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA)
2229 if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2234 if ((lpSubItem->pszText != NULL) &&
2235 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
2237 COMCTL32_Free(lpSubItem->pszText);
2240 lpSubItem->pszText = LPSTR_TEXTCALLBACKA;
2244 if (lpSubItem->pszText == LPSTR_TEXTCALLBACKA)
2246 lpSubItem->pszText = NULL;
2249 bResult = Str_SetPtrA(&lpSubItem->pszText, lpLVItem->pszText);
2260 * Adds a subitem at a given position (column index).
2263 * [I] HWND : window handle
2264 * [I] LPLVITEM : new subitem atttributes
2270 static BOOL LISTVIEW_AddSubItem(HWND hwnd, LPLVITEMA lpLVItem)
2272 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2273 LISTVIEW_SUBITEM *lpSubItem = NULL;
2274 BOOL bResult = FALSE;
2276 INT nPosition, nItem;
2277 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2279 if (lStyle & LVS_OWNERDATA)
2282 if (lpLVItem != NULL)
2284 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2285 if (hdpaSubItems != NULL)
2287 lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2288 if (lpSubItem != NULL)
2290 ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2291 if (LISTVIEW_InitSubItem(hwnd, lpSubItem, lpLVItem) != FALSE)
2293 nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2294 lpSubItem->iSubItem);
2295 nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2305 /* cleanup if unsuccessful */
2306 if ((bResult == FALSE) && (lpSubItem != NULL))
2308 COMCTL32_Free(lpSubItem);
2316 * Finds the dpa insert position (array index).
2319 * [I] HWND : window handle
2320 * [I] INT : subitem index
2326 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2328 LISTVIEW_SUBITEM *lpSubItem;
2331 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2333 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2334 if (lpSubItem != NULL)
2336 if (lpSubItem->iSubItem > nSubItem)
2343 return hdpaSubItems->nItemCount;
2348 * Retrieves a listview subitem at a given position (column index).
2351 * [I] HWND : window handle
2352 * [I] INT : subitem index
2358 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2360 LISTVIEW_SUBITEM *lpSubItem;
2363 for (i = 1; i < hdpaSubItems->nItemCount; i++)
2365 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2366 if (lpSubItem != NULL)
2368 if (lpSubItem->iSubItem == nSubItem)
2372 else if (lpSubItem->iSubItem > nSubItem)
2384 * Sets item attributes.
2387 * [I] HWND : window handle
2388 * [I] LPLVITEM : new item atttributes
2394 static BOOL LISTVIEW_SetItem(HWND hwnd, LPLVITEMA lpLVItem)
2396 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2397 BOOL bResult = FALSE;
2399 LISTVIEW_ITEM *lpItem;
2401 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
2402 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2404 UINT uView = lStyle & LVS_TYPEMASK;
2408 if (lStyle & LVS_OWNERDATA)
2410 if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2414 ZeroMemory(&itm,sizeof(LVITEMA));
2415 itm.mask = LVIF_STATE | LVIF_PARAM;
2416 itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2417 itm.iItem = lpLVItem->iItem;
2419 ListView_GetItemA(hwnd,&itm);
2422 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2423 nmlv.hdr.hwndFrom = hwnd;
2424 nmlv.hdr.idFrom = lCtrlId;
2425 nmlv.hdr.code = LVN_ITEMCHANGING;
2426 nmlv.uNewState = lpLVItem->state;
2427 nmlv.uOldState = itm.state;
2428 nmlv.uChanged = LVIF_STATE;
2429 nmlv.lParam = itm.lParam;
2430 nmlv.iItem = lpLVItem->iItem;
2432 if ((itm.state & lpLVItem->stateMask) !=
2433 (lpLVItem->state & lpLVItem->stateMask))
2435 /* send LVN_ITEMCHANGING notification */
2436 if (!ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv))
2438 if (lpLVItem->stateMask & LVIS_FOCUSED)
2440 if (lpLVItem->state & LVIS_FOCUSED)
2441 infoPtr->nFocusedItem = lpLVItem->iItem;
2442 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2443 infoPtr->nFocusedItem = -1;
2445 if (lpLVItem->stateMask & LVIS_SELECTED)
2447 if (lpLVItem->state & LVIS_SELECTED)
2448 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2450 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2454 nmlv.hdr.code = LVN_ITEMCHANGED;
2456 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2458 rcItem.left = LVIR_BOUNDS;
2459 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2460 InvalidateRect(hwnd, &rcItem, TRUE);
2468 if (lpLVItem != NULL)
2470 if (lpLVItem->iSubItem == 0)
2472 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2473 if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2475 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2478 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2479 nmlv.hdr.hwndFrom = hwnd;
2480 nmlv.hdr.idFrom = lCtrlId;
2481 nmlv.hdr.code = LVN_ITEMCHANGING;
2482 nmlv.lParam = lpItem->lParam;
2483 uChanged = LISTVIEW_GetItemChanges(lpItem, lpLVItem);
2486 if (uChanged & LVIF_STATE)
2488 nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2489 nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2491 if (nmlv.uNewState & LVIS_SELECTED)
2494 * This is redundent if called through SetSelection
2496 * however is required if the used directly calls SetItem
2497 * to set the selection.
2499 if (lStyle & LVS_SINGLESEL)
2501 LISTVIEW_RemoveAllSelections(hwnd);
2504 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2507 else if (lpLVItem->stateMask & LVIS_SELECTED)
2509 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2512 if (nmlv.uNewState & LVIS_FOCUSED)
2515 * This is a fun hoop to jump to try to catch if
2516 * the user is calling us directly to call focuse or if
2517 * this function is being called as a result of a
2518 * SetItemFocuse call.
2520 if (infoPtr->nFocusedItem >= 0)
2521 LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2525 nmlv.uChanged = uChanged;
2526 nmlv.iItem = lpLVItem->iItem;
2527 nmlv.lParam = lpItem->lParam;
2528 /* send LVN_ITEMCHANGING notification */
2529 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2531 /* copy information */
2532 bResult = LISTVIEW_InitItem(hwnd, lpItem, lpLVItem);
2534 /* if LVS_LIST or LVS_SMALLICON, update the width of the items based on */
2535 /* the width of the items text */
2536 if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2538 item_width = LISTVIEW_GetStringWidthA(hwnd, lpItem->pszText);
2540 if(item_width > infoPtr->nItemWidth)
2541 infoPtr->nItemWidth = item_width;
2544 /* send LVN_ITEMCHANGED notification */
2545 nmlv.hdr.code = LVN_ITEMCHANGED;
2546 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2555 rcItem.left = LVIR_BOUNDS;
2556 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2557 InvalidateRect(hwnd, &rcItem, TRUE);
2569 * Sets subitem attributes.
2572 * [I] HWND : window handle
2573 * [I] LPLVITEM : new subitem atttributes
2579 static BOOL LISTVIEW_SetSubItem(HWND hwnd, LPLVITEMA lpLVItem)
2581 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2582 BOOL bResult = FALSE;
2584 LISTVIEW_SUBITEM *lpSubItem;
2585 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2588 if (lStyle & LVS_OWNERDATA)
2591 if (lpLVItem != NULL)
2593 if (lpLVItem->iSubItem > 0)
2595 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2596 if (hdpaSubItems != NULL)
2598 /* set subitem only if column is present */
2599 if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2601 lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2602 if (lpSubItem != NULL)
2604 bResult = LISTVIEW_InitSubItem(hwnd, lpSubItem, lpLVItem);
2608 bResult = LISTVIEW_AddSubItem(hwnd, lpLVItem);
2611 rcItem.left = LVIR_BOUNDS;
2612 LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2613 InvalidateRect(hwnd, &rcItem, FALSE);
2624 * Retrieves the index of the item at coordinate (0, 0) of the client area.
2627 * [I] HWND : window handle
2632 static INT LISTVIEW_GetTopIndex(HWND hwnd)
2634 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2635 UINT uView = lStyle & LVS_TYPEMASK;
2637 SCROLLINFO scrollInfo;
2639 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2640 scrollInfo.cbSize = sizeof(SCROLLINFO);
2641 scrollInfo.fMask = SIF_POS;
2643 if (uView == LVS_LIST)
2645 if (lStyle & WS_HSCROLL)
2647 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
2649 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
2653 else if (uView == LVS_REPORT)
2655 if (lStyle & WS_VSCROLL)
2657 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
2659 nItem = scrollInfo.nPos;
2672 * [I] HWND : window handle
2673 * [I] HDC : device context handle
2674 * [I] INT : item index
2675 * [I] INT : subitem index
2676 * [I] RECT * : clipping rectangle
2681 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
2682 RECT rcItem, BOOL Selected)
2684 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2685 CHAR szDispText[DISP_TEXT_SIZE];
2688 TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
2691 /* get information needed for drawing the item */
2692 ZeroMemory(&lvItem, sizeof(LVITEMA));
2693 lvItem.mask = LVIF_TEXT;
2694 lvItem.iItem = nItem;
2695 lvItem.iSubItem = nSubItem;
2696 lvItem.cchTextMax = DISP_TEXT_SIZE;
2697 lvItem.pszText = szDispText;
2698 LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
2700 /* set item colors */
2701 if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED)
2702 &&(infoPtr->bFocus != FALSE) && Selected)
2704 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
2705 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2709 SetBkColor(hdc, infoPtr->clrTextBk);
2710 SetTextColor(hdc, infoPtr->clrText);
2713 ExtTextOutA(hdc, rcItem.left, rcItem.top, ETO_OPAQUE | ETO_CLIPPED,
2714 &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
2718 /* fill in the gap */
2720 if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
2722 CopyRect(&rec,&rcItem);
2723 rec.left = rec.right;
2724 rec.right = rec.left+REPORT_MARGINX;
2725 ExtTextOutA(hdc, rec.left , rec.top, ETO_OPAQUE | ETO_CLIPPED,
2726 &rec, NULL, 0, NULL);
2728 CopyRect(&rec,&rcItem);
2729 rec.right = rec.left;
2730 rec.left = rec.left - REPORT_MARGINX;
2731 ExtTextOutA(hdc, rec.left , rec.top, ETO_OPAQUE | ETO_CLIPPED,
2732 &rec, NULL, 0, NULL);
2742 * [I] HWND : window handle
2743 * [I] HDC : device context handle
2744 * [I] INT : item index
2745 * [I] RECT * : clipping rectangle
2750 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
2752 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2753 CHAR szDispText[DISP_TEXT_SIZE];
2758 DWORD dwTextColor,dwTextX;
2759 BOOL bImage = FALSE;
2762 TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
2765 /* get information needed for drawing the item */
2766 ZeroMemory(&lvItem, sizeof(LVITEMA));
2767 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
2768 lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK;
2769 lvItem.iItem = nItem;
2770 lvItem.iSubItem = 0;
2771 lvItem.cchTextMax = DISP_TEXT_SIZE;
2772 lvItem.pszText = szDispText;
2773 LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
2776 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
2778 rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
2781 SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
2785 if (infoPtr->himlState != NULL)
2787 UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
2788 if (uStateImage > 0)
2790 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
2791 rcItem.top, ILD_NORMAL);
2794 rcItem.left += infoPtr->iconSize.cx;
2796 SuggestedFocus->left += infoPtr->iconSize.cx;
2801 if (infoPtr->himlSmall != NULL)
2803 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
2806 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
2807 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
2808 rcItem.top, ILD_SELECTED);
2810 else if (lvItem.iImage>=0)
2812 ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
2813 ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
2814 rcItem.top, ILD_NORMAL);
2817 rcItem.left += infoPtr->iconSize.cx;
2820 SuggestedFocus->left += infoPtr->iconSize.cx;
2824 /* Don't bother painting item being edited */
2825 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect)
2828 if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
2830 /* set item colors */
2831 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
2832 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2833 /* set raster mode */
2834 nMixMode = SetROP2(hdc, R2_XORPEN);
2836 else if ((GetWindowLongA(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
2837 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
2839 dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
2840 dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
2841 /* set raster mode */
2842 nMixMode = SetROP2(hdc, R2_COPYPEN);
2846 /* set item colors */
2847 dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
2848 dwTextColor = SetTextColor(hdc, infoPtr->clrText);
2849 /* set raster mode */
2850 nMixMode = SetROP2(hdc, R2_COPYPEN);
2853 nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
2854 if (rcItem.left + nLabelWidth < rcItem.right)
2857 rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
2859 rcItem.right += IMAGE_PADDING;
2863 dwTextX = rcItem.left + 1;
2865 dwTextX += IMAGE_PADDING;
2866 ExtTextOutA(hdc, dwTextX, rcItem.top, ETO_OPAQUE | ETO_CLIPPED,
2867 &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
2869 if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
2871 /* fill in the gap */
2873 CopyRect(&rec,&rcItem);
2874 rec.left = rec.right;
2875 rec.right = rec.left+REPORT_MARGINX;
2876 ExtTextOutA(hdc, rec.left , rec.top, ETO_OPAQUE | ETO_CLIPPED,
2877 &rec, NULL, 0, NULL);
2881 CopyRect(SuggestedFocus,&rcItem);
2885 SetROP2(hdc, R2_COPYPEN);
2886 SetBkColor(hdc, infoPtr->clrTextBk);
2887 SetTextColor(hdc, infoPtr->clrText);
2893 * Draws an item when in large icon display mode.
2896 * [I] HWND : window handle
2897 * [I] HDC : device context handle
2898 * [I] LISTVIEW_ITEM * : item
2899 * [I] INT : item index
2900 * [I] RECT * : clipping rectangle
2905 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
2906 RECT *SuggestedFocus)
2908 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2909 CHAR szDispText[DISP_TEXT_SIZE];
2910 INT nDrawPosX = rcItem.left;
2911 INT nLabelWidth, rcWidth;
2915 TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, \
2916 bottom=%d)\n", hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right,
2919 /* get information needed for drawing the item */
2920 ZeroMemory(&lvItem, sizeof(LVITEMA));
2921 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
2922 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
2923 lvItem.iItem = nItem;
2924 lvItem.iSubItem = 0;
2925 lvItem.cchTextMax = DISP_TEXT_SIZE;
2926 lvItem.pszText = szDispText;
2927 LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
2929 if (lvItem.state & LVIS_SELECTED)
2931 /* set item colors */
2932 SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
2933 SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2934 /* set raster mode */
2935 SetROP2(hdc, R2_XORPEN);
2939 /* set item colors */
2940 SetBkColor(hdc, infoPtr->clrTextBk);
2941 SetTextColor(hdc, infoPtr->clrText);
2942 /* set raster mode */
2943 SetROP2(hdc, R2_COPYPEN);
2946 if (infoPtr->himlNormal != NULL)
2948 rcItem.top += ICON_TOP_PADDING;
2949 nDrawPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
2950 if ((lvItem.state & LVIS_SELECTED) && (lvItem.iImage>=0))
2952 ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX,
2953 rcItem.top, ILD_SELECTED);
2955 else if (lvItem.iImage>=0)
2957 ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX,
2958 rcItem.top, ILD_NORMAL);
2962 /* Don't bother painting item being edited */
2963 if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED)
2966 InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
2967 rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2968 nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
2969 GetTextMetricsA(hdc, &tm);
2971 /* append an ellipse ('...') if the caption won't fit in the rect */
2972 rcWidth = max(0, rcItem.right - rcItem.left);
2973 if (nLabelWidth > rcWidth)
2975 INT i, len, eos, nCharsFit;
2976 /* give or take a couple, how many average sized chars would fit? */
2977 nCharsFit = tm.tmAveCharWidth > 0 ? (rcWidth/tm.tmAveCharWidth)+2 : 0;
2978 /* place the ellipse accordingly, without overrunning the buffer */
2979 len = strlen(szDispText);
2980 eos = min((nCharsFit > 1 && nCharsFit < len) ? nCharsFit+3 : len+2,
2981 sizeof(szDispText)-1);
2983 nLabelWidth = ListView_GetStringWidthA(hwnd, szDispText);
2984 while ((nLabelWidth > rcWidth) && (eos > 3))
2986 for (i = 1; i < 4; i++)
2987 szDispText[eos-i] = '.';
2988 /* shift the ellipse one char to the left for each iteration */
2989 szDispText[eos--] = '\0';
2990 nLabelWidth = ListView_GetStringWidthA(hwnd, szDispText);
2994 InflateRect(&rcItem, 2*CAPTION_BORDER, 0);
2995 nDrawPosX = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
2998 rcItem.left += nDrawPosX / 2;
2999 rcItem.right = rcItem.left + nLabelWidth + 2*CAPTION_BORDER;
3004 rcItem.right = rcItem.left + infoPtr->iconSpacing.cx - 1;
3008 rcItem.bottom = rcItem.top + tm.tmHeight + HEIGHT_PADDING;
3009 ExtTextOutA(hdc, rcItem.left + CAPTION_BORDER, rcItem.top, ETO_OPAQUE | ETO_CLIPPED,
3010 &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
3013 CopyRect(SuggestedFocus,&rcItem);
3018 * Draws listview items when in report display mode.
3021 * [I] HWND : window handle
3022 * [I] HDC : device context handle
3027 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc)
3029 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3030 SCROLLINFO scrollInfo;
3031 INT nDrawPosY = infoPtr->rcList.top;
3039 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3040 scrollInfo.cbSize = sizeof(SCROLLINFO);
3041 scrollInfo.fMask = SIF_POS;
3043 nItem = ListView_GetTopIndex(hwnd);
3045 /* add 1 for displaying a partial item at the bottom */
3046 nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3047 nLast = min(nLast, GETITEMCOUNT(infoPtr));
3049 /* send cache hint notification */
3050 if (GetWindowLongA(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3054 nmlv.hdr.hwndFrom = hwnd;
3055 nmlv.hdr.idFrom = GetWindowLongA(hwnd,GWL_ID);
3056 nmlv.hdr.code = LVN_ODCACHEHINT;
3060 SendMessageA(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3064 nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3065 FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3067 for (; nItem < nLast; nItem++)
3069 RECT SuggestedFocusRect;
3075 Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3076 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3078 ir.left += REPORT_MARGINX;
3079 ir.right = max(ir.left, br.right - REPORT_MARGINX);
3081 ir.bottom = ir.top + infoPtr->nItemHeight;
3083 CopyRect(&SuggestedFocusRect,&ir);
3086 for (j = 0; j < nColumnCount; j++)
3088 Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3090 rcItem.left += REPORT_MARGINX;
3091 rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3092 rcItem.top = nDrawPosY;
3093 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3095 /* Offset the Scroll Bar Pos */
3096 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
3098 rcItem.left -= (scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE);
3099 rcItem.right -= (scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE);
3104 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3105 &SuggestedFocusRect);
3109 LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem,
3116 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3119 if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3120 rop = SetROP2(hdc, R2_XORPEN);
3122 Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3123 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3126 SetROP2(hdc, R2_COPYPEN);
3128 nDrawPosY += infoPtr->nItemHeight;
3134 * Retrieves the number of items that can fit vertically in the client area.
3137 * [I] HWND : window handle
3140 * Number of items per row.
3142 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3144 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3145 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3146 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3147 INT nCountPerRow = 1;
3151 if (uView == LVS_REPORT)
3157 nCountPerRow = nListWidth / infoPtr->nItemWidth;
3158 if (nCountPerRow == 0)
3165 return nCountPerRow;
3170 * Retrieves the number of items that can fit horizontally in the client
3174 * [I] HWND : window handle
3177 * Number of items per column.
3179 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3181 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
3182 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3183 INT nCountPerColumn = 1;
3185 if (nListHeight > 0)
3187 nCountPerColumn = nListHeight / infoPtr->nItemHeight;
3188 if (nCountPerColumn == 0)
3190 nCountPerColumn = 1;
3194 return nCountPerColumn;
3199 * Retrieves the number of columns needed to display all the items when in
3200 * list display mode.
3203 * [I] HWND : window handle
3206 * Number of columns.
3208 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3210 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3211 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3212 INT nColumnCount = 0;
3214 if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3216 if (infoPtr->rcList.right % infoPtr->nItemWidth == 0)
3218 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3222 nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth + 1;
3226 return nColumnCount;
3232 * Draws listview items when in list display mode.
3235 * [I] HWND : window handle
3236 * [I] HDC : device context handle
3241 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc)
3243 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3244 RECT rcItem,FocusRect;
3248 INT nCountPerColumn;
3249 INT nItemWidth = infoPtr->nItemWidth;
3250 INT nItemHeight = infoPtr->nItemHeight;
3252 /* get number of fully visible columns */
3253 nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3254 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3255 nItem = ListView_GetTopIndex(hwnd);
3257 for (i = 0; i < nColumnCount; i++)
3259 for (j = 0; j < nCountPerColumn; j++, nItem++)
3261 if (nItem >= GETITEMCOUNT(infoPtr))
3264 rcItem.top = j * nItemHeight;
3265 rcItem.left = i * nItemWidth;
3266 rcItem.bottom = rcItem.top + nItemHeight;
3267 rcItem.right = rcItem.left + nItemWidth;
3268 LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3272 if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3273 Rectangle(hdc, FocusRect.left, FocusRect.top,
3274 FocusRect.right,FocusRect.bottom);
3281 * Draws listview items when in icon or small icon display mode.
3284 * [I] HWND : window handle
3285 * [I] HDC : device context handle
3290 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall)
3292 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3295 RECT rcItem,SuggestedFocus;
3298 LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3299 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3301 LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3302 ptPosition.x += ptOrigin.x;
3303 ptPosition.y += ptOrigin.y;
3305 if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3307 if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3309 if (ptPosition.y < infoPtr->rcList.bottom)
3311 if (ptPosition.x < infoPtr->rcList.right)
3313 rcItem.top = ptPosition.y;
3314 rcItem.left = ptPosition.x;
3315 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3316 rcItem.right = rcItem.left + infoPtr->nItemWidth;
3317 if (bSmall == FALSE)
3319 LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3323 LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3328 if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3330 Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3331 SuggestedFocus.right,SuggestedFocus.bottom);
3341 * Draws listview items.
3344 * [I] HWND : window handle
3345 * [I] HDC : device context handle
3350 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3352 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3353 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3358 hOldFont = SelectObject(hdc, infoPtr->hFont);
3360 /* select the doted pen (for drawing the focus box) */
3361 hPen = CreatePen(PS_DOT, 1, 0);
3362 hOldPen = SelectObject(hdc, hPen);
3364 /* select transparent brush (for drawing the focus box) */
3365 SelectObject(hdc, GetStockObject(NULL_BRUSH));
3367 if (uView == LVS_LIST)
3369 LISTVIEW_RefreshList(hwnd, hdc);
3371 else if (uView == LVS_REPORT)
3373 LISTVIEW_RefreshReport(hwnd, hdc);
3375 else if (uView == LVS_SMALLICON)
3377 LISTVIEW_RefreshIcon(hwnd, hdc, TRUE);
3379 else if (uView == LVS_ICON)
3381 LISTVIEW_RefreshIcon(hwnd, hdc, FALSE);
3384 /* unselect objects */
3385 SelectObject(hdc, hOldFont);
3386 SelectObject(hdc, hOldPen);
3395 * Calculates the approximate width and height of a given number of items.
3398 * [I] HWND : window handle
3399 * [I] INT : number of items
3404 * Returns a DWORD. The width in the low word and the height in high word.
3406 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
3407 WORD wWidth, WORD wHeight)
3409 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3410 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3411 INT nItemCountPerColumn = 1;
3412 INT nColumnCount = 0;
3413 DWORD dwViewRect = 0;
3415 if (nItemCount == -1)
3417 nItemCount = GETITEMCOUNT(infoPtr);
3420 if (uView == LVS_LIST)
3422 if (wHeight == 0xFFFF)
3424 /* use current height */
3425 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3428 if (wHeight < infoPtr->nItemHeight)
3430 wHeight = infoPtr->nItemHeight;
3435 if (infoPtr->nItemHeight > 0)
3437 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
3438 if (nItemCountPerColumn == 0)
3440 nItemCountPerColumn = 1;
3443 if (nItemCount % nItemCountPerColumn != 0)
3445 nColumnCount = nItemCount / nItemCountPerColumn;
3449 nColumnCount = nItemCount / nItemCountPerColumn + 1;
3454 /* Microsoft padding magic */
3455 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
3456 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
3458 dwViewRect = MAKELONG(wWidth, wHeight);
3460 else if (uView == LVS_REPORT)
3464 else if (uView == LVS_SMALLICON)
3468 else if (uView == LVS_ICON)
3478 * Arranges listview items in icon display mode.
3481 * [I] HWND : window handle
3482 * [I] INT : alignment code
3488 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
3490 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3491 BOOL bResult = FALSE;
3493 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3506 case LVA_SNAPTOGRID:
3515 /* << LISTVIEW_CreateDragImage >> */
3520 * Removes all listview items and subitems.
3523 * [I] HWND : window handle
3529 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
3531 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3532 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
3533 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3534 UINT uView = lStyle & LVS_TYPEMASK;
3535 LISTVIEW_ITEM *lpItem;
3536 LISTVIEW_SUBITEM *lpSubItem;
3539 BOOL bResult = FALSE;
3544 TRACE("(hwnd=%x,)\n", hwnd);
3545 LISTVIEW_RemoveAllSelections(hwnd);
3547 if (lStyle & LVS_OWNERDATA)
3549 infoPtr->hdpaItems->nItemCount = 0;
3550 InvalidateRect(hwnd, NULL, TRUE);
3554 if (GETITEMCOUNT(infoPtr) > 0)
3556 /* initialize memory */
3557 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3559 /* send LVN_DELETEALLITEMS notification */
3560 nmlv.hdr.hwndFrom = hwnd;
3561 nmlv.hdr.idFrom = lCtrlId;
3562 nmlv.hdr.code = LVN_DELETEALLITEMS;
3565 /* verify if subsequent LVN_DELETEITEM notifications should be
3567 bSuppress = ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
3569 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3571 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
3572 if (hdpaSubItems != NULL)
3574 for (j = 1; j < hdpaSubItems->nItemCount; j++)
3576 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
3577 if (lpSubItem != NULL)
3579 /* free subitem string */
3580 if ((lpSubItem->pszText != NULL) &&
3581 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
3583 COMCTL32_Free(lpSubItem->pszText);
3587 COMCTL32_Free(lpSubItem);
3591 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3594 if (bSuppress == FALSE)
3596 /* send LVN_DELETEITEM notification */
3597 nmlv.hdr.code = LVN_DELETEITEM;
3599 nmlv.lParam = lpItem->lParam;
3600 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
3603 /* free item string */
3604 if ((lpItem->pszText != NULL) &&
3605 (lpItem->pszText != LPSTR_TEXTCALLBACKA))
3607 COMCTL32_Free(lpItem->pszText);
3611 COMCTL32_Free(lpItem);
3614 DPA_Destroy(hdpaSubItems);
3618 /* reinitialize listview memory */
3619 bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
3621 /* align items (set position of each item) */
3622 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
3624 if (lStyle & LVS_ALIGNLEFT)
3626 LISTVIEW_AlignLeft(hwnd);
3630 LISTVIEW_AlignTop(hwnd);
3634 LISTVIEW_UpdateScroll(hwnd);
3636 /* invalidate client area (optimization needed) */
3637 InvalidateRect(hwnd, NULL, TRUE);
3645 * Removes a column from the listview control.
3648 * [I] HWND : window handle
3649 * [I] INT : column index
3655 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
3657 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3658 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3659 BOOL bResult = FALSE;
3661 if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
3663 bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
3665 /* Need to reset the item width when deleting a column */
3666 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
3668 /* reset scroll parameters */
3669 if (uView == LVS_REPORT)
3671 /* update scrollbar(s) */
3672 LISTVIEW_UpdateScroll(hwnd);
3674 /* refresh client area */
3675 InvalidateRect(hwnd, NULL, FALSE);
3684 * Removes an item from the listview control.
3687 * [I] HWND : window handle
3688 * [I] INT : item index
3694 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
3696 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3697 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3698 UINT uView = lStyle & LVS_TYPEMASK;
3699 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
3701 BOOL bResult = FALSE;
3703 LISTVIEW_ITEM *lpItem;
3704 LISTVIEW_SUBITEM *lpSubItem;
3707 TRACE("(hwnd=%x,nItem=%d)\n", hwnd, nItem);
3709 LISTVIEW_ShiftSelections(hwnd,nItem,-1);
3711 if (lStyle & LVS_OWNERDATA)
3713 infoPtr->hdpaItems->nItemCount --;
3714 InvalidateRect(hwnd, NULL, TRUE);
3718 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
3720 /* initialize memory */
3721 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3723 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
3724 if (hdpaSubItems != NULL)
3726 for (i = 1; i < hdpaSubItems->nItemCount; i++)
3728 lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
3729 if (lpSubItem != NULL)
3731 /* free item string */
3732 if ((lpSubItem->pszText != NULL) &&
3733 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
3735 COMCTL32_Free(lpSubItem->pszText);
3739 COMCTL32_Free(lpSubItem);
3743 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3746 /* send LVN_DELETEITEM notification */
3747 nmlv.hdr.hwndFrom = hwnd;
3748 nmlv.hdr.idFrom = lCtrlId;
3749 nmlv.hdr.code = LVN_DELETEITEM;
3751 nmlv.lParam = lpItem->lParam;
3752 SendMessageA(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
3755 /* free item string */
3756 if ((lpItem->pszText != NULL) &&
3757 (lpItem->pszText != LPSTR_TEXTCALLBACKA))
3759 COMCTL32_Free(lpItem->pszText);
3763 COMCTL32_Free(lpItem);
3766 bResult = DPA_Destroy(hdpaSubItems);
3769 /* align items (set position of each item) */
3770 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
3772 if (lStyle & LVS_ALIGNLEFT)
3774 LISTVIEW_AlignLeft(hwnd);
3778 LISTVIEW_AlignTop(hwnd);
3782 /* If this item had focus change focus to next or previous item */
3783 if (GETITEMCOUNT(infoPtr) > 0)
3785 int sItem = nItem < GETITEMCOUNT(infoPtr) ? nItem : nItem - 1;
3786 if (infoPtr->nFocusedItem == nItem)
3787 LISTVIEW_SetItemFocus(hwnd, sItem);
3790 infoPtr->nFocusedItem = -1;
3792 LISTVIEW_UpdateScroll(hwnd);
3794 /* refresh client area */
3795 InvalidateRect(hwnd, NULL, TRUE);
3804 * Return edit control handle of current edit label
3807 * [I] HWND : window handle
3813 static LRESULT LISTVIEW_GetEditControl(hwnd)
3815 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3816 return infoPtr->hwndEdit;
3822 * Callback implementation for editlabel control
3825 * [I] HWND : window handle
3826 * [I] LPSTR : modified text
3827 * [I] DWORD : item index
3834 static BOOL LISTVIEW_EndEditLabel(HWND hwnd, LPSTR pszText, DWORD nItem)
3836 NMLVDISPINFOA dispInfo;
3837 LISTVIEW_ITEM *lpItem;
3838 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
3839 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3840 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3842 BOOL bUpdateItemText;
3843 LISTVIEW_ITEM lvItemRef;
3846 ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
3848 if (!(lStyle & LVS_OWNERDATA))
3850 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
3853 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
3858 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
3859 ZeroMemory(&item,sizeof(LVITEMA));
3862 item.mask = LVIF_PARAM | LVIF_STATE;
3863 ListView_GetItemA(hwnd,&item);
3864 lvItemRef.state = item.state;
3865 lvItemRef.iImage = item.iImage;
3866 lvItemRef.lParam = item.lParam;
3867 lpItem = &lvItemRef;
3870 dispInfo.hdr.hwndFrom = hwnd;
3871 dispInfo.hdr.idFrom = nCtrlId;
3872 dispInfo.hdr.code = LVN_ENDLABELEDITA;
3873 dispInfo.item.mask = 0;
3874 dispInfo.item.iItem = nItem;
3875 dispInfo.item.state = lpItem->state;
3876 dispInfo.item.stateMask = 0;
3877 dispInfo.item.pszText = pszText;
3878 dispInfo.item.cchTextMax = pszText ? strlen(pszText) : 0;
3879 dispInfo.item.iImage = lpItem->iImage;
3880 dispInfo.item.lParam = lpItem->lParam;
3881 infoPtr->hwndEdit = 0;
3883 bUpdateItemText = ListView_Notify(GetParent(hwnd), nCtrlId, &dispInfo);
3885 /* Do we need to update the Item Text */
3888 if ((lpItem->pszText != LPSTR_TEXTCALLBACKA)&&(!(lStyle & LVS_OWNERDATA)))
3890 Str_SetPtrA(&lpItem->pszText, pszText);
3899 * Begin in place editing of specified list view item
3902 * [I] HWND : window handle
3903 * [I] INT : item index
3910 static HWND LISTVIEW_EditLabelA(HWND hwnd, INT nItem)
3912 NMLVDISPINFOA dispInfo;
3914 LISTVIEW_ITEM *lpItem;
3916 HINSTANCE hinst = GetWindowLongA(hwnd, GWL_HINSTANCE);
3917 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
3918 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3920 CHAR szDispText[DISP_TEXT_SIZE];
3921 LVITEMA lvItem,item;
3922 LISTVIEW_ITEM lvItemRef;
3923 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
3925 if (~GetWindowLongA(hwnd, GWL_STYLE) & LVS_EDITLABELS)
3928 /* Is the EditBox still there, if so remove it */
3929 if(infoPtr->hwndEdit != 0)
3934 LISTVIEW_SetSelection(hwnd, nItem);
3935 LISTVIEW_SetItemFocus(hwnd, nItem);
3937 ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
3938 if (!(lStyle & LVS_OWNERDATA))
3940 if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
3943 if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
3948 ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
3949 ZeroMemory(&item,sizeof(LVITEMA));
3952 item.mask = LVIF_PARAM | LVIF_STATE;
3953 ListView_GetItemA(hwnd,&item);
3954 lvItemRef.iImage = item.iImage;
3955 lvItemRef.state = item.state;
3956 lvItemRef.lParam = item.lParam;
3957 lpItem = &lvItemRef;
3960 /* get information needed for drawing the item */
3961 ZeroMemory(&lvItem, sizeof(LVITEMA));
3962 lvItem.mask = LVIF_TEXT;
3963 lvItem.iItem = nItem;
3964 lvItem.iSubItem = 0;
3965 lvItem.cchTextMax = DISP_TEXT_SIZE;
3966 lvItem.pszText = szDispText;
3967 ListView_GetItemA(hwnd, &lvItem);
3969 dispInfo.hdr.hwndFrom = hwnd;
3970 dispInfo.hdr.idFrom = nCtrlId;
3971 dispInfo.hdr.code = LVN_BEGINLABELEDITA;
3972 dispInfo.item.mask = 0;
3973 dispInfo.item.iItem = nItem;
3974 dispInfo.item.state = lpItem->state;
3975 dispInfo.item.stateMask = 0;
3976 dispInfo.item.pszText = lvItem.pszText;
3977 dispInfo.item.cchTextMax = strlen(lvItem.pszText);
3978 dispInfo.item.iImage = lpItem->iImage;
3979 dispInfo.item.lParam = lpItem->lParam;
3981 if (ListView_LVNotify(GetParent(hwnd), nCtrlId, &dispInfo))
3984 rect.left = LVIR_LABEL;
3985 if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
3988 if (!(hedit = CreateEditLabel(szDispText , WS_VISIBLE,
3989 rect.left-2, rect.top-1, 0,
3990 rect.bottom - rect.top+2,
3991 hwnd, hinst, LISTVIEW_EndEditLabel, nItem)))
3994 infoPtr->hwndEdit = hedit;
3996 SendMessageA(hedit, EM_SETSEL, 0, -1);
4004 * Ensures the specified item is visible, scrolling into view if necessary.
4007 * [I] HWND : window handle
4008 * [I] INT : item index
4009 * [I] BOOL : partially or entirely visible
4015 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4017 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4018 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4019 INT nScrollPosHeight = 0;
4020 INT nScrollPosWidth = 0;
4021 SCROLLINFO scrollInfo;
4023 BOOL bRedraw = FALSE;
4025 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4026 scrollInfo.cbSize = sizeof(SCROLLINFO);
4027 scrollInfo.fMask = SIF_POS;
4029 /* ALWAYS bPartial == FALSE, FOR NOW! */
4031 rcItem.left = LVIR_BOUNDS;
4032 if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4034 if (rcItem.left < infoPtr->rcList.left)
4036 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4040 if (uView == LVS_LIST)
4042 nScrollPosWidth = infoPtr->nItemWidth;
4043 rcItem.left += infoPtr->rcList.left;
4045 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4047 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4048 rcItem.left += infoPtr->rcList.left;
4051 /* When in LVS_REPORT view, the scroll position should
4053 if (nScrollPosWidth != 0)
4055 if (rcItem.left % nScrollPosWidth == 0)
4057 scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4061 scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4064 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4068 else if (rcItem.right > infoPtr->rcList.right)
4070 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4074 if (uView == LVS_LIST)
4076 rcItem.right -= infoPtr->rcList.right;
4077 nScrollPosWidth = infoPtr->nItemWidth;
4079 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4081 rcItem.right -= infoPtr->rcList.right;
4082 nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE;
4085 /* When in LVS_REPORT view, the scroll position should
4087 if (nScrollPosWidth != 0)
4089 if (rcItem.right % nScrollPosWidth == 0)
4091 scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4095 scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4098 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4103 if (rcItem.top < infoPtr->rcList.top)
4107 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4109 if (uView == LVS_REPORT)
4111 rcItem.top -= infoPtr->rcList.top;
4112 nScrollPosHeight = infoPtr->nItemHeight;
4114 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4116 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4117 rcItem.top += infoPtr->rcList.top;
4120 if (rcItem.top % nScrollPosHeight == 0)
4122 scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4126 scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4129 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4132 else if (rcItem.bottom > infoPtr->rcList.bottom)
4136 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4138 if (uView == LVS_REPORT)
4140 rcItem.bottom -= infoPtr->rcList.bottom;
4141 nScrollPosHeight = infoPtr->nItemHeight;
4143 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4145 nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE;
4146 rcItem.bottom -= infoPtr->rcList.bottom;
4149 if (rcItem.bottom % nScrollPosHeight == 0)
4151 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4155 scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4158 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4164 InvalidateRect(hwnd,NULL,TRUE);
4170 * Retrieves the nearest item, given a position and a direction.
4173 * [I] HWND : window handle
4174 * [I] POINT : start position
4175 * [I] UINT : direction
4178 * Item index if successdful, -1 otherwise.
4180 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4182 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4183 LVHITTESTINFO lvHitTestInfo;
4187 if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4189 ZeroMemory(&lvHitTestInfo, sizeof(LVHITTESTINFO));
4190 LISTVIEW_GetOrigin(hwnd, &lvHitTestInfo.pt);
4191 lvHitTestInfo.pt.x += pt.x;
4192 lvHitTestInfo.pt.y += pt.y;
4196 if (vkDirection == VK_DOWN)
4198 lvHitTestInfo.pt.y += infoPtr->nItemHeight;
4200 else if (vkDirection == VK_UP)
4202 lvHitTestInfo.pt.y -= infoPtr->nItemHeight;
4204 else if (vkDirection == VK_LEFT)
4206 lvHitTestInfo.pt.x -= infoPtr->nItemWidth;
4208 else if (vkDirection == VK_RIGHT)
4210 lvHitTestInfo.pt.x += infoPtr->nItemWidth;
4213 if (PtInRect(&rcView, lvHitTestInfo.pt) == FALSE)
4219 nItem = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo);
4223 while (nItem == -1);
4231 * Searches for an item with specific characteristics.
4234 * [I] HWND : window handle
4235 * [I] INT : base item index
4236 * [I] LPLVFINDINFO : item information to look for
4239 * SUCCESS : index of item
4242 static LRESULT LISTVIEW_FindItem(HWND hwnd, INT nStart,
4243 LPLVFINDINFO lpFindInfo)
4245 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4247 CHAR szDispText[DISP_TEXT_SIZE];
4251 INT nLast = GETITEMCOUNT(infoPtr);
4253 if ((nItem >= -1) && (lpFindInfo != NULL))
4255 ZeroMemory(&lvItem, sizeof(LVITEMA));
4257 if (lpFindInfo->flags & LVFI_PARAM)
4259 lvItem.mask |= LVIF_PARAM;
4262 if (lpFindInfo->flags & LVFI_STRING)
4264 lvItem.mask |= LVIF_TEXT;
4265 lvItem.pszText = szDispText;
4266 lvItem.cchTextMax = DISP_TEXT_SIZE;
4269 if (lpFindInfo->flags & LVFI_PARTIAL)
4271 lvItem.mask |= LVIF_TEXT;
4272 lvItem.pszText = szDispText;
4273 lvItem.cchTextMax = DISP_TEXT_SIZE;
4276 if (lpFindInfo->flags & LVFI_WRAP)
4281 if (lpFindInfo->flags & LVFI_NEARESTXY)
4283 ptItem.x = lpFindInfo->pt.x;
4284 ptItem.y = lpFindInfo->pt.y;
4289 while (nItem < nLast)
4291 if (lpFindInfo->flags & LVFI_NEARESTXY)
4293 nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4294 lpFindInfo->vkDirection);
4297 /* get position of the new item index */
4298 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4309 lvItem.iItem = nItem;
4310 lvItem.iSubItem = 0;
4311 if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
4313 if (lvItem.mask & LVIF_TEXT)
4315 if (lpFindInfo->flags & LVFI_PARTIAL)
4317 if (strstr(lvItem.pszText, lpFindInfo->psz) == NULL)
4322 if (strcmp(lvItem.pszText, lpFindInfo->psz) != 0)
4327 if (lvItem.mask & LVIF_PARAM)
4329 if (lpFindInfo->lParam != lvItem.lParam)
4355 * Retrieves the background color of the listview control.
4358 * [I] HWND : window handle
4361 * COLORREF associated with the background.
4363 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4365 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4367 return infoPtr->clrBk;
4372 * Retrieves the background image of the listview control.
4375 * [I] HWND : window handle
4376 * [O] LPLVMKBIMAGE : background image attributes
4382 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */
4384 /* FIXME (listview, "empty stub!\n"); */
4390 * Retrieves the callback mask.
4393 * [I] HWND : window handle
4398 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
4400 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4402 return infoPtr->uCallbackMask;
4407 * Retrieves column attributes.
4410 * [I] HWND : window handle
4411 * [I] INT : column index
4412 * [IO] LPLVCOLUMNA : column information
4418 static LRESULT LISTVIEW_GetColumnA(HWND hwnd, INT nItem, LPLVCOLUMNA lpColumn)
4420 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4422 BOOL bResult = FALSE;
4424 if (lpColumn != NULL)
4426 /* initialize memory */
4427 ZeroMemory(&hdi, sizeof(HDITEMA));
4429 if (lpColumn->mask & LVCF_FMT)
4431 hdi.mask |= HDI_FORMAT;
4434 if (lpColumn->mask & LVCF_WIDTH)
4436 hdi.mask |= HDI_WIDTH;
4439 if (lpColumn->mask & LVCF_TEXT)
4441 hdi.mask |= HDI_TEXT;
4442 hdi.cchTextMax = lpColumn->cchTextMax;
4443 hdi.pszText = lpColumn->pszText;
4446 if (lpColumn->mask & LVCF_IMAGE)
4448 hdi.mask |= HDI_IMAGE;
4451 if (lpColumn->mask & LVCF_ORDER)
4453 hdi.mask |= HDI_ORDER;
4456 bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
4457 if (bResult != FALSE)
4459 if (lpColumn->mask & LVCF_FMT)
4463 if (hdi.fmt & HDF_LEFT)
4465 lpColumn->fmt |= LVCFMT_LEFT;
4467 else if (hdi.fmt & HDF_RIGHT)
4469 lpColumn->fmt |= LVCFMT_RIGHT;
4471 else if (hdi.fmt & HDF_CENTER)
4473 lpColumn->fmt |= LVCFMT_CENTER;
4476 if (hdi.fmt & HDF_IMAGE)
4478 lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
4481 if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
4483 lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
4487 if (lpColumn->mask & LVCF_WIDTH)
4489 lpColumn->cx = hdi.cxy;
4492 if (lpColumn->mask & LVCF_IMAGE)
4494 lpColumn->iImage = hdi.iImage;
4497 if (lpColumn->mask & LVCF_ORDER)
4499 lpColumn->iOrder = hdi.iOrder;
4507 /* LISTVIEW_GetColumnW */
4510 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
4512 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); */
4519 for (i = 0; i < iCount; i++)
4527 * Retrieves the column width.
4530 * [I] HWND : window handle
4531 * [I] int : column index
4534 * SUCCESS : column width
4537 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
4539 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4540 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4541 INT nColumnWidth = 0;
4544 if (uView == LVS_LIST)
4546 nColumnWidth = infoPtr->nItemWidth;
4548 else if (uView == LVS_REPORT)
4550 /* get column width from header */
4551 ZeroMemory(&hdi, sizeof(HDITEMA));
4552 hdi.mask = HDI_WIDTH;
4553 if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
4555 nColumnWidth = hdi.cxy;
4559 return nColumnWidth;
4564 * In list or report display mode, retrieves the number of items that can fit
4565 * vertically in the visible area. In icon or small icon display mode,
4566 * retrieves the total number of visible items.
4569 * [I] HWND : window handle
4572 * Number of fully visible items.
4574 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
4576 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4577 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4580 if (uView == LVS_LIST)
4582 if (infoPtr->rcList.right > infoPtr->nItemWidth)
4584 nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
4585 LISTVIEW_GetCountPerColumn(hwnd);
4588 else if (uView == LVS_REPORT)
4590 nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
4594 nItemCount = GETITEMCOUNT(infoPtr);
4600 /* LISTVIEW_GetEditControl */
4604 * Retrieves the extended listview style.
4607 * [I] HWND : window handle
4610 * SUCCESS : previous style
4613 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
4615 LISTVIEW_INFO *infoPtr;
4617 /* make sure we can get the listview info */
4618 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
4621 return (infoPtr->dwExStyle);
4626 * Retrieves the handle to the header control.
4629 * [I] HWND : window handle
4634 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
4636 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4638 return infoPtr->hwndHeader;
4641 /* LISTVIEW_GetHotCursor */
4645 * Returns the time that the mouse cursor must hover over an item
4646 * before it is selected.
4649 * [I] HWND : window handle
4652 * Returns the previously set hover time or (DWORD)-1 to indicate that the
4653 * hover time is set to the default hover time.
4655 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
4657 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4659 return infoPtr->dwHoverTime;
4664 * Retrieves an image list handle.
4667 * [I] HWND : window handle
4668 * [I] INT : image list identifier
4671 * SUCCESS : image list handle
4674 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
4676 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4677 HIMAGELIST himl = NULL;
4682 himl = infoPtr->himlNormal;
4685 himl = infoPtr->himlSmall;
4688 himl = infoPtr->himlState;
4692 return (LRESULT)himl;
4695 /* LISTVIEW_GetISearchString */
4699 * Retrieves item attributes.
4702 * [I] HWND : window handle
4703 * [IO] LPLVITEMA : item info
4704 * [I] internal : if true then we will use tricks that avoid copies
4705 * but are not compatible with the regular interface
4711 static LRESULT LISTVIEW_GetItemA(HWND hwnd, LPLVITEMA lpLVItem, BOOL internal)
4713 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4714 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
4715 NMLVDISPINFOA dispInfo;
4716 LISTVIEW_SUBITEM *lpSubItem;
4717 LISTVIEW_ITEM *lpItem;
4721 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4722 /* In the following:
4723 * lpLVItem describes the information requested by the user
4724 * lpItem/lpSubItem is what we have
4725 * dispInfo is a structure we use to request the missing
4726 * information from the application
4729 TRACE("(hwnd=%x, lpLVItem=%p)\n", hwnd, lpLVItem);
4731 if ((lpLVItem == NULL) ||
4732 (lpLVItem->iItem < 0) ||
4733 (lpLVItem->iItem >= GETITEMCOUNT(infoPtr))
4737 if (lStyle & LVS_OWNERDATA)
4739 if (lpLVItem->mask & ~LVIF_STATE)
4741 dispInfo.hdr.hwndFrom = hwnd;
4742 dispInfo.hdr.idFrom = lCtrlId;
4743 dispInfo.hdr.code = LVN_GETDISPINFOA;
4744 memcpy(&dispInfo.item,lpLVItem,sizeof(LVITEMA));
4746 ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
4747 memcpy(lpLVItem,&dispInfo.item,sizeof(LVITEMA));
4750 if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
4752 lpLVItem->state = 0;
4753 if (infoPtr->nFocusedItem == lpLVItem->iItem)
4754 lpLVItem->state |= LVIS_FOCUSED;
4755 if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
4756 lpLVItem->state |= LVIS_SELECTED;
4763 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4764 if (hdpaSubItems == NULL)
4767 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4771 ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
4772 if (lpLVItem->iSubItem == 0)
4774 piImage=&lpItem->iImage;
4775 ppszText=&lpItem->pszText;
4776 if ((infoPtr->uCallbackMask != 0) && (lpLVItem->mask & LVIF_STATE))
4778 dispInfo.item.mask |= LVIF_STATE;
4779 dispInfo.item.stateMask = infoPtr->uCallbackMask;
4784 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4785 if (lpSubItem != NULL)
4787 piImage=&lpSubItem->iImage;
4788 ppszText=&lpSubItem->pszText;
4797 if ((lpLVItem->mask & LVIF_IMAGE) &&
4798 ((piImage==NULL) || (*piImage == I_IMAGECALLBACK)))
4800 dispInfo.item.mask |= LVIF_IMAGE;
4803 if ((lpLVItem->mask & LVIF_TEXT) &&
4804 ((ppszText==NULL) || (*ppszText == LPSTR_TEXTCALLBACKA)))
4806 dispInfo.item.mask |= LVIF_TEXT;
4807 dispInfo.item.pszText = lpLVItem->pszText;
4808 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
4811 if (dispInfo.item.mask != 0)
4813 /* We don't have all the requested info, query the application */
4814 dispInfo.hdr.hwndFrom = hwnd;
4815 dispInfo.hdr.idFrom = lCtrlId;
4816 dispInfo.hdr.code = LVN_GETDISPINFOA;
4817 dispInfo.item.iItem = lpLVItem->iItem;
4818 dispInfo.item.iSubItem = lpLVItem->iSubItem;
4819 dispInfo.item.lParam = lpItem->lParam;
4820 ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
4823 if (dispInfo.item.mask & LVIF_IMAGE)
4825 lpLVItem->iImage = dispInfo.item.iImage;
4827 else if (lpLVItem->mask & LVIF_IMAGE)
4829 lpLVItem->iImage = *piImage;
4832 if (dispInfo.item.mask & LVIF_PARAM)
4834 lpLVItem->lParam = dispInfo.item.lParam;
4836 else if (lpLVItem->mask & LVIF_PARAM)
4838 lpLVItem->lParam = lpItem->lParam;
4841 if (dispInfo.item.mask & LVIF_TEXT)
4843 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (ppszText != NULL))
4845 Str_SetPtrA(ppszText, dispInfo.item.pszText);
4847 /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
4848 /* some apps give a new pointer in ListView_Notify so we can't be sure. */
4849 if (lpLVItem->pszText!=dispInfo.item.pszText) {
4850 lstrcpynA(lpLVItem->pszText, dispInfo.item.pszText, lpLVItem->cchTextMax);
4853 else if (lpLVItem->mask & LVIF_TEXT)
4857 lpLVItem->pszText=*ppszText;
4859 lstrcpynA(lpLVItem->pszText, *ppszText, lpLVItem->cchTextMax);
4863 if (lpLVItem->iSubItem == 0)
4865 if (dispInfo.item.mask & LVIF_STATE)
4867 lpLVItem->state = lpItem->state;
4868 lpLVItem->state &= ~dispInfo.item.stateMask;
4869 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
4871 lpLVItem->state &= ~LVIS_SELECTED;
4872 if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
4873 (LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem)))
4874 lpLVItem->state |= LVIS_SELECTED;
4876 else if (lpLVItem->mask & LVIF_STATE)
4878 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
4880 lpLVItem->state &= ~LVIS_SELECTED;
4881 if ((lpLVItem->stateMask & LVIS_SELECTED) &&
4882 (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem)))
4883 lpLVItem->state |= LVIS_SELECTED;
4886 if (lpLVItem->mask & LVIF_PARAM)
4888 lpLVItem->lParam = lpItem->lParam;
4891 if (lpLVItem->mask & LVIF_INDENT)
4893 lpLVItem->iIndent = lpItem->iIndent;
4900 /* LISTVIEW_GetItemW */
4901 /* LISTVIEW_GetHotCursor */
4905 * Retrieves the index of the hot item.
4908 * [I] HWND : window handle
4911 * SUCCESS : hot item index
4912 * FAILURE : -1 (no hot item)
4914 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
4916 LISTVIEW_INFO *infoPtr;
4918 /* make sure we can get the listview info */
4919 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
4922 return (infoPtr->nHotItem);
4925 /* LISTVIEW_GetHoverTime */
4929 * Retrieves the number of items in the listview control.
4932 * [I] HWND : window handle
4937 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
4939 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4941 return GETITEMCOUNT(infoPtr);
4946 * Retrieves the position (upper-left) of the listview control item.
4949 * [I] HWND : window handle
4950 * [I] INT : item index
4951 * [O] LPPOINT : coordinate information
4957 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem,
4958 LPPOINT lpptPosition)
4960 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4961 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4962 BOOL bResult = FALSE;
4964 LISTVIEW_ITEM *lpItem;
4965 INT nCountPerColumn;
4968 TRACE("(hwnd=%x,nItem=%d,lpptPosition=%p)\n", hwnd, nItem,
4971 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
4972 (lpptPosition != NULL))
4974 if (uView == LVS_LIST)
4977 nItem = nItem - ListView_GetTopIndex(hwnd);
4978 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
4981 nRow = nItem % nCountPerColumn;
4984 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
4985 lpptPosition->y = 0;
4989 lpptPosition->x = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
4990 lpptPosition->y = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
4995 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
4996 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
4999 else if (uView == LVS_REPORT)
5002 lpptPosition->x = REPORT_MARGINX;
5003 lpptPosition->y = ((nItem - ListView_GetTopIndex(hwnd)) *
5004 infoPtr->nItemHeight) + infoPtr->rcList.top;
5008 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5009 if (hdpaSubItems != NULL)
5011 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
5015 lpptPosition->x = lpItem->ptPosition.x;
5016 lpptPosition->y = lpItem->ptPosition.y;
5026 * Retrieves the bounding rectangle for a listview control item.
5029 * [I] HWND : window handle
5030 * [I] INT : item index
5031 * [IO] LPRECT : bounding rectangle coordinates
5037 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5039 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5040 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5041 BOOL bResult = FALSE;
5052 TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5054 if (uView & LVS_REPORT)
5056 ZeroMemory(&lvItem, sizeof(LVITEMA));
5057 lvItem.mask = LVIF_INDENT;
5058 lvItem.iItem = nItem;
5059 lvItem.iSubItem = 0;
5060 LISTVIEW_GetItemA(hwnd, &lvItem, TRUE);
5063 if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5065 nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5073 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5075 if (ListView_GetItemPosition(hwnd, nItem, &ptItem) != FALSE)
5080 if (uView == LVS_ICON)
5082 if (infoPtr->himlNormal != NULL)
5084 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5087 lprc->left = ptItem.x + ptOrigin.x;
5088 lprc->top = ptItem.y + ptOrigin.y;
5089 lprc->right = lprc->left + infoPtr->iconSize.cx;
5090 lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5091 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5095 else if (uView == LVS_SMALLICON)
5097 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5100 lprc->left = ptItem.x + ptOrigin.x;
5101 lprc->top = ptItem.y + ptOrigin.y;
5102 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5104 if (infoPtr->himlState != NULL)
5105 lprc->left += infoPtr->iconSize.cx;
5107 if (infoPtr->himlSmall != NULL)
5108 lprc->right = lprc->left + infoPtr->iconSize.cx;
5110 lprc->right = lprc->left;
5116 lprc->left = ptItem.x;
5117 if (uView & LVS_REPORT)
5118 lprc->left += nIndent;
5119 lprc->top = ptItem.y;
5120 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5122 if (infoPtr->himlState != NULL)
5124 lprc->left += infoPtr->iconSize.cx;
5127 if (infoPtr->himlSmall != NULL)
5129 lprc->right = lprc->left + infoPtr->iconSize.cx;
5133 lprc->right = lprc->left;
5139 if (uView == LVS_ICON)
5141 if (infoPtr->himlNormal != NULL)
5143 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5146 lprc->left = ptItem.x + ptOrigin.x;
5147 lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5148 ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5149 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5150 if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5152 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5153 lprc->right = lprc->left + nLabelWidth;
5158 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5162 hOldFont = SelectObject(hdc, infoPtr->hFont);
5163 GetTextMetricsA(hdc, &tm);
5164 lprc->bottom = lprc->top + tm.tmHeight + HEIGHT_PADDING;
5165 SelectObject(hdc, hOldFont);
5166 ReleaseDC(hwnd, hdc);
5170 else if (uView == LVS_SMALLICON)
5172 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5175 nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5176 lprc->top = ptItem.y + ptOrigin.y;
5177 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5179 if (infoPtr->himlState != NULL)
5181 lprc->left += infoPtr->iconSize.cx;
5184 if (infoPtr->himlSmall != NULL)
5186 lprc->left += infoPtr->iconSize.cx;
5189 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5190 nLabelWidth += TRAILING_PADDING;
5191 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5193 lprc->right = lprc->left + nLabelWidth;
5197 lprc->right = nLeftPos + infoPtr->nItemWidth;
5204 if (uView & LVS_REPORT)
5205 nLeftPos = lprc->left = ptItem.x + nIndent;
5207 nLeftPos = lprc->left = ptItem.x;
5208 lprc->top = ptItem.y;
5209 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5211 if (infoPtr->himlState != NULL)
5213 lprc->left += infoPtr->iconSize.cx;
5216 if (infoPtr->himlSmall != NULL)
5218 lprc->left += infoPtr->iconSize.cx;
5221 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5222 nLabelWidth += TRAILING_PADDING;
5223 if (infoPtr->himlSmall)
5224 nLabelWidth += IMAGE_PADDING;
5225 if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5227 lprc->right = lprc->left + nLabelWidth;
5231 lprc->right = nLeftPos + infoPtr->nItemWidth;
5237 if (uView == LVS_ICON)
5239 if (infoPtr->himlNormal != NULL)
5241 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5244 lprc->left = ptItem.x + ptOrigin.x;
5245 lprc->top = ptItem.y + ptOrigin.y;
5246 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5247 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5251 else if (uView == LVS_SMALLICON)
5253 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5256 lprc->left = ptItem.x + ptOrigin.x;
5257 lprc->right = lprc->left;
5258 lprc->top = ptItem.y + ptOrigin.y;
5259 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5260 if (infoPtr->himlState != NULL)
5261 lprc->right += infoPtr->iconSize.cx;
5262 if (infoPtr->himlSmall != NULL)
5263 lprc->right += infoPtr->iconSize.cx;
5265 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5266 nLabelWidth += TRAILING_PADDING;
5267 if (infoPtr->himlSmall)
5268 nLabelWidth += IMAGE_PADDING;
5269 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5271 lprc->right += nLabelWidth;
5275 lprc->right = lprc->left + infoPtr->nItemWidth;
5282 lprc->left = ptItem.x;
5283 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
5284 lprc->left += nIndent;
5285 lprc->right = lprc->left;
5286 lprc->top = ptItem.y;
5287 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5289 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5292 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5293 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5295 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5299 if (infoPtr->himlState != NULL)
5301 lprc->right += infoPtr->iconSize.cx;
5304 if (infoPtr->himlSmall != NULL)
5306 lprc->right += infoPtr->iconSize.cx;
5309 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5310 nLabelWidth += TRAILING_PADDING;
5311 if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5313 lprc->right += nLabelWidth;
5317 lprc->right = lprc->left + infoPtr->nItemWidth;
5323 case LVIR_SELECTBOUNDS:
5324 if (uView == LVS_ICON)
5326 if (infoPtr->himlNormal != NULL)
5328 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5331 lprc->left = ptItem.x + ptOrigin.x;
5332 lprc->top = ptItem.y + ptOrigin.y;
5333 lprc->right = lprc->left + infoPtr->iconSpacing.cx;
5334 lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
5338 else if (uView == LVS_SMALLICON)
5340 if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5343 nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
5344 lprc->top = ptItem.y + ptOrigin.y;
5345 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5347 if (infoPtr->himlState != NULL)
5349 lprc->left += infoPtr->iconSize.cx;
5352 lprc->right = lprc->left;
5354 if (infoPtr->himlSmall != NULL)
5356 lprc->right += infoPtr->iconSize.cx;
5359 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5360 nLabelWidth += TRAILING_PADDING;
5361 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5363 lprc->right += nLabelWidth;
5367 lprc->right = nLeftPos + infoPtr->nItemWidth;
5374 if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
5375 nLeftPos = lprc->left = ptItem.x + nIndent;
5377 nLeftPos = lprc->left = ptItem.x;
5378 lprc->top = ptItem.y;
5379 lprc->bottom = lprc->top + infoPtr->nItemHeight;
5381 if (infoPtr->himlState != NULL)
5383 lprc->left += infoPtr->iconSize.cx;
5386 lprc->right = lprc->left;
5388 if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
5391 int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
5392 Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
5394 lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
5398 if (infoPtr->himlSmall != NULL)
5400 lprc->right += infoPtr->iconSize.cx;
5403 nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5404 nLabelWidth += TRAILING_PADDING;
5405 if (infoPtr->himlSmall)
5406 nLabelWidth += IMAGE_PADDING;
5407 if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5409 lprc->right += nLabelWidth;
5413 lprc->right = nLeftPos + infoPtr->nItemWidth;
5426 * Retrieves the width of a label.
5429 * [I] HWND : window handle
5432 * SUCCESS : string width (in pixels)
5435 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
5437 CHAR szDispText[DISP_TEXT_SIZE];
5438 INT nLabelWidth = 0;
5441 TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
5443 ZeroMemory(&lvItem, sizeof(LVITEMA));
5444 lvItem.mask = LVIF_TEXT;
5445 lvItem.iItem = nItem;
5446 lvItem.cchTextMax = DISP_TEXT_SIZE;
5447 lvItem.pszText = szDispText;
5448 if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
5450 nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
5458 * Retrieves the spacing between listview control items.
5461 * [I] HWND : window handle
5462 * [I] BOOL : flag for small or large icon
5465 * Horizontal + vertical spacing
5467 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
5469 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5472 if (bSmall == FALSE)
5474 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5478 /* TODO: need to store width of smallicon item */
5479 lResult = MAKELONG(0, infoPtr->nItemHeight);
5487 * Retrieves the state of a listview control item.
5490 * [I] HWND : window handle
5491 * [I] INT : item index
5492 * [I] UINT : state mask
5495 * State specified by the mask.
5497 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
5499 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5503 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5505 ZeroMemory(&lvItem, sizeof(LVITEMA));
5506 lvItem.iItem = nItem;
5507 lvItem.stateMask = uMask;
5508 lvItem.mask = LVIF_STATE;
5509 if (LISTVIEW_GetItemA(hwnd, &lvItem, TRUE) != FALSE)
5511 uState = lvItem.state;
5520 * Retrieves the text of a listview control item or subitem.
5523 * [I] HWND : window handle
5524 * [I] INT : item index
5525 * [IO] LPLVITEMA : item information
5528 * SUCCESS : string length
5531 static LRESULT LISTVIEW_GetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
5533 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5536 if (lpLVItem != NULL)
5538 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5540 lpLVItem->mask = LVIF_TEXT;
5541 lpLVItem->iItem = nItem;
5542 if (LISTVIEW_GetItemA(hwnd, lpLVItem, FALSE) != FALSE)
5544 nLength = lstrlenA(lpLVItem->pszText);
5554 * Searches for an item based on properties + relationships.
5557 * [I] HWND : window handle
5558 * [I] INT : item index
5559 * [I] INT : relationship flag
5562 * SUCCESS : item index
5565 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
5567 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5568 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5570 LVFINDINFO lvFindInfo;
5571 INT nCountPerColumn;
5574 if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
5576 ZeroMemory(&lvFindInfo, sizeof(LVFINDINFO));
5578 if (uFlags & LVNI_CUT)
5581 if (uFlags & LVNI_DROPHILITED)
5582 uMask |= LVIS_DROPHILITED;
5584 if (uFlags & LVNI_FOCUSED)
5585 uMask |= LVIS_FOCUSED;
5587 if (uFlags & LVNI_SELECTED)
5588 uMask |= LVIS_SELECTED;
5590 if (uFlags & LVNI_ABOVE)
5592 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5597 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5603 lvFindInfo.flags = LVFI_NEARESTXY;
5604 lvFindInfo.vkDirection = VK_UP;
5605 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5606 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5608 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5613 else if (uFlags & LVNI_BELOW)
5615 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5617 while (nItem < GETITEMCOUNT(infoPtr))
5620 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5626 lvFindInfo.flags = LVFI_NEARESTXY;
5627 lvFindInfo.vkDirection = VK_DOWN;
5628 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5629 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5631 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5636 else if (uFlags & LVNI_TOLEFT)
5638 if (uView == LVS_LIST)
5640 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5641 while (nItem - nCountPerColumn >= 0)
5643 nItem -= nCountPerColumn;
5644 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5648 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5650 lvFindInfo.flags = LVFI_NEARESTXY;
5651 lvFindInfo.vkDirection = VK_LEFT;
5652 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5653 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5655 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5660 else if (uFlags & LVNI_TORIGHT)
5662 if (uView == LVS_LIST)
5664 nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5665 while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
5667 nItem += nCountPerColumn;
5668 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5672 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5674 lvFindInfo.flags = LVFI_NEARESTXY;
5675 lvFindInfo.vkDirection = VK_RIGHT;
5676 ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
5677 while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
5679 if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
5688 /* search by index */
5689 for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
5691 if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
5700 /* LISTVIEW_GetNumberOfWorkAreas */
5704 * Retrieves the origin coordinates when in icon or small icon display mode.
5707 * [I] HWND : window handle
5708 * [O] LPPOINT : coordinate information
5714 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
5716 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
5717 UINT uView = lStyle & LVS_TYPEMASK;
5718 BOOL bResult = FALSE;
5720 TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
5722 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5724 SCROLLINFO scrollInfo;
5725 ZeroMemory(lpptOrigin, sizeof(POINT));
5726 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5727 scrollInfo.cbSize = sizeof(SCROLLINFO);
5729 if (lStyle & WS_HSCROLL)
5731 scrollInfo.fMask = SIF_POS;
5732 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
5734 lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5738 if (lStyle & WS_VSCROLL)
5740 scrollInfo.fMask = SIF_POS;
5741 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
5743 lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
5755 * Retrieves the number of items that are marked as selected.
5758 * [I] HWND : window handle
5761 * Number of items selected.
5763 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
5766 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5767 INT nSelectedCount = 0;
5770 for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
5772 if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
5778 return nSelectedCount;
5783 * Retrieves item index that marks the start of a multiple selection.
5786 * [I] HWND : window handle
5789 * Index number or -1 if there is no selection mark.
5791 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
5793 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5795 return infoPtr->nSelectionMark;
5800 * Retrieves the width of a string.
5803 * [I] HWND : window handle
5806 * SUCCESS : string width (in pixels)
5809 static LRESULT LISTVIEW_GetStringWidthA(HWND hwnd, LPCSTR lpszText)
5811 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5812 HFONT hFont, hOldFont;
5816 ZeroMemory(&stringSize, sizeof(SIZE));
5817 if (lpszText != NULL && lpszText != (LPCSTR)-1)
5819 hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
5821 hOldFont = SelectObject(hdc, hFont);
5822 GetTextExtentPointA(hdc, lpszText, lstrlenA(lpszText), &stringSize);
5823 SelectObject(hdc, hOldFont);
5824 ReleaseDC(hwnd, hdc);
5827 return stringSize.cx;
5832 * Retrieves the text backgound color.
5835 * [I] HWND : window handle
5838 * COLORREF associated with the the background.
5840 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
5842 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
5844 return infoPtr->clrTextBk;
5849 * Retrieves the text color.
5852 * [I] HWND : window handle
5855 * COLORREF associated with the text.
5857 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
5859 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
5861 return infoPtr->clrText;
5866 * Determines which section of the item was selected (if any).
5869 * [I] HWND : window handle
5870 * [IO] LPLVHITTESTINFO : hit test information
5873 * SUCCESS : item index
5876 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
5878 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5880 INT i,topindex,bottomindex;
5881 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
5882 UINT uView = lStyle & LVS_TYPEMASK;
5885 TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
5886 lpHitTestInfo->pt.y);
5888 topindex = ListView_GetTopIndex(hwnd);
5889 if (uView == LVS_REPORT)
5891 bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
5892 bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
5896 bottomindex = GETITEMCOUNT(infoPtr);
5899 for (i = topindex; i < bottomindex; i++)
5901 rcItem.left = LVIR_BOUNDS;
5902 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
5904 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
5906 rcItem.left = LVIR_ICON;
5907 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
5909 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
5911 lpHitTestInfo->flags = LVHT_ONITEMICON;
5912 lpHitTestInfo->iItem = i;
5913 lpHitTestInfo->iSubItem = 0;
5918 rcItem.left = LVIR_LABEL;
5919 if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
5921 if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
5923 lpHitTestInfo->flags = LVHT_ONITEMLABEL;
5924 lpHitTestInfo->iItem = i;
5925 lpHitTestInfo->iSubItem = 0;
5930 lpHitTestInfo->flags = LVHT_ONITEMSTATEICON;
5931 lpHitTestInfo->iItem = i;
5932 lpHitTestInfo->iSubItem = 0;
5938 lpHitTestInfo->flags = LVHT_NOWHERE;
5945 * Determines which listview item is located at the specified position.
5948 * [I] HWND : window handle
5949 * [IO} LPLVHITTESTINFO : hit test information
5952 * SUCCESS : item index
5955 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
5957 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5960 lpHitTestInfo->flags = 0;
5962 if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
5964 lpHitTestInfo->flags = LVHT_TOLEFT;
5966 else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
5968 lpHitTestInfo->flags = LVHT_TORIGHT;
5970 if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
5972 lpHitTestInfo->flags |= LVHT_ABOVE;
5974 else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
5976 lpHitTestInfo->flags |= LVHT_BELOW;
5979 if (lpHitTestInfo->flags == 0)
5981 nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo);
5989 * Inserts a new column.
5992 * [I] HWND : window handle
5993 * [I] INT : column index
5994 * [I] LPLVCOLUMNA : column information
5997 * SUCCESS : new column index
6000 static LRESULT LISTVIEW_InsertColumnA(HWND hwnd, INT nColumn,
6001 LPLVCOLUMNA lpColumn)
6003 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6005 INT nNewColumn = -1;
6007 TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn,
6010 if (lpColumn != NULL)
6012 /* initialize memory */
6013 ZeroMemory(&hdi, sizeof(HDITEMA));
6015 if (lpColumn->mask & LVCF_FMT)
6017 /* format member is valid */
6018 hdi.mask |= HDI_FORMAT;
6020 /* set text alignment (leftmost column must be left-aligned) */
6023 hdi.fmt |= HDF_LEFT;
6027 if (lpColumn->fmt & LVCFMT_LEFT)
6029 hdi.fmt |= HDF_LEFT;
6031 else if (lpColumn->fmt & LVCFMT_RIGHT)
6033 hdi.fmt |= HDF_RIGHT;
6035 else if (lpColumn->fmt & LVCFMT_CENTER)
6037 hdi.fmt |= HDF_CENTER;
6041 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6043 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6047 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6052 if (lpColumn->fmt & LVCFMT_IMAGE)
6054 hdi.fmt |= HDF_IMAGE;
6055 hdi.iImage = I_IMAGECALLBACK;
6059 if (lpColumn->mask & LVCF_WIDTH)
6061 hdi.mask |= HDI_WIDTH;
6062 hdi.cxy = lpColumn->cx;
6065 if (lpColumn->mask & LVCF_TEXT)
6067 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6068 hdi.pszText = lpColumn->pszText;
6069 hdi.cchTextMax = lstrlenA(lpColumn->pszText);
6070 hdi.fmt |= HDF_STRING;
6073 if (lpColumn->mask & LVCF_IMAGE)
6075 hdi.mask |= HDI_IMAGE;
6076 hdi.iImage = lpColumn->iImage;
6079 if (lpColumn->mask & LVCF_ORDER)
6081 hdi.mask |= HDI_ORDER;
6082 hdi.iOrder = lpColumn->iOrder;
6085 /* insert item in header control */
6086 nNewColumn = SendMessageA(infoPtr->hwndHeader, HDM_INSERTITEMA,
6087 (WPARAM)nColumn, (LPARAM)&hdi);
6089 /* Need to reset the item width when inserting a new column */
6090 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6092 LISTVIEW_UpdateScroll(hwnd);
6093 InvalidateRect(hwnd, NULL, FALSE);
6099 static LRESULT LISTVIEW_InsertColumnW(HWND hwnd, INT nColumn,
6100 LPLVCOLUMNW lpColumn)
6105 memcpy(&lvca,lpColumn,sizeof(lvca));
6106 if (lpColumn->mask & LVCF_TEXT)
6107 lvca.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpColumn->pszText);
6108 lres = LISTVIEW_InsertColumnA(hwnd,nColumn,&lvca);
6109 if (lpColumn->mask & LVCF_TEXT)
6110 HeapFree(GetProcessHeap(),0,lvca.pszText);
6114 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6115 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6116 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6117 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6118 their own sort proc. when sending LVM_SORTITEMS.
6121 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6123 LVS_SORTXXX must be specified,
6124 LVS_OWNERDRAW is not set,
6125 <item>.pszText is not LPSTR_TEXTCALLBACK.
6127 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6128 are sorted based on item text..."
6130 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6132 HDPA hdpa_first = (HDPA) first;
6133 HDPA hdpa_second = (HDPA) second;
6134 LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_first, 0 );
6135 LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( hdpa_second, 0 );
6136 LONG lStyle = GetWindowLongA((HWND) lParam, GWL_STYLE);
6137 INT cmpv = lstrcmpA( lv_first->pszText, lv_second->pszText );
6138 /* if we're sorting descending, negate the return value */
6139 return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6144 * Inserts a new item in the listview control.
6147 * [I] HWND : window handle
6148 * [I] LPLVITEMA : item information
6151 * SUCCESS : new item index
6154 static LRESULT LISTVIEW_InsertItemA(HWND hwnd, LPLVITEMA lpLVItem)
6156 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6157 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6158 UINT uView = lStyle & LVS_TYPEMASK;
6159 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
6164 LISTVIEW_ITEM *lpItem = NULL;
6166 TRACE("(hwnd=%x,lpLVItem=%p)\n", hwnd, lpLVItem);
6168 if (lStyle & LVS_OWNERDATA)
6170 nItem = infoPtr->hdpaItems->nItemCount;
6171 infoPtr->hdpaItems->nItemCount ++;
6175 if (lpLVItem != NULL)
6177 /* make sure it's not a subitem; cannot insert a subitem */
6178 if (lpLVItem->iSubItem == 0)
6180 lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM));
6183 ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6184 if (LISTVIEW_InitItem(hwnd, lpItem, lpLVItem) != FALSE)
6186 /* insert item in listview control data structure */
6187 hdpaSubItems = DPA_Create(8);
6188 if (hdpaSubItems != NULL)
6190 nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
6193 if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6194 && !(lStyle & LVS_OWNERDRAWFIXED)
6195 && (LPSTR_TEXTCALLBACKA != lpLVItem->pszText) )
6197 /* Insert the item in the proper sort order based on the pszText
6198 member. See comments for LISTVIEW_InsertCompare() for greater detail */
6199 nItem = DPA_InsertPtr( infoPtr->hdpaItems,
6200 GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6201 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6202 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6206 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
6211 LISTVIEW_ShiftSelections(hwnd,nItem,1);
6213 /* manage item focus */
6214 if (lpLVItem->mask & LVIF_STATE)
6216 lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6217 if (lpLVItem->stateMask & LVIS_SELECTED)
6219 LISTVIEW_SetSelection(hwnd, nItem);
6221 else if (lpLVItem->stateMask & LVIS_FOCUSED)
6223 LISTVIEW_SetItemFocus(hwnd, nItem);
6227 /* send LVN_INSERTITEM notification */
6228 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6229 nmlv.hdr.hwndFrom = hwnd;
6230 nmlv.hdr.idFrom = lCtrlId;
6231 nmlv.hdr.code = LVN_INSERTITEM;
6233 nmlv.lParam = lpItem->lParam;;
6234 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
6236 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6238 nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
6239 if (nItemWidth > infoPtr->nItemWidth)
6241 infoPtr->nItemWidth = nItemWidth;
6245 /* align items (set position of each item) */
6246 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6248 if (lStyle & LVS_ALIGNLEFT)
6250 LISTVIEW_AlignLeft(hwnd);
6254 LISTVIEW_AlignTop(hwnd);
6258 LISTVIEW_UpdateScroll(hwnd);
6259 /* refresh client area */
6260 InvalidateRect(hwnd, NULL, FALSE);
6269 /* free memory if unsuccessful */
6270 if ((nItem == -1) && (lpItem != NULL))
6272 COMCTL32_Free(lpItem);
6278 static LRESULT LISTVIEW_InsertItemW(HWND hwnd, LPLVITEMW lpLVItem) {
6282 memcpy(&lvia,lpLVItem,sizeof(LVITEMA));
6283 if (lvia.mask & LVIF_TEXT) {
6284 if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
6285 lvia.pszText = LPSTR_TEXTCALLBACKA;
6287 lvia.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpLVItem->pszText);
6289 lres = LISTVIEW_InsertItemA(hwnd, &lvia);
6290 if (lvia.mask & LVIF_TEXT) {
6291 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6292 HeapFree(GetProcessHeap(),0,lvia.pszText);
6297 /* LISTVIEW_InsertItemW */
6301 * Redraws a range of items.
6304 * [I] HWND : window handle
6305 * [I] INT : first item
6306 * [I] INT : last item
6312 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6314 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6315 BOOL bResult = FALSE;
6318 if (nFirst <= nLast)
6320 if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
6322 if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
6325 InvalidateRect(hwnd, &rc, FALSE);
6333 /* LISTVIEW_Scroll */
6337 * Sets the background color.
6340 * [I] HWND : window handle
6341 * [I] COLORREF : background color
6347 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
6349 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6351 infoPtr->clrBk = clrBk;
6352 InvalidateRect(hwnd, NULL, TRUE);
6357 /* LISTVIEW_SetBkImage */
6361 * Sets the callback mask. This mask will be used when the parent
6362 * window stores state information (some or all).
6365 * [I] HWND : window handle
6366 * [I] UINT : state mask
6372 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
6374 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6376 infoPtr->uCallbackMask = uMask;
6383 * Sets the attributes of a header item.
6386 * [I] HWND : window handle
6387 * [I] INT : column index
6388 * [I] LPLVCOLUMNA : column attributes
6394 static LRESULT LISTVIEW_SetColumnA(HWND hwnd, INT nColumn,
6395 LPLVCOLUMNA lpColumn)
6397 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6398 BOOL bResult = FALSE;
6399 HDITEMA hdi, hdiget;
6401 if ((lpColumn != NULL) && (nColumn >= 0) &&
6402 (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
6404 /* initialize memory */
6405 ZeroMemory(&hdi, sizeof(HDITEMA));
6407 if (lpColumn->mask & LVCF_FMT)
6409 /* format member is valid */
6410 hdi.mask |= HDI_FORMAT;
6412 /* get current format first */
6413 hdiget.mask = HDI_FORMAT;
6414 if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdiget))
6415 /* preserve HDF_STRING if present */
6416 hdi.fmt = hdiget.fmt & HDF_STRING;
6418 /* set text alignment (leftmost column must be left-aligned) */
6421 hdi.fmt |= HDF_LEFT;
6425 if (lpColumn->fmt & LVCFMT_LEFT)
6427 hdi.fmt |= HDF_LEFT;
6429 else if (lpColumn->fmt & LVCFMT_RIGHT)
6431 hdi.fmt |= HDF_RIGHT;
6433 else if (lpColumn->fmt & LVCFMT_CENTER)
6435 hdi.fmt |= HDF_CENTER;
6439 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6441 hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6444 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6446 hdi.fmt |= HDF_IMAGE;
6449 if (lpColumn->fmt & LVCFMT_IMAGE)
6451 hdi.fmt |= HDF_IMAGE;
6452 hdi.iImage = I_IMAGECALLBACK;
6456 if (lpColumn->mask & LVCF_WIDTH)
6458 hdi.mask |= HDI_WIDTH;
6459 hdi.cxy = lpColumn->cx;
6462 if (lpColumn->mask & LVCF_TEXT)
6464 hdi.mask |= HDI_TEXT | HDI_FORMAT;
6465 hdi.pszText = lpColumn->pszText;
6466 hdi.cchTextMax = lstrlenA(lpColumn->pszText);
6467 hdi.fmt |= HDF_STRING;
6470 if (lpColumn->mask & LVCF_IMAGE)
6472 hdi.mask |= HDI_IMAGE;
6473 hdi.iImage = lpColumn->iImage;
6476 if (lpColumn->mask & LVCF_ORDER)
6478 hdi.mask |= HDI_ORDER;
6479 hdi.iOrder = lpColumn->iOrder;
6482 /* set header item attributes */
6483 bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
6489 /* LISTVIEW_SetColumnW */
6493 * Sets the column order array
6496 * [I] HWND : window handle
6497 * [I] INT : number of elements in column order array
6498 * [I] INT : pointer to column order array
6504 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
6506 /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); */
6508 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6519 * Sets the width of a column
6522 * [I] HWND : window handle
6523 * [I] INT : column index
6524 * [I] INT : column width
6530 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
6532 LISTVIEW_INFO *infoPtr;
6535 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6536 UINT uView = lStyle & LVS_TYPEMASK;
6541 CHAR text_buffer[DISP_TEXT_SIZE];
6542 INT header_item_count;
6547 /* make sure we can get the listview info */
6548 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6551 if (!infoPtr->hwndHeader) /* make sure we have a header */
6554 /* set column width only if in report or list mode */
6555 if ((uView != LVS_REPORT) && (uView != LVS_LIST))
6558 /* take care of invalid cx values */
6559 if((uView == LVS_REPORT) && (cx < -2))
6560 cx = LVSCW_AUTOSIZE;
6561 else if (uView == LVS_LIST && (cx < 1))
6564 /* resize all columns if in LVS_LIST mode */
6565 if(uView == LVS_LIST) {
6566 infoPtr->nItemWidth = cx;
6567 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6571 /* autosize based on listview items width */
6572 if(cx == LVSCW_AUTOSIZE)
6574 /* set the width of the header to the width of the widest item */
6575 for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
6577 if(cx < LISTVIEW_GetLabelWidth(hwnd, item_index))
6578 cx = LISTVIEW_GetLabelWidth(hwnd, item_index);
6580 } /* autosize based on listview header width */
6581 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6583 header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
6585 /* if iCol is the last column make it fill the remainder of the controls width */
6586 if(iCol == (header_item_count - 1)) {
6587 /* get the width of every item except the current one */
6588 hdi.mask = HDI_WIDTH;
6591 for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
6592 Header_GetItemA(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
6596 /* retrieve the layout of the header */
6597 GetWindowRect(infoPtr->hwndHeader, &rcHeader);
6599 cx = (rcHeader.right - rcHeader.left) - cx;
6603 /* retrieve header font */
6604 header_font = SendMessageA(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
6606 /* retrieve header text */
6607 hdi.mask = HDI_TEXT;
6608 hdi.cchTextMax = sizeof(text_buffer);
6609 hdi.pszText = text_buffer;
6611 Header_GetItemA(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
6613 /* determine the width of the text in the header */
6615 old_font = SelectObject(hdc, header_font); /* select the font into hdc */
6617 GetTextExtentPoint32A(hdc, text_buffer, strlen(text_buffer), &size);
6619 SelectObject(hdc, old_font); /* restore the old font */
6620 ReleaseDC(hwnd, hdc);
6622 /* set the width of this column to the width of the text */
6627 /* call header to update the column change */
6628 hdi.mask = HDI_WIDTH;
6631 lret = Header_SetItemA(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
6633 InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
6640 * Sets the extended listview style.
6643 * [I] HWND : window handle
6648 * SUCCESS : previous style
6651 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
6653 LISTVIEW_INFO *infoPtr;
6656 /* make sure we can get the listview info */
6657 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6660 /* store previous style */
6661 dwOldStyle = infoPtr->dwExStyle;
6665 infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6667 infoPtr->dwExStyle = dwStyle;
6669 return (dwOldStyle);
6672 /* LISTVIEW_SetHotCursor */
6676 * Sets the hot item index.
6679 * [I] HWND : window handle
6683 * SUCCESS : previous hot item index
6684 * FAILURE : -1 (no hot item)
6686 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
6688 LISTVIEW_INFO *infoPtr;
6691 /* make sure we can get the listview info */
6692 if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
6695 /* store previous index */
6696 iOldIndex = infoPtr->nHotItem;
6699 infoPtr->nHotItem = iIndex;
6706 * Sets the amount of time the cursor must hover over an item before it is selected.
6709 * [I] HWND : window handle
6710 * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
6713 * Returns the previous hover time
6715 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
6717 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6718 DWORD oldHoverTime = infoPtr->dwHoverTime;
6720 infoPtr->dwHoverTime = dwHoverTime;
6722 return oldHoverTime;
6725 /* LISTVIEW_SetIconSpacing */
6732 * [I] HWND : window handle
6733 * [I] INT : image list type
6734 * [I] HIMAGELIST : image list handle
6737 * SUCCESS : old image list
6740 static LRESULT LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
6742 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6743 HIMAGELIST himlTemp = 0;
6748 himlTemp = infoPtr->himlNormal;
6749 infoPtr->himlNormal = himl;
6750 return (LRESULT)himlTemp;
6753 himlTemp = infoPtr->himlSmall;
6754 infoPtr->himlSmall = himl;
6755 return (LRESULT)himlTemp;
6758 himlTemp = infoPtr->himlState;
6759 infoPtr->himlState = himl;
6760 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
6761 return (LRESULT)himlTemp;
6764 return (LRESULT)NULL;
6770 * Sets the attributes of an item.
6773 * [I] HWND : window handle
6774 * [I] LPLVITEM : item information
6780 static LRESULT LISTVIEW_SetItemA(HWND hwnd, LPLVITEMA lpLVItem)
6782 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6783 BOOL bResult = FALSE;
6785 if (lpLVItem != NULL)
6787 if ((lpLVItem->iItem >= 0) && (lpLVItem->iItem < GETITEMCOUNT(infoPtr)))
6789 if (lpLVItem->iSubItem == 0)
6791 bResult = LISTVIEW_SetItem(hwnd, lpLVItem);
6795 bResult = LISTVIEW_SetSubItem(hwnd, lpLVItem);
6804 /* LISTVIEW_SetItemW */
6808 * Preallocates memory.
6811 * [I] HWND : window handle
6812 * [I] INT : item count (projected number of items)
6813 * [I] DWORD : update flags
6819 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
6821 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6823 FIXME("(%d %08lx)stub!\n", nItems, dwFlags);
6826 return LISTVIEW_DeleteAllItems (hwnd);
6828 if (GetWindowLongA(hwnd, GWL_STYLE) & LVS_OWNERDATA)
6830 int precount,topvisible;
6831 TRACE("LVS_OWNERDATA is set!\n");
6833 precount = infoPtr->hdpaItems->nItemCount;
6834 topvisible = ListView_GetTopIndex(hwnd) +
6835 LISTVIEW_GetCountPerColumn(hwnd) + 1;
6837 infoPtr->hdpaItems->nItemCount = nItems;
6839 LISTVIEW_UpdateSize(hwnd);
6840 LISTVIEW_UpdateScroll(hwnd);
6841 if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
6842 InvalidateRect(hwnd, NULL, TRUE);
6846 if (nItems > GETITEMCOUNT(infoPtr))
6849 FIXME("append items\n");
6852 else if (nItems < GETITEMCOUNT(infoPtr))
6855 while(nItems < GETITEMCOUNT(infoPtr)) {
6856 LISTVIEW_DeleteItem(hwnd, GETITEMCOUNT(infoPtr) - 1);
6866 * Sets the position of an item.
6869 * [I] HWND : window handle
6870 * [I] INT : item index
6871 * [I] INT : x coordinate
6872 * [I] INT : y coordinate
6878 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
6879 INT nPosX, INT nPosY)
6881 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6882 UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6883 UINT uView = lStyle & LVS_TYPEMASK;
6884 LISTVIEW_ITEM *lpItem;
6886 BOOL bResult = FALSE;
6888 TRACE("(hwnd=%x,nItem=%d,X=%d,Y=%d)\n", hwnd, nItem, nPosX, nPosY);
6890 if (lStyle & LVS_OWNERDATA)
6893 if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
6895 if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
6897 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6898 if (hdpaSubItems != NULL)
6900 lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
6904 lpItem->ptPosition.x = nPosX;
6905 lpItem->ptPosition.y = nPosY;
6914 /* LISTVIEW_SetItemPosition32 */
6918 * Sets the state of one or many items.
6921 * [I] HWND : window handle
6922 * [I]INT : item index
6923 * [I] LPLVITEM : item or subitem info
6929 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
6931 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6932 BOOL bResult = FALSE;
6939 ZeroMemory(&lvItem, sizeof(LVITEMA));
6940 lvItem.mask = LVIF_STATE;
6941 lvItem.state = lpLVItem->state;
6942 lvItem.stateMask = lpLVItem->stateMask ;
6944 /* apply to all items */
6945 for (i = 0; i< GETITEMCOUNT(infoPtr); i++)
6948 if (ListView_SetItemA(hwnd, &lvItem) == FALSE)
6956 ZeroMemory(&lvItem, sizeof(LVITEMA));
6957 lvItem.mask = LVIF_STATE;
6958 lvItem.state = lpLVItem->state;
6959 lvItem.stateMask = lpLVItem->stateMask;
6960 lvItem.iItem = nItem;
6961 bResult = ListView_SetItemA(hwnd, &lvItem);
6969 * Sets the text of an item or subitem.
6972 * [I] HWND : window handle
6973 * [I] INT : item index
6974 * [I] LPLVITEMA : item or subitem info
6980 static BOOL LISTVIEW_SetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
6982 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6983 BOOL bResult = FALSE;
6986 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6988 ZeroMemory(&lvItem, sizeof(LVITEMA));
6989 lvItem.mask = LVIF_TEXT;
6990 lvItem.pszText = lpLVItem->pszText;
6991 lvItem.iItem = nItem;
6992 lvItem.iSubItem = lpLVItem->iSubItem;
6993 bResult = ListView_SetItemA(hwnd, &lvItem);
6999 /* LISTVIEW_SetItemTextW */
7003 * Set item index that marks the start of a multiple selection.
7006 * [I] HWND : window handle
7010 * Index number or -1 if there is no selection mark.
7012 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7014 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7015 INT nOldIndex = infoPtr->nSelectionMark;
7017 infoPtr->nSelectionMark = nIndex;
7024 * Sets the text background color.
7027 * [I] HWND : window handle
7028 * [I] COLORREF : text background color
7034 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7036 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7038 infoPtr->clrTextBk = clrTextBk;
7039 InvalidateRect(hwnd, NULL, TRUE);
7046 * Sets the text foreground color.
7049 * [I] HWND : window handle
7050 * [I] COLORREF : text color
7056 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7058 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7060 infoPtr->clrText = clrText;
7061 InvalidateRect(hwnd, NULL, TRUE);
7066 /* LISTVIEW_SetToolTips */
7067 /* LISTVIEW_SetUnicodeFormat */
7068 /* LISTVIEW_SetWorkAreas */
7072 * Callback internally used by LISTVIEW_SortItems()
7075 * [I] LPVOID : first LISTVIEW_ITEM to compare
7076 * [I] LPVOID : second LISTVIEW_ITEM to compare
7077 * [I] LPARAM : HWND of control
7080 * if first comes before second : negative
7081 * if first comes after second : positive
7082 * if first and second are equivalent : zero
7084 static INT WINAPI LISTVIEW_CallBackCompare(
7089 /* Forward the call to the client defined callback */
7091 HWND hwnd = (HWND)lParam;
7092 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7094 rv = (infoPtr->pfnCompare)( ((LISTVIEW_ITEM*) first)->lParam,
7095 ((LISTVIEW_ITEM*) second)->lParam, infoPtr->lParamSort );
7102 * Sorts the listview items.
7105 * [I] HWND : window handle
7106 * [I] WPARAM : application-defined value
7107 * [I] LPARAM : pointer to comparision callback
7113 static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam)
7115 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7117 LISTVIEW_ITEM *lpItem;
7120 UINT lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7122 if (lStyle & LVS_OWNERDATA)
7125 if (!infoPtr || !infoPtr->hdpaItems)
7128 nCount = GETITEMCOUNT(infoPtr);
7129 /* if there are 0 or 1 items, there is no need to sort */
7132 sortList = DPA_Create(nCount);
7134 infoPtr->pfnCompare = (PFNLVCOMPARE)lParam;
7135 infoPtr->lParamSort = (LPARAM)wParam;
7137 /* append pointers one by one to sortList */
7138 for (i = 0; i < nCount; i++)
7140 if ((hdpaSubItems = (HDPA) DPA_GetPtr(infoPtr->hdpaItems, i)))
7141 if ((lpItem = (LISTVIEW_ITEM *) DPA_GetPtr(hdpaSubItems, 0)))
7142 DPA_InsertPtr(sortList, nCount + 1, lpItem);
7145 /* sort the sortList */
7146 DPA_Sort(sortList, LISTVIEW_CallBackCompare, hwnd);
7148 /* copy the pointers back */
7149 for (i = 0; i < nCount; i++)
7151 if ((hdpaSubItems = (HDPA) DPA_GetPtr(infoPtr->hdpaItems, i)) &&
7152 (lpItem = (LISTVIEW_ITEM *) DPA_GetPtr(sortList, i)))
7153 DPA_SetPtr(hdpaSubItems, 0, lpItem);
7156 DPA_Destroy(sortList);
7162 /* LISTVIEW_SubItemHitTest */
7166 * Updates an items or rearranges the listview control.
7169 * [I] HWND : window handle
7170 * [I] INT : item index
7176 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7178 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7179 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7180 BOOL bResult = FALSE;
7183 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7187 /* rearrange with default alignment style */
7188 if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7189 ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON)))
7191 ListView_Arrange(hwnd, 0);
7195 /* get item bounding rectangle */
7196 rc.left = LVIR_BOUNDS;
7197 ListView_GetItemRect(hwnd, nItem, &rc);
7198 InvalidateRect(hwnd, &rc, TRUE);
7207 * Creates the listview control.
7210 * [I] HWND : window handle
7215 static LRESULT LISTVIEW_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
7217 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7218 LPCREATESTRUCTA lpcs = (LPCREATESTRUCTA)lParam;
7219 UINT uView = lpcs->style & LVS_TYPEMASK;
7222 /* initialize info pointer */
7223 ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
7225 /* determine the type of structures to use */
7226 infoPtr->notifyFormat = SendMessageA(GetParent(hwnd), WM_NOTIFYFORMAT,
7227 (WPARAM)hwnd, (LPARAM)NF_QUERY);
7228 if (infoPtr->notifyFormat != NFR_ANSI)
7230 FIXME("ANSI notify format is NOT used\n");
7233 /* initialize color information */
7234 infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
7235 infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
7236 infoPtr->clrTextBk = GetSysColor(COLOR_WINDOW);
7238 /* set default values */
7239 infoPtr->uCallbackMask = 0;
7240 infoPtr->nFocusedItem = -1;
7241 infoPtr->nSelectionMark = -1;
7242 infoPtr->nHotItem = -1;
7243 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7244 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7245 ZeroMemory(&infoPtr->rcList, sizeof(RECT));
7246 infoPtr->hwndEdit = 0;
7247 infoPtr->pedititem = NULL;
7248 infoPtr->nEditLabelItem = -1;
7250 /* get default font (icon title) */
7251 SystemParametersInfoA(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7252 infoPtr->hDefaultFont = CreateFontIndirectA(&logFont);
7253 infoPtr->hFont = infoPtr->hDefaultFont;
7256 infoPtr->hwndHeader = CreateWindowA(WC_HEADERA, (LPCSTR)NULL,
7257 WS_CHILD | HDS_HORZ | HDS_BUTTONS,
7258 0, 0, 0, 0, hwnd, (HMENU)0,
7259 lpcs->hInstance, NULL);
7261 /* set header font */
7262 SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
7265 if (uView == LVS_ICON)
7267 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
7268 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
7270 else if (uView == LVS_REPORT)
7272 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7274 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7278 /* resize our header to nothing */
7282 memset(&zeroRect,0,sizeof(RECT));
7286 Header_Layout(infoPtr->hwndHeader,&hd);
7290 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7291 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7295 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
7296 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
7299 /* display unsupported listview window styles */
7300 LISTVIEW_UnsupportedStyles(lpcs->style);
7302 /* allocate memory for the data structure */
7303 infoPtr->hdpaItems = DPA_Create(10);
7305 /* allocate memory for the selection ranges */
7306 infoPtr->hdpaSelectionRanges = DPA_Create(10);
7308 /* initialize size of items */
7309 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7310 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7312 /* initialize the hover time to -1(indicating the default system hover time) */
7313 infoPtr->dwHoverTime = -1;
7320 * Erases the background of the listview control.
7323 * [I] HWND : window handle
7324 * [I] WPARAM : device context handle
7325 * [I] LPARAM : not used
7331 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
7334 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7337 if (infoPtr->clrBk == CLR_NONE)
7339 bResult = SendMessageA(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
7344 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
7345 GetClientRect(hwnd, &rc);
7346 FillRect((HDC)wParam, &rc, hBrush);
7347 DeleteObject(hBrush);
7356 * Retrieves the listview control font.
7359 * [I] HWND : window handle
7364 static LRESULT LISTVIEW_GetFont(HWND hwnd)
7366 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7368 return infoPtr->hFont;
7373 * Performs vertical scrolling.
7376 * [I] HWND : window handle
7377 * [I] INT : scroll code
7378 * [I] SHORT : current scroll position if scroll code is SB_THIMBPOSITION
7380 * [I] HWND : scrollbar control window handle
7385 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7388 SCROLLINFO scrollInfo;
7390 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7391 scrollInfo.cbSize = sizeof(SCROLLINFO);
7392 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7394 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7396 INT nOldScrollPos = scrollInfo.nPos;
7397 switch (nScrollCode)
7400 if (scrollInfo.nPos > scrollInfo.nMin)
7407 if (scrollInfo.nPos < scrollInfo.nMax)
7414 if (scrollInfo.nPos > scrollInfo.nMin)
7416 if (scrollInfo.nPos >= scrollInfo.nPage)
7418 scrollInfo.nPos -= scrollInfo.nPage;
7422 scrollInfo.nPos = scrollInfo.nMin;
7428 if (scrollInfo.nPos < scrollInfo.nMax)
7430 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7432 scrollInfo.nPos += scrollInfo.nPage;
7436 scrollInfo.nPos = scrollInfo.nMax;
7442 scrollInfo.nPos = nCurrentPos;
7443 if (scrollInfo.nPos > scrollInfo.nMax)
7444 scrollInfo.nPos=scrollInfo.nMax;
7446 if (scrollInfo.nPos < scrollInfo.nMin)
7447 scrollInfo.nPos=scrollInfo.nMin;
7452 if (nOldScrollPos != scrollInfo.nPos)
7454 scrollInfo.fMask = SIF_POS;
7455 SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
7456 InvalidateRect(hwnd, NULL, TRUE);
7465 * Performs horizontal scrolling.
7468 * [I] HWND : window handle
7469 * [I] INT : scroll code
7470 * [I] SHORT : current scroll position if scroll code is SB_THIMBPOSITION
7472 * [I] HWND : scrollbar control window handle
7477 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
7480 SCROLLINFO scrollInfo;
7482 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7483 scrollInfo.cbSize = sizeof(SCROLLINFO);
7484 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
7486 if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
7488 INT nOldScrollPos = scrollInfo.nPos;
7490 switch (nScrollCode)
7493 if (scrollInfo.nPos > scrollInfo.nMin)
7500 if (scrollInfo.nPos < scrollInfo.nMax)
7507 if (scrollInfo.nPos > scrollInfo.nMin)
7509 if (scrollInfo.nPos >= scrollInfo.nPage)
7511 scrollInfo.nPos -= scrollInfo.nPage;
7515 scrollInfo.nPos = scrollInfo.nMin;
7521 if (scrollInfo.nPos < scrollInfo.nMax)
7523 if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
7525 scrollInfo.nPos += scrollInfo.nPage;
7529 scrollInfo.nPos = scrollInfo.nMax;
7535 scrollInfo.nPos = nCurrentPos;
7537 if (scrollInfo.nPos > scrollInfo.nMax)
7538 scrollInfo.nPos=scrollInfo.nMax;
7540 if (scrollInfo.nPos < scrollInfo.nMin)
7541 scrollInfo.nPos=scrollInfo.nMin;
7545 if (nOldScrollPos != scrollInfo.nPos)
7547 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7548 scrollInfo.fMask = SIF_POS;
7549 SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
7550 if(uView == LVS_REPORT)
7552 scrollInfo.fMask = SIF_POS;
7553 GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
7554 LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
7556 InvalidateRect(hwnd, NULL, TRUE);
7563 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
7565 INT gcWheelDelta = 0;
7566 UINT pulScrollLines = 3;
7567 SCROLLINFO scrollInfo;
7569 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7571 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7572 gcWheelDelta -= wheelDelta;
7574 ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
7575 scrollInfo.cbSize = sizeof(SCROLLINFO);
7576 scrollInfo.fMask = SIF_POS | SIF_RANGE;
7583 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7584 * should be fixed in the future.
7586 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7587 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
7591 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7593 if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
7595 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
7596 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7597 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
7603 LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
7614 * [I] HWND : window handle
7615 * [I] INT : virtual key
7616 * [I] LONG : key data
7621 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
7623 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7624 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
7625 HWND hwndParent = GetParent(hwnd);
7626 NMLVKEYDOWN nmKeyDown;
7629 BOOL bRedraw = FALSE;
7631 /* send LVN_KEYDOWN notification */
7632 ZeroMemory(&nmKeyDown, sizeof(NMLVKEYDOWN));
7633 nmKeyDown.hdr.hwndFrom = hwnd;
7634 nmKeyDown.hdr.idFrom = nCtrlId;
7635 nmKeyDown.hdr.code = LVN_KEYDOWN;
7636 nmKeyDown.wVKey = nVirtualKey;
7637 nmKeyDown.flags = 0;
7638 SendMessageA(hwndParent, WM_NOTIFY, (WPARAM)nCtrlId, (LPARAM)&nmKeyDown);
7641 nmh.hwndFrom = hwnd;
7642 nmh.idFrom = nCtrlId;
7644 switch (nVirtualKey)
7647 if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
7649 /* send NM_RETURN notification */
7650 nmh.code = NM_RETURN;
7651 ListView_Notify(hwndParent, nCtrlId, &nmh);
7653 /* send LVN_ITEMACTIVATE notification */
7654 nmh.code = LVN_ITEMACTIVATE;
7655 ListView_Notify(hwndParent, nCtrlId, &nmh);
7660 if (GETITEMCOUNT(infoPtr) > 0)
7667 if (GETITEMCOUNT(infoPtr) > 0)
7669 nItem = GETITEMCOUNT(infoPtr) - 1;
7674 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
7678 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
7682 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
7686 nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
7698 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
7700 bRedraw = LISTVIEW_KeySelection(hwnd, nItem);
7701 if (bRedraw != FALSE)
7703 /* refresh client area */
7716 * [I] HWND : window handle
7721 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
7723 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
7724 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
7728 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7729 UINT uView = lStyle & LVS_TYPEMASK;
7731 TRACE("(hwnd=%x)\n", hwnd);
7733 /* send NM_KILLFOCUS notification */
7734 nmh.hwndFrom = hwnd;
7735 nmh.idFrom = nCtrlId;
7736 nmh.code = NM_KILLFOCUS;
7737 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
7739 /* set window focus flag */
7740 infoPtr->bFocus = FALSE;
7742 /* NEED drawing optimization ; redraw the selected items */
7743 if (uView & LVS_REPORT)
7745 nTop = LISTVIEW_GetTopIndex(hwnd);
7747 LISTVIEW_GetCountPerColumn(hwnd) + 1;
7752 nBottom = GETITEMCOUNT(infoPtr);
7754 for (i = nTop; i<nBottom; i++)
7756 if (LISTVIEW_IsSelected(hwnd,i))
7758 rcItem.left = LVIR_BOUNDS;
7759 LISTVIEW_GetItemRect(hwnd, i, &rcItem);
7760 InvalidateRect(hwnd, &rcItem, FALSE);
7769 * Processes double click messages (left mouse button).
7772 * [I] HWND : window handle
7773 * [I] WORD : key flag
7774 * [I] WORD : x coordinate
7775 * [I] WORD : y coordinate
7780 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
7783 LONG nCtrlId = GetWindowLongA(hwnd, GWL_ID);
7785 LVHITTESTINFO htInfo;
7787 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
7789 /* send NM_DBLCLK notification */
7790 nmh.hwndFrom = hwnd;
7791 nmh.idFrom = nCtrlId;
7792 nmh.code = NM_DBLCLK;
7793 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
7795 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
7796 ZeroMemory(&htInfo, sizeof(LVHITTESTINFO));
7797 htInfo.pt.x = wPosX;
7798 htInfo.pt.y = wPosY;
7799 if(LISTVIEW_HitTest(hwnd, &htInfo) != -1)
7801 /* send LVN_ITEMACTIVATE notification */
7802 nmh.code = LVN_ITEMACTIVATE;
7803 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
7811 * Processes mouse down messages (left mouse button).
7814 * [I] HWND : window handle
7815 * [I] WORD : key flag
7816 * [I] WORD : x coordinate
7817 * [I] WORD : y coordinate
7822 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
7825 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7826 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7827 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
7828 static BOOL bGroupSelect = TRUE;
7833 TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX,
7836 /* send NM_RELEASEDCAPTURE notification */
7837 nmh.hwndFrom = hwnd;
7838 nmh.idFrom = nCtrlId;
7839 nmh.code = NM_RELEASEDCAPTURE;
7840 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
7842 if (infoPtr->bFocus == FALSE)
7847 /* set left button down flag */
7848 infoPtr->bLButtonDown = TRUE;
7850 ptPosition.x = wPosX;
7851 ptPosition.y = wPosY;
7852 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
7853 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7855 if (lStyle & LVS_SINGLESEL)
7857 if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
7858 && infoPtr->nEditLabelItem == -1)
7860 infoPtr->nEditLabelItem = nItem;
7864 LISTVIEW_SetSelection(hwnd, nItem);
7869 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
7871 if (bGroupSelect != FALSE)
7873 LISTVIEW_AddGroupSelection(hwnd, nItem);
7877 LISTVIEW_AddSelection(hwnd, nItem);
7880 else if (wKey & MK_CONTROL)
7882 bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
7884 else if (wKey & MK_SHIFT)
7886 LISTVIEW_SetGroupSelection(hwnd, nItem);
7890 if (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED
7891 && infoPtr->nEditLabelItem == -1)
7893 infoPtr->nEditLabelItem = nItem;
7897 LISTVIEW_SetSelection(hwnd, nItem);
7905 /* remove all selections */
7906 LISTVIEW_RemoveAllSelections(hwnd);
7909 InvalidateRect(hwnd, NULL, TRUE);
7916 * Processes mouse up messages (left mouse button).
7919 * [I] HWND : window handle
7920 * [I] WORD : key flag
7921 * [I] WORD : x coordinate
7922 * [I] WORD : y coordinate
7927 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
7930 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7932 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
7934 if (infoPtr->bLButtonDown != FALSE)
7936 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
7939 /* send NM_CLICK notification */
7940 nmh.hwndFrom = hwnd;
7941 nmh.idFrom = nCtrlId;
7942 nmh.code = NM_CLICK;
7943 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
7945 /* set left button flag */
7946 infoPtr->bLButtonDown = FALSE;
7948 if(infoPtr->nEditLabelItem != -1)
7952 ptPosition.x = wPosX;
7953 ptPosition.y = wPosY;
7954 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
7955 if(nItem == infoPtr->nEditLabelItem)
7957 LISTVIEW_EditLabelA(hwnd, nItem);
7959 infoPtr->nEditLabelItem = -1;
7968 * Creates the listview control (called before WM_CREATE).
7971 * [I] HWND : window handle
7972 * [I] WPARAM : unhandled
7973 * [I] LPARAM : widow creation info
7978 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
7980 LISTVIEW_INFO *infoPtr;
7982 TRACE("(hwnd=%x,wParam=%x,lParam=%lx)\n", hwnd, wParam, lParam);
7984 /* allocate memory for info structure */
7985 infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
7986 SetWindowLongA(hwnd, 0, (LONG)infoPtr);
7987 if (infoPtr == NULL)
7989 ERR("could not allocate info memory!\n");
7993 if ((LISTVIEW_INFO *)GetWindowLongA(hwnd, 0) != infoPtr)
7995 ERR("pointer assignment error!\n");
7999 return DefWindowProcA(hwnd, WM_NCCREATE, wParam, lParam);
8004 * Destroys the listview control (called after WM_DESTROY).
8007 * [I] HWND : window handle
8012 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8014 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8016 TRACE("(hwnd=%x)\n", hwnd);
8018 /* delete all items */
8019 LISTVIEW_DeleteAllItems(hwnd);
8021 /* destroy data structure */
8022 DPA_Destroy(infoPtr->hdpaItems);
8023 DPA_Destroy(infoPtr->hdpaSelectionRanges);
8026 infoPtr->hFont = (HFONT)0;
8027 if (infoPtr->hDefaultFont)
8029 DeleteObject(infoPtr->hDefaultFont);
8032 /* free listview info pointer*/
8033 COMCTL32_Free(infoPtr);
8035 SetWindowLongA(hwnd, 0, 0);
8041 * Handles notifications from children.
8044 * [I] HWND : window handle
8045 * [I] INT : control identifier
8046 * [I] LPNMHDR : notification information
8051 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8053 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8055 if (lpnmh->hwndFrom == infoPtr->hwndHeader)
8057 /* handle notification from header control */
8058 if (lpnmh->code == HDN_ENDTRACKA)
8060 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8061 InvalidateRect(hwnd, NULL, TRUE);
8063 else if(lpnmh->code == HDN_ITEMCLICKA)
8065 /* Handle sorting by Header Column */
8067 LPNMHEADERA pnmHeader = (LPNMHEADERA) lpnmh;
8068 LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
8070 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8071 nmlv.hdr.hwndFrom = hwnd;
8072 nmlv.hdr.idFrom = lCtrlId;
8073 nmlv.hdr.code = LVN_COLUMNCLICK;
8075 nmlv.iSubItem = pnmHeader->iItem;
8077 ListView_LVNotify(GetParent(hwnd),lCtrlId, &nmlv);
8080 else if(lpnmh->code == NM_RELEASEDCAPTURE)
8082 /* Idealy this should be done in HDN_ENDTRACKA
8083 * but since SetItemBounds in Header.c is called after
8084 * the notification is sent, it is neccessary to handle the
8085 * update of the scroll bar here (Header.c works fine as it is,
8086 * no need to disturb it)
8088 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8089 LISTVIEW_UpdateScroll(hwnd);
8090 InvalidateRect(hwnd, NULL, TRUE);
8100 * Determines the type of structure to use.
8103 * [I] HWND : window handle of the sender
8104 * [I] HWND : listview window handle
8105 * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8110 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8112 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8114 if (nCommand == NF_REQUERY)
8116 /* determine the type of structure to use */
8117 infoPtr->notifyFormat = SendMessageA(hwndFrom, WM_NOTIFYFORMAT,
8118 (WPARAM)hwnd, (LPARAM)NF_QUERY);
8119 if (infoPtr->notifyFormat == NFR_UNICODE)
8121 FIXME("NO support for unicode structures");
8130 * Paints/Repaints the listview control.
8133 * [I] HWND : window handle
8134 * [I] HDC : device context handle
8139 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8143 TRACE("(hwnd=%x,hdc=%x)\n", hwnd, hdc);
8147 hdc = BeginPaint(hwnd, &ps);
8148 LISTVIEW_Refresh(hwnd, hdc);
8149 EndPaint(hwnd, &ps);
8153 LISTVIEW_Refresh(hwnd, hdc);
8161 * Processes double click messages (right mouse button).
8164 * [I] HWND : window handle
8165 * [I] WORD : key flag
8166 * [I] WORD : x coordinate
8167 * [I] WORD : y coordinate
8172 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8175 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8178 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8180 /* send NM_RELEASEDCAPTURE notification */
8181 nmh.hwndFrom = hwnd;
8182 nmh.idFrom = nCtrlId;
8183 nmh.code = NM_RELEASEDCAPTURE;
8184 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8186 /* send NM_RDBLCLK notification */
8187 nmh.code = NM_RDBLCLK;
8188 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8195 * Processes mouse down messages (right mouse button).
8198 * [I] HWND : window handle
8199 * [I] WORD : key flag
8200 * [I] WORD : x coordinate
8201 * [I] WORD : y coordinate
8206 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8209 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8210 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8215 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8217 /* send NM_RELEASEDCAPTURE notification */
8218 nmh.hwndFrom = hwnd;
8219 nmh.idFrom = nCtrlId;
8220 nmh.code = NM_RELEASEDCAPTURE;
8221 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8223 /* make sure the listview control window has the focus */
8224 if (infoPtr->bFocus == FALSE)
8229 /* set right button down flag */
8230 infoPtr->bRButtonDown = TRUE;
8232 /* determine the index of the selected item */
8233 ptPosition.x = wPosX;
8234 ptPosition.y = wPosY;
8235 nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8236 if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8238 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)))
8240 LISTVIEW_SetSelection(hwnd, nItem);
8245 LISTVIEW_RemoveAllSelections(hwnd);
8253 * Processes mouse up messages (right mouse button).
8256 * [I] HWND : window handle
8257 * [I] WORD : key flag
8258 * [I] WORD : x coordinate
8259 * [I] WORD : y coordinate
8264 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8267 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8268 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8271 TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8273 if (infoPtr->bRButtonDown != FALSE)
8279 /* Send NM_RClICK notification */
8280 ZeroMemory(&nmh, sizeof(NMHDR));
8281 nmh.hwndFrom = hwnd;
8282 nmh.idFrom = nCtrlId;
8283 nmh.code = NM_RCLICK;
8284 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8286 /* set button flag */
8287 infoPtr->bRButtonDown = FALSE;
8289 /* Change to screen coordinate for WM_CONTEXTMENU */
8290 ClientToScreen(hwnd, &pt);
8292 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8293 SendMessageA( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
8304 * [I] HWND : window handle
8305 * [I] HWND : window handle of previously focused window
8310 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
8312 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8313 INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
8316 TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
8318 /* send NM_SETFOCUS notification */
8319 nmh.hwndFrom = hwnd;
8320 nmh.idFrom = nCtrlId;
8321 nmh.code = NM_SETFOCUS;
8322 ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
8324 /* set window focus flag */
8325 infoPtr->bFocus = TRUE;
8337 * [I] HWND : window handle
8338 * [I] HFONT : font handle
8339 * [I] WORD : redraw flag
8344 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
8346 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8347 UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8349 TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
8353 infoPtr->hFont = infoPtr->hDefaultFont;
8357 infoPtr->hFont = hFont;
8360 if (uView == LVS_REPORT)
8362 /* set header font */
8363 SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
8364 MAKELPARAM(fRedraw, 0));
8367 /* invalidate listview control client area */
8368 InvalidateRect(hwnd, NULL, TRUE);
8370 if (fRedraw != FALSE)
8380 * Message handling for WM_SETREDRAW.
8381 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8384 * [I] HWND : window handle
8385 * [I] bRedraw: state of redraw flag
8388 * DefWinProc return value
8390 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
8393 lResult = DefWindowProcA(hwnd, WM_SETREDRAW, bRedraw, 0);
8396 RedrawWindow(hwnd, NULL, 0,
8397 RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
8404 * Resizes the listview control. This function processes WM_SIZE
8405 * messages. At this time, the width and height are not used.
8408 * [I] HWND : window handle
8409 * [I] WORD : new width
8410 * [I] WORD : new height
8415 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
8417 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8418 UINT uView = lStyle & LVS_TYPEMASK;
8420 TRACE("(hwnd=%x, width=%d, height=%d)\n",hwnd, Width, Height);
8422 LISTVIEW_UpdateSize(hwnd);
8424 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
8426 if (lStyle & LVS_ALIGNLEFT)
8428 LISTVIEW_AlignLeft(hwnd);
8432 LISTVIEW_AlignTop(hwnd);
8436 LISTVIEW_UpdateScroll(hwnd);
8438 /* invalidate client area + erase background */
8439 InvalidateRect(hwnd, NULL, TRUE);
8446 * Sets the size information.
8449 * [I] HWND : window handle
8454 static VOID LISTVIEW_UpdateSize(HWND hwnd)
8456 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8457 LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
8458 UINT uView = lStyle & LVS_TYPEMASK;
8461 GetClientRect(hwnd, &rcList);
8462 infoPtr->rcList.left = 0;
8463 infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
8464 infoPtr->rcList.top = 0;
8465 infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
8467 if (uView == LVS_LIST)
8469 if ((lStyle & WS_HSCROLL) == 0)
8471 INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
8472 if (infoPtr->rcList.bottom > nHScrollHeight)
8474 infoPtr->rcList.bottom -= nHScrollHeight;
8478 else if ((uView == LVS_REPORT)&&(!(LVS_NOCOLUMNHEADER & lStyle)))
8485 Header_Layout(infoPtr->hwndHeader, &hl);
8487 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8489 if (!(LVS_NOCOLUMNHEADER & lStyle))
8491 infoPtr->rcList.top = max(wp.cy, 0);
8498 * Processes WM_STYLECHANGED messages.
8501 * [I] HWND : window handle
8502 * [I] WPARAM : window style type (normal or extended)
8503 * [I] LPSTYLESTRUCT : window style information
8508 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
8511 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
8512 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
8513 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
8514 RECT rcList = infoPtr->rcList;
8516 TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n",
8517 hwnd, wStyleType, lpss);
8519 if (wStyleType == GWL_STYLE)
8521 if (uOldView == LVS_REPORT)
8523 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8526 if ((lpss->styleOld & WS_HSCROLL) != 0)
8528 ShowScrollBar(hwnd, SB_HORZ, FALSE);
8531 if ((lpss->styleOld & WS_VSCROLL) != 0)
8533 ShowScrollBar(hwnd, SB_VERT, FALSE);
8536 if (uNewView == LVS_ICON)
8538 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8539 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8540 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8541 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8542 if (lpss->styleNew & LVS_ALIGNLEFT)
8544 LISTVIEW_AlignLeft(hwnd);
8548 LISTVIEW_AlignTop(hwnd);
8551 else if (uNewView == LVS_REPORT)
8556 if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
8560 Header_Layout(infoPtr->hwndHeader, &hl);
8561 SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
8563 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8568 ZeroMemory(&zeroRect,sizeof(RECT));
8572 Header_Layout(infoPtr->hwndHeader, &hl);
8575 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8576 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8577 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8578 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8580 else if (uNewView == LVS_LIST)
8582 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8583 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8584 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8585 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8589 infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8590 infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8591 infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8592 infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8593 if (lpss->styleNew & LVS_ALIGNLEFT)
8595 LISTVIEW_AlignLeft(hwnd);
8599 LISTVIEW_AlignTop(hwnd);
8603 /* update the size of the client area */
8604 LISTVIEW_UpdateSize(hwnd);
8606 /* add scrollbars if needed */
8607 LISTVIEW_UpdateScroll(hwnd);
8609 /* invalidate client area + erase background */
8610 InvalidateRect(hwnd, NULL, TRUE);
8612 /* print the list of unsupported window styles */
8613 LISTVIEW_UnsupportedStyles(lpss->styleNew);
8616 /* If they change the view and we have an active edit control
8617 we will need to kill the control since the redraw will
8618 misplace the edit control.
8620 if (infoPtr->hwndEdit &&
8621 ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
8622 ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
8624 SendMessageA(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8632 * Window procedure of the listview control.
8635 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
8638 TRACE("hwnd=%x uMsg=%x wParam=%x lParam=%lx\n", hwnd, uMsg, wParam, lParam);
8639 if (!GetWindowLongA(hwnd, 0) && (uMsg != WM_NCCREATE))
8640 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
8643 case LVM_APPROXIMATEVIEWRECT:
8644 return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
8645 LOWORD(lParam), HIWORD(lParam));
8647 return LISTVIEW_Arrange(hwnd, (INT)wParam);
8649 /* case LVM_CREATEDRAGIMAGE: */
8651 case LVM_DELETEALLITEMS:
8652 return LISTVIEW_DeleteAllItems(hwnd);
8654 case LVM_DELETECOLUMN:
8655 return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
8657 case LVM_DELETEITEM:
8658 return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
8660 case LVM_EDITLABELW:
8661 case LVM_EDITLABELA:
8662 return LISTVIEW_EditLabelA(hwnd, (INT)wParam);
8664 case LVM_ENSUREVISIBLE:
8665 return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
8668 return LISTVIEW_FindItem(hwnd, (INT)wParam, (LPLVFINDINFO)lParam);
8670 case LVM_GETBKCOLOR:
8671 return LISTVIEW_GetBkColor(hwnd);
8673 /* case LVM_GETBKIMAGE: */
8675 case LVM_GETCALLBACKMASK:
8676 return LISTVIEW_GetCallbackMask(hwnd);
8678 case LVM_GETCOLUMNA:
8679 return LISTVIEW_GetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
8681 /* case LVM_GETCOLUMNW: */
8683 case LVM_GETCOLUMNORDERARRAY:
8684 return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
8686 case LVM_GETCOLUMNWIDTH:
8687 return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
8689 case LVM_GETCOUNTPERPAGE:
8690 return LISTVIEW_GetCountPerPage(hwnd);
8692 case LVM_GETEDITCONTROL:
8693 return LISTVIEW_GetEditControl(hwnd);
8695 case LVM_GETEXTENDEDLISTVIEWSTYLE:
8696 return LISTVIEW_GetExtendedListViewStyle(hwnd);
8699 return LISTVIEW_GetHeader(hwnd);
8701 /* case LVM_GETHOTCURSOR: */
8703 case LVM_GETHOTITEM:
8704 return LISTVIEW_GetHotItem(hwnd);
8706 case LVM_GETHOVERTIME:
8707 return LISTVIEW_GetHoverTime(hwnd);
8709 case LVM_GETIMAGELIST:
8710 return LISTVIEW_GetImageList(hwnd, (INT)wParam);
8712 /* case LVM_GETISEARCHSTRING: */
8715 return LISTVIEW_GetItemA(hwnd, (LPLVITEMA)lParam, FALSE);
8717 /* case LVM_GETITEMW: */
8719 case LVM_GETITEMCOUNT:
8720 return LISTVIEW_GetItemCount(hwnd);
8722 case LVM_GETITEMPOSITION:
8723 return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
8725 case LVM_GETITEMRECT:
8726 return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
8728 case LVM_GETITEMSPACING:
8729 return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
8731 case LVM_GETITEMSTATE:
8732 return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
8734 case LVM_GETITEMTEXTA:
8735 LISTVIEW_GetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
8738 /* case LVM_GETITEMTEXTW: */
8740 case LVM_GETNEXTITEM:
8741 return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
8743 /* case LVM_GETNUMBEROFWORKAREAS: */
8746 return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
8748 case LVM_GETSELECTEDCOUNT:
8749 return LISTVIEW_GetSelectedCount(hwnd);
8751 case LVM_GETSELECTIONMARK:
8752 return LISTVIEW_GetSelectionMark(hwnd);
8754 case LVM_GETSTRINGWIDTHA:
8755 return LISTVIEW_GetStringWidthA (hwnd, (LPCSTR)lParam);
8757 /* case LVM_GETSTRINGWIDTHW: */
8758 /* case LVM_GETSUBITEMRECT: */
8760 case LVM_GETTEXTBKCOLOR:
8761 return LISTVIEW_GetTextBkColor(hwnd);
8763 case LVM_GETTEXTCOLOR:
8764 return LISTVIEW_GetTextColor(hwnd);
8766 /* case LVM_GETTOOLTIPS: */
8768 case LVM_GETTOPINDEX:
8769 return LISTVIEW_GetTopIndex(hwnd);
8771 /* case LVM_GETUNICODEFORMAT: */
8773 case LVM_GETVIEWRECT:
8774 return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
8776 /* case LVM_GETWORKAREAS: */
8779 return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
8781 case LVM_INSERTCOLUMNA:
8782 return LISTVIEW_InsertColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
8784 case LVM_INSERTCOLUMNW:
8785 return LISTVIEW_InsertColumnW(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam);
8787 case LVM_INSERTITEMA:
8788 return LISTVIEW_InsertItemA(hwnd, (LPLVITEMA)lParam);
8790 case LVM_INSERTITEMW:
8791 return LISTVIEW_InsertItemW(hwnd, (LPLVITEMW)lParam);
8793 case LVM_REDRAWITEMS:
8794 return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
8796 /* case LVM_SCROLL: */
8797 /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
8799 case LVM_SETBKCOLOR:
8800 return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
8802 /* case LVM_SETBKIMAGE: */
8804 case LVM_SETCALLBACKMASK:
8805 return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
8807 case LVM_SETCOLUMNA:
8808 return LISTVIEW_SetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
8810 case LVM_SETCOLUMNW:
8811 FIXME("Unimplemented msg LVM_SETCOLUMNW\n");
8814 case LVM_SETCOLUMNORDERARRAY:
8815 return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
8817 case LVM_SETCOLUMNWIDTH:
8818 return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
8820 case LVM_SETEXTENDEDLISTVIEWSTYLE:
8821 return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
8823 /* case LVM_SETHOTCURSOR: */
8825 case LVM_SETHOTITEM:
8826 return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
8828 case LVM_SETHOVERTIME:
8829 return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
8831 /* case LVM_SETICONSPACING: */
8833 case LVM_SETIMAGELIST:
8834 return LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
8837 return LISTVIEW_SetItemA(hwnd, (LPLVITEMA)lParam);
8839 /* case LVM_SETITEMW: */
8841 case LVM_SETITEMCOUNT:
8842 return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
8844 case LVM_SETITEMPOSITION:
8845 return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
8846 (INT)HIWORD(lParam));
8848 /* case LVM_SETITEMPOSITION32: */
8850 case LVM_SETITEMSTATE:
8851 return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMA)lParam);
8853 case LVM_SETITEMTEXTA:
8854 return LISTVIEW_SetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
8856 /* case LVM_SETITEMTEXTW: */
8858 case LVM_SETSELECTIONMARK:
8859 return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
8861 case LVM_SETTEXTBKCOLOR:
8862 return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
8864 case LVM_SETTEXTCOLOR:
8865 return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
8867 /* case LVM_SETTOOLTIPS: */
8868 /* case LVM_SETUNICODEFORMAT: */
8869 /* case LVM_SETWORKAREAS: */
8872 return LISTVIEW_SortItems(hwnd, wParam, lParam);
8874 /* case LVM_SUBITEMHITTEST: */
8877 return LISTVIEW_Update(hwnd, (INT)wParam);
8881 return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
8884 return LISTVIEW_Command(hwnd, wParam, lParam);
8887 return LISTVIEW_Create(hwnd, wParam, lParam);
8890 return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
8893 return DLGC_WANTCHARS | DLGC_WANTARROWS;
8896 return LISTVIEW_GetFont(hwnd);
8899 return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
8900 (INT)HIWORD(wParam), (HWND)lParam);
8903 return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
8906 return LISTVIEW_KillFocus(hwnd);
8908 case WM_LBUTTONDBLCLK:
8909 return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
8912 case WM_LBUTTONDOWN:
8913 return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
8916 return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
8919 return LISTVIEW_MouseMove (hwnd, wParam, lParam);
8922 return LISTVIEW_MouseHover(hwnd, wParam, lParam);
8925 return LISTVIEW_NCCreate(hwnd, wParam, lParam);
8928 return LISTVIEW_NCDestroy(hwnd);
8931 return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
8933 case WM_NOTIFYFORMAT:
8934 return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
8937 return LISTVIEW_Paint(hwnd, (HDC)wParam);
8939 case WM_RBUTTONDBLCLK:
8940 return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
8943 case WM_RBUTTONDOWN:
8944 return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
8948 return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
8952 return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
8955 return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
8958 return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
8961 return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
8963 case WM_STYLECHANGED:
8964 return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
8966 /* case WM_TIMER: */
8969 return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
8970 (INT)HIWORD(wParam), (HWND)lParam);
8973 if (wParam & (MK_SHIFT | MK_CONTROL))
8974 return DefWindowProcA( hwnd, uMsg, wParam, lParam );
8975 return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */
8977 /* case WM_WININICHANGE: */
8980 if (uMsg >= WM_USER)
8982 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
8986 /* call default window procedure */
8987 return DefWindowProcA(hwnd, uMsg, wParam, lParam);
8995 * Registers the window class.
9003 VOID LISTVIEW_Register(void)
9007 ZeroMemory(&wndClass, sizeof(WNDCLASSA));
9008 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9009 wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9010 wndClass.cbClsExtra = 0;
9011 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9012 wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
9013 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9014 wndClass.lpszClassName = WC_LISTVIEWA;
9015 RegisterClassA(&wndClass);
9020 * Unregisters the window class.
9028 VOID LISTVIEW_Unregister(void)
9030 UnregisterClassA(WC_LISTVIEWA, (HINSTANCE)NULL);
9035 * Handle any WM_COMMAND messages
9041 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9043 switch (HIWORD(wParam))
9048 * Adjust the edit window size
9051 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
9052 HDC hdc = GetDC(infoPtr->hwndEdit);
9053 HFONT hFont, hOldFont = 0;
9058 len = GetWindowTextA(infoPtr->hwndEdit, buffer, 1023);
9059 GetWindowRect(infoPtr->hwndEdit, &rect);
9061 /* Select font to get the right dimension of the string */
9062 hFont = SendMessageA(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9065 hOldFont = SelectObject(hdc, hFont);
9068 if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
9070 TEXTMETRICA textMetric;
9072 /* Add Extra spacing for the next character */
9073 GetTextMetricsA(hdc, &textMetric);
9074 sz.cx += (textMetric.tmMaxCharWidth * 2);
9082 rect.bottom - rect.top,
9083 SWP_DRAWFRAME|SWP_NOMOVE);
9087 SelectObject(hdc, hOldFont);
9090 ReleaseDC(hwnd, hdc);
9096 return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9105 * Subclassed edit control windproc function
9111 LRESULT CALLBACK EditLblWndProc(HWND hwnd, UINT uMsg,
9112 WPARAM wParam, LPARAM lParam)
9114 BOOL cancel = FALSE;
9115 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(GetParent(hwnd), 0);
9116 EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9117 static BOOL bIgnoreKillFocus = FALSE;
9121 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9124 if(bIgnoreKillFocus)
9132 WNDPROC editProc = einfo->EditWndProc;
9133 SetWindowLongA(hwnd, GWL_WNDPROC, (LONG)editProc);
9134 COMCTL32_Free(einfo);
9135 infoPtr->pedititem = NULL;
9136 return CallWindowProcA(editProc, hwnd, uMsg, wParam, lParam);
9140 if (VK_ESCAPE == (INT)wParam)
9146 else if (VK_RETURN == (INT)wParam)
9150 return CallWindowProcA(einfo->EditWndProc, hwnd,
9151 uMsg, wParam, lParam);
9154 if (einfo->EditLblCb)
9156 char *buffer = NULL;
9161 int len = 1 + GetWindowTextLengthA(hwnd);
9165 if (NULL != (buffer = (char *)COMCTL32_Alloc(len*sizeof(char))))
9167 GetWindowTextA(hwnd, buffer, len);
9171 /* Processing LVN_ENDLABELEDIT message could kill the focus */
9172 /* eg. Using a messagebox */
9173 bIgnoreKillFocus = TRUE;
9174 einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9177 COMCTL32_Free(buffer);
9179 einfo->EditLblCb = NULL;
9180 bIgnoreKillFocus = FALSE;
9183 SendMessageA(hwnd, WM_CLOSE, 0, 0);
9190 * Creates a subclassed edit cotrol
9196 HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y,
9197 INT width, INT height, HWND parent, HINSTANCE hinst,
9198 EditlblCallback EditLblCb, DWORD param)
9204 TEXTMETRICA textMetric;
9205 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(parent, 0);
9207 if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
9210 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
9211 hdc = GetDC(parent);
9213 /* Select the font to get appropriate metric dimensions */
9214 if(infoPtr->hFont != 0)
9216 hOldFont = SelectObject(hdc, infoPtr->hFont);
9219 /*Get String Lenght in pixels */
9220 GetTextExtentPoint32A(hdc, text, strlen(text), &sz);
9222 /*Add Extra spacing for the next character */
9223 GetTextMetricsA(hdc, &textMetric);
9224 sz.cx += (textMetric.tmMaxCharWidth * 2);
9226 if(infoPtr->hFont != 0)
9228 SelectObject(hdc, hOldFont);
9231 ReleaseDC(parent, hdc);
9232 if (!(hedit = CreateWindowA("Edit", text, style, x, y, sz.cx, height,
9233 parent, 0, hinst, 0)))
9235 COMCTL32_Free(infoPtr->pedititem);
9239 infoPtr->pedititem->param = param;
9240 infoPtr->pedititem->EditLblCb = EditLblCb;
9241 infoPtr->pedititem->EditWndProc = (WNDPROC)SetWindowLongA(hedit,
9242 GWL_WNDPROC, (LONG) EditLblWndProc);
9244 SendMessageA(hedit, WM_SETFONT, infoPtr->hFont, FALSE);