/* * Listview control * * Copyright 1998, 1999 Eric Kohl * Copyright 1999 Luc Tourangeau * Copyright 2000 Jason Mawdsley * Copyright 2001 Codeweavers Inc. * Copyright 2002 Dimitrie O. Paun * * NOTES * Listview control implementation. * * TODO: * 1. No horizontal scrolling when header is larger than the client area. * 2. Drawing optimizations. * 3. Hot item handling. * * Notifications: * LISTVIEW_Notify : most notifications from children (editbox and header) * * Data structure: * LISTVIEW_SetItemCount : not completed for non OWNERDATA * * Advanced functionality: * LISTVIEW_GetNumberOfWorkAreas : not implemented * LISTVIEW_GetHotCursor : not implemented * LISTVIEW_GetISearchString : not implemented * LISTVIEW_GetBkImage : not implemented * LISTVIEW_SetBkImage : not implemented * LISTVIEW_GetColumnOrderArray : simple hack only * LISTVIEW_SetColumnOrderArray : simple hack only * LISTVIEW_Arrange : empty stub * LISTVIEW_ApproximateViewRect : incomplete * LISTVIEW_Scroll : not implemented * LISTVIEW_Update : not completed * * Known differences in message stream from native control (not known if * these differences cause problems): * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases. * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED. * WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0. * WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and * does *not* invoke DefWindowProc * WM_CREATE does not issue WM_QUERYUISTATE and associated registry * processing for "USEDOUBLECLICKTIME". */ #include #include #include #include #include "winbase.h" #include "winnt.h" #include "heap.h" #include "commctrl.h" #include "debugtools.h" DEFAULT_DEBUG_CHANNEL(listview); /* Some definitions for inline edit control */ typedef BOOL (*EditlblCallbackW)(HWND, LPWSTR, DWORD); typedef BOOL (*EditlblCallbackA)(HWND, LPWSTR, DWORD); typedef struct tagLV_INTHIT { LVHITTESTINFO ht; DWORD distance; /* distance to closest item */ INT iDistItem; /* item number that is closest */ } LV_INTHIT, *LPLV_INTHIT; typedef struct tagEDITLABEL_ITEM { WNDPROC EditWndProc; DWORD param; EditlblCallbackW EditLblCb; } EDITLABEL_ITEM; typedef struct tagLISTVIEW_SUBITEM { LPWSTR pszText; INT iImage; INT iSubItem; } LISTVIEW_SUBITEM; typedef struct tagLISTVIEW_ITEM { UINT state; LPWSTR pszText; INT iImage; LPARAM lParam; INT iIndent; POINT ptPosition; } LISTVIEW_ITEM; typedef struct tagLISTVIEW_SELECTION { DWORD lower; DWORD upper; } LISTVIEW_SELECTION; typedef struct tagLISTVIEW_INFO { HWND hwndSelf; COLORREF clrBk; COLORREF clrText; COLORREF clrTextBk; HIMAGELIST himlNormal; HIMAGELIST himlSmall; HIMAGELIST himlState; BOOL bLButtonDown; BOOL bRButtonDown; INT nFocusedItem; HDPA hdpaSelectionRanges; INT nItemHeight; INT nItemWidth; INT nSelectionMark; INT nHotItem; SHORT notifyFormat; RECT rcList; RECT rcView; SIZE iconSize; SIZE iconSpacing; UINT uCallbackMask; HWND hwndHeader; HFONT hDefaultFont; HFONT hFont; INT ntmHeight; /* from GetTextMetrics from above font */ INT ntmAveCharWidth; /* from GetTextMetrics from above font */ BOOL bFocus; DWORD dwExStyle; /* extended listview style */ HDPA hdpaItems; PFNLVCOMPARE pfnCompare; LPARAM lParamSort; HWND hwndEdit; INT nEditLabelItem; EDITLABEL_ITEM *pedititem; DWORD dwHoverTime; INT nColumnCount; /* the number of columns in this control */ DWORD lastKeyPressTimestamp; /* Added */ WPARAM charCode; /* Added */ INT nSearchParamLength; /* Added */ WCHAR szSearchParam[ MAX_PATH ]; /* Added */ } LISTVIEW_INFO; /* * constants */ /* maximum size of a label */ #define DISP_TEXT_SIZE 512 /* padding for items in list and small icon display modes */ #define WIDTH_PADDING 12 /* padding for items in list, report and small icon display modes */ #define HEIGHT_PADDING 1 /* offset of items in report display mode */ #define REPORT_MARGINX 2 /* padding for icon in large icon display mode * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area * that HITTEST will see. * ICON_TOP_PADDING_HITABLE - spacing between above and icon. * ICON_TOP_PADDING - sum of the two above. * ICON_BOTTOM_PADDING - between bottom of icon and top of text * LABEL_VERT_OFFSET - between bottom of text and end of box */ #define ICON_TOP_PADDING_NOTHITABLE 2 #define ICON_TOP_PADDING_HITABLE 2 #define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE #define ICON_BOTTOM_PADDING 4 #define LABEL_VERT_OFFSET 10 /* default label width for items in list and small icon display modes */ #define DEFAULT_LABEL_WIDTH 40 /* default column width for items in list display mode */ #define DEFAULT_COLUMN_WIDTH 96 /* Increment size of the horizontal scroll bar */ #define LISTVIEW_SCROLL_DIV_SIZE 10 /* Padding betwen image and label */ #define IMAGE_PADDING 2 /* Padding behind the label */ #define TRAILING_PADDING 5 /* Border for the icon caption */ #define CAPTION_BORDER 2 /* * macros */ /* retrieve the number of items in the listview */ #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount) #define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA ) HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y, INT width, INT height, HWND parent, HINSTANCE hinst, EditlblCallbackW EditLblCb, DWORD param, BOOL isW); /* * forward declarations */ static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW); static INT LISTVIEW_SuperHitTestItem(HWND, LPLV_INTHIT, BOOL); static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL); static INT LISTVIEW_GetCountPerRow(HWND); static INT LISTVIEW_GetCountPerColumn(HWND); static VOID LISTVIEW_AlignLeft(HWND); static VOID LISTVIEW_AlignTop(HWND); static VOID LISTVIEW_AddGroupSelection(HWND, INT); static VOID LISTVIEW_AddSelection(HWND, INT); static BOOL LISTVIEW_AddSubItemT(HWND, LPLVITEMW, BOOL); static INT LISTVIEW_FindInsertPosition(HDPA, INT); static INT LISTVIEW_GetItemHeight(HWND); static BOOL LISTVIEW_GetItemBoundBox(HWND, INT, LPRECT); static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT); static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT); static INT LISTVIEW_GetItemWidth(HWND); static INT LISTVIEW_GetLabelWidth(HWND, INT); static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT); static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem); static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT); static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT); static BOOL LISTVIEW_InitItemT(HWND, LISTVIEW_ITEM *, LPLVITEMW, BOOL); static BOOL LISTVIEW_InitSubItemT(HWND, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL); static LRESULT LISTVIEW_MouseSelection(HWND, POINT); static BOOL LISTVIEW_RemoveColumn(HDPA, INT); static BOOL LISTVIEW_RemoveSubItem(HDPA, INT); static VOID LISTVIEW_SetGroupSelection(HWND, INT); static BOOL LISTVIEW_SetItemT(HWND, LPLVITEMW, BOOL); static BOOL LISTVIEW_SetItemFocus(HWND, INT); static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG); static VOID LISTVIEW_UpdateScroll(HWND); static VOID LISTVIEW_SetSelection(HWND, INT); static VOID LISTVIEW_UpdateSize(HWND); static BOOL LISTVIEW_SetSubItemT(HWND, LPLVITEMW, BOOL); static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT); static BOOL LISTVIEW_ToggleSelection(HWND, INT); static VOID LISTVIEW_UnsupportedStyles(LONG lStyle); static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW); static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem); static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem); static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam); static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort); static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW); static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData ); static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem); static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask); static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem); static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem); static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem); static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc); /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/ #define KEY_DELAY 450 #define COUNTOF(array) (sizeof(array)/sizeof(array[0])) static inline BOOL is_textW(LPCWSTR text) { return text != NULL && text != LPSTR_TEXTCALLBACKW; } static inline BOOL is_textT(LPCWSTR text, BOOL isW) { /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */ return is_textW(text); } static inline int textlenT(LPCWSTR text, BOOL isW) { return !is_textT(text, isW) ? 0 : isW ? lstrlenW(text) : lstrlenA((LPCSTR)text); } static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max) { if (isDestW) if (isSrcW) lstrcpynW(dest, src, max); else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max); else if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL); else lstrcpynA((LPSTR)dest, (LPCSTR)src, max); } static inline LPCSTR debugstr_t(LPCWSTR text, BOOL isW) { return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text); } static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n) { return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n); } static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW) { LPWSTR wstr = (LPWSTR)text; TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW); if (!isW && text) { INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0); wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len); } TRACE(" wstr=%s\n", debugstr_w(wstr)); return wstr; } static inline void textfreeT(LPWSTR wstr, BOOL isW) { if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr); } /* * dest is a pointer to a Unicode string * src is a pointer to a string (Unicode if isW, ANSI if !isW) */ static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW) { LPWSTR pszText = textdupTtoW(src, isW); BOOL bResult = TRUE; if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL; bResult = Str_SetPtrW(dest, pszText); textfreeT(pszText, isW); return bResult; } static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW) { if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam); else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam); } static inline BOOL notify(HWND self, INT code, LPNMHDR pnmh) { pnmh->hwndFrom = self; pnmh->idFrom = GetWindowLongW(self, GWL_ID); pnmh->code = code; return (BOOL)SendMessageW(GetParent(self), WM_NOTIFY, (WPARAM)pnmh->idFrom, (LPARAM)pnmh); } static inline BOOL hdr_notify(HWND self, INT code) { NMHDR nmh; return notify(self, code, &nmh); } static inline BOOL listview_notify(HWND self, INT code, LPNMLISTVIEW plvnm) { return notify(self, code, (LPNMHDR)plvnm); } static int tabNotification[] = { LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA, LVN_ENDLABELEDITW, LVN_ENDLABELEDITA, LVN_GETDISPINFOW, LVN_GETDISPINFOA, LVN_SETDISPINFOW, LVN_SETDISPINFOA, LVN_ODFINDITEMW, LVN_ODFINDITEMA, LVN_GETINFOTIPW, LVN_GETINFOTIPA, 0 }; static int get_ansi_notification(INT unicodeNotificationCode) { int *pTabNotif = tabNotification; while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++)); if (*pTabNotif) return *pTabNotif; ERR("unknown notification %x\n", unicodeNotificationCode); return unicodeNotificationCode; } /* Send notification. depends on dispinfoW having same structure as dispinfoA. self : listview handle notificationCode : *Unicode* notification code pdi : dispinfo structure (can be unicode or ansi) isW : TRUE if dispinfo is Unicode */ static BOOL dispinfo_notifyT(HWND self, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(self, 0); BOOL bResult = FALSE; BOOL convertToAnsi = FALSE, convertToUnicode = FALSE; INT realNotifCode; INT cchTempBufMax = 0, savCchTextMax = 0; LPWSTR pszTempBuf = NULL, savPszText = NULL; TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self, notificationCode, pdi, isW); TRACE(" notifyFormat=%s\n", infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" : infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)"); if (infoPtr->notifyFormat == NFR_ANSI) realNotifCode = get_ansi_notification(notificationCode); else realNotifCode = notificationCode; if (is_textT(pdi->item.pszText, isW)) { if (isW && infoPtr->notifyFormat == NFR_ANSI) convertToAnsi = TRUE; if (!isW && infoPtr->notifyFormat == NFR_UNICODE) convertToUnicode = TRUE; } if (convertToAnsi || convertToUnicode) { TRACE(" we have to convert the text to the correct format\n"); if (notificationCode != LVN_GETDISPINFOW) { /* length of existing text */ cchTempBufMax = convertToUnicode ? MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0): WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL); } else cchTempBufMax = pdi->item.cchTextMax; pszTempBuf = HeapAlloc(GetProcessHeap(), 0, (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax); if (!pszTempBuf) return FALSE; if (convertToUnicode) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, pszTempBuf, cchTempBufMax); else WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf, cchTempBufMax, NULL, NULL); TRACE(" text=%s\n", debugstr_t(pszTempBuf, convertToUnicode)); savCchTextMax = pdi->item.cchTextMax; savPszText = pdi->item.pszText; pdi->item.pszText = pszTempBuf; pdi->item.cchTextMax = cchTempBufMax; } bResult = notify(self, realNotifCode, (LPNMHDR)pdi); if (convertToUnicode || convertToAnsi) { /* convert back result */ TRACE(" returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode)); if (convertToUnicode) /* note : pointer can be changed by app ! */ WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText, savCchTextMax, NULL, NULL); else MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1, savPszText, savCchTextMax); pdi->item.pszText = savPszText; /* restores our buffer */ pdi->item.cchTextMax = savCchTextMax; HeapFree(GetProcessHeap(), 0, pszTempBuf); } return bResult; } static inline LRESULT LISTVIEW_GetItemW(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal) { return LISTVIEW_GetItemT(hwnd, lpLVItem, internal, TRUE); } static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n) { int res; n = min(min(n, strlenW(s1)), strlenW(s2)); res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n); return res ? res - 2 : res; } static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW) { static int index = 0; static char buffers[20][256]; char* buf = buffers[index++ % 20]; if (lpLVItem == NULL) return "(null)"; snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x," " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}", lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem, lpLVItem->state, lpLVItem->stateMask, lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam, lpLVItem->iIndent); return buf; } static void LISTVIEW_DumpListview(LISTVIEW_INFO *iP, INT line) { DWORD dwStyle = GetWindowLongW (iP->hwndSelf, GWL_STYLE); TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", iP->hwndSelf, line, iP->clrBk, iP->clrText, iP->clrTextBk, iP->nItemHeight, iP->nItemWidth, dwStyle); TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n", iP->hwndSelf, line, iP->himlNormal, iP->himlSmall, iP->himlState, iP->nFocusedItem, iP->nHotItem, iP->dwExStyle); } static BOOL LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc, RECT rc) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); NMLVCUSTOMDRAW nmcdhdr; LPNMCUSTOMDRAW nmcd; TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd, dwDrawStage, hdc); nmcd= & nmcdhdr.nmcd; nmcd->hdr.hwndFrom = hwnd; nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID); nmcd->hdr.code = NM_CUSTOMDRAW; nmcd->dwDrawStage= dwDrawStage; nmcd->hdc = hdc; nmcd->rc.left = rc.left; nmcd->rc.right = rc.right; nmcd->rc.bottom = rc.bottom; nmcd->rc.top = rc.top; nmcd->dwItemSpec = 0; nmcd->uItemState = 0; nmcd->lItemlParam= 0; nmcdhdr.clrText = infoPtr->clrText; nmcdhdr.clrTextBk= infoPtr->clrBk; return (BOOL)SendMessageW (GetParent (hwnd), WM_NOTIFY, (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr); } static BOOL LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc, UINT iItem, UINT iSubItem, UINT uItemDrawState) { LISTVIEW_INFO *infoPtr; NMLVCUSTOMDRAW nmcdhdr; LPNMCUSTOMDRAW nmcd; DWORD dwDrawStage,dwItemSpec; UINT uItemState; INT retval; RECT itemRect; LVITEMW item; infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); ZeroMemory(&item,sizeof(item)); item.iItem = iItem; item.mask = LVIF_PARAM; ListView_GetItemW(hwnd,&item); dwDrawStage=CDDS_ITEM | uItemDrawState; dwItemSpec=iItem; uItemState=0; if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED; if (iItem==infoPtr->nFocusedItem) uItemState|=CDIS_FOCUS; if (iItem==infoPtr->nHotItem) uItemState|=CDIS_HOT; itemRect.left = LVIR_BOUNDS; LISTVIEW_GetItemRect(hwnd, iItem, &itemRect); nmcd= & nmcdhdr.nmcd; nmcd->hdr.hwndFrom = hwnd; nmcd->hdr.idFrom = GetWindowLongW( hwnd, GWL_ID); nmcd->hdr.code = NM_CUSTOMDRAW; nmcd->dwDrawStage= dwDrawStage; nmcd->hdc = hdc; nmcd->rc.left = itemRect.left; nmcd->rc.right = itemRect.right; nmcd->rc.bottom = itemRect.bottom; nmcd->rc.top = itemRect.top; nmcd->dwItemSpec = dwItemSpec; nmcd->uItemState = uItemState; nmcd->lItemlParam= item.lParam; nmcdhdr.clrText = infoPtr->clrText; nmcdhdr.clrTextBk= infoPtr->clrBk; nmcdhdr.iSubItem =iSubItem; TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n", nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec, nmcd->uItemState, nmcd->lItemlParam); retval=SendMessageW (GetParent (hwnd), WM_NOTIFY, (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr); infoPtr->clrText=nmcdhdr.clrText; infoPtr->clrBk =nmcdhdr.clrTextBk; return (BOOL) retval; } /************************************************************************* * LISTVIEW_ProcessLetterKeys * * Processes keyboard messages generated by pressing the letter keys * on the keyboard. * What this does is perform a case insensitive search from the * current position with the following quirks: * - If two chars or more are pressed in quick succession we search * for the corresponding string (e.g. 'abc'). * - If there is a delay we wipe away the current search string and * restart with just that char. * - If the user keeps pressing the same character, whether slowly or * fast, so that the search string is entirely composed of this * character ('aaaaa' for instance), then we search for first item * that starting with that character. * - If the user types the above character in quick succession, then * we must also search for the corresponding string ('aaaaa'), and * go to that string if there is a match. * * RETURNS * * Zero. * * BUGS * * - The current implementation has a list of characters it will * accept and it ignores averything else. In particular it will * ignore accentuated characters which seems to match what * Windows does. But I'm not sure it makes sense to follow * Windows there. * - We don't sound a beep when the search fails. * * SEE ALSO * * TREEVIEW_ProcessLetterKeys */ static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, /* handle to the window */ WPARAM charCode, /* the character code, the actual character */ LPARAM keyData /* key data */ ) { LISTVIEW_INFO *infoPtr; INT nItem; INT nSize; INT endidx,idx; LVITEMW item; WCHAR buffer[MAX_PATH]; DWORD timestamp,elapsed; /* simple parameter checking */ if (!hwnd || !charCode || !keyData) return 0; infoPtr=(LISTVIEW_INFO*)GetWindowLongW(hwnd, 0); if (!infoPtr) return 0; /* only allow the valid WM_CHARs through */ if (!isalnum(charCode) && charCode != '.' && charCode != '`' && charCode != '!' && charCode != '@' && charCode != '#' && charCode != '$' && charCode != '%' && charCode != '^' && charCode != '&' && charCode != '*' && charCode != '(' && charCode != ')' && charCode != '-' && charCode != '_' && charCode != '+' && charCode != '=' && charCode != '\\'&& charCode != ']' && charCode != '}' && charCode != '[' && charCode != '{' && charCode != '/' && charCode != '?' && charCode != '>' && charCode != '<' && charCode != ',' && charCode != '~') return 0; nSize=GETITEMCOUNT(infoPtr); /* if there's one item or less, there is no where to go */ if (nSize <= 1) return 0; /* compute how much time elapsed since last keypress */ timestamp=GetTickCount(); if (timestamp > infoPtr->lastKeyPressTimestamp) { elapsed=timestamp-infoPtr->lastKeyPressTimestamp; } else { elapsed=infoPtr->lastKeyPressTimestamp-timestamp; } /* update the search parameters */ infoPtr->lastKeyPressTimestamp=timestamp; if (elapsed < KEY_DELAY) { if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) { infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode; } if (infoPtr->charCode != charCode) { infoPtr->charCode=charCode=0; } } else { infoPtr->charCode=charCode; infoPtr->szSearchParam[0]=charCode; infoPtr->nSearchParamLength=1; /* Redundant with the 1 char string */ charCode=0; } /* and search from the current position */ nItem=-1; if (infoPtr->nFocusedItem >= 0) { endidx=infoPtr->nFocusedItem; idx=endidx; /* if looking for single character match, * then we must always move forward */ if (infoPtr->nSearchParamLength == 1) idx++; } else { endidx=nSize; idx=0; } do { if (idx == nSize) { if (endidx == nSize) break; idx=0; } /* get item */ ZeroMemory(&item, sizeof(item)); item.mask = LVIF_TEXT; item.iItem = idx; item.iSubItem = 0; item.pszText = buffer; item.cchTextMax = COUNTOF(buffer); ListView_GetItemW( hwnd, &item ); /* check for a match */ if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) { nItem=idx; break; } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) && (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) { /* This would work but we must keep looking for a longer match */ nItem=idx; } idx++; } while (idx != endidx); if (nItem != -1) { if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) { /* refresh client area */ InvalidateRect(hwnd, NULL, TRUE); UpdateWindow(hwnd); } } return 0; } /************************************************************************* * LISTVIEW_UpdateHeaderSize [Internal] * * Function to resize the header control * * PARAMS * hwnd [I] handle to a window * nNewScrollPos [I] Scroll Pos to Set * * RETURNS * nothing * * NOTES */ static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); RECT winRect; POINT point[2]; GetWindowRect(infoPtr->hwndHeader, &winRect); point[0].x = winRect.left; point[0].y = winRect.top; point[1].x = winRect.right; point[1].y = winRect.bottom; MapWindowPoints(HWND_DESKTOP, hwnd, point, 2); point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE); point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE); SetWindowPos(infoPtr->hwndHeader,0, point[0].x,point[0].y,point[1].x,point[1].y, SWP_NOZORDER | SWP_NOACTIVATE); } /*** * DESCRIPTION: * Update the scrollbars. This functions should be called whenever * the content, size or view changes. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * None */ static VOID LISTVIEW_UpdateScroll(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); UINT uView = lStyle & LVS_TYPEMASK; INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left; SCROLLINFO scrollInfo; if (lStyle & LVS_NOSCROLL) return; ZeroMemory(&scrollInfo, sizeof(SCROLLINFO)); scrollInfo.cbSize = sizeof(SCROLLINFO); if (uView == LVS_LIST) { /* update horizontal scrollbar */ INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd); INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd); INT nNumOfItems = GETITEMCOUNT(infoPtr); scrollInfo.nMax = nNumOfItems / nCountPerColumn; if((nNumOfItems % nCountPerColumn) == 0) { scrollInfo.nMax--; } scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn; scrollInfo.nPage = nCountPerRow; scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE); ShowScrollBar(hwnd, SB_VERT, FALSE); } else if (uView == LVS_REPORT) { /* update vertical scrollbar */ scrollInfo.nMin = 0; scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1; scrollInfo.nPos = ListView_GetTopIndex(hwnd); scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd); scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE); /* update horizontal scrollbar */ nListWidth = infoPtr->rcList.right - infoPtr->rcList.left; if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE || GETITEMCOUNT(infoPtr) == 0) { scrollInfo.nPos = 0; } scrollInfo.nMin = 0; scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE ; scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE; scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1; SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE); /* Update the Header Control */ scrollInfo.fMask = SIF_POS; GetScrollInfo(hwnd, SB_HORZ, &scrollInfo); LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos); } else { RECT rcView; if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE) { INT nViewWidth = rcView.right - rcView.left; INT nViewHeight = rcView.bottom - rcView.top; /* Update Horizontal Scrollbar */ scrollInfo.fMask = SIF_POS; if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE || GETITEMCOUNT(infoPtr) == 0) { scrollInfo.nPos = 0; } scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1; scrollInfo.nMin = 0; scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE; scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE); /* Update Vertical Scrollbar */ nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; scrollInfo.fMask = SIF_POS; if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE || GETITEMCOUNT(infoPtr) == 0) { scrollInfo.nPos = 0; } scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1; scrollInfo.nMin = 0; scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE; scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE); } } } /*** * DESCRIPTION: * Prints a message for unsupported window styles. * A kind of TODO list for window styles. * * PARAMETER(S): * [I] LONG : window style * * RETURN: * None */ static VOID LISTVIEW_UnsupportedStyles(LONG lStyle) { if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL) FIXME(" LVS_NOSCROLL\n"); if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSORTHEADER) FIXME(" LVS_NOSORTHEADER\n"); if ((LVS_TYPESTYLEMASK & lStyle) == LVS_OWNERDRAWFIXED) FIXME(" LVS_OWNERDRAWFIXED\n"); if (lStyle & LVS_EDITLABELS) FIXME(" LVS_EDITLABELS\n"); if (lStyle & LVS_NOLABELWRAP) FIXME(" LVS_NOLABELWRAP\n"); if (lStyle & LVS_SHAREIMAGELISTS) FIXME(" LVS_SHAREIMAGELISTS\n"); if (lStyle & LVS_SORTASCENDING) FIXME(" LVS_SORTASCENDING\n"); if (lStyle & LVS_SORTDESCENDING) FIXME(" LVS_SORTDESCENDING\n"); } /*** * DESCRIPTION: * Aligns the items with the top edge of the window. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * None */ static VOID LISTVIEW_AlignTop(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left; POINT ptItem; RECT rcView; INT i, off_x=0, off_y=0; if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) { /* Since SetItemPosition uses upper-left of icon, and for style=LVS_ICON the icon is not left adjusted, get the offset */ if (uView == LVS_ICON) { off_y = ICON_TOP_PADDING; off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2; } ptItem.x = off_x; ptItem.y = off_y; ZeroMemory(&rcView, sizeof(RECT)); if (nListWidth > infoPtr->nItemWidth) { for (i = 0; i < GETITEMCOUNT(infoPtr); i++) { if (ptItem.x + infoPtr->nItemWidth > nListWidth) { ptItem.x = off_x; ptItem.y += infoPtr->nItemHeight; } LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y); ptItem.x += infoPtr->nItemWidth; rcView.right = max(rcView.right, ptItem.x); } rcView.bottom = ptItem.y + infoPtr->nItemHeight; } else { for (i = 0; i < GETITEMCOUNT(infoPtr); i++) { LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y); ptItem.y += infoPtr->nItemHeight; } rcView.right = infoPtr->nItemWidth; rcView.bottom = ptItem.y; } LISTVIEW_SetViewRect(hwnd, &rcView); } } /*** * DESCRIPTION: * Aligns the items with the left edge of the window. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * None */ static VOID LISTVIEW_AlignLeft(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; POINT ptItem; RECT rcView; INT i, off_x=0, off_y=0; if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) { /* Since SetItemPosition uses upper-left of icon, and for style=LVS_ICON the icon is not left adjusted, get the offset */ if (uView == LVS_ICON) { off_y = ICON_TOP_PADDING; off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2; } ptItem.x = off_x; ptItem.y = off_y; ZeroMemory(&rcView, sizeof(RECT)); if (nListHeight > infoPtr->nItemHeight) { for (i = 0; i < GETITEMCOUNT(infoPtr); i++) { if (ptItem.y + infoPtr->nItemHeight > nListHeight) { ptItem.y = off_y; ptItem.x += infoPtr->nItemWidth; } LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y); ptItem.y += infoPtr->nItemHeight; rcView.bottom = max(rcView.bottom, ptItem.y); } rcView.right = ptItem.x + infoPtr->nItemWidth; } else { for (i = 0; i < GETITEMCOUNT(infoPtr); i++) { LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y); ptItem.x += infoPtr->nItemWidth; } rcView.bottom = infoPtr->nItemHeight; rcView.right = ptItem.x; } LISTVIEW_SetViewRect(hwnd, &rcView); } } /*** * DESCRIPTION: * Set the bounding rectangle of all the items. * * PARAMETER(S): * [I] HWND : window handle * [I] LPRECT : bounding rectangle * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0); BOOL bResult = FALSE; TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd, lprcView->left, lprcView->top, lprcView->right, lprcView->bottom); if (lprcView != NULL) { bResult = TRUE; infoPtr->rcView.left = lprcView->left; infoPtr->rcView.top = lprcView->top; infoPtr->rcView.right = lprcView->right; infoPtr->rcView.bottom = lprcView->bottom; } return bResult; } /*** * DESCRIPTION: * Retrieves the bounding rectangle of all the items. * * PARAMETER(S): * [I] HWND : window handle * [O] LPRECT : bounding rectangle * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0); BOOL bResult = FALSE; POINT ptOrigin; TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView); if (lprcView != NULL) { bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin); if (bResult != FALSE) { lprcView->left = infoPtr->rcView.left + ptOrigin.x; lprcView->top = infoPtr->rcView.top + ptOrigin.y; lprcView->right = infoPtr->rcView.right + ptOrigin.x; lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y; } TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n", lprcView->left, lprcView->top, lprcView->right, lprcView->bottom); } return bResult; } /*** * DESCRIPTION: * Retrieves the subitem pointer associated with the subitem index. * * PARAMETER(S): * [I] HDPA : DPA handle for a specific item * [I] INT : index of subitem * * RETURN: * SUCCESS : subitem pointer * FAILURE : NULL */ static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem) { LISTVIEW_SUBITEM *lpSubItem; INT i; for (i = 1; i < hdpaSubItems->nItemCount; i++) { lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i); if (lpSubItem != NULL) { if (lpSubItem->iSubItem == nSubItem) { return lpSubItem; } } } return NULL; } /*** * DESCRIPTION: * Calculates the width of an item. * * PARAMETER(S): * [I] HWND : window handle * [I] LONG : window style * * RETURN: * Returns item width. */ static INT LISTVIEW_GetItemWidth(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG style = GetWindowLongW(hwnd, GWL_STYLE); UINT uView = style & LVS_TYPEMASK; INT nHeaderItemCount; RECT rcHeaderItem; INT nItemWidth = 0; INT nLabelWidth; INT i; TRACE("(hwnd=%x)\n", hwnd); if (uView == LVS_ICON) { nItemWidth = infoPtr->iconSpacing.cx; } else if (uView == LVS_REPORT) { /* calculate width of header */ nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader); for (i = 0; i < nHeaderItemCount; i++) { if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0) { nItemWidth += (rcHeaderItem.right - rcHeaderItem.left); } } } else { for (i = 0; i < GETITEMCOUNT(infoPtr); i++) { nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i); nItemWidth = max(nItemWidth, nLabelWidth); } /* default label size */ if (GETITEMCOUNT(infoPtr) == 0) { nItemWidth = DEFAULT_COLUMN_WIDTH; } else { if (nItemWidth == 0) { nItemWidth = DEFAULT_LABEL_WIDTH; } else { /* add padding */ nItemWidth += WIDTH_PADDING; if (infoPtr->himlSmall != NULL) { nItemWidth += infoPtr->iconSize.cx; } if (infoPtr->himlState != NULL) { nItemWidth += infoPtr->iconSize.cx; } } } } if(nItemWidth == 0) { /* nItemWidth Cannot be Zero */ nItemWidth = 1; } return nItemWidth; } /*** * DESCRIPTION: * Calculates the width of a specific item. * * PARAMETER(S): * [I] HWND : window handle * [I] LPSTR : string * * RETURN: * Returns the width of an item width a specified string. */ static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; INT nHeaderItemCount; RECT rcHeaderItem; INT nItemWidth = 0; INT i; TRACE("(hwnd=%x)\n", hwnd); if (uView == LVS_ICON) { nItemWidth = infoPtr->iconSpacing.cx; } else if (uView == LVS_REPORT) { /* calculate width of header */ nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader); for (i = 0; i < nHeaderItemCount; i++) { if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0) { nItemWidth += (rcHeaderItem.right - rcHeaderItem.left); } } } else { /* get width of string */ nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem); /* default label size */ if (GETITEMCOUNT(infoPtr) == 0) { nItemWidth = DEFAULT_COLUMN_WIDTH; } else { if (nItemWidth == 0) { nItemWidth = DEFAULT_LABEL_WIDTH; } else { /* add padding */ nItemWidth += WIDTH_PADDING; if (infoPtr->himlSmall != NULL) { nItemWidth += infoPtr->iconSize.cx; } if (infoPtr->himlState != NULL) { nItemWidth += infoPtr->iconSize.cx; } } } } return nItemWidth; } /*** * DESCRIPTION: * Retrieves and saves important text metrics info for the current * Listview font. * * PARAMETER(S): * [I] HWND : window handle * */ static VOID LISTVIEW_SaveTextMetrics(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); TEXTMETRICW tm; HDC hdc = GetDC(hwnd); HFONT hOldFont = SelectObject(hdc, infoPtr->hFont); INT oldHeight, oldACW; GetTextMetricsW(hdc, &tm); oldHeight = infoPtr->ntmHeight; oldACW = infoPtr->ntmAveCharWidth; infoPtr->ntmHeight = tm.tmHeight; infoPtr->ntmAveCharWidth = tm.tmAveCharWidth; SelectObject(hdc, hOldFont); ReleaseDC(hwnd, hdc); TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n", oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth); } /*** * DESCRIPTION: * Calculates the height of an item. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Returns item height. */ static INT LISTVIEW_GetItemHeight(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; INT nItemHeight = 0; if (uView == LVS_ICON) { nItemHeight = infoPtr->iconSpacing.cy; } else { if(infoPtr->himlState || infoPtr->himlSmall) nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING; else nItemHeight = infoPtr->ntmHeight; } return nItemHeight; } static void LISTVIEW_PrintSelectionRanges(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LISTVIEW_SELECTION *selection; INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount; INT i; TRACE("Selections are:\n"); for (i = 0; i < topSelection; i++) { selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i); TRACE(" %lu - %lu\n",selection->lower,selection->upper); } } /*** * DESCRIPTION: * A compare function for selection ranges * *PARAMETER(S) * [I] LPVOID : Item 1; * [I] LPVOID : Item 2; * [I] LPARAM : flags * *RETURNS: * >0 : if Item 1 > Item 2 * <0 : if Item 2 > Item 1 * 0 : if Item 1 == Item 2 */ static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2, LPARAM flags) { int l1 = ((LISTVIEW_SELECTION*)(range1))->lower; int l2 = ((LISTVIEW_SELECTION*)(range2))->lower; int u1 = ((LISTVIEW_SELECTION*)(range1))->upper; int u2 = ((LISTVIEW_SELECTION*)(range2))->upper; int rc=0; if (u1 < l2) rc= -1; if (u2 < l1) rc= 1; return rc; } /** * DESCRIPTION: * Adds a selection range. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : lower item index * [I] INT : upper item index * * RETURN: * None */ static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LISTVIEW_SELECTION *selection; INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount; BOOL lowerzero=FALSE; selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION)); selection->lower = lItem; selection->upper = uItem; TRACE("Add range %i - %i\n", lItem, uItem); if (topSelection) { LISTVIEW_SELECTION *checkselection,*checkselection2; INT index,mergeindex; /* find overlapping selections */ /* we want to catch adjacent ranges so expand our range by 1 */ selection->upper++; if (selection->lower == 0) lowerzero = TRUE; else selection->lower--; index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0, LISTVIEW_CompareSelectionRanges, 0,0); selection->upper --; if (lowerzero) lowerzero=FALSE; else selection->lower ++; if (index >=0) { checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index); TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower, checkselection->upper); checkselection->lower = min(selection->lower,checkselection->lower); checkselection->upper = max(selection->upper,checkselection->upper); TRACE("New range (%lu - %lu)\n", checkselection->lower, checkselection->upper); COMCTL32_Free(selection); /* merge now common selection ranges in the lower group*/ do { checkselection->upper ++; if (checkselection->lower == 0) lowerzero = TRUE; else checkselection->lower --; TRACE("search lower range (%lu - %lu)\n", checkselection->lower, checkselection->upper); /* not sorted yet */ mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0, LISTVIEW_CompareSelectionRanges, 0, 0); checkselection->upper --; if (lowerzero) lowerzero = FALSE; else checkselection->lower ++; if (mergeindex >=0 && mergeindex != index) { TRACE("Merge with index %i\n",mergeindex); checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges, mergeindex); checkselection->lower = min(checkselection->lower, checkselection2->lower); checkselection->upper = max(checkselection->upper, checkselection2->upper); COMCTL32_Free(checkselection2); DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex); index --; } } while (mergeindex > -1 && mergeindex upper ++; if (checkselection->lower == 0) lowerzero = TRUE; else checkselection->lower --; TRACE("search upper range %i (%lu - %lu)\n",index, checkselection->lower, checkselection->upper); /* not sorted yet */ mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, index+1, LISTVIEW_CompareSelectionRanges, 0, 0); checkselection->upper --; if (lowerzero) lowerzero = FALSE; else checkselection->lower ++; if (mergeindex >=0 && mergeindex !=index) { TRACE("Merge with index %i\n",mergeindex); checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges, mergeindex); checkselection->lower = min(checkselection->lower, checkselection2->lower); checkselection->upper = max(checkselection->upper, checkselection2->upper); COMCTL32_Free(checkselection2); DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex); } } while (mergeindex > -1); } else { index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0, LISTVIEW_CompareSelectionRanges, 0, DPAS_INSERTAFTER); TRACE("Insert before index %i\n",index); if (index == -1) index = 0; DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection); } } else { DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection); } /* * Incase of error */ DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0); LISTVIEW_PrintSelectionRanges(hwnd); } /** * DESCRIPTION: * check if a specified index is selected. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * * RETURN: * None */ static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LISTVIEW_SELECTION selection; INT index; selection.upper = nItem; selection.lower = nItem; index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0, LISTVIEW_CompareSelectionRanges, 0,DPAS_SORTED); if (index != -1) return TRUE; else return FALSE; } /*** * DESCRIPTION: * Removes all selection ranges * * Parameters(s): * HWND: window handle * * RETURNS: * SUCCESS : TRUE * FAILURE : TRUE */ static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LISTVIEW_SELECTION *selection; INT i; LVITEMW item; TRACE("(0x%x)\n",hwnd); ZeroMemory(&item,sizeof(item)); item.stateMask = LVIS_SELECTED; do { selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0); if (selection) { TRACE("Removing %lu to %lu\n",selection->lower, selection->upper); for (i = selection->lower; i<=selection->upper; i++) LISTVIEW_SetItemState(hwnd,i,&item); LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper); } } while (infoPtr->hdpaSelectionRanges->nItemCount>0); TRACE("done\n"); return TRUE; } /** * DESCRIPTION: * Removes a range selections. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : lower item index * [I] INT : upper item index * * RETURN: * None */ static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LISTVIEW_SELECTION removeselection,*checkselection; INT index; removeselection.lower = lItem; removeselection.upper = uItem; TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper); LISTVIEW_PrintSelectionRanges(hwnd); index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0, LISTVIEW_CompareSelectionRanges, 0,0); if (index == -1) return; checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges, index); TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower, checkselection->upper); /* case 1: Same */ if ((checkselection->upper == removeselection.upper) && (checkselection->lower == removeselection.lower)) { DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index); TRACE("Case 1\n"); } /* case 2: engulf */ else if (((checkselection->upper < removeselection.upper) && (checkselection->lower > removeselection.lower))|| ((checkselection->upper <= removeselection.upper) && (checkselection->lower > removeselection.lower)) || ((checkselection->upper < removeselection.upper) && (checkselection->lower >= removeselection.lower))) { DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index); /* do it again because others may also get caught */ TRACE("Case 2\n"); LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem); } /* case 3: overlap upper */ else if ((checkselection->upper < removeselection.upper) && (checkselection->lower < removeselection.lower)) { checkselection->upper = removeselection.lower - 1; TRACE("Case 3\n"); LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem); } /* case 4: overlap lower */ else if ((checkselection->upper > removeselection.upper) && (checkselection->lower > removeselection.lower)) { checkselection->lower = removeselection.upper + 1; TRACE("Case 4\n"); LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem); } /* case 5: fully internal */ else if (checkselection->upper == removeselection.upper) checkselection->upper = removeselection.lower - 1; else if (checkselection->lower == removeselection.lower) checkselection->lower = removeselection.upper + 1; else { /* bisect the range */ LISTVIEW_SELECTION *newselection; newselection = (LISTVIEW_SELECTION *) COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION)); newselection -> lower = checkselection->lower; newselection -> upper = removeselection.lower - 1; checkselection -> lower = removeselection.upper + 1; DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection); TRACE("Case 5\n"); DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0); } LISTVIEW_PrintSelectionRanges(hwnd); } /** * DESCRIPTION: * Updates the various indices after an item has been inserted or deleted. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * [I] INT : Direction of shift, +1 or -1. * * RETURN: * None */ static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LISTVIEW_SELECTION selection,*checkselection; INT index; TRACE("Shifting %iu, %i steps\n",nItem,direction); selection.upper = nItem; selection.lower = nItem; index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0, LISTVIEW_CompareSelectionRanges, 0,DPAS_SORTED|DPAS_INSERTAFTER); while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1)) { checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index); if ((checkselection->lower >= nItem)&& (checkselection->lower + direction >= 0)) checkselection->lower += direction; if ((checkselection->upper >= nItem)&& (checkselection->upper + direction >=0)) checkselection->upper += direction; index ++; } /* Note that the following will fail if direction != +1 and -1 */ if (infoPtr->nSelectionMark > nItem) infoPtr->nSelectionMark += direction; else if (infoPtr->nSelectionMark == nItem) { if (direction > 0) infoPtr->nSelectionMark += direction; else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr)) infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1; } if (infoPtr->nFocusedItem > nItem) infoPtr->nFocusedItem += direction; else if (infoPtr->nFocusedItem == nItem) { if (direction > 0) infoPtr->nFocusedItem += direction; else { if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr)) infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1; if (infoPtr->nFocusedItem >= 0) LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem); } } /* But we are not supposed to modify nHotItem! */ } /** * DESCRIPTION: * Adds a block of selections. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * * RETURN: * None */ static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); INT nFirst = min(infoPtr->nSelectionMark, nItem); INT nLast = max(infoPtr->nSelectionMark, nItem); INT i; LVITEMW item; if (nFirst == -1) nFirst = nItem; ZeroMemory(&item,sizeof(item)); item.stateMask = LVIS_SELECTED; item.state = LVIS_SELECTED; for (i = nFirst; i <= nLast; i++) LISTVIEW_SetItemState(hwnd,i,&item); LISTVIEW_SetItemFocus(hwnd, nItem); infoPtr->nSelectionMark = nItem; } /*** * DESCRIPTION: * Adds a single selection. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * * RETURN: * None */ static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LVITEMW item; ZeroMemory(&item,sizeof(item)); item.state = LVIS_SELECTED; item.stateMask = LVIS_SELECTED; LISTVIEW_SetItemState(hwnd,nItem,&item); LISTVIEW_SetItemFocus(hwnd, nItem); infoPtr->nSelectionMark = nItem; } /*** * DESCRIPTION: * Selects or unselects an item. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * * RETURN: * SELECT: TRUE * UNSELECT : FALSE */ static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); BOOL bResult; LVITEMW item; ZeroMemory(&item,sizeof(item)); item.stateMask = LVIS_SELECTED; if (LISTVIEW_IsSelected(hwnd,nItem)) { LISTVIEW_SetItemState(hwnd,nItem,&item); bResult = FALSE; } else { item.state = LVIS_SELECTED; LISTVIEW_SetItemState(hwnd,nItem,&item); bResult = TRUE; } LISTVIEW_SetItemFocus(hwnd, nItem); infoPtr->nSelectionMark = nItem; return bResult; } /*** * DESCRIPTION: * Selects items based on view coordinates. * * PARAMETER(S): * [I] HWND : window handle * [I] RECT : selection rectangle * * RETURN: * None */ static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); POINT ptItem; INT i; LVITEMW item; ZeroMemory(&item,sizeof(item)); item.stateMask = LVIS_SELECTED; for (i = 0; i < GETITEMCOUNT(infoPtr); i++) { LISTVIEW_GetItemPosition(hwnd, i, &ptItem); if (PtInRect(&rcSelRect, ptItem) != FALSE) item.state = LVIS_SELECTED; else item.state = 0; LISTVIEW_SetItemState(hwnd,i,&item); } } /*** * DESCRIPTION: * Sets a single group selection. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * * RETURN: * None */ static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; LVITEMW item; ZeroMemory(&item,sizeof(item)); item.stateMask = LVIS_SELECTED; if ((uView == LVS_LIST) || (uView == LVS_REPORT)) { INT i; INT nFirst, nLast; if (infoPtr->nSelectionMark == -1) { infoPtr->nSelectionMark = nFirst = nLast = nItem; } else { nFirst = min(infoPtr->nSelectionMark, nItem); nLast = max(infoPtr->nSelectionMark, nItem); } for (i = 0; i <= GETITEMCOUNT(infoPtr); i++) { if ((i < nFirst) || (i > nLast)) item.state = 0; else item.state = LVIS_SELECTED; LISTVIEW_SetItemState(hwnd,i,&item); } } else { RECT rcItem; RECT rcSelMark; RECT rcSel; LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcItem); LISTVIEW_GetItemBoundBox(hwnd, infoPtr->nSelectionMark, &rcSelMark); rcSel.left = min(rcSelMark.left, rcItem.left); rcSel.top = min(rcSelMark.top, rcItem.top); rcSel.right = max(rcSelMark.right, rcItem.right); rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom); LISTVIEW_SetSelectionRect(hwnd, rcSel); TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n", nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom, infoPtr->nSelectionMark, rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom, rcSel.left, rcSel.top, rcSel.right, rcSel.bottom); } LISTVIEW_SetItemFocus(hwnd, nItem); } /*** * DESCRIPTION: * Manages the item focus. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * * RETURN: * TRUE : focused item changed * FALSE : focused item has NOT changed */ static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); BOOL bResult = FALSE; LVITEMW lvItem; if (infoPtr->nFocusedItem != nItem) { if (infoPtr->nFocusedItem >= 0) { INT oldFocus = infoPtr->nFocusedItem; bResult = TRUE; infoPtr->nFocusedItem = -1; ZeroMemory(&lvItem, sizeof(lvItem)); lvItem.stateMask = LVIS_FOCUSED; ListView_SetItemState(hwnd, oldFocus, &lvItem); } lvItem.state = LVIS_FOCUSED; lvItem.stateMask = LVIS_FOCUSED; ListView_SetItemState(hwnd, nItem, &lvItem); infoPtr->nFocusedItem = nItem; ListView_EnsureVisible(hwnd, nItem, FALSE); } return bResult; } /*** * DESCRIPTION: * Sets a single selection. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * * RETURN: * None */ static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LVITEMW lvItem; ZeroMemory(&lvItem, sizeof(lvItem)); lvItem.stateMask = LVIS_FOCUSED; ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem); LISTVIEW_RemoveAllSelections(hwnd); lvItem.state = LVIS_FOCUSED|LVIS_SELECTED; lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED; ListView_SetItemState(hwnd, nItem, &lvItem); infoPtr->nFocusedItem = nItem; infoPtr->nSelectionMark = nItem; } /*** * DESCRIPTION: * Set selection(s) with keyboard. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * * RETURN: * SUCCESS : TRUE (needs to be repainted) * FAILURE : FALSE (nothing has changed) */ static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); WORD wShift = HIWORD(GetKeyState(VK_SHIFT)); WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL)); BOOL bResult = FALSE; if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr))) { if (lStyle & LVS_SINGLESEL) { bResult = TRUE; LISTVIEW_SetSelection(hwnd, nItem); ListView_EnsureVisible(hwnd, nItem, FALSE); } else { if (wShift) { bResult = TRUE; LISTVIEW_SetGroupSelection(hwnd, nItem); } else if (wCtrl) { bResult = LISTVIEW_SetItemFocus(hwnd, nItem); } else { bResult = TRUE; LISTVIEW_SetSelection(hwnd, nItem); ListView_EnsureVisible(hwnd, nItem, FALSE); } } } return bResult; } /*** * DESCRIPTION: * Called when the mouse is being actively tracked and has hovered for a specified * amount of time * * PARAMETER(S): * [I] HWND : window handle * [I] wParam : key indicator * [I] lParam : mouse position * * RETURN: * 0 if the message was processed, non-zero if there was an error * * INFO: * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains * over the item for a certain period of time. * */ static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); POINT pt; pt.x = (INT)LOWORD(lParam); pt.y = (INT)HIWORD(lParam); if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) { /* select the item under the cursor */ LISTVIEW_MouseSelection(hwnd, pt); } return 0; } /*** * DESCRIPTION: * Called whenever WM_MOUSEMOVE is received. * * PARAMETER(S): * [I] HWND : window handle * [I] wParam : key indicators * [I] lParam : cursor position * * RETURN: * 0 if the message is processed, non-zero if there was an error */ static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); TRACKMOUSEEVENT trackinfo; /* see if we are supposed to be tracking mouse hovering */ if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) { /* fill in the trackinfo struct */ trackinfo.cbSize = sizeof(TRACKMOUSEEVENT); trackinfo.dwFlags = TME_QUERY; trackinfo.hwndTrack = hwnd; trackinfo.dwHoverTime = infoPtr->dwHoverTime; /* see if we are already tracking this hwnd */ _TrackMouseEvent(&trackinfo); if(!(trackinfo.dwFlags & TME_HOVER)) { trackinfo.dwFlags = TME_HOVER; /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */ _TrackMouseEvent(&trackinfo); } } return 0; } /*** * DESCRIPTION: * Selects an item based on coordinates. * * PARAMETER(S): * [I] HWND : window handle * [I] POINT : mouse click ccordinates * * RETURN: * SUCCESS : item index * FAILURE : -1 */ static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); RECT rcItem; INT i,topindex,bottomindex; LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); UINT uView = lStyle & LVS_TYPEMASK; topindex = ListView_GetTopIndex(hwnd); if (uView == LVS_REPORT) { bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1; bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr)); } else { bottomindex = GETITEMCOUNT(infoPtr); } for (i = topindex; i < bottomindex; i++) { rcItem.left = LVIR_SELECTBOUNDS; if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE) { if (PtInRect(&rcItem, pt) != FALSE) { return i; } } } return -1; } /*** * DESCRIPTION: * Removes a column. * * PARAMETER(S): * [IO] HDPA : dynamic pointer array handle * [I] INT : column index (subitem index) * * RETURN: * SUCCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem) { BOOL bResult = TRUE; HDPA hdpaSubItems; INT i; for (i = 0; i < hdpaItems->nItemCount; i++) { hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i); if (hdpaSubItems != NULL) { if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE) { bResult = FALSE; } } } return bResult; } /*** * DESCRIPTION: * Removes a subitem at a given position. * * PARAMETER(S): * [IO] HDPA : dynamic pointer array handle * [I] INT : subitem index * * RETURN: * SUCCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem) { LISTVIEW_SUBITEM *lpSubItem; INT i; for (i = 1; i < hdpaSubItems->nItemCount; i++) { lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i); if (lpSubItem != NULL) { if (lpSubItem->iSubItem == nSubItem) { /* free string */ if (is_textW(lpSubItem->pszText)) COMCTL32_Free(lpSubItem->pszText); /* free item */ COMCTL32_Free(lpSubItem); /* free dpa memory */ if (DPA_DeletePtr(hdpaSubItems, i) == NULL) return FALSE; } else if (lpSubItem->iSubItem > nSubItem) return TRUE; } } return TRUE; } /*** * DESCRIPTION: * Compares the item information. * * PARAMETER(S): * [I] LISTVIEW_ITEM *: destination item * [I] LPLVITEM : source item * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI * * RETURN: * SUCCCESS : TRUE (EQUAL) * FAILURE : FALSE (NOT EQUAL) */ static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW) { UINT uChanged = 0; if ((lpItem != NULL) && (lpLVItem != NULL)) { if (lpLVItem->mask & LVIF_STATE) { if ((lpItem->state & lpLVItem->stateMask) != (lpLVItem->state & lpLVItem->stateMask)) uChanged |= LVIF_STATE; } if (lpLVItem->mask & LVIF_IMAGE) { if (lpItem->iImage != lpLVItem->iImage) uChanged |= LVIF_IMAGE; } if (lpLVItem->mask & LVIF_PARAM) { if (lpItem->lParam != lpLVItem->lParam) uChanged |= LVIF_PARAM; } if (lpLVItem->mask & LVIF_INDENT) { if (lpItem->iIndent != lpLVItem->iIndent) uChanged |= LVIF_INDENT; } if (lpLVItem->mask & LVIF_TEXT) { if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) { if (lpItem->pszText != LPSTR_TEXTCALLBACKW) uChanged |= LVIF_TEXT; } else { if (lpItem->pszText == LPSTR_TEXTCALLBACKW) { uChanged |= LVIF_TEXT; } else { if (lpLVItem->pszText) { if (lpItem->pszText) { LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW); if (pszText && strcmpW(pszText, lpItem->pszText)) uChanged |= LVIF_TEXT; textfreeT(pszText, isW); } else { uChanged |= LVIF_TEXT; } } else { if (lpItem->pszText) uChanged |= LVIF_TEXT; } } } } } return uChanged; } /*** * DESCRIPTION: * Initializes item attributes. * * PARAMETER(S): * [I] HWND : window handle * [O] LISTVIEW_ITEM *: destination item * [I] LPLVITEM : source item * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI * * RETURN: * SUCCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW) { LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); BOOL bResult = FALSE; if ((lpItem != NULL) && (lpLVItem != NULL)) { bResult = TRUE; if (lpLVItem->mask & LVIF_STATE) { lpItem->state &= ~lpLVItem->stateMask; lpItem->state |= (lpLVItem->state & lpLVItem->stateMask); } if (lpLVItem->mask & LVIF_IMAGE) lpItem->iImage = lpLVItem->iImage; if (lpLVItem->mask & LVIF_PARAM) lpItem->lParam = lpLVItem->lParam; if (lpLVItem->mask & LVIF_INDENT) lpItem->iIndent = lpLVItem->iIndent; if (lpLVItem->mask & LVIF_TEXT) { if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) { if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING)) return FALSE; if (is_textW(lpItem->pszText)) COMCTL32_Free(lpItem->pszText); lpItem->pszText = LPSTR_TEXTCALLBACKW; } else bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW); } } return bResult; } /*** * DESCRIPTION: * Initializes subitem attributes. * * NOTE: The documentation specifies that the operation fails if the user * tries to set the indent of a subitem. * * PARAMETER(S): * [I] HWND : window handle * [O] LISTVIEW_SUBITEM *: destination subitem * [I] LPLVITEM : source subitem * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI * * RETURN: * SUCCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem, LPLVITEMW lpLVItem, BOOL isW) { LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); BOOL bResult = FALSE; TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n", hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW); if ((lpSubItem != NULL) && (lpLVItem != NULL)) { if (!(lpLVItem->mask & LVIF_INDENT)) { bResult = TRUE; lpSubItem->iSubItem = lpLVItem->iSubItem; if (lpLVItem->mask & LVIF_IMAGE) lpSubItem->iImage = lpLVItem->iImage; if (lpLVItem->mask & LVIF_TEXT) { if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) { if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING)) return FALSE; if (is_textW(lpSubItem->pszText)) COMCTL32_Free(lpSubItem->pszText); lpSubItem->pszText = LPSTR_TEXTCALLBACKW; } else bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW); } } } return bResult; } /*** * DESCRIPTION: * Adds a subitem at a given position (column index). * * PARAMETER(S): * [I] HWND : window handle * [I] LPLVITEM : new subitem atttributes * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LISTVIEW_SUBITEM *lpSubItem = NULL; BOOL bResult = FALSE; HDPA hdpaSubItems; INT nPosition, nItem; LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW); if (lStyle & LVS_OWNERDATA) return FALSE; if (lpLVItem != NULL) { hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); if (hdpaSubItems != NULL) { lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM)); if (lpSubItem != NULL) { ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM)); if (LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW)) { nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems, lpSubItem->iSubItem); nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem); if (nItem != -1) bResult = TRUE; } } } } /* cleanup if unsuccessful */ if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem); return bResult; } /*** * DESCRIPTION: * Finds the dpa insert position (array index). * * PARAMETER(S): * [I] HWND : window handle * [I] INT : subitem index * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem) { LISTVIEW_SUBITEM *lpSubItem; INT i; for (i = 1; i < hdpaSubItems->nItemCount; i++) { lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i); if (lpSubItem && lpSubItem->iSubItem > nSubItem) return i; } return hdpaSubItems->nItemCount; } /*** * DESCRIPTION: * Retrieves a listview subitem at a given position (column index). * * PARAMETER(S): * [I] HWND : window handle * [I] INT : subitem index * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem) { LISTVIEW_SUBITEM *lpSubItem; INT i; for (i = 1; i < hdpaSubItems->nItemCount; i++) { lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i); if (lpSubItem != NULL) { if (lpSubItem->iSubItem == nSubItem) return lpSubItem; else if (lpSubItem->iSubItem > nSubItem) return NULL; } } return NULL; } /*** * DESCRIPTION: * Sets item attributes. * * PARAMETER(S): * [I] HWND : window handle * [I] LPLVITEM : new item atttributes * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); BOOL bResult = FALSE; HDPA hdpaSubItems; LISTVIEW_ITEM *lpItem; NMLISTVIEW nmlv; LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); UINT uChanged; UINT uView = lStyle & LVS_TYPEMASK; INT item_width; RECT rcItem; TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW); if (lStyle & LVS_OWNERDATA) { if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE)) { LVITEMW itm; ZeroMemory(&itm, sizeof(itm)); itm.mask = LVIF_STATE | LVIF_PARAM; itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED; itm.iItem = lpLVItem->iItem; itm.iSubItem = 0; ListView_GetItemW(hwnd, &itm); ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); nmlv.uNewState = lpLVItem->state; nmlv.uOldState = itm.state; nmlv.uChanged = LVIF_STATE; nmlv.lParam = itm.lParam; nmlv.iItem = lpLVItem->iItem; if ((itm.state & lpLVItem->stateMask) != (lpLVItem->state & lpLVItem->stateMask)) { /* send LVN_ITEMCHANGING notification */ if (!listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv)) { if (lpLVItem->stateMask & LVIS_FOCUSED) { if (lpLVItem->state & LVIS_FOCUSED) infoPtr->nFocusedItem = lpLVItem->iItem; else if (infoPtr->nFocusedItem == lpLVItem->iItem) infoPtr->nFocusedItem = -1; } if (lpLVItem->stateMask & LVIS_SELECTED) { if (lpLVItem->state & LVIS_SELECTED) { if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd); LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem); } else LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem, lpLVItem->iItem); } listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv); rcItem.left = LVIR_BOUNDS; LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem); InvalidateRect(hwnd, &rcItem, TRUE); } } return TRUE; } return FALSE; } if (lpLVItem != NULL) { if (lpLVItem->iSubItem == 0) { hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1) { lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem); if (lpItem != NULL) { ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); nmlv.lParam = lpItem->lParam; uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW); if (uChanged != 0) { if (uChanged & LVIF_STATE) { nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask; nmlv.uOldState = lpItem->state & lpLVItem->stateMask; if (nmlv.uNewState & LVIS_SELECTED) { /* * This is redundant if called through SetSelection * * however is required if the used directly calls SetItem * to set the selection. */ if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd); LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem, lpLVItem->iItem); } else if (lpLVItem->stateMask & LVIS_SELECTED) { LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem, lpLVItem->iItem); } if (nmlv.uNewState & LVIS_FOCUSED) { /* * This is a fun hoop to jump to try to catch if * the user is calling us directly to call focus or if * this function is being called as a result of a * SetItemFocus call. */ if (infoPtr->nFocusedItem >= 0) LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem); } } nmlv.uChanged = uChanged; nmlv.iItem = lpLVItem->iItem; nmlv.lParam = lpItem->lParam; /* send LVN_ITEMCHANGING notification */ listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv); /* copy information */ bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW); /* if LVS_LIST or LVS_SMALLICON, update the width of the items based on the width of the items text */ if((uView == LVS_LIST) || (uView == LVS_SMALLICON)) { item_width = LISTVIEW_GetStringWidthT(hwnd, lpItem->pszText, TRUE); if(item_width > infoPtr->nItemWidth) infoPtr->nItemWidth = item_width; } /* send LVN_ITEMCHANGED notification */ listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv); } else { bResult = TRUE; } if (uChanged) { rcItem.left = LVIR_BOUNDS; LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem); InvalidateRect(hwnd, &rcItem, TRUE); } } } } } return bResult; } /*** * DESCRIPTION: * Sets subitem attributes. * * PARAMETER(S): * [I] HWND : window handle * [I] LPLVITEM : new subitem atttributes * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); BOOL bResult = FALSE; HDPA hdpaSubItems; LISTVIEW_SUBITEM *lpSubItem; RECT rcItem; TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW); if (lStyle & LVS_OWNERDATA) return FALSE; if (lpLVItem != NULL) { if (lpLVItem->iSubItem > 0) { hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); if (hdpaSubItems != NULL) { /* set subitem only if column is present */ if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem) { lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem); if (lpSubItem != NULL) bResult = LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW); else bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW); rcItem.left = LVIR_BOUNDS; LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem); InvalidateRect(hwnd, &rcItem, FALSE); } } } } return bResult; } /*** * DESCRIPTION: * Sets item attributes. * * PARAMETER(S): * [I] HWND : window handle * [I] LPLVITEM : new item atttributes * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem>=GETITEMCOUNT(infoPtr)) return FALSE; if (lpLVItem->iSubItem == 0) return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW); else return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW); } /*** * DESCRIPTION: * Retrieves the index of the item at coordinate (0, 0) of the client area. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * item index */ static INT LISTVIEW_GetTopIndex(HWND hwnd) { LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); UINT uView = lStyle & LVS_TYPEMASK; INT nItem = 0; SCROLLINFO scrollInfo; ZeroMemory(&scrollInfo, sizeof(SCROLLINFO)); scrollInfo.cbSize = sizeof(SCROLLINFO); scrollInfo.fMask = SIF_POS; if (uView == LVS_LIST) { if ((lStyle & WS_HSCROLL) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo)) nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd); } else if (uView == LVS_REPORT) { if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo)) nItem = scrollInfo.nPos; } return nItem; } /*** * DESCRIPTION: * Draws a subitem. * * PARAMETER(S): * [I] HWND : window handle * [I] HDC : device context handle * [I] INT : item index * [I] INT : subitem index * [I] RECT * : clipping rectangle * * RETURN: * None */ static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem, RECT rcItem, BOOL Selected) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); WCHAR szDispText[DISP_TEXT_SIZE]; LVITEMW lvItem; UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE; RECT rcTemp; TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc, nItem, nSubItem); /* get information needed for drawing the item */ ZeroMemory(&lvItem, sizeof(lvItem)); lvItem.mask = LVIF_TEXT; lvItem.iItem = nItem; lvItem.iSubItem = nSubItem; lvItem.cchTextMax = DISP_TEXT_SIZE; lvItem.pszText = szDispText; *lvItem.pszText = '\0'; LISTVIEW_GetItemW(hwnd, &lvItem, TRUE); TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE)); /* redraw the background of the item */ rcTemp = rcItem; if(infoPtr->nColumnCount == (nSubItem + 1)) rcTemp.right = infoPtr->rcList.right; else rcTemp.right += WIDTH_PADDING; LISTVIEW_FillBackground(hwnd, hdc, &rcTemp); /* set item colors */ if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected) { if (infoPtr->bFocus) { SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); } else { SetBkColor(hdc, GetSysColor(COLOR_3DFACE)); SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT)); } } else { if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) ) { SetBkMode(hdc, TRANSPARENT); textoutOptions &= ~ETO_OPAQUE; } else { SetBkMode(hdc, OPAQUE); SetBkColor(hdc, infoPtr->clrTextBk); } SetTextColor(hdc, infoPtr->clrText); } ExtTextOutW(hdc, rcItem.left, rcItem.top, textoutOptions, &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL); if (Selected) { /* fill in the gap */ RECT rec; if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1) { CopyRect(&rec,&rcItem); rec.left = rec.right; rec.right = rec.left+REPORT_MARGINX; ExtTextOutW(hdc, rec.left , rec.top, textoutOptions, &rec, NULL, 0, NULL); } CopyRect(&rec,&rcItem); rec.right = rec.left; rec.left = rec.left - REPORT_MARGINX; ExtTextOutW(hdc, rec.left , rec.top, textoutOptions, &rec, NULL, 0, NULL); } } /*** * DESCRIPTION: * Draws an item. * * PARAMETER(S): * [I] HWND : window handle * [I] HDC : device context handle * [I] INT : item index * [I] RECT * : clipping rectangle * * RETURN: * None */ static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); WCHAR szDispText[DISP_TEXT_SIZE]; INT nLabelWidth; LVITEMW lvItem; INT nMixMode; DWORD dwBkColor; DWORD dwTextColor,dwTextX; BOOL bImage = FALSE; INT iBkMode = -1; UINT textoutOptions = ETO_OPAQUE | ETO_CLIPPED; RECT rcTemp; TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem); /* get information needed for drawing the item */ ZeroMemory(&lvItem, sizeof(lvItem)); lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT; lvItem.stateMask = LVIS_SELECTED | LVIS_STATEIMAGEMASK; lvItem.iItem = nItem; lvItem.iSubItem = 0; lvItem.cchTextMax = DISP_TEXT_SIZE; lvItem.pszText = szDispText; *lvItem.pszText = '\0'; LISTVIEW_GetItemW(hwnd, &lvItem, TRUE); TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE)); /* redraw the background of the item */ rcTemp = rcItem; if(infoPtr->nColumnCount == (nItem + 1)) rcTemp.right = infoPtr->rcList.right; else rcTemp.right+=WIDTH_PADDING; LISTVIEW_FillBackground(hwnd, hdc, &rcTemp); /* do indent */ if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0) { rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent; if (SuggestedFocus) SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent; } /* state icons */ if (infoPtr->himlState != NULL) { UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12; if (uStateImage > 0) { ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left, rcItem.top, ILD_NORMAL); } rcItem.left += infoPtr->iconSize.cx; if (SuggestedFocus) SuggestedFocus->left += infoPtr->iconSize.cx; bImage = TRUE; } /* small icons */ if (infoPtr->himlSmall != NULL) { if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) && (lvItem.iImage>=0)) { ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE); ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left, rcItem.top, ILD_SELECTED); } else if (lvItem.iImage>=0) { ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE); ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left, rcItem.top, ILD_NORMAL); } rcItem.left += infoPtr->iconSize.cx; if (SuggestedFocus) SuggestedFocus->left += infoPtr->iconSize.cx; bImage = TRUE; } /* Don't bother painting item being edited */ if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED && !FullSelect) return; if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE)) { /* set item colors */ dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); /* set raster mode */ nMixMode = SetROP2(hdc, R2_XORPEN); } else if ((GetWindowLongW(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) && (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE)) { dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE)); dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT)); /* set raster mode */ nMixMode = SetROP2(hdc, R2_COPYPEN); } else { /* set item colors */ if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) ) { dwBkColor = GetBkColor(hdc); iBkMode = SetBkMode(hdc, TRANSPARENT); textoutOptions &= ~ETO_OPAQUE; } else { dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk); iBkMode = SetBkMode(hdc, OPAQUE); } dwTextColor = SetTextColor(hdc, infoPtr->clrText); /* set raster mode */ nMixMode = SetROP2(hdc, R2_COPYPEN); } nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText); if (rcItem.left + nLabelWidth < rcItem.right) { if (!FullSelect) rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING; if (bImage) rcItem.right += IMAGE_PADDING; } /* draw label */ dwTextX = rcItem.left + 1; if (bImage) dwTextX += IMAGE_PADDING; if (lvItem.pszText) ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions, &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL); if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1)) { /* fill in the gap */ RECT rec; CopyRect(&rec,&rcItem); rec.left = rec.right; rec.right = rec.left+REPORT_MARGINX; ExtTextOutW(hdc, rec.left , rec.top, textoutOptions, &rec, NULL, 0, NULL); } if (!FullSelect) CopyRect(SuggestedFocus,&rcItem); if (nMixMode != 0) { SetROP2(hdc, R2_COPYPEN); SetBkColor(hdc, dwBkColor); SetTextColor(hdc, dwTextColor); if (iBkMode != -1) SetBkMode(hdc, iBkMode); } } /*** * DESCRIPTION: * Draws an item when in large icon display mode. * * PARAMETER(S): * [I] HWND : window handle * [I] HDC : device context handle * [I] LISTVIEW_ITEM * : item * [I] INT : item index * [I] RECT * : clipping rectangle * * RETURN: * None */ static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, RECT *SuggestedFocus) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE; WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; INT nDrawPosX, nLabelWidth, rcWidth; LVITEMW lvItem; RECT rcTemp; TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom); /* get information needed for drawing the item */ ZeroMemory(&lvItem, sizeof(lvItem)); lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE; lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED; lvItem.iItem = nItem; lvItem.iSubItem = 0; lvItem.cchTextMax = DISP_TEXT_SIZE; lvItem.pszText = szDispText; *lvItem.pszText = '\0'; LISTVIEW_GetItemW(hwnd, &lvItem, FALSE); TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE)); /* redraw the background of the item */ rcTemp = rcItem; if(infoPtr->nColumnCount == (nItem + 1)) rcTemp.right = infoPtr->rcList.right; else rcTemp.right+=WIDTH_PADDING; TRACE("background rect (%d,%d)-(%d,%d)\n", rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom); LISTVIEW_FillBackground(hwnd, hdc, &rcTemp); if (lvItem.state & LVIS_SELECTED) { /* set item colors */ SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); /* set raster mode */ SetROP2(hdc, R2_XORPEN); } else { /* set item colors */ if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) ) { SetBkMode(hdc, TRANSPARENT); textoutOptions &= ~ETO_OPAQUE; } else { SetBkMode(hdc, OPAQUE); SetBkColor(hdc, infoPtr->clrTextBk); } SetTextColor(hdc, infoPtr->clrText); /* set raster mode */ SetROP2(hdc, R2_COPYPEN); } /* draw the icon */ if (infoPtr->himlNormal != NULL) { if ((lvItem.state & LVIS_SELECTED) && (lvItem.iImage>=0)) { ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left, rcItem.top, ILD_SELECTED); } else if (lvItem.iImage>=0) { ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left, rcItem.top, ILD_NORMAL); } } /* Don't bother painting item being edited */ if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED) return; /* Since rcItem.left is left point of icon, compute left point of item box */ rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2); rcItem.right = rcItem.left + infoPtr->nItemWidth; rcItem.bottom = rcItem.top + infoPtr->nItemHeight; TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n", rcItem.left, rcItem.top, rcItem.right, rcItem.bottom, infoPtr->iconSize.cx, infoPtr->nItemWidth); TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n", infoPtr->rcList.left, infoPtr->rcList.top, infoPtr->rcList.right, infoPtr->rcList.bottom, infoPtr->rcView.left, infoPtr->rcView.top, infoPtr->rcView.right, infoPtr->rcView.bottom); InflateRect(&rcItem, -(2*CAPTION_BORDER), 0); rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING; nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText); TRACE("rect for text sizing (%d,%d)-(%d,%d), text width %d\n", rcItem.left, rcItem.top, rcItem.right, rcItem.bottom, nLabelWidth); /* append an ellipse ('...') if the caption won't fit in the rect */ rcWidth = max(0, rcItem.right - rcItem.left); if (nLabelWidth > rcWidth) { /* give or take a couple, how many average sized chars would fit? */ INT nCharsFit = infoPtr->ntmAveCharWidth > 0 ? rcWidth/infoPtr->ntmAveCharWidth + 2 : 0; INT eos = min(strlenW(lvItem.pszText), nCharsFit); if (eos > 0) lvItem.pszText[eos-1] = '.'; if (eos > 1) lvItem.pszText[eos-2] = '.'; if (eos < 3) lvItem.pszText[eos] = '\0'; for ( ; eos > 2; eos--) { lvItem.pszText[eos - 3] = '.'; lvItem.pszText[eos] = '\0'; if (ListView_GetStringWidthW(hwnd, lvItem.pszText) <= rcWidth) break; } } InflateRect(&rcItem, 2*CAPTION_BORDER, 0); nDrawPosX = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth; if (nDrawPosX > 1) { rcItem.left += nDrawPosX / 2; rcItem.right = rcItem.left + nLabelWidth + 2*CAPTION_BORDER; } else { rcItem.left += 1; rcItem.right = rcItem.left + infoPtr->iconSpacing.cx - 1; } /* draw label */ rcItem.bottom = rcItem.top + infoPtr->ntmHeight + HEIGHT_PADDING; TRACE("rect for text (%d,%d)-(%d,%d)\n", rcItem.left, rcItem.top, rcItem.right, rcItem.bottom); if (lstrlenW(lvItem.pszText)) { ExtTextOutW(hdc, rcItem.left + CAPTION_BORDER, rcItem.top, textoutOptions, &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL); CopyRect(SuggestedFocus, &rcItem); } else SetRectEmpty(SuggestedFocus); } /*** * DESCRIPTION: * Draws listview items when in report display mode. * * PARAMETER(S): * [I] HWND : window handle * [I] HDC : device context handle * * RETURN: * None */ static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0); SCROLLINFO scrollInfo; INT nDrawPosY = infoPtr->rcList.top; INT nColumnCount; RECT rcItem, rcTemp; INT j; INT nItem; INT nLast; BOOL FullSelected; DWORD cditemmode = CDRF_DODEFAULT; LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); INT scrollOffset; ZeroMemory(&scrollInfo, sizeof(SCROLLINFO)); scrollInfo.cbSize = sizeof(SCROLLINFO); scrollInfo.fMask = SIF_POS; nItem = ListView_GetTopIndex(hwnd); /* add 1 for displaying a partial item at the bottom */ nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1; nLast = min(nLast, GETITEMCOUNT(infoPtr)); /* send cache hint notification */ if (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA) { NMLVCACHEHINT nmlv; nmlv.hdr.hwndFrom = hwnd; nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID); nmlv.hdr.code = LVN_ODCACHEHINT; nmlv.iFrom = nItem; nmlv.iTo = nLast; SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom, (LPARAM)&nmlv); } nColumnCount = Header_GetItemCount(infoPtr->hwndHeader); infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */ FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT; /* clear the background of any part of the control that doesn't contain items */ SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView); LISTVIEW_FillBackground(hwnd, hdc, &rcTemp); /* nothing to draw */ if(GETITEMCOUNT(infoPtr) == 0) return; /* Get scroll bar info once before loop */ GetScrollInfo(hwnd, SB_HORZ, &scrollInfo); scrollOffset = scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE; for (; nItem < nLast; nItem++) { RECT SuggestedFocusRect; /* Do Owner Draw */ if (lStyle & LVS_OWNERDRAWFIXED) { UINT uID = GetWindowLongW( hwnd, GWL_ID); DRAWITEMSTRUCT dis; LVITEMW item; RECT br; TRACE("Owner Drawn\n"); dis.CtlType = ODT_LISTVIEW; dis.CtlID = uID; dis.itemID = nItem; dis.itemAction = ODA_DRAWENTIRE; dis.itemState = 0; if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED; if (nItem==infoPtr->nFocusedItem) dis.itemState|=ODS_FOCUS; dis.hwndItem = hwnd; dis.hDC = hdc; Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br); dis.rcItem.left = -scrollOffset; dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset); dis.rcItem.top = nDrawPosY; dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight; ZeroMemory(&item,sizeof(item)); item.iItem = nItem; item.mask = LVIF_PARAM; ListView_GetItemW(hwnd, &item); dis.itemData = item.lParam; if (SendMessageW(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis)) { nDrawPosY += infoPtr->nItemHeight; continue; } } if (FullSelected) { RECT ir,br; Header_GetItemRect(infoPtr->hwndHeader, 0, &ir); Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br); ir.left += REPORT_MARGINX; ir.right = max(ir.left, br.right - REPORT_MARGINX); ir.top = nDrawPosY; ir.bottom = ir.top + infoPtr->nItemHeight; CopyRect(&SuggestedFocusRect,&ir); } for (j = 0; j < nColumnCount; j++) { if (cdmode & CDRF_NOTIFYITEMDRAW) cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j, CDDS_ITEMPREPAINT); if (cditemmode & CDRF_SKIPDEFAULT) continue; Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem); rcItem.left += REPORT_MARGINX; rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX); rcItem.top = nDrawPosY; rcItem.bottom = rcItem.top + infoPtr->nItemHeight; /* Offset the Scroll Bar Pos */ rcItem.left -= scrollOffset; rcItem.right -= scrollOffset; if (j == 0) { LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected, &SuggestedFocusRect); } else { LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, FullSelected); } if (cditemmode & CDRF_NOTIFYPOSTPAINT) LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0, CDDS_ITEMPOSTPAINT); } /* * Draw Focus Rect */ if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus) { BOOL rop=FALSE; if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED)) rop = SetROP2(hdc, R2_XORPEN); Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top, SuggestedFocusRect.right,SuggestedFocusRect.bottom); if (rop) SetROP2(hdc, R2_COPYPEN); } nDrawPosY += infoPtr->nItemHeight; } } /*** * DESCRIPTION: * Retrieves the number of items that can fit vertically in the client area. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Number of items per row. */ static INT LISTVIEW_GetCountPerRow(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left; INT nCountPerRow = 1; if (nListWidth > 0) { if (uView != LVS_REPORT) { nCountPerRow = nListWidth / infoPtr->nItemWidth; if (nCountPerRow == 0) nCountPerRow = 1; } } return nCountPerRow; } /*** * DESCRIPTION: * Retrieves the number of items that can fit horizontally in the client * area. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Number of items per column. */ static INT LISTVIEW_GetCountPerColumn(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0); INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; INT nCountPerColumn = 1; if (nListHeight > 0) { nCountPerColumn = nListHeight / infoPtr->nItemHeight; if (nCountPerColumn == 0) nCountPerColumn = 1; } return nCountPerColumn; } /*** * DESCRIPTION: * Retrieves the number of columns needed to display all the items when in * list display mode. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Number of columns. */ static INT LISTVIEW_GetColumnCount(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); INT nColumnCount = 0; if ((lStyle & LVS_TYPEMASK) == LVS_LIST) { nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth; if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++; } return nColumnCount; } /*** * DESCRIPTION: * Draws listview items when in list display mode. * * PARAMETER(S): * [I] HWND : window handle * [I] HDC : device context handle * * RETURN: * None */ static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); RECT rcItem, FocusRect, rcTemp; INT i, j; INT nItem; INT nColumnCount; INT nCountPerColumn; INT nItemWidth = infoPtr->nItemWidth; INT nItemHeight = infoPtr->nItemHeight; DWORD cditemmode = CDRF_DODEFAULT; /* get number of fully visible columns */ nColumnCount = LISTVIEW_GetColumnCount(hwnd); infoPtr->nColumnCount = nColumnCount; nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd); nItem = ListView_GetTopIndex(hwnd); /* paint the background of the control that doesn't contain any items */ SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView); LISTVIEW_FillBackground(hwnd, hdc, &rcTemp); /* nothing to draw, return here */ if(GETITEMCOUNT(infoPtr) == 0) return; for (i = 0; i < nColumnCount; i++) { for (j = 0; j < nCountPerColumn; j++, nItem++) { if (nItem >= GETITEMCOUNT(infoPtr)) return; if (cdmode & CDRF_NOTIFYITEMDRAW) cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0, CDDS_ITEMPREPAINT); if (cditemmode & CDRF_SKIPDEFAULT) continue; rcItem.top = j * nItemHeight; rcItem.left = i * nItemWidth; rcItem.bottom = rcItem.top + nItemHeight; rcItem.right = rcItem.left + nItemWidth; LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect); /* * Draw Focus Rect */ if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus) Rectangle(hdc, FocusRect.left, FocusRect.top, FocusRect.right,FocusRect.bottom); if (cditemmode & CDRF_NOTIFYPOSTPAINT) LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0, CDDS_ITEMPOSTPAINT); } } } /*** * DESCRIPTION: * Draws listview items when in icon or small icon display mode. * * PARAMETER(S): * [I] HWND : window handle * [I] HDC : device context handle * * RETURN: * None */ static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); POINT ptPosition; POINT ptOrigin; RECT rcItem, SuggestedFocus, rcTemp; INT i; DWORD cditemmode = CDRF_DODEFAULT; infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */ /* DrawItem from erasing the incorrect background area */ /* paint the background of the control that doesn't contain any items */ SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView); LISTVIEW_FillBackground(hwnd, hdc, &rcTemp); /* nothing to draw, return here */ if(GETITEMCOUNT(infoPtr) == 0) return; LISTVIEW_GetOrigin(hwnd, &ptOrigin); for (i = 0; i < GETITEMCOUNT(infoPtr); i++) { if (cdmode & CDRF_NOTIFYITEMDRAW) cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0, CDDS_ITEMPREPAINT); if (cditemmode & CDRF_SKIPDEFAULT) continue; LISTVIEW_GetItemPosition(hwnd, i, &ptPosition); ptPosition.x += ptOrigin.x; ptPosition.y += ptOrigin.y; if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top) { if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left) { if (ptPosition.y < infoPtr->rcList.bottom) { if (ptPosition.x < infoPtr->rcList.right) { rcItem.top = ptPosition.y; rcItem.left = ptPosition.x; rcItem.bottom = rcItem.top + infoPtr->nItemHeight; rcItem.right = rcItem.left + infoPtr->nItemWidth; if (bSmall) LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus); else LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus); /* * Draw Focus Rect */ if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) && infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus)) Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top, SuggestedFocus.right,SuggestedFocus.bottom); } } } } if (cditemmode & CDRF_NOTIFYPOSTPAINT) LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0, CDDS_ITEMPOSTPAINT); } } /*** * DESCRIPTION: * Draws listview items. * * PARAMETER(S): * [I] HWND : window handle * [I] HDC : device context handle * * RETURN: * NoneX */ static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; HFONT hOldFont; HPEN hPen, hOldPen; DWORD cdmode; RECT rect; LISTVIEW_DumpListview (infoPtr, __LINE__); GetClientRect(hwnd, &rect); cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect); if (cdmode == CDRF_SKIPDEFAULT) return; /* select font */ hOldFont = SelectObject(hdc, infoPtr->hFont); /* select the dotted pen (for drawing the focus box) */ hPen = CreatePen(PS_ALTERNATE, 1, 0); hOldPen = SelectObject(hdc, hPen); /* select transparent brush (for drawing the focus box) */ SelectObject(hdc, GetStockObject(NULL_BRUSH)); if (uView == LVS_LIST) LISTVIEW_RefreshList(hwnd, hdc, cdmode); else if (uView == LVS_REPORT) LISTVIEW_RefreshReport(hwnd, hdc, cdmode); else if (uView == LVS_SMALLICON) LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode); else if (uView == LVS_ICON) LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode); /* unselect objects */ SelectObject(hdc, hOldFont); SelectObject(hdc, hOldPen); /* delete pen */ DeleteObject(hPen); if (cdmode & CDRF_NOTIFYPOSTPAINT) LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect); } /*** * DESCRIPTION: * Calculates the approximate width and height of a given number of items. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : number of items * [I] INT : width * [I] INT : height * * RETURN: * Returns a DWORD. The width in the low word and the height in high word. */ static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount, WORD wWidth, WORD wHeight) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; INT nItemCountPerColumn = 1; INT nColumnCount = 0; DWORD dwViewRect = 0; if (nItemCount == -1) nItemCount = GETITEMCOUNT(infoPtr); if (uView == LVS_LIST) { if (wHeight == 0xFFFF) { /* use current height */ wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; } if (wHeight < infoPtr->nItemHeight) wHeight = infoPtr->nItemHeight; if (nItemCount > 0) { if (infoPtr->nItemHeight > 0) { nItemCountPerColumn = wHeight / infoPtr->nItemHeight; if (nItemCountPerColumn == 0) nItemCountPerColumn = 1; if (nItemCount % nItemCountPerColumn != 0) nColumnCount = nItemCount / nItemCountPerColumn; else nColumnCount = nItemCount / nItemCountPerColumn + 1; } } /* Microsoft padding magic */ wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2; wWidth = nColumnCount * infoPtr->nItemWidth + 2; dwViewRect = MAKELONG(wWidth, wHeight); } else if (uView == LVS_REPORT) FIXME("uView == LVS_REPORT: not implemented\n"); else if (uView == LVS_SMALLICON) FIXME("uView == LVS_SMALLICON: not implemented\n"); else if (uView == LVS_ICON) FIXME("uView == LVS_ICON: not implemented\n"); return dwViewRect; } /*** * DESCRIPTION: * Arranges listview items in icon display mode. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : alignment code * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode) { UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; BOOL bResult = FALSE; if ((uView == LVS_ICON) || (uView == LVS_SMALLICON)) { switch (nAlignCode) { case LVA_ALIGNLEFT: FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n"); break; case LVA_ALIGNTOP: FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n"); break; case LVA_DEFAULT: FIXME("nAlignCode=LVA_DEFAULT: not implemented\n"); break; case LVA_SNAPTOGRID: FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n"); break; } } return bResult; } /* << LISTVIEW_CreateDragImage >> */ /*** * DESCRIPTION: * Removes all listview items and subitems. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); UINT uView = lStyle & LVS_TYPEMASK; LISTVIEW_ITEM *lpItem; LISTVIEW_SUBITEM *lpSubItem; NMLISTVIEW nmlv; BOOL bSuppress; BOOL bResult = FALSE; HDPA hdpaSubItems; TRACE("(hwnd=%x,)\n", hwnd); LISTVIEW_RemoveAllSelections(hwnd); infoPtr->nSelectionMark=-1; infoPtr->nFocusedItem=-1; /* But we are supposed to leave nHotItem as is! */ if (lStyle & LVS_OWNERDATA) { infoPtr->hdpaItems->nItemCount = 0; InvalidateRect(hwnd, NULL, TRUE); return TRUE; } if (GETITEMCOUNT(infoPtr) > 0) { INT i, j; /* send LVN_DELETEALLITEMS notification */ /* verify if subsequent LVN_DELETEITEM notifications should be suppressed */ ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); nmlv.iItem = -1; bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &nmlv); for (i = 0; i < GETITEMCOUNT(infoPtr); i++) { hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i); if (hdpaSubItems != NULL) { for (j = 1; j < hdpaSubItems->nItemCount; j++) { lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j); if (lpSubItem != NULL) { /* free subitem string */ if (is_textW(lpSubItem->pszText)) COMCTL32_Free(lpSubItem->pszText); /* free subitem */ COMCTL32_Free(lpSubItem); } } lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0); if (lpItem != NULL) { if (!bSuppress) { /* send LVN_DELETEITEM notification */ nmlv.iItem = i; nmlv.lParam = lpItem->lParam; listview_notify(hwnd, LVN_DELETEITEM, &nmlv); } /* free item string */ if (is_textW(lpItem->pszText)) COMCTL32_Free(lpItem->pszText); /* free item */ COMCTL32_Free(lpItem); } DPA_Destroy(hdpaSubItems); } } /* reinitialize listview memory */ bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems); /* align items (set position of each item) */ if ((uView == LVS_ICON) || (uView == LVS_SMALLICON)) { if (lStyle & LVS_ALIGNLEFT) { LISTVIEW_AlignLeft(hwnd); } else { LISTVIEW_AlignTop(hwnd); } } LISTVIEW_UpdateScroll(hwnd); /* invalidate client area (optimization needed) */ InvalidateRect(hwnd, NULL, TRUE); } return bResult; } /*** * DESCRIPTION: * Removes a column from the listview control. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : column index * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; UINT uOwnerData = GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA; BOOL bResult = FALSE; if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE) { if (!uOwnerData) bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn); /* Need to reset the item width when deleting a column */ infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd); /* reset scroll parameters */ if (uView == LVS_REPORT) { /* update scrollbar(s) */ LISTVIEW_UpdateScroll(hwnd); /* refresh client area */ InvalidateRect(hwnd, NULL, FALSE); } } return bResult; } /*** * DESCRIPTION: * Removes an item from the listview control. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); UINT uView = lStyle & LVS_TYPEMASK; LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID); NMLISTVIEW nmlv; BOOL bResult = FALSE; HDPA hdpaSubItems; LISTVIEW_ITEM *lpItem; LISTVIEW_SUBITEM *lpSubItem; INT i; LVITEMW item; TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem); /* First, send LVN_DELETEITEM notification. */ memset(&nmlv, 0, sizeof (NMLISTVIEW)); nmlv.hdr.hwndFrom = hwnd; nmlv.hdr.idFrom = lCtrlId; nmlv.hdr.code = LVN_DELETEITEM; nmlv.iItem = nItem; SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId, (LPARAM)&nmlv); /* remove it from the selection range */ ZeroMemory(&item,sizeof(item)); item.stateMask = LVIS_SELECTED; LISTVIEW_SetItemState(hwnd,nItem,&item); if (lStyle & LVS_OWNERDATA) { infoPtr->hdpaItems->nItemCount --; InvalidateRect(hwnd, NULL, TRUE); return TRUE; } if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr))) { /* initialize memory */ ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem); if (hdpaSubItems != NULL) { for (i = 1; i < hdpaSubItems->nItemCount; i++) { lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i); if (lpSubItem != NULL) { /* free item string */ if (is_textW(lpSubItem->pszText)) COMCTL32_Free(lpSubItem->pszText); /* free item */ COMCTL32_Free(lpSubItem); } } lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0); if (lpItem != NULL) { /* free item string */ if (is_textW(lpItem->pszText)) COMCTL32_Free(lpItem->pszText); /* free item */ COMCTL32_Free(lpItem); } bResult = DPA_Destroy(hdpaSubItems); } LISTVIEW_ShiftIndices(hwnd,nItem,-1); /* align items (set position of each item) */ if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) { if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(hwnd); else LISTVIEW_AlignTop(hwnd); } LISTVIEW_UpdateScroll(hwnd); /* refresh client area */ InvalidateRect(hwnd, NULL, TRUE); } return bResult; } /*** * DESCRIPTION: * Return edit control handle of current edit label * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * SUCCESS : HWND * FAILURE : 0 */ static LRESULT LISTVIEW_GetEditControl(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); return infoPtr->hwndEdit; } /*** * DESCRIPTION: * Callback implementation for editlabel control * * PARAMETER(S): * [I] HWND : window handle * [I] LPSTR : modified text * [I] DWORD : item index * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_EndEditLabelT(HWND hwnd, LPWSTR pszText, DWORD nItem, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); NMLVDISPINFOW dispInfo; LISTVIEW_ITEM *lpItem; HDPA hdpaSubItems; LISTVIEW_ITEM lvItemRef; LVITEMW item; BOOL bResult = TRUE; TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd, debugstr_t(pszText, isW), nItem, isW); if (!(lStyle & LVS_OWNERDATA)) { if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem))) return FALSE; if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0))) return FALSE; } else { ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM)); ZeroMemory(&item,sizeof(item)); item.iItem = nItem; item.iSubItem = 0; item.mask = LVIF_PARAM | LVIF_STATE; ListView_GetItemW(hwnd, &item); lvItemRef.state = item.state; lvItemRef.iImage = item.iImage; lvItemRef.lParam = item.lParam; lpItem = &lvItemRef; } ZeroMemory(&dispInfo, sizeof(dispInfo)); dispInfo.item.mask = 0; dispInfo.item.iItem = nItem; dispInfo.item.state = lpItem->state; dispInfo.item.stateMask = 0; dispInfo.item.pszText = pszText; dispInfo.item.cchTextMax = textlenT(pszText, isW); dispInfo.item.iImage = lpItem->iImage; dispInfo.item.lParam = lpItem->lParam; infoPtr->hwndEdit = 0; /* Do we need to update the Item Text */ if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, isW)) if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA)) bResult = textsetptrT(&lpItem->pszText, pszText, isW); return bResult; } /*** * DESCRIPTION: * Callback implementation for editlabel control * * PARAMETER(S): * [I] HWND : window handle * [I] LPSTR : modified text * [I] DWORD : item index * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem) { return LISTVIEW_EndEditLabelT(hwnd, pszText, nItem, TRUE); } /*** * DESCRIPTION: * Callback implementation for editlabel control * * PARAMETER(S): * [I] HWND : window handle * [I] LPSTR : modified text * [I] DWORD : item index * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem) { return LISTVIEW_EndEditLabelT(hwnd, (LPWSTR)pszText, nItem, FALSE); } /*** * DESCRIPTION: * Begin in place editing of specified list view item * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW) { NMLVDISPINFOW dispInfo; RECT rect; LISTVIEW_ITEM *lpItem; HWND hedit; HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE); LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); HDPA hdpaSubItems; WCHAR szDispText[DISP_TEXT_SIZE]; LVITEMW lvItem; LISTVIEW_ITEM lvItemRef; LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); if (~GetWindowLongW(hwnd, GWL_STYLE) & LVS_EDITLABELS) return FALSE; /* Is the EditBox still there, if so remove it */ if(infoPtr->hwndEdit != 0) SetFocus(hwnd); LISTVIEW_SetSelection(hwnd, nItem); LISTVIEW_SetItemFocus(hwnd, nItem); if (!(lStyle & LVS_OWNERDATA)) { if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem))) return 0; if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0))) return 0; } else { LVITEMW item; ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM)); ZeroMemory(&item, sizeof(item)); item.iItem = nItem; item.iSubItem = 0; item.mask = LVIF_PARAM | LVIF_STATE; ListView_GetItemW(hwnd, &item); lvItemRef.iImage = item.iImage; lvItemRef.state = item.state; lvItemRef.lParam = item.lParam; lpItem = &lvItemRef; } /* get information needed for drawing the item */ ZeroMemory(&lvItem, sizeof(lvItem)); lvItem.mask = LVIF_TEXT; lvItem.iItem = nItem; lvItem.iSubItem = 0; lvItem.cchTextMax = DISP_TEXT_SIZE; lvItem.pszText = szDispText; *lvItem.pszText = '\0'; LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW); ZeroMemory(&dispInfo, sizeof(dispInfo)); dispInfo.item.mask = 0; dispInfo.item.iItem = nItem; dispInfo.item.state = lpItem->state; dispInfo.item.stateMask = 0; dispInfo.item.pszText = lvItem.pszText; dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText); dispInfo.item.iImage = lpItem->iImage; dispInfo.item.lParam = lpItem->lParam; if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW)) return 0; rect.left = LVIR_LABEL; if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect)) return 0; if (!(hedit = CreateEditLabelT(szDispText , WS_VISIBLE, rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, hwnd, hinst, isW ? LISTVIEW_EndEditLabelW : (EditlblCallbackW)LISTVIEW_EndEditLabelA, nItem, isW))) return 0; infoPtr->hwndEdit = hedit; SetFocus(hedit); SendMessageW(hedit, EM_SETSEL, 0, -1); return hedit; } /*** * DESCRIPTION: * Ensures the specified item is visible, scrolling into view if necessary. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * [I] BOOL : partially or entirely visible * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; INT nScrollPosHeight = 0; INT nScrollPosWidth = 0; SCROLLINFO scrollInfo; RECT rcItem; BOOL bRedraw = FALSE; ZeroMemory(&scrollInfo, sizeof(SCROLLINFO)); scrollInfo.cbSize = sizeof(SCROLLINFO); scrollInfo.fMask = SIF_POS; /* ALWAYS bPartial == FALSE, FOR NOW! */ rcItem.left = LVIR_BOUNDS; if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE) { if (rcItem.left < infoPtr->rcList.left) { if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE) { /* scroll left */ bRedraw = TRUE; if (uView == LVS_LIST) { nScrollPosWidth = infoPtr->nItemWidth; rcItem.left += infoPtr->rcList.left; } else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) { nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE; rcItem.left += infoPtr->rcList.left; } /* When in LVS_REPORT view, the scroll position should not be updated. */ if (nScrollPosWidth != 0) { if (rcItem.left % nScrollPosWidth == 0) scrollInfo.nPos += rcItem.left / nScrollPosWidth; else scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1; SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE); } } } else if (rcItem.right > infoPtr->rcList.right) { if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE) { /* scroll right */ bRedraw = TRUE; if (uView == LVS_LIST) { rcItem.right -= infoPtr->rcList.right; nScrollPosWidth = infoPtr->nItemWidth; } else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) { rcItem.right -= infoPtr->rcList.right; nScrollPosWidth = LISTVIEW_SCROLL_DIV_SIZE; } /* When in LVS_REPORT view, the scroll position should not be updated. */ if (nScrollPosWidth != 0) { if (rcItem.right % nScrollPosWidth == 0) scrollInfo.nPos += rcItem.right / nScrollPosWidth; else scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1; SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE); } } } if (rcItem.top < infoPtr->rcList.top) { /* scroll up */ bRedraw = TRUE; if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE) { if (uView == LVS_REPORT) { rcItem.top -= infoPtr->rcList.top; nScrollPosHeight = infoPtr->nItemHeight; } else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON)) { nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE; rcItem.top += infoPtr->rcList.top; } if (rcItem.top % nScrollPosHeight == 0) scrollInfo.nPos += rcItem.top / nScrollPosHeight; else scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1; SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE); } } else if (rcItem.bottom > infoPtr->rcList.bottom) { /* scroll down */ bRedraw = TRUE; if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE) { if (uView == LVS_REPORT) { rcItem.bottom -= infoPtr->rcList.bottom; nScrollPosHeight = infoPtr->nItemHeight; } else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON)) { nScrollPosHeight = LISTVIEW_SCROLL_DIV_SIZE; rcItem.bottom -= infoPtr->rcList.bottom; } if (rcItem.bottom % nScrollPosHeight == 0) scrollInfo.nPos += rcItem.bottom / nScrollPosHeight; else scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1; SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE); } } } if(bRedraw) InvalidateRect(hwnd,NULL,TRUE); return TRUE; } /*** * DESCRIPTION: * Retrieves the nearest item, given a position and a direction. * * PARAMETER(S): * [I] HWND : window handle * [I] POINT : start position * [I] UINT : direction * * RETURN: * Item index if successdful, -1 otherwise. */ static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LV_INTHIT lvIntHit; INT nItem = -1; RECT rcView; TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y, (vkDirection == VK_DOWN) ? "VK_DOWN" : ((vkDirection == VK_UP) ? "VK_UP" : ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT"))); if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE) { ZeroMemory(&lvIntHit, sizeof(lvIntHit)); LISTVIEW_GetOrigin(hwnd, &lvIntHit.ht.pt); lvIntHit.ht.pt.x += pt.x; lvIntHit.ht.pt.y += pt.y; if (vkDirection == VK_DOWN) lvIntHit.ht.pt.y += infoPtr->nItemHeight; else if (vkDirection == VK_UP) lvIntHit.ht.pt.y -= infoPtr->nItemHeight; else if (vkDirection == VK_LEFT) lvIntHit.ht.pt.x -= infoPtr->nItemWidth; else if (vkDirection == VK_RIGHT) lvIntHit.ht.pt.x += infoPtr->nItemWidth; if (PtInRect(&rcView, lvIntHit.ht.pt) == FALSE) return -1; else { nItem = LISTVIEW_SuperHitTestItem(hwnd, &lvIntHit, TRUE); return nItem == -1 ? lvIntHit.iDistItem : nItem; } } return nItem; } /*** * DESCRIPTION: * Searches for an item with specific characteristics. * * PARAMETER(S): * [I] hwnd : window handle * [I] nStart : base item index * [I] lpFindInfo : item information to look for * * RETURN: * SUCCESS : index of item * FAILURE : -1 */ static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart, LPLVFINDINFOW lpFindInfo) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); POINT ptItem; WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; LVITEMW lvItem; BOOL bWrap = FALSE; INT nItem = nStart; INT nLast = GETITEMCOUNT(infoPtr); if ((nItem >= -1) && (lpFindInfo != NULL)) { ZeroMemory(&lvItem, sizeof(lvItem)); if (lpFindInfo->flags & LVFI_PARAM) { lvItem.mask |= LVIF_PARAM; } if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL)) { lvItem.mask |= LVIF_TEXT; lvItem.pszText = szDispText; lvItem.cchTextMax = DISP_TEXT_SIZE; } if (lpFindInfo->flags & LVFI_WRAP) bWrap = TRUE; if (lpFindInfo->flags & LVFI_NEARESTXY) { ptItem.x = lpFindInfo->pt.x; ptItem.y = lpFindInfo->pt.y; } while (1) { while (nItem < nLast) { if (lpFindInfo->flags & LVFI_NEARESTXY) { nItem = LISTVIEW_GetNearestItem(hwnd, ptItem, lpFindInfo->vkDirection); if (nItem != -1) { /* get position of the new item index */ if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE) return -1; } else return -1; } else { nItem++; } lvItem.iItem = nItem; lvItem.iSubItem = 0; if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE)) { if (lvItem.mask & LVIF_TEXT) { if (lpFindInfo->flags & LVFI_PARTIAL) { if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue; } else { if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue; } } if (lvItem.mask & LVIF_PARAM) { if (lpFindInfo->lParam != lvItem.lParam) continue; } return nItem; } } if (bWrap) { nItem = -1; nLast = nStart + 1; bWrap = FALSE; } else { return -1; } } } return -1; } /*** * DESCRIPTION: * Searches for an item with specific characteristics. * * PARAMETER(S): * [I] hwnd : window handle * [I] nStart : base item index * [I] lpFindInfo : item information to look for * * RETURN: * SUCCESS : index of item * FAILURE : -1 */ static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart, LPLVFINDINFOA lpFindInfo) { BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL); LVFINDINFOW fiw; LRESULT res; memcpy(&fiw, lpFindInfo, sizeof(fiw)); if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE); res = LISTVIEW_FindItemW(hwnd, nStart, &fiw); if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE); return res; } /*** * DESCRIPTION: * Retrieves the background color of the listview control. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * COLORREF associated with the background. */ static LRESULT LISTVIEW_GetBkColor(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); return infoPtr->clrBk; } /*** * DESCRIPTION: * Retrieves the background image of the listview control. * * PARAMETER(S): * [I] HWND : window handle * [O] LPLVMKBIMAGE : background image attributes * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage) */ /* { */ /* FIXME (listview, "empty stub!\n"); */ /* return FALSE; */ /* } */ /*** * DESCRIPTION: * Retrieves the callback mask. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Value of mask */ static UINT LISTVIEW_GetCallbackMask(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); return infoPtr->uCallbackMask; } /*** * DESCRIPTION: * Retrieves column attributes. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : column index * [IO] LPLVCOLUMNW : column information * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW * otherwise it is in fact a LPLVCOLUMNA * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); HDITEMW hdi; BOOL bResult = FALSE; if (lpColumn != NULL) { /* initialize memory */ ZeroMemory(&hdi, sizeof(hdi)); if (lpColumn->mask & LVCF_FMT) hdi.mask |= HDI_FORMAT; if (lpColumn->mask & LVCF_WIDTH) hdi.mask |= HDI_WIDTH; if (lpColumn->mask & LVCF_TEXT) { hdi.mask |= HDI_TEXT; hdi.cchTextMax = lpColumn->cchTextMax; hdi.pszText = lpColumn->pszText; } if (lpColumn->mask & LVCF_IMAGE) hdi.mask |= HDI_IMAGE; if (lpColumn->mask & LVCF_ORDER) hdi.mask |= HDI_ORDER; if (isW) bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi); else bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi); if (bResult != FALSE) { if (lpColumn->mask & LVCF_FMT) { lpColumn->fmt = 0; if (hdi.fmt & HDF_LEFT) lpColumn->fmt |= LVCFMT_LEFT; else if (hdi.fmt & HDF_RIGHT) lpColumn->fmt |= LVCFMT_RIGHT; else if (hdi.fmt & HDF_CENTER) lpColumn->fmt |= LVCFMT_CENTER; if (hdi.fmt & HDF_IMAGE) lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES; if (hdi.fmt & HDF_BITMAP_ON_RIGHT) lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT; } if (lpColumn->mask & LVCF_WIDTH) lpColumn->cx = hdi.cxy; if (lpColumn->mask & LVCF_IMAGE) lpColumn->iImage = hdi.iImage; if (lpColumn->mask & LVCF_ORDER) lpColumn->iOrder = hdi.iOrder; } } return bResult; } static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray) { /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */ INT i; if (!lpiArray) return FALSE; /* little hack */ for (i = 0; i < iCount; i++) lpiArray[i] = i; return TRUE; } /*** * DESCRIPTION: * Retrieves the column width. * * PARAMETER(S): * [I] HWND : window handle * [I] int : column index * * RETURN: * SUCCESS : column width * FAILURE : zero */ static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; INT nColumnWidth = 0; HDITEMW hdi; if (uView == LVS_LIST) { nColumnWidth = infoPtr->nItemWidth; } else if (uView == LVS_REPORT) { /* get column width from header */ ZeroMemory(&hdi, sizeof(hdi)); hdi.mask = HDI_WIDTH; if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi) != FALSE) nColumnWidth = hdi.cxy; } return nColumnWidth; } /*** * DESCRIPTION: * In list or report display mode, retrieves the number of items that can fit * vertically in the visible area. In icon or small icon display mode, * retrieves the total number of visible items. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Number of fully visible items. */ static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; INT nItemCount = 0; if (uView == LVS_LIST) { if (infoPtr->rcList.right > infoPtr->nItemWidth) { nItemCount = LISTVIEW_GetCountPerRow(hwnd) * LISTVIEW_GetCountPerColumn(hwnd); } } else if (uView == LVS_REPORT) { nItemCount = LISTVIEW_GetCountPerColumn(hwnd); } else { nItemCount = GETITEMCOUNT(infoPtr); } return nItemCount; } /* LISTVIEW_GetEditControl */ /*** * DESCRIPTION: * Retrieves the extended listview style. * * PARAMETERS: * [I] HWND : window handle * * RETURN: * SUCCESS : previous style * FAILURE : 0 */ static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd) { LISTVIEW_INFO *infoPtr; /* make sure we can get the listview info */ if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0))) return (0); return (infoPtr->dwExStyle); } /*** * DESCRIPTION: * Retrieves the handle to the header control. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Header handle. */ static LRESULT LISTVIEW_GetHeader(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); return infoPtr->hwndHeader; } /* LISTVIEW_GetHotCursor */ /*** * DESCRIPTION: * Returns the time that the mouse cursor must hover over an item * before it is selected. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Returns the previously set hover time or (DWORD)-1 to indicate that the * hover time is set to the default hover time. */ static LRESULT LISTVIEW_GetHoverTime(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); return infoPtr->dwHoverTime; } /*** * DESCRIPTION: * Retrieves an image list handle. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : image list identifier * * RETURN: * SUCCESS : image list handle * FAILURE : NULL */ static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); HIMAGELIST himl = NULL; switch (nImageList) { case LVSIL_NORMAL: himl = infoPtr->himlNormal; break; case LVSIL_SMALL: himl = infoPtr->himlSmall; break; case LVSIL_STATE: himl = infoPtr->himlState; break; } return (LRESULT)himl; } /* LISTVIEW_GetISearchString */ /*** * DESCRIPTION: * Retrieves item attributes. * * PARAMETER(S): * [I] hwnd : window handle * [IO] lpLVItem : item info * [I] internal : if true then we will use tricks that avoid copies * but are not compatible with the regular interface * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW, * if FALSE, the lpLVItem is a LPLVITEMA. * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); NMLVDISPINFOW dispInfo; LISTVIEW_SUBITEM *lpSubItem; LISTVIEW_ITEM *lpItem; HDPA hdpaSubItems; void* null = NULL; INT* piImage = (INT*)&null; LPWSTR* ppszText= (LPWSTR*)&null; LPARAM* plParam = (LPARAM*)&null; if (internal && !isW) { ERR("We can't have internal non-Unicode GetItem!\n"); return FALSE; } /* In the following: * lpLVItem describes the information requested by the user * lpItem/lpSubItem is what we have * dispInfo is a structure we use to request the missing * information from the application */ TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), internal, isW); if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) || (lpLVItem->iItem >= GETITEMCOUNT(infoPtr))) return FALSE; ZeroMemory(&dispInfo, sizeof(dispInfo)); if (lStyle & LVS_OWNERDATA) { if (lpLVItem->mask & ~LVIF_STATE) { memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW)); dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW); memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW)); TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW)); } if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0)) { lpLVItem->state = 0; if (infoPtr->nFocusedItem == lpLVItem->iItem) lpLVItem->state |= LVIS_FOCUSED; if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem)) lpLVItem->state |= LVIS_SELECTED; } return TRUE; } hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); if (hdpaSubItems == NULL) return FALSE; if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL) return FALSE; ZeroMemory(&dispInfo.item, sizeof(LVITEMW)); if (lpLVItem->iSubItem == 0) { piImage=&lpItem->iImage; ppszText=&lpItem->pszText; plParam=&lpItem->lParam; if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask) { dispInfo.item.mask |= LVIF_STATE; dispInfo.item.stateMask = infoPtr->uCallbackMask; } } else { lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem); if (lpSubItem != NULL) { piImage=&lpSubItem->iImage; ppszText=&lpSubItem->pszText; } } if ((lpLVItem->mask & LVIF_IMAGE) && !(*piImage && *piImage!=I_IMAGECALLBACK)) { dispInfo.item.mask |= LVIF_IMAGE; } if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText)) { dispInfo.item.mask |= LVIF_TEXT; dispInfo.item.pszText = lpLVItem->pszText; dispInfo.item.cchTextMax = lpLVItem->cchTextMax; if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0) *dispInfo.item.pszText = '\0'; if (dispInfo.item.pszText && (*ppszText == NULL)) *dispInfo.item.pszText = '\0'; } if (dispInfo.item.mask != 0) { /* We don't have all the requested info, query the application */ dispInfo.item.iItem = lpLVItem->iItem; dispInfo.item.iSubItem = lpLVItem->iSubItem; dispInfo.item.lParam = lpItem->lParam; dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW); TRACE(" getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW)); } if (dispInfo.item.mask & LVIF_IMAGE) { lpLVItem->iImage = dispInfo.item.iImage; if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *piImage) *piImage = dispInfo.item.iImage; } else if (lpLVItem->mask & LVIF_IMAGE) { lpLVItem->iImage = *piImage; } if (dispInfo.item.mask & LVIF_PARAM) { lpLVItem->lParam = dispInfo.item.lParam; if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *plParam) *plParam = dispInfo.item.lParam; } else if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = lpItem->lParam; if (dispInfo.item.mask & LVIF_TEXT) { if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText) textsetptrT(ppszText, dispInfo.item.pszText, isW); /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */ /* some apps give a new pointer in ListView_Notify so we can't be sure. */ if (lpLVItem->pszText != dispInfo.item.pszText) textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax); } else if (lpLVItem->mask & LVIF_TEXT) { if (internal) lpLVItem->pszText = *ppszText; else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax); } if (lpLVItem->iSubItem == 0) { if (dispInfo.item.mask & LVIF_STATE) { lpLVItem->state = lpItem->state; lpLVItem->state &= ~dispInfo.item.stateMask; lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask); lpLVItem->state &= ~LVIS_SELECTED; if ((dispInfo.item.stateMask & LVIS_SELECTED) && LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem)) lpLVItem->state |= LVIS_SELECTED; } else if (lpLVItem->mask & LVIF_STATE) { lpLVItem->state = lpItem->state & lpLVItem->stateMask; lpLVItem->state &= ~LVIS_SELECTED; if ((lpLVItem->stateMask & LVIS_SELECTED) && LISTVIEW_IsSelected(hwnd,lpLVItem->iItem)) lpLVItem->state |= LVIS_SELECTED; } if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = lpItem->lParam; if (lpLVItem->mask & LVIF_INDENT) lpLVItem->iIndent = lpItem->iIndent; } return TRUE; } /* LISTVIEW_GetHotCursor */ /*** * DESCRIPTION: * Retrieves the index of the hot item. * * PARAMETERS: * [I] HWND : window handle * * RETURN: * SUCCESS : hot item index * FAILURE : -1 (no hot item) */ static LRESULT LISTVIEW_GetHotItem(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); return infoPtr->nHotItem; } /* LISTVIEW_GetHoverTime */ /*** * DESCRIPTION: * Retrieves the number of items in the listview control. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Number of items. */ static LRESULT LISTVIEW_GetItemCount(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); return GETITEMCOUNT(infoPtr); } /*** * DESCRIPTION: * Retrieves the rectangle enclosing the item icon and text. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * [O] LPRECT : coordinate information * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_GetItemBoundBox(HWND hwnd, INT nItem, LPRECT lpRect) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); UINT uView = lStyle & LVS_TYPEMASK; BOOL bResult = FALSE; HDPA hdpaSubItems; LISTVIEW_ITEM *lpItem; INT nCountPerColumn; INT nRow; TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd, nItem, lpRect); if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lpRect != NULL)) { if (uView == LVS_LIST) { bResult = TRUE; nItem = nItem - ListView_GetTopIndex(hwnd); nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd); if (nItem < 0) { nRow = nItem % nCountPerColumn; if (nRow == 0) { lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth; lpRect->top = 0; } else { lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth; lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight; } } else { lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth; lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight; } } else if (uView == LVS_REPORT) { bResult = TRUE; lpRect->left = REPORT_MARGINX; lpRect->top = ((nItem - ListView_GetTopIndex(hwnd)) * infoPtr->nItemHeight) + infoPtr->rcList.top; if (!(lStyle & LVS_NOSCROLL)) { SCROLLINFO scrollInfo; /* Adjust position by scrollbar offset */ ZeroMemory(&scrollInfo, sizeof(SCROLLINFO)); scrollInfo.cbSize = sizeof(SCROLLINFO); scrollInfo.fMask = SIF_POS; GetScrollInfo(hwnd, SB_HORZ, &scrollInfo); lpRect->left -= scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE; } } else /* either LVS_ICON or LVS_SMALLICON */ { if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem))) { if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0))) { bResult = TRUE; lpRect->left = lpItem->ptPosition.x; lpRect->top = lpItem->ptPosition.y; } } } } lpRect->right = lpRect->left + infoPtr->nItemWidth; lpRect->bottom = lpRect->top + infoPtr->nItemHeight; TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE", lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); return bResult; } /*** * DESCRIPTION: * Retrieves the position (upper-left) of the listview control item. * Note that for LVS_ICON style, the upper-left is that of the icon * and not the bounding box. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * [O] LPPOINT : coordinate information * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, LPPOINT lpptPosition) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK; BOOL bResult = FALSE; RECT rcBounding; TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd, nItem, lpptPosition); if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lpptPosition != NULL)) { bResult = LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcBounding); lpptPosition->x = rcBounding.left; lpptPosition->y = rcBounding.top; if (uView == LVS_ICON) { lpptPosition->y += ICON_TOP_PADDING; lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2; } TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE", lpptPosition->x, lpptPosition->y); } return bResult; } /*** * DESCRIPTION: * Retrieves the bounding rectangle for a listview control item. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * [IO] LPRECT : bounding rectangle coordinates * lprc->left specifies the portion of the item for which the bounding * rectangle will be retrieved. * * LVIR_BOUNDS Returns the bounding rectangle of the entire item, * including the icon and label. * LVIR_ICON Returns the bounding rectangle of the icon or small icon. * LVIR_LABEL Returns the bounding rectangle of the item text. * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL * rectangles, but excludes columns in report view. * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; BOOL bResult = FALSE; POINT ptOrigin; POINT ptItem; INT nLeftPos; INT nLabelWidth; INT nIndent; LVITEMW lvItem; RECT rcInternal; TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc); if (uView & LVS_REPORT) { ZeroMemory(&lvItem, sizeof(lvItem)); lvItem.mask = LVIF_INDENT; lvItem.iItem = nItem; lvItem.iSubItem = 0; LISTVIEW_GetItemW(hwnd, &lvItem, TRUE); /* do indent */ if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0) nIndent = infoPtr->iconSize.cx * lvItem.iIndent; else nIndent = 0; } else nIndent = 0; if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL)) { switch(lprc->left) { case LVIR_ICON: if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break; if (uView == LVS_ICON) { if (infoPtr->himlNormal != NULL) { if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE) { bResult = TRUE; lprc->left = ptItem.x + ptOrigin.x; lprc->top = ptItem.y + ptOrigin.y; lprc->right = lprc->left + infoPtr->iconSize.cx; lprc->bottom = (lprc->top + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING + ICON_TOP_PADDING); } } } else if (uView == LVS_SMALLICON) { if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE) { bResult = TRUE; lprc->left = ptItem.x + ptOrigin.x; lprc->top = ptItem.y + ptOrigin.y; lprc->bottom = lprc->top + infoPtr->nItemHeight; if (infoPtr->himlState != NULL) lprc->left += infoPtr->iconSize.cx; if (infoPtr->himlSmall != NULL) lprc->right = lprc->left + infoPtr->iconSize.cx; else lprc->right = lprc->left; } } else { bResult = TRUE; lprc->left = ptItem.x; if (uView & LVS_REPORT) lprc->left += nIndent; lprc->top = ptItem.y; lprc->bottom = lprc->top + infoPtr->nItemHeight; if (infoPtr->himlState != NULL) lprc->left += infoPtr->iconSize.cx; if (infoPtr->himlSmall != NULL) lprc->right = lprc->left + infoPtr->iconSize.cx; else lprc->right = lprc->left; } break; case LVIR_LABEL: if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break; if (uView == LVS_ICON) { if (infoPtr->himlNormal != NULL) { if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE) { bResult = TRUE; lprc->left = ptItem.x + ptOrigin.x; lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING); nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem); if (infoPtr->iconSpacing.cx - nLabelWidth > 1) { lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2; lprc->right = lprc->left + nLabelWidth; } else { lprc->left += 1; lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1; } lprc->bottom = lprc->top + infoPtr->ntmHeight + HEIGHT_PADDING; } } } else if (uView == LVS_SMALLICON) { if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE) { bResult = TRUE; nLeftPos = lprc->left = ptItem.x + ptOrigin.x; lprc->top = ptItem.y + ptOrigin.y; lprc->bottom = lprc->top + infoPtr->nItemHeight; if (infoPtr->himlState != NULL) lprc->left += infoPtr->iconSize.cx; if (infoPtr->himlSmall != NULL) lprc->left += infoPtr->iconSize.cx; nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem); nLabelWidth += TRAILING_PADDING; if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth) lprc->right = lprc->left + nLabelWidth; else lprc->right = nLeftPos + infoPtr->nItemWidth; } } else { bResult = TRUE; if (uView & LVS_REPORT) nLeftPos = lprc->left = ptItem.x + nIndent; else nLeftPos = lprc->left = ptItem.x; lprc->top = ptItem.y; lprc->bottom = lprc->top + infoPtr->nItemHeight; if (infoPtr->himlState != NULL) lprc->left += infoPtr->iconSize.cx; if (infoPtr->himlSmall != NULL) lprc->left += infoPtr->iconSize.cx; nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem); nLabelWidth += TRAILING_PADDING; if (infoPtr->himlSmall) nLabelWidth += IMAGE_PADDING; if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth) lprc->right = lprc->left + nLabelWidth; else lprc->right = nLeftPos + infoPtr->nItemWidth; } break; case LVIR_BOUNDS: if (!LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcInternal)) break; ptItem.x = rcInternal.left; ptItem.y = rcInternal.top; if (uView == LVS_ICON) { if (infoPtr->himlNormal != NULL) { if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE) { INT text_left, text_right, icon_left, text_pos_x; /* for style LVS_ICON bounds * left = min(icon.left, text.left) * right = max(icon.right, text.right) * top = boundbox.top + NOTHITABLE * bottom = text.bottom + 1 */ bResult = TRUE; icon_left = text_left = ptItem.x; /* Correct ptItem to icon upper-left */ icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2; ptItem.y += ICON_TOP_PADDING; /* Compute the label left and right */ nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem); text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth; if (text_pos_x > 1) { text_left += text_pos_x / 2; text_right = text_left + nLabelWidth + 2*CAPTION_BORDER; } else { text_left += 1; text_right = text_left + infoPtr->iconSpacing.cx - 1; } /* Compute rectangle w/o the text height */ lprc->left = min(icon_left, text_left) + ptOrigin.x; lprc->right = max(icon_left + infoPtr->iconSize.cx, text_right) + ptOrigin.x; lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE; lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE + infoPtr->iconSize.cy + 1 + ICON_BOTTOM_PADDING; lprc->bottom += (infoPtr->ntmHeight + HEIGHT_PADDING); } } } else if (uView == LVS_SMALLICON) { if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE) { bResult = TRUE; lprc->left = ptItem.x + ptOrigin.x; lprc->right = lprc->left; lprc->top = ptItem.y + ptOrigin.y; lprc->bottom = lprc->top + infoPtr->nItemHeight; if (infoPtr->himlState != NULL) lprc->right += infoPtr->iconSize.cx; if (infoPtr->himlSmall != NULL) lprc->right += infoPtr->iconSize.cx; nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem); nLabelWidth += TRAILING_PADDING; if (infoPtr->himlSmall) nLabelWidth += IMAGE_PADDING; if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth) lprc->right += nLabelWidth; else lprc->right = lprc->left + infoPtr->nItemWidth; } } else { bResult = TRUE; lprc->left = ptItem.x; if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT) lprc->left += nIndent; lprc->right = lprc->left; lprc->top = ptItem.y; lprc->bottom = lprc->top + infoPtr->nItemHeight; if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT)) { RECT br; int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader); Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br); lprc->right = max(lprc->left, br.right - REPORT_MARGINX); } else { if (infoPtr->himlState != NULL) lprc->right += infoPtr->iconSize.cx; if (infoPtr->himlSmall != NULL) lprc->right += infoPtr->iconSize.cx; nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem); nLabelWidth += TRAILING_PADDING; if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth) lprc->right += nLabelWidth; else lprc->right = lprc->left + infoPtr->nItemWidth; } } break; case LVIR_SELECTBOUNDS: if (!ListView_GetItemPosition(hwnd, nItem, &ptItem)) break; if (uView == LVS_ICON) { if (infoPtr->himlNormal != NULL) { if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE) { bResult = TRUE; lprc->left = ptItem.x + ptOrigin.x; lprc->top = ptItem.y + ptOrigin.y; lprc->right = lprc->left + infoPtr->iconSpacing.cx; lprc->bottom = lprc->top + infoPtr->iconSpacing.cy; } } } else if (uView == LVS_SMALLICON) { if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE) { bResult = TRUE; nLeftPos= lprc->left = ptItem.x + ptOrigin.x; lprc->top = ptItem.y + ptOrigin.y; lprc->bottom = lprc->top + infoPtr->nItemHeight; if (infoPtr->himlState != NULL) lprc->left += infoPtr->iconSize.cx; lprc->right = lprc->left; if (infoPtr->himlSmall != NULL) lprc->right += infoPtr->iconSize.cx; nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem); nLabelWidth += TRAILING_PADDING; if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth) lprc->right += nLabelWidth; else lprc->right = nLeftPos + infoPtr->nItemWidth; } } else { bResult = TRUE; if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT)) nLeftPos = lprc->left = ptItem.x + nIndent; else nLeftPos = lprc->left = ptItem.x; lprc->top = ptItem.y; lprc->bottom = lprc->top + infoPtr->nItemHeight; if (infoPtr->himlState != NULL) lprc->left += infoPtr->iconSize.cx; lprc->right = lprc->left; if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) { RECT br; int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader); Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br); lprc->right = max(lprc->left, br.right - REPORT_MARGINX); } else { if (infoPtr->himlSmall != NULL) lprc->right += infoPtr->iconSize.cx; nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem); nLabelWidth += TRAILING_PADDING; if (infoPtr->himlSmall) nLabelWidth += IMAGE_PADDING; if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth) lprc->right += nLabelWidth; else lprc->right = nLeftPos + infoPtr->nItemWidth; } } break; } } TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE", lprc->left, lprc->top, lprc->right, lprc->bottom); return bResult; } /*** * DESCRIPTION: * Retrieves the width of a label. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * SUCCESS : string width (in pixels) * FAILURE : zero */ static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem) { WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; INT nLabelWidth = 0; LVITEMW lvItem; TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem); ZeroMemory(&lvItem, sizeof(lvItem)); lvItem.mask = LVIF_TEXT; lvItem.iItem = nItem; lvItem.cchTextMax = DISP_TEXT_SIZE; lvItem.pszText = szDispText; if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE)) nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText); return nLabelWidth; } /*** * DESCRIPTION: * Retrieves the spacing between listview control items. * * PARAMETER(S): * [I] HWND : window handle * [I] BOOL : flag for small or large icon * * RETURN: * Horizontal + vertical spacing */ static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG lResult; if (bSmall == FALSE) { lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy); } else { LONG style = GetWindowLongW(hwnd, GWL_STYLE); if ((style & LVS_TYPEMASK) == LVS_ICON) lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING); else lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight); } return lResult; } /*** * DESCRIPTION: * Retrieves the state of a listview control item. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * [I] UINT : state mask * * RETURN: * State specified by the mask. */ static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LVITEMW lvItem; UINT uState = 0; if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr))) { ZeroMemory(&lvItem, sizeof(lvItem)); lvItem.iItem = nItem; lvItem.stateMask = uMask; lvItem.mask = LVIF_STATE; if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE)) uState = lvItem.state; } return uState; } /*** * DESCRIPTION: * Retrieves the text of a listview control item or subitem. * * PARAMETER(S): * [I] hwnd : window handle * [I] nItem : item index * [IO] lpLVItem : item information * [I] isW : TRUE if lpLVItem is Unicode * * RETURN: * SUCCESS : string length * FAILURE : 0 */ static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); INT nLength = 0; if (lpLVItem != NULL) { if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr))) { lpLVItem->mask = LVIF_TEXT; lpLVItem->iItem = nItem; if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW)) nLength = textlenT(lpLVItem->pszText, isW); } } return nLength; } /*** * DESCRIPTION: * Searches for an item based on properties + relationships. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * [I] INT : relationship flag * * RETURN: * SUCCESS : item index * FAILURE : -1 */ static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; UINT uMask = 0; LVFINDINFOW lvFindInfo; INT nCountPerColumn; INT i; if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr))) { ZeroMemory(&lvFindInfo, sizeof(lvFindInfo)); if (uFlags & LVNI_CUT) uMask |= LVIS_CUT; if (uFlags & LVNI_DROPHILITED) uMask |= LVIS_DROPHILITED; if (uFlags & LVNI_FOCUSED) uMask |= LVIS_FOCUSED; if (uFlags & LVNI_SELECTED) uMask |= LVIS_SELECTED; if (uFlags & LVNI_ABOVE) { if ((uView == LVS_LIST) || (uView == LVS_REPORT)) { while (nItem >= 0) { nItem--; if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask) return nItem; } } else { lvFindInfo.flags = LVFI_NEARESTXY; lvFindInfo.vkDirection = VK_UP; ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt); while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1) { if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask) return nItem; } } } else if (uFlags & LVNI_BELOW) { if ((uView == LVS_LIST) || (uView == LVS_REPORT)) { while (nItem < GETITEMCOUNT(infoPtr)) { nItem++; if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask) return nItem; } } else { lvFindInfo.flags = LVFI_NEARESTXY; lvFindInfo.vkDirection = VK_DOWN; ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt); while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1) { if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask) return nItem; } } } else if (uFlags & LVNI_TOLEFT) { if (uView == LVS_LIST) { nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd); while (nItem - nCountPerColumn >= 0) { nItem -= nCountPerColumn; if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask) return nItem; } } else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) { lvFindInfo.flags = LVFI_NEARESTXY; lvFindInfo.vkDirection = VK_LEFT; ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt); while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1) { if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask) return nItem; } } } else if (uFlags & LVNI_TORIGHT) { if (uView == LVS_LIST) { nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd); while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr)) { nItem += nCountPerColumn; if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask) return nItem; } } else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) { lvFindInfo.flags = LVFI_NEARESTXY; lvFindInfo.vkDirection = VK_RIGHT; ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt); while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1) { if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask) return nItem; } } } else { nItem++; /* search by index */ for (i = nItem; i < GETITEMCOUNT(infoPtr); i++) { if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask) return i; } } } return -1; } /* LISTVIEW_GetNumberOfWorkAreas */ /*** * DESCRIPTION: * Retrieves the origin coordinates when in icon or small icon display mode. * * PARAMETER(S): * [I] HWND : window handle * [O] LPPOINT : coordinate information * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin) { LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); UINT uView = lStyle & LVS_TYPEMASK; BOOL bResult = FALSE; TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin); if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) { SCROLLINFO scrollInfo; ZeroMemory(lpptOrigin, sizeof(POINT)); ZeroMemory(&scrollInfo, sizeof(SCROLLINFO)); scrollInfo.cbSize = sizeof(SCROLLINFO); if (lStyle & WS_HSCROLL) { scrollInfo.fMask = SIF_POS; if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE) lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE; } if (lStyle & WS_VSCROLL) { scrollInfo.fMask = SIF_POS; if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE) lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE; } bResult = TRUE; } return bResult; } /*** * DESCRIPTION: * Retrieves the number of items that are marked as selected. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Number of items selected. */ static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd) { /* REDO THIS */ LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); INT nSelectedCount = 0; INT i; for (i = 0; i < GETITEMCOUNT(infoPtr); i++) { if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED) nSelectedCount++; } return nSelectedCount; } /*** * DESCRIPTION: * Retrieves item index that marks the start of a multiple selection. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Index number or -1 if there is no selection mark. */ static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); return infoPtr->nSelectionMark; } /*** * DESCRIPTION: * Retrieves the width of a string. * * PARAMETER(S): * [I] hwnd : window handle * [I] lpszText : text string to process * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise * * RETURN: * SUCCESS : string width (in pixels) * FAILURE : zero */ static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW) { if (is_textT(lpszText, isW)) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont; HDC hdc = GetDC(hwnd); HFONT hOldFont = SelectObject(hdc, hFont); SIZE stringSize; ZeroMemory(&stringSize, sizeof(SIZE)); if (isW) GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize); else GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize); SelectObject(hdc, hOldFont); ReleaseDC(hwnd, hdc); return stringSize.cx; } return 0; } /*** * DESCRIPTION: * Retrieves the text backgound color. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * COLORREF associated with the the background. */ static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0); return infoPtr->clrTextBk; } /*** * DESCRIPTION: * Retrieves the text color. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * COLORREF associated with the text. */ static LRESULT LISTVIEW_GetTextColor(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0); return infoPtr->clrText; } /*** * DESCRIPTION: * Determines item if a hit or closest if not * * PARAMETER(S): * [I] HWND : window handle * [IO] LPLV_INTHIT : hit test information * [I] subitem : fill out iSubItem. * * RETURN: * SUCCESS : item index of hit * FAILURE : -1 */ static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); UINT uView = lStyle & LVS_TYPEMASK; INT i,topindex,bottomindex; RECT rcItem; DWORD xterm, yterm, dist; TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.pt.y); topindex = ListView_GetTopIndex(hwnd); if (uView == LVS_REPORT) { bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1; bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr)); } else { bottomindex = GETITEMCOUNT(infoPtr); } lpInt->distance = 0x7fffffff; lpInt->iDistItem = -1; for (i = topindex; i < bottomindex; i++) { rcItem.left = LVIR_BOUNDS; if (LISTVIEW_GetItemRect(hwnd, i, &rcItem)) { if (PtInRect(&rcItem, lpInt->ht.pt)) { rcItem.left = LVIR_ICON; if (LISTVIEW_GetItemRect(hwnd, i, &rcItem)) { if (PtInRect(&rcItem, lpInt->ht.pt)) { lpInt->ht.flags = LVHT_ONITEMICON; lpInt->ht.iItem = i; if (subitem) lpInt->ht.iSubItem = 0; return i; } } rcItem.left = LVIR_LABEL; if (LISTVIEW_GetItemRect(hwnd, i, &rcItem)) { if (PtInRect(&rcItem, lpInt->ht.pt)) { lpInt->ht.flags = LVHT_ONITEMLABEL; lpInt->ht.iItem = i; if (subitem) lpInt->ht.iSubItem = 0; return i; } } lpInt->ht.flags = LVHT_ONITEMSTATEICON; lpInt->ht.iItem = i; if (subitem) lpInt->ht.iSubItem = 0; return i; } else { /* * Now compute distance from point to center of boundary * box. Since we are only interested in the relative * distance, we can skip the nasty square root operation */ xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x; yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y; dist = xterm * xterm + yterm * yterm; if (dist < lpInt->distance) { lpInt->distance = dist; lpInt->iDistItem = i; } } } } lpInt->ht.flags = LVHT_NOWHERE; TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance); return -1; } /*** * DESCRIPTION: * Determines which section of the item was selected (if any). * * PARAMETER(S): * [I] HWND : window handle * [IO] LPLVHITTESTINFO : hit test information * [I] subitem : fill out iSubItem. * * RETURN: * SUCCESS : item index * FAILURE : -1 */ static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem) { INT ret; LV_INTHIT lv_inthit; TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x, lpHitTestInfo->pt.y); memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO)); ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem); memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO)); return ret; } /*** * DESCRIPTION: * Determines which listview item is located at the specified position. * * PARAMETER(S): * [I] HWND : window handle * [IO} LPLVHITTESTINFO : hit test information * * RETURN: * SUCCESS : item index * FAILURE : -1 */ static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); INT nItem = -1; lpHitTestInfo->flags = 0; if (infoPtr->rcList.left > lpHitTestInfo->pt.x) lpHitTestInfo->flags = LVHT_TOLEFT; else if (infoPtr->rcList.right < lpHitTestInfo->pt.x) lpHitTestInfo->flags = LVHT_TORIGHT; if (infoPtr->rcList.top > lpHitTestInfo->pt.y) lpHitTestInfo->flags |= LVHT_ABOVE; else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y) lpHitTestInfo->flags |= LVHT_BELOW; if (lpHitTestInfo->flags == 0) { /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for * an app might pass only a structure with space up to iItem! * (MS Office 97 does that for instance in the file open dialog) */ nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE); } return nItem; } /*** * DESCRIPTION: * Inserts a new column. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : column index * [I] LPLVCOLUMNW : column information * * RETURN: * SUCCESS : new column index * FAILURE : -1 */ static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); INT nNewColumn = -1; HDITEMW hdi; TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn); if (lpColumn != NULL) { /* initialize memory */ ZeroMemory(&hdi, sizeof(hdi)); if (lpColumn->mask & LVCF_FMT) { /* format member is valid */ hdi.mask |= HDI_FORMAT; /* set text alignment (leftmost column must be left-aligned) */ if (nColumn == 0) { hdi.fmt |= HDF_LEFT; } else { if (lpColumn->fmt & LVCFMT_LEFT) { hdi.fmt |= HDF_LEFT; } else if (lpColumn->fmt & LVCFMT_RIGHT) { hdi.fmt |= HDF_RIGHT; } else if (lpColumn->fmt & LVCFMT_CENTER) { hdi.fmt |= HDF_CENTER; } } if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT) { hdi.fmt |= HDF_BITMAP_ON_RIGHT; /* ??? */ } if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES) { /* ??? */ } if (lpColumn->fmt & LVCFMT_IMAGE) { hdi.fmt |= HDF_IMAGE; hdi.iImage = I_IMAGECALLBACK; } } if (lpColumn->mask & LVCF_WIDTH) { hdi.mask |= HDI_WIDTH; hdi.cxy = lpColumn->cx; } if (lpColumn->mask & LVCF_TEXT) { hdi.mask |= HDI_TEXT | HDI_FORMAT; hdi.pszText = lpColumn->pszText; hdi.cchTextMax = textlenT(lpColumn->pszText, isW); hdi.fmt |= HDF_STRING; } if (lpColumn->mask & LVCF_IMAGE) { hdi.mask |= HDI_IMAGE; hdi.iImage = lpColumn->iImage; } if (lpColumn->mask & LVCF_ORDER) { hdi.mask |= HDI_ORDER; hdi.iOrder = lpColumn->iOrder; } /* insert item in header control */ nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW), (WPARAM)nColumn, (LPARAM)&hdi); /* Need to reset the item width when inserting a new column */ infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd); LISTVIEW_UpdateScroll(hwnd); InvalidateRect(hwnd, NULL, FALSE); } return nNewColumn; } /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem. This function should only be used for inserting items into a sorted list (LVM_INSERTITEM) and not during the processing of a LVM_SORTITEMS message. Applications should provide their own sort proc. when sending LVM_SORTITEMS. */ /* Platform SDK: (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion... if: LVS_SORTXXX must be specified, LVS_OWNERDRAW is not set, .pszText is not LPSTR_TEXTCALLBACK. (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices are sorted based on item text..." */ static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam) { LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE); LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 ); LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 ); INT cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText ); /* if we're sorting descending, negate the return value */ return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv; } /*** * nESCRIPTION: * Inserts a new item in the listview control. * * PARAMETER(S): * [I] HWND : window handle * [I] LPLVITEMW : item information * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI * * RETURN: * SUCCESS : new item index * FAILURE : -1 */ static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); UINT uView = lStyle & LVS_TYPEMASK; INT nItem = -1; HDPA hdpaSubItems; INT nItemWidth = 0; LISTVIEW_ITEM *lpItem = NULL; TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW); if (lStyle & LVS_OWNERDATA) { nItem = infoPtr->hdpaItems->nItemCount; infoPtr->hdpaItems->nItemCount ++; return nItem; } if (lpLVItem != NULL) { /* make sure it's not a subitem; cannot insert a subitem */ if (lpLVItem->iSubItem == 0) { if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) ) { ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM)); if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW)) { /* insert item in listview control data structure */ if ( (hdpaSubItems = DPA_Create(8)) ) { if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1) { if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING)) && !(lStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) ) { /* Insert the item in the proper sort order based on the pszText member. See comments for LISTVIEW_InsertCompare() for greater detail */ nItem = DPA_InsertPtr( infoPtr->hdpaItems, GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems ); DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd ); nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems ); } else { nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem, hdpaSubItems); } if (nItem != -1) { NMLISTVIEW nmlv; LISTVIEW_ShiftIndices(hwnd,nItem,1); /* manage item focus */ if (lpLVItem->mask & LVIF_STATE) { lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED); if (lpLVItem->stateMask & LVIS_SELECTED) LISTVIEW_SetSelection(hwnd, nItem); else if (lpLVItem->stateMask & LVIS_FOCUSED) LISTVIEW_SetItemFocus(hwnd, nItem); } /* send LVN_INSERTITEM notification */ ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); nmlv.iItem = nItem; nmlv.lParam = lpItem->lParam; listview_notify(hwnd, LVN_INSERTITEM, &nmlv); if ((uView == LVS_SMALLICON) || (uView == LVS_LIST)) { nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem); if (nItemWidth > infoPtr->nItemWidth) infoPtr->nItemWidth = nItemWidth; } /* align items (set position of each item) */ if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) { if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(hwnd); else LISTVIEW_AlignTop(hwnd); } LISTVIEW_UpdateScroll(hwnd); /* refresh client area */ InvalidateRect(hwnd, NULL, FALSE); } } } } } } } /* free memory if unsuccessful */ if ((nItem == -1) && (lpItem != NULL)) COMCTL32_Free(lpItem); return nItem; } /*** * DESCRIPTION: * Redraws a range of items. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : first item * [I] INT : last item * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); BOOL bResult = FALSE; RECT rcItem; INT i; if (nFirst <= nLast) { if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr))) { if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr))) { for (i = nFirst; i <= nLast; i++) { rcItem.left = LVIR_BOUNDS; LISTVIEW_GetItemRect(hwnd, i, &rcItem); InvalidateRect(hwnd, &rcItem, TRUE); } } } } return bResult; } /* LISTVIEW_Scroll */ /*** * DESCRIPTION: * Sets the background color. * * PARAMETER(S): * [I] HWND : window handle * [I] COLORREF : background color * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); infoPtr->clrBk = clrBk; InvalidateRect(hwnd, NULL, TRUE); return TRUE; } /* LISTVIEW_SetBkImage */ /*** * DESCRIPTION: * Sets the callback mask. This mask will be used when the parent * window stores state information (some or all). * * PARAMETER(S): * [I] HWND : window handle * [I] UINT : state mask * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); infoPtr->uCallbackMask = uMask; return TRUE; } /*** * DESCRIPTION: * Sets the attributes of a header item. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : column index * [I] LPLVCOLUMNW : column attributes * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, * otherwise it is in fact a LPLVCOLUMNA * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); BOOL bResult = FALSE; HDITEMW hdi, hdiget; if ((lpColumn != NULL) && (nColumn >= 0) && (nColumn < Header_GetItemCount(infoPtr->hwndHeader))) { /* initialize memory */ ZeroMemory(&hdi, sizeof(hdi)); if (lpColumn->mask & LVCF_FMT) { /* format member is valid */ hdi.mask |= HDI_FORMAT; /* get current format first */ hdiget.mask = HDI_FORMAT; if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget)) /* preserve HDF_STRING if present */ hdi.fmt = hdiget.fmt & HDF_STRING; /* set text alignment (leftmost column must be left-aligned) */ if (nColumn == 0) { hdi.fmt |= HDF_LEFT; } else { if (lpColumn->fmt & LVCFMT_LEFT) hdi.fmt |= HDF_LEFT; else if (lpColumn->fmt & LVCFMT_RIGHT) hdi.fmt |= HDF_RIGHT; else if (lpColumn->fmt & LVCFMT_CENTER) hdi.fmt |= HDF_CENTER; } if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT) hdi.fmt |= HDF_BITMAP_ON_RIGHT; if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES) hdi.fmt |= HDF_IMAGE; if (lpColumn->fmt & LVCFMT_IMAGE) { hdi.fmt |= HDF_IMAGE; hdi.iImage = I_IMAGECALLBACK; } } if (lpColumn->mask & LVCF_WIDTH) { hdi.mask |= HDI_WIDTH; hdi.cxy = lpColumn->cx; } if (lpColumn->mask & LVCF_TEXT) { hdi.mask |= HDI_TEXT | HDI_FORMAT; hdi.pszText = lpColumn->pszText; hdi.cchTextMax = textlenT(lpColumn->pszText, isW); hdi.fmt |= HDF_STRING; } if (lpColumn->mask & LVCF_IMAGE) { hdi.mask |= HDI_IMAGE; hdi.iImage = lpColumn->iImage; } if (lpColumn->mask & LVCF_ORDER) { hdi.mask |= HDI_ORDER; hdi.iOrder = lpColumn->iOrder; } /* set header item attributes */ bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi); } return bResult; } /*** * DESCRIPTION: * Sets the column order array * * PARAMETERS: * [I] HWND : window handle * [I] INT : number of elements in column order array * [I] INT : pointer to column order array * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray) { /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */ FIXME("iCount %d lpiArray %p\n", iCount, lpiArray); if (!lpiArray) return FALSE; return TRUE; } /*** * DESCRIPTION: * Sets the width of a column * * PARAMETERS: * [I] HWND : window handle * [I] INT : column index * [I] INT : column width * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx) { LISTVIEW_INFO *infoPtr; HDITEMW hdi; LRESULT lret; LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); UINT uView = lStyle & LVS_TYPEMASK; HDC hdc; HFONT header_font; HFONT old_font; SIZE size; WCHAR text_buffer[DISP_TEXT_SIZE]; INT header_item_count; INT item_index; RECT rcHeader; /* make sure we can get the listview info */ if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0))) return (FALSE); if (!infoPtr->hwndHeader) /* make sure we have a header */ return (FALSE); /* set column width only if in report or list mode */ if ((uView != LVS_REPORT) && (uView != LVS_LIST)) return (FALSE); /* take care of invalid cx values */ if((uView == LVS_REPORT) && (cx < -2)) cx = LVSCW_AUTOSIZE; else if (uView == LVS_LIST && (cx < 1)) return FALSE; /* resize all columns if in LVS_LIST mode */ if(uView == LVS_LIST) { infoPtr->nItemWidth = cx; InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */ return TRUE; } /* autosize based on listview items width */ if(cx == LVSCW_AUTOSIZE) { /* set the width of the header to the width of the widest item */ for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++) { if(cx < LISTVIEW_GetLabelWidth(hwnd, item_index)) cx = LISTVIEW_GetLabelWidth(hwnd, item_index); } } /* autosize based on listview header width */ else if(cx == LVSCW_AUTOSIZE_USEHEADER) { header_item_count = Header_GetItemCount(infoPtr->hwndHeader); /* if iCol is the last column make it fill the remainder of the controls width */ if(iCol == (header_item_count - 1)) { /* get the width of every item except the current one */ hdi.mask = HDI_WIDTH; cx = 0; for(item_index = 0; item_index < (header_item_count - 1); item_index++) { Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi)); cx+=hdi.cxy; } /* retrieve the layout of the header */ GetWindowRect(infoPtr->hwndHeader, &rcHeader); cx = (rcHeader.right - rcHeader.left) - cx; } else { /* retrieve header font */ header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L); /* retrieve header text */ hdi.mask = HDI_TEXT; hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]); hdi.pszText = text_buffer; Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi)); /* determine the width of the text in the header */ hdc = GetDC(hwnd); old_font = SelectObject(hdc, header_font); /* select the font into hdc */ GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size); SelectObject(hdc, old_font); /* restore the old font */ ReleaseDC(hwnd, hdc); /* set the width of this column to the width of the text */ cx = size.cx; } } /* call header to update the column change */ hdi.mask = HDI_WIDTH; hdi.cxy = cx; lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi); InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */ return lret; } /*** * DESCRIPTION: * Sets the extended listview style. * * PARAMETERS: * [I] HWND : window handle * [I] DWORD : mask * [I] DWORD : style * * RETURN: * SUCCESS : previous style * FAILURE : 0 */ static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); DWORD dwOldStyle = infoPtr->dwExStyle; /* set new style */ if (dwMask) infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask); else infoPtr->dwExStyle = dwStyle; return dwOldStyle; } /* LISTVIEW_SetHotCursor */ /*** * DESCRIPTION: * Sets the hot item index. * * PARAMETERS: * [I] HWND : window handle * [I] INT : index * * RETURN: * SUCCESS : previous hot item index * FAILURE : -1 (no hot item) */ static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); INT iOldIndex = infoPtr->nHotItem; /* set new style */ infoPtr->nHotItem = iIndex; return iOldIndex; } /*** * DESCRIPTION: * Sets the amount of time the cursor must hover over an item before it is selected. * * PARAMETER(S): * [I] HWND : window handle * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default * * RETURN: * Returns the previous hover time */ static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); DWORD oldHoverTime = infoPtr->dwHoverTime; infoPtr->dwHoverTime = dwHoverTime; return oldHoverTime; } /*** * DESCRIPTION: * Sets spacing for icons of LVS_ICON style. * * PARAMETER(S): * [I] HWND : window handle * [I] DWORD : MAKELONG(cx, cy) * * RETURN: * MAKELONG(oldcx, oldcy) */ static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); INT cy = HIWORD(spacing); INT cx = LOWORD(spacing); DWORD oldspacing; LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE); UINT uView = lStyle & LVS_TYPEMASK; oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy); if (cx == -1) /* set to default */ cx = GetSystemMetrics(SM_CXICONSPACING); if (cy == -1) /* set to default */ cy = GetSystemMetrics(SM_CYICONSPACING); if (cx) infoPtr->iconSpacing.cx = cx; else { /* if 0 then compute width */ if (uView == LVS_ICON) FIXME("width computation not yet done\n"); /* * Should scan each item and determine max width of * icon or label, then make that the width */ else /* FIXME: unknown computation for non LVS_ICON - this is a guess */ infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd); } if (cy) infoPtr->iconSpacing.cy = cy; else { /* if 0 then compute height */ if (uView == LVS_ICON) infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + infoPtr->ntmHeight + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET; else /* FIXME: unknown computation for non LVS_ICON - this is a guess */ infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd); } TRACE("old=(%d,%d), new=(%ld,%ld)\n", LOWORD(oldspacing), HIWORD(oldspacing), infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy); /* these depend on the iconSpacing */ infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd); infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd); return oldspacing; } /*** * DESCRIPTION: * Sets image lists. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : image list type * [I] HIMAGELIST : image list handle * * RETURN: * SUCCESS : old image list * FAILURE : NULL */ static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); HIMAGELIST himlOld = 0; INT oldHeight; switch (nType) { case LVSIL_NORMAL: himlOld = infoPtr->himlNormal; infoPtr->himlNormal = himl; break; case LVSIL_SMALL: himlOld = infoPtr->himlSmall; infoPtr->himlSmall = himl; break; case LVSIL_STATE: himlOld = infoPtr->himlState; infoPtr->himlState = himl; ImageList_SetBkColor(infoPtr->himlState, CLR_NONE); break; } oldHeight = infoPtr->nItemHeight; infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd); if (infoPtr->nItemHeight != oldHeight) LISTVIEW_UpdateScroll(hwnd); return himlOld; } /*** * DESCRIPTION: * Preallocates memory (does *not* set the actual count of items !) * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item count (projected number of items to allocate) * [I] DWORD : update flags * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0); TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags); if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA) { int precount,topvisible; TRACE("LVS_OWNERDATA is set!\n"); if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL)) FIXME("flags %s %s not implemented\n", (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL" : "", (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : ""); /* * Internally remove all the selections. */ do { LISTVIEW_SELECTION *selection; selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0); if (selection) LISTVIEW_RemoveSelectionRange(hwnd,selection->lower, selection->upper); } while (infoPtr->hdpaSelectionRanges->nItemCount>0); precount = infoPtr->hdpaItems->nItemCount; topvisible = ListView_GetTopIndex(hwnd) + LISTVIEW_GetCountPerColumn(hwnd) + 1; infoPtr->hdpaItems->nItemCount = nItems; LISTVIEW_UpdateSize(hwnd); LISTVIEW_UpdateScroll(hwnd); if (min(precount,infoPtr->hdpaItems->nItemCount)= 0) || (nItem < GETITEMCOUNT(infoPtr))) { if ((uView == LVS_ICON) || (uView == LVS_SMALLICON)) { if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) ) { if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) ) { POINT orig; bResult = TRUE; orig = lpItem->ptPosition; if ((nPosX == -1) && (nPosY == -1)) { /* This point value seems to be an undocumented feature. The * best guess is that it means either at the origin, or at * the true beginning of the list. I will assume the origin. */ POINT pt1; if (!LISTVIEW_GetOrigin(hwnd, &pt1)) { pt1.x = 0; pt1.y = 0; } nPosX = pt1.x; nPosY = pt1.y; if (uView == LVS_ICON) { nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2; nPosY += ICON_TOP_PADDING; } TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n", nPosX, nPosY); } lpItem->ptPosition.x = nPosX; lpItem->ptPosition.y = nPosY; if (uView == LVS_ICON) { lpItem->ptPosition.y -= ICON_TOP_PADDING; lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2; if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0)) { FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n", orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y); /* if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0; if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0; */ } else { TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n", orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y); } } } } } } return bResult; } /*** * DESCRIPTION: * Sets the state of one or many items. * * PARAMETER(S): * [I] HWND : window handle * [I]INT : item index * [I] LPLVITEM : item or subitem info * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); BOOL bResult = TRUE; LVITEMW lvItem; TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n", hwnd, nItem, debuglvitem_t(lpLVItem, TRUE)); ZeroMemory(&lvItem, sizeof(lvItem)); lvItem.mask = LVIF_STATE; lvItem.state = lpLVItem->state; lvItem.stateMask = lpLVItem->stateMask ; lvItem.iItem = nItem; if (nItem == -1) { /* apply to all items */ for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++) if (!ListView_SetItemW(hwnd, &lvItem)) bResult = FALSE; } else bResult = ListView_SetItemW(hwnd, &lvItem); return bResult; } /*** * DESCRIPTION: * Sets the text of an item or subitem. * * PARAMETER(S): * [I] hwnd : window handle * [I] nItem : item index * [I] lpLVItem : item or subitem info * [I] isW : TRUE if input is Unicode * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); BOOL bResult = FALSE; LVITEMW lvItem; TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n", hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW); if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr))) { ZeroMemory(&lvItem, sizeof(LVITEMW)); lvItem.mask = LVIF_TEXT; lvItem.pszText = lpLVItem->pszText; lvItem.iItem = nItem; lvItem.iSubItem = lpLVItem->iSubItem; if(isW) bResult = ListView_SetItemW(hwnd, &lvItem); else bResult = ListView_SetItemA(hwnd, &lvItem); } return bResult; } /*** * DESCRIPTION: * Set item index that marks the start of a multiple selection. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : index * * RETURN: * Index number or -1 if there is no selection mark. */ static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); INT nOldIndex = infoPtr->nSelectionMark; TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex); infoPtr->nSelectionMark = nIndex; return nOldIndex; } /*** * DESCRIPTION: * Sets the text background color. * * PARAMETER(S): * [I] HWND : window handle * [I] COLORREF : text background color * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk); infoPtr->clrTextBk = clrTextBk; InvalidateRect(hwnd, NULL, TRUE); return TRUE; } /*** * DESCRIPTION: * Sets the text foreground color. * * PARAMETER(S): * [I] HWND : window handle * [I] COLORREF : text color * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText); infoPtr->clrText = clrText; InvalidateRect(hwnd, NULL, TRUE); return TRUE; } /* LISTVIEW_SetToolTips */ /* LISTVIEW_SetUnicodeFormat */ /* LISTVIEW_SetWorkAreas */ /*** * DESCRIPTION: * Callback internally used by LISTVIEW_SortItems() * * PARAMETER(S): * [I] LPVOID : first LISTVIEW_ITEM to compare * [I] LPVOID : second LISTVIEW_ITEM to compare * [I] LPARAM : HWND of control * * RETURN: * if first comes before second : negative * if first comes after second : positive * if first and second are equivalent : zero */ static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0); LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 ); LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 ); /* Forward the call to the client defined callback */ return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort ); } /*** * DESCRIPTION: * Sorts the listview items. * * PARAMETER(S): * [I] HWND : window handle * [I] WPARAM : application-defined value * [I] LPARAM : pointer to comparision callback * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE); HDPA hdpaSubItems=NULL; LISTVIEW_ITEM *pLVItem=NULL; LPVOID selectionMarkItem; int nCount, i; TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort); if (lStyle & LVS_OWNERDATA) return FALSE; if (!infoPtr || !infoPtr->hdpaItems) return FALSE; nCount = GETITEMCOUNT(infoPtr); /* if there are 0 or 1 items, there is no need to sort */ if (nCount < 2) return TRUE; infoPtr->pfnCompare = pfnCompare; infoPtr->lParamSort = lParamSort; DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd); /* Adjust selections and indices so that they are the way they should * be after the sort (otherwise, the list items move around, but * whatever is at the item's previous original position will be * selected instead) */ selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL; for (i=0; i < nCount; i++) { hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i); pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0); if (pLVItem->state & LVIS_SELECTED) LISTVIEW_AddSelectionRange(hwnd, i, i); else LISTVIEW_RemoveSelectionRange(hwnd, i, i); if (pLVItem->state & LVIS_FOCUSED) infoPtr->nFocusedItem=i; } if (selectionMarkItem != NULL) infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem); /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */ /* align the items */ LISTVIEW_AlignTop(hwnd); /* refresh the display */ InvalidateRect(hwnd, NULL, TRUE); return TRUE; } /* LISTVIEW_SubItemHitTest */ /*** * DESCRIPTION: * Updates an items or rearranges the listview control. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : item index * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); BOOL bResult = FALSE; RECT rc; TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem); if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr))) { bResult = TRUE; /* rearrange with default alignment style */ if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) || ((lStyle & LVS_TYPEMASK) == LVS_SMALLICON))) { ListView_Arrange(hwnd, 0); } else { /* get item bounding rectangle */ ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS); InvalidateRect(hwnd, &rc, TRUE); } } return bResult; } /*** * DESCRIPTION: * Creates the listview control. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Zero */ static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = lpcs->style & LVS_TYPEMASK; LOGFONTW logFont; TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs); /* initialize info pointer */ ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO)); /* determine the type of structures to use */ infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT, (WPARAM)hwnd, (LPARAM)NF_QUERY); /* initialize color information */ infoPtr->clrBk = GetSysColor(COLOR_WINDOW); infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT); infoPtr->clrTextBk = CLR_DEFAULT; /* set default values */ infoPtr->hwndSelf = hwnd; infoPtr->uCallbackMask = 0; infoPtr->nFocusedItem = -1; infoPtr->nSelectionMark = -1; infoPtr->nHotItem = -1; infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING); infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING); ZeroMemory(&infoPtr->rcList, sizeof(RECT)); infoPtr->hwndEdit = 0; infoPtr->pedititem = NULL; infoPtr->nEditLabelItem = -1; /* get default font (icon title) */ SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0); infoPtr->hDefaultFont = CreateFontIndirectW(&logFont); infoPtr->hFont = infoPtr->hDefaultFont; LISTVIEW_SaveTextMetrics(hwnd); /* create header */ infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL, WS_CHILD | HDS_HORZ | HDS_BUTTONS, 0, 0, 0, 0, hwnd, (HMENU)0, lpcs->hInstance, NULL); /* set header font */ SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE); if (uView == LVS_ICON) { infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON); infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON); } else if (uView == LVS_REPORT) { if (!(LVS_NOCOLUMNHEADER & lpcs->style)) { ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL); } else { /* set HDS_HIDDEN flag to hide the header bar */ SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN); } infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON); infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON); } else { infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON); infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON); } /* display unsupported listview window styles */ LISTVIEW_UnsupportedStyles(lpcs->style); /* allocate memory for the data structure */ infoPtr->hdpaItems = DPA_Create(10); /* allocate memory for the selection ranges */ infoPtr->hdpaSelectionRanges = DPA_Create(10); /* initialize size of items */ infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd); infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd); /* initialize the hover time to -1(indicating the default system hover time) */ infoPtr->dwHoverTime = -1; return 0; } /*** * DESCRIPTION: * Erases the background of the listview control. * * PARAMETER(S): * [I] HWND : window handle * [I] WPARAM : device context handle * [I] LPARAM : not used * * RETURN: * SUCCESS : TRUE * FAILURE : FALSE */ static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam, LPARAM lParam) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); BOOL bResult; TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam); if (infoPtr->clrBk == CLR_NONE) { bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam); } else { RECT rc; HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk); GetClientRect(hwnd, &rc); FillRect((HDC)wParam, &rc, hBrush); DeleteObject(hBrush); bResult = TRUE; } return bResult; } static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc); if (infoPtr->clrBk != CLR_NONE) { HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk); FillRect(hdc, rc, hBrush); DeleteObject(hBrush); } } /*** * DESCRIPTION: * Retrieves the listview control font. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Font handle. */ static LRESULT LISTVIEW_GetFont(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); TRACE("(hwnd=%x)\n", hwnd); return infoPtr->hFont; } /*** * DESCRIPTION: * Performs vertical scrolling. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : scroll code * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION * or SB_THUMBTRACK. * [I] HWND : scrollbar control window handle * * RETURN: * Zero */ static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos, HWND hScrollWnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); SCROLLINFO scrollInfo; TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n", hwnd, nScrollCode, nCurrentPos, hScrollWnd); SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0); ZeroMemory(&scrollInfo, sizeof(SCROLLINFO)); scrollInfo.cbSize = sizeof(SCROLLINFO); scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE) { INT nOldScrollPos = scrollInfo.nPos; switch (nScrollCode) { case SB_LINEUP: if (scrollInfo.nPos > scrollInfo.nMin) scrollInfo.nPos--; break; case SB_LINEDOWN: if (scrollInfo.nPos < scrollInfo.nMax) scrollInfo.nPos++; break; case SB_PAGEUP: if (scrollInfo.nPos > scrollInfo.nMin) { if (scrollInfo.nPos >= scrollInfo.nPage) scrollInfo.nPos -= scrollInfo.nPage; else scrollInfo.nPos = scrollInfo.nMin; } break; case SB_PAGEDOWN: if (scrollInfo.nPos < scrollInfo.nMax) { if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage) scrollInfo.nPos += scrollInfo.nPage; else scrollInfo.nPos = scrollInfo.nMax; } break; case SB_THUMBPOSITION: case SB_THUMBTRACK: scrollInfo.nPos = nCurrentPos; if (scrollInfo.nPos > scrollInfo.nMax) scrollInfo.nPos=scrollInfo.nMax; if (scrollInfo.nPos < scrollInfo.nMin) scrollInfo.nPos=scrollInfo.nMin; break; } if (nOldScrollPos != scrollInfo.nPos) { scrollInfo.fMask = SIF_POS; SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE); if (IsWindowVisible(infoPtr->hwndHeader)) { RECT rListview, rcHeader, rDest; GetClientRect(hwnd, &rListview); GetWindowRect(infoPtr->hwndHeader, &rcHeader); MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2); SubtractRect(&rDest, &rListview, &rcHeader); InvalidateRect(hwnd, &rDest, TRUE); } else InvalidateRect(hwnd, NULL, TRUE); } } return 0; } /*** * DESCRIPTION: * Performs horizontal scrolling. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : scroll code * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION * or SB_THUMBTRACK. * [I] HWND : scrollbar control window handle * * RETURN: * Zero */ static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos, HWND hScrollWnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); SCROLLINFO scrollInfo; TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n", hwnd, nScrollCode, nCurrentPos, hScrollWnd); SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0); ZeroMemory(&scrollInfo, sizeof(SCROLLINFO)); scrollInfo.cbSize = sizeof(SCROLLINFO); scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE) { INT nOldScrollPos = scrollInfo.nPos; switch (nScrollCode) { case SB_LINELEFT: if (scrollInfo.nPos > scrollInfo.nMin) scrollInfo.nPos--; break; case SB_LINERIGHT: if (scrollInfo.nPos < scrollInfo.nMax) scrollInfo.nPos++; break; case SB_PAGELEFT: if (scrollInfo.nPos > scrollInfo.nMin) { if (scrollInfo.nPos >= scrollInfo.nPage) scrollInfo.nPos -= scrollInfo.nPage; else scrollInfo.nPos = scrollInfo.nMin; } break; case SB_PAGERIGHT: if (scrollInfo.nPos < scrollInfo.nMax) { if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage) scrollInfo.nPos += scrollInfo.nPage; else scrollInfo.nPos = scrollInfo.nMax; } break; case SB_THUMBPOSITION: case SB_THUMBTRACK: scrollInfo.nPos = nCurrentPos; if (scrollInfo.nPos > scrollInfo.nMax) scrollInfo.nPos=scrollInfo.nMax; if (scrollInfo.nPos < scrollInfo.nMin) scrollInfo.nPos=scrollInfo.nMin; break; } if (nOldScrollPos != scrollInfo.nPos) { UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; scrollInfo.fMask = SIF_POS; SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE); if(uView == LVS_REPORT) { scrollInfo.fMask = SIF_POS; GetScrollInfo(hwnd, SB_HORZ, &scrollInfo); LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos); } InvalidateRect(hwnd, NULL, TRUE); } } return 0; } static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta) { UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; INT gcWheelDelta = 0; UINT pulScrollLines = 3; SCROLLINFO scrollInfo; TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta); SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0); gcWheelDelta -= wheelDelta; ZeroMemory(&scrollInfo, sizeof(SCROLLINFO)); scrollInfo.cbSize = sizeof(SCROLLINFO); scrollInfo.fMask = SIF_POS | SIF_RANGE; switch(uView) { case LVS_ICON: case LVS_SMALLICON: /* * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number * should be fixed in the future. */ if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE) LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0); break; case LVS_REPORT: if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines) { if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE) { int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines); cLineScroll *= (gcWheelDelta / WHEEL_DELTA); LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0); } } break; case LVS_LIST: LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0); break; } return 0; } /*** * DESCRIPTION: * ??? * * PARAMETER(S): * [I] HWND : window handle * [I] INT : virtual key * [I] LONG : key data * * RETURN: * Zero */ static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; INT nItem = -1; NMLVKEYDOWN nmKeyDown; TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData); /* send LVN_KEYDOWN notification */ nmKeyDown.wVKey = nVirtualKey; nmKeyDown.flags = 0; notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr); switch (nVirtualKey) { case VK_RETURN: if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1)) { hdr_notify(hwnd, NM_RETURN); /* NM_RETURN notification */ hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */ } break; case VK_HOME: if (GETITEMCOUNT(infoPtr) > 0) nItem = 0; break; case VK_END: if (GETITEMCOUNT(infoPtr) > 0) nItem = GETITEMCOUNT(infoPtr) - 1; break; case VK_LEFT: nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT); break; case VK_UP: nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE); break; case VK_RIGHT: nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT); break; case VK_DOWN: nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW); break; case VK_PRIOR: if (uView == LVS_REPORT) nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd); else nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd) * LISTVIEW_GetCountPerRow(hwnd); if(nItem < 0) nItem = 0; break; case VK_NEXT: if (uView == LVS_REPORT) nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd); else nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd) * LISTVIEW_GetCountPerRow(hwnd); if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1; break; } if ((nItem != -1) && (nItem != infoPtr->nFocusedItem)) { if (LISTVIEW_KeySelection(hwnd, nItem)) UpdateWindow(hwnd); /* update client area */ } return 0; } /*** * DESCRIPTION: * Kills the focus. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Zero */ static LRESULT LISTVIEW_KillFocus(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; INT i,nTop,nBottom; TRACE("(hwnd=%x)\n", hwnd); /* send NM_KILLFOCUS notification */ hdr_notify(hwnd, NM_KILLFOCUS); /* set window focus flag */ infoPtr->bFocus = FALSE; /* NEED drawing optimization ; redraw the selected items */ if (uView & LVS_REPORT) { nTop = LISTVIEW_GetTopIndex(hwnd); nBottom = nTop + LISTVIEW_GetCountPerColumn(hwnd) + 1; } else { nTop = 0; nBottom = GETITEMCOUNT(infoPtr); } for (i = nTop; ibFocus == FALSE) SetFocus(hwnd); /* set left button down flag */ infoPtr->bLButtonDown = TRUE; ptPosition.x = wPosX; ptPosition.y = wPosY; nItem = LISTVIEW_MouseSelection(hwnd, ptPosition); if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr))) { if (lStyle & LVS_SINGLESEL) { if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED) && infoPtr->nEditLabelItem == -1) infoPtr->nEditLabelItem = nItem; else LISTVIEW_SetSelection(hwnd, nItem); } else { if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT)) { if (bGroupSelect) LISTVIEW_AddGroupSelection(hwnd, nItem); else LISTVIEW_AddSelection(hwnd, nItem); } else if (wKey & MK_CONTROL) { bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem); } else if (wKey & MK_SHIFT) { LISTVIEW_SetGroupSelection(hwnd, nItem); } else { BOOL was_selected = (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED); /* set selection (clears other pre-existing selections) */ LISTVIEW_SetSelection(hwnd, nItem); if (was_selected && infoPtr->nEditLabelItem == -1) infoPtr->nEditLabelItem = nItem; } } } else { /* remove all selections */ LISTVIEW_RemoveAllSelections(hwnd); } /* redraw if we could have possibly selected something */ if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE); return 0; } /*** * DESCRIPTION: * Processes mouse up messages (left mouse button). * * PARAMETER(S): * [I] HWND : window handle * [I] WORD : key flag * [I] WORD : x coordinate * [I] WORD : y coordinate * * RETURN: * Zero */ static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX, WORD wPosY) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY); if (infoPtr->bLButtonDown != FALSE) { LVHITTESTINFO lvHitTestInfo; NMLISTVIEW nmlv; lvHitTestInfo.pt.x = wPosX; lvHitTestInfo.pt.y = wPosY; /* send NM_CLICK notification */ ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1) { nmlv.iItem = lvHitTestInfo.iItem; nmlv.iSubItem = lvHitTestInfo.iSubItem; } else { nmlv.iItem = -1; nmlv.iSubItem = 0; } nmlv.ptAction.x = wPosX; nmlv.ptAction.y = wPosY; listview_notify(hwnd, NM_CLICK, &nmlv); /* set left button flag */ infoPtr->bLButtonDown = FALSE; if(infoPtr->nEditLabelItem != -1) { if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem) LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE); infoPtr->nEditLabelItem = -1; } } return 0; } /*** * DESCRIPTION: * Creates the listview control (called before WM_CREATE). * * PARAMETER(S): * [I] HWND : window handle * [I] WPARAM : unhandled * [I] LPARAM : widow creation info * * RETURN: * Zero */ static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam) { LISTVIEW_INFO *infoPtr; TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam); /* allocate memory for info structure */ infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO)); if (infoPtr == NULL) { ERR("could not allocate info memory!\n"); return 0; } SetWindowLongW(hwnd, 0, (LONG)infoPtr); if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr) { ERR("pointer assignment error!\n"); return 0; } return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam); } /*** * DESCRIPTION: * Destroys the listview control (called after WM_DESTROY). * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Zero */ static LRESULT LISTVIEW_NCDestroy(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); TRACE("(hwnd=%x)\n", hwnd); /* delete all items */ LISTVIEW_DeleteAllItems(hwnd); /* destroy data structure */ DPA_Destroy(infoPtr->hdpaItems); DPA_Destroy(infoPtr->hdpaSelectionRanges); /* destroy font */ infoPtr->hFont = (HFONT)0; if (infoPtr->hDefaultFont) { DeleteObject(infoPtr->hDefaultFont); } /* free listview info pointer*/ COMCTL32_Free(infoPtr); SetWindowLongW(hwnd, 0, 0); return 0; } /*** * DESCRIPTION: * Handles notifications from children. * * PARAMETER(S): * [I] HWND : window handle * [I] INT : control identifier * [I] LPNMHDR : notification information * * RETURN: * Zero */ static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh); if (lpnmh->hwndFrom == infoPtr->hwndHeader) { /* handle notification from header control */ if (lpnmh->code == HDN_ENDTRACKW) { infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd); InvalidateRect(hwnd, NULL, TRUE); } else if(lpnmh->code == HDN_ITEMCLICKW) { /* Handle sorting by Header Column */ NMLISTVIEW nmlv; ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); nmlv.iItem = -1; nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem; listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv); } else if(lpnmh->code == NM_RELEASEDCAPTURE) { /* Idealy this should be done in HDN_ENDTRACKA * but since SetItemBounds in Header.c is called after * the notification is sent, it is neccessary to handle the * update of the scroll bar here (Header.c works fine as it is, * no need to disturb it) */ infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd); LISTVIEW_UpdateScroll(hwnd); InvalidateRect(hwnd, NULL, TRUE); } } return 0; } /*** * DESCRIPTION: * Determines the type of structure to use. * * PARAMETER(S): * [I] HWND : window handle of the sender * [I] HWND : listview window handle * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT * * RETURN: * Zero */ static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand); if (nCommand == NF_REQUERY) infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)hwnd, (LPARAM)NF_QUERY); return 0; } /*** * DESCRIPTION: * Paints/Repaints the listview control. * * PARAMETER(S): * [I] HWND : window handle * [I] HDC : device context handle * * RETURN: * Zero */ static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc) { PAINTSTRUCT ps; TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc); if (hdc == 0) { hdc = BeginPaint(hwnd, &ps); LISTVIEW_Refresh(hwnd, hdc); EndPaint(hwnd, &ps); } else { LISTVIEW_Refresh(hwnd, hdc); } return 0; } /*** * DESCRIPTION: * Processes double click messages (right mouse button). * * PARAMETER(S): * [I] HWND : window handle * [I] WORD : key flag * [I] WORD : x coordinate * [I] WORD : y coordinate * * RETURN: * Zero */ static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX, WORD wPosY) { TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY); /* send NM_RELEASEDCAPTURE notification */ hdr_notify(hwnd, NM_RELEASEDCAPTURE); /* send NM_RDBLCLK notification */ hdr_notify(hwnd, NM_RDBLCLK); return 0; } /*** * DESCRIPTION: * Processes mouse down messages (right mouse button). * * PARAMETER(S): * [I] HWND : window handle * [I] WORD : key flag * [I] WORD : x coordinate * [I] WORD : y coordinate * * RETURN: * Zero */ static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX, WORD wPosY) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); POINT ptPosition; INT nItem; NMLISTVIEW nmlv; LVHITTESTINFO lvHitTestInfo; TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY); /* send NM_RELEASEDCAPTURE notification */ hdr_notify(hwnd, NM_RELEASEDCAPTURE); /* make sure the listview control window has the focus */ if (infoPtr->bFocus == FALSE) SetFocus(hwnd); /* set right button down flag */ infoPtr->bRButtonDown = TRUE; /* determine the index of the selected item */ ptPosition.x = wPosX; ptPosition.y = wPosY; nItem = LISTVIEW_MouseSelection(hwnd, ptPosition); if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr))) { LISTVIEW_SetItemFocus(hwnd,nItem); if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) && !LISTVIEW_IsSelected(hwnd,nItem)) LISTVIEW_SetSelection(hwnd, nItem); } else { LISTVIEW_RemoveAllSelections(hwnd); } lvHitTestInfo.pt.x = wPosX; lvHitTestInfo.pt.y = wPosY; /* Send NM_RClICK notification */ ZeroMemory(&nmlv, sizeof(nmlv)); if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1) { nmlv.iItem = lvHitTestInfo.iItem; nmlv.iSubItem = lvHitTestInfo.iSubItem; } else { nmlv.iItem = -1; nmlv.iSubItem = 0; } nmlv.ptAction.x = wPosX; nmlv.ptAction.y = wPosY; listview_notify(hwnd, NM_RCLICK, &nmlv); return 0; } /*** * DESCRIPTION: * Processes mouse up messages (right mouse button). * * PARAMETER(S): * [I] HWND : window handle * [I] WORD : key flag * [I] WORD : x coordinate * [I] WORD : y coordinate * * RETURN: * Zero */ static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX, WORD wPosY) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY); if (infoPtr->bRButtonDown) { POINT pt; pt.x = wPosX; pt.y = wPosY; /* set button flag */ infoPtr->bRButtonDown = FALSE; /* Change to screen coordinate for WM_CONTEXTMENU */ ClientToScreen(hwnd, &pt); /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */ SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y)); } return 0; } /*** * DESCRIPTION: * Sets the focus. * * PARAMETER(S): * [I] HWND : window handle * [I] HWND : window handle of previously focused window * * RETURN: * Zero */ static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus); /* send NM_SETFOCUS notification */ hdr_notify(hwnd, NM_SETFOCUS); /* set window focus flag */ infoPtr->bFocus = TRUE; UpdateWindow(hwnd); return 0; } /*** * DESCRIPTION: * Sets the font. * * PARAMETER(S): * [I] HWND : window handle * [I] HFONT : font handle * [I] WORD : redraw flag * * RETURN: * Zero */ static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK; TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw); infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont; LISTVIEW_SaveTextMetrics(hwnd); if (uView == LVS_REPORT) { /* set header font */ SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0)); } /* invalidate listview control client area */ InvalidateRect(hwnd, NULL, TRUE); if (fRedraw != FALSE) UpdateWindow(hwnd); return 0; } /*** * DESCRIPTION: * Message handling for WM_SETREDRAW. * For the Listview, it invalidates the entire window (the doc specifies otherwise) * * PARAMETER(S): * [I] HWND : window handle * [I] bRedraw: state of redraw flag * * RETURN: * DefWinProc return value */ static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw) { LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0); if(bRedraw) RedrawWindow(hwnd, NULL, 0, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW); return lResult; } /*** * DESCRIPTION: * Resizes the listview control. This function processes WM_SIZE * messages. At this time, the width and height are not used. * * PARAMETER(S): * [I] HWND : window handle * [I] WORD : new width * [I] WORD : new height * * RETURN: * Zero */ static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height) { LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); UINT uView = lStyle & LVS_TYPEMASK; TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height); LISTVIEW_UpdateSize(hwnd); if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) { if (lStyle & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(hwnd); else LISTVIEW_AlignTop(hwnd); } LISTVIEW_UpdateScroll(hwnd); /* invalidate client area + erase background */ InvalidateRect(hwnd, NULL, TRUE); return 0; } /*** * DESCRIPTION: * Sets the size information. * * PARAMETER(S): * [I] HWND : window handle * * RETURN: * Zero */ static VOID LISTVIEW_UpdateSize(HWND hwnd) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); UINT uView = lStyle & LVS_TYPEMASK; RECT rcList; TRACE("(hwnd=%x)\n", hwnd); GetClientRect(hwnd, &rcList); infoPtr->rcList.left = 0; infoPtr->rcList.right = max(rcList.right - rcList.left, 1); infoPtr->rcList.top = 0; infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1); if (uView == LVS_LIST) { if (lStyle & WS_HSCROLL) { INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL); if (infoPtr->rcList.bottom > nHScrollHeight) infoPtr->rcList.bottom -= nHScrollHeight; } } else if (uView == LVS_REPORT) { HDLAYOUT hl; WINDOWPOS wp; hl.prc = &rcList; hl.pwpos = ℘ Header_Layout(infoPtr->hwndHeader, &hl); SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags); if (!(LVS_NOCOLUMNHEADER & lStyle)) infoPtr->rcList.top = max(wp.cy, 0); } } /*** * DESCRIPTION: * Processes WM_STYLECHANGED messages. * * PARAMETER(S): * [I] HWND : window handle * [I] WPARAM : window style type (normal or extended) * [I] LPSTYLESTRUCT : window style information * * RETURN: * Zero */ static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType, LPSTYLESTRUCT lpss) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); UINT uNewView = lpss->styleNew & LVS_TYPEMASK; UINT uOldView = lpss->styleOld & LVS_TYPEMASK; RECT rcList = infoPtr->rcList; TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n", hwnd, wStyleType, lpss); if (wStyleType == GWL_STYLE) { if (uOldView == LVS_REPORT) ShowWindow(infoPtr->hwndHeader, SW_HIDE); if ((lpss->styleOld & WS_HSCROLL) != 0) ShowScrollBar(hwnd, SB_HORZ, FALSE); if ((lpss->styleOld & WS_VSCROLL) != 0) ShowScrollBar(hwnd, SB_VERT, FALSE); if (uNewView == LVS_ICON) { infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON); infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON); infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd); infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd); if (lpss->styleNew & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(hwnd); else LISTVIEW_AlignTop(hwnd); } else if (uNewView == LVS_REPORT) { HDLAYOUT hl; WINDOWPOS wp; hl.prc = &rcList; hl.pwpos = ℘ Header_Layout(infoPtr->hwndHeader, &hl); SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy, wp.flags); if (!(LVS_NOCOLUMNHEADER & lpss->styleNew)) ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL); infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON); infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON); infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd); infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd); } else if (uNewView == LVS_LIST) { infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON); infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON); infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd); infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd); } else { infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON); infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON); infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd); infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd); if (lpss->styleNew & LVS_ALIGNLEFT) LISTVIEW_AlignLeft(hwnd); else LISTVIEW_AlignTop(hwnd); } /* update the size of the client area */ LISTVIEW_UpdateSize(hwnd); /* add scrollbars if needed */ LISTVIEW_UpdateScroll(hwnd); /* invalidate client area + erase background */ InvalidateRect(hwnd, NULL, TRUE); /* print the list of unsupported window styles */ LISTVIEW_UnsupportedStyles(lpss->styleNew); } /* If they change the view and we have an active edit control we will need to kill the control since the redraw will misplace the edit control. */ if (infoPtr->hwndEdit && ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) != ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView))) { SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0); } return 0; } /*** * DESCRIPTION: * Window procedure of the listview control. * */ static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam); if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE)) return DefWindowProcW( hwnd, uMsg, wParam, lParam ); switch (uMsg) { case LVM_APPROXIMATEVIEWRECT: return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam, LOWORD(lParam), HIWORD(lParam)); case LVM_ARRANGE: return LISTVIEW_Arrange(hwnd, (INT)wParam); /* case LVM_CREATEDRAGIMAGE: */ case LVM_DELETEALLITEMS: return LISTVIEW_DeleteAllItems(hwnd); case LVM_DELETECOLUMN: return LISTVIEW_DeleteColumn(hwnd, (INT)wParam); case LVM_DELETEITEM: return LISTVIEW_DeleteItem(hwnd, (INT)wParam); case LVM_EDITLABELW: return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE); case LVM_EDITLABELA: return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE); case LVM_ENSUREVISIBLE: return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam); case LVM_FINDITEMW: return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam); case LVM_FINDITEMA: return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam); case LVM_GETBKCOLOR: return LISTVIEW_GetBkColor(hwnd); /* case LVM_GETBKIMAGE: */ case LVM_GETCALLBACKMASK: return LISTVIEW_GetCallbackMask(hwnd); case LVM_GETCOLUMNA: return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE); case LVM_GETCOLUMNW: return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE); case LVM_GETCOLUMNORDERARRAY: return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam); case LVM_GETCOLUMNWIDTH: return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam); case LVM_GETCOUNTPERPAGE: return LISTVIEW_GetCountPerPage(hwnd); case LVM_GETEDITCONTROL: return LISTVIEW_GetEditControl(hwnd); case LVM_GETEXTENDEDLISTVIEWSTYLE: return LISTVIEW_GetExtendedListViewStyle(hwnd); case LVM_GETHEADER: return LISTVIEW_GetHeader(hwnd); case LVM_GETHOTCURSOR: FIXME("LVM_GETHOTCURSOR: unimplemented\n"); return FALSE; case LVM_GETHOTITEM: return LISTVIEW_GetHotItem(hwnd); case LVM_GETHOVERTIME: return LISTVIEW_GetHoverTime(hwnd); case LVM_GETIMAGELIST: return LISTVIEW_GetImageList(hwnd, (INT)wParam); case LVM_GETISEARCHSTRINGA: case LVM_GETISEARCHSTRINGW: FIXME("LVM_GETISEARCHSTRING: unimplemented\n"); return FALSE; case LVM_GETITEMA: return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE); case LVM_GETITEMW: return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE); case LVM_GETITEMCOUNT: return LISTVIEW_GetItemCount(hwnd); case LVM_GETITEMPOSITION: return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam); case LVM_GETITEMRECT: return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam); case LVM_GETITEMSPACING: return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam); case LVM_GETITEMSTATE: return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam); case LVM_GETITEMTEXTA: return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE); case LVM_GETITEMTEXTW: return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE); case LVM_GETNEXTITEM: return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam)); case LVM_GETNUMBEROFWORKAREAS: FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n"); return 1; case LVM_GETORIGIN: return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam); case LVM_GETSELECTEDCOUNT: return LISTVIEW_GetSelectedCount(hwnd); case LVM_GETSELECTIONMARK: return LISTVIEW_GetSelectionMark(hwnd); case LVM_GETSTRINGWIDTHA: return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE); case LVM_GETSTRINGWIDTHW: return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE); case LVM_GETSUBITEMRECT: FIXME("LVM_GETSUBITEMRECT: unimplemented\n"); return FALSE; case LVM_GETTEXTBKCOLOR: return LISTVIEW_GetTextBkColor(hwnd); case LVM_GETTEXTCOLOR: return LISTVIEW_GetTextColor(hwnd); case LVM_GETTOOLTIPS: FIXME("LVM_GETTOOLTIPS: unimplemented\n"); return FALSE; case LVM_GETTOPINDEX: return LISTVIEW_GetTopIndex(hwnd); /*case LVM_GETUNICODEFORMAT: FIXME("LVM_GETUNICODEFORMAT: unimplemented\n"); return FALSE;*/ case LVM_GETVIEWRECT: return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam); case LVM_GETWORKAREAS: FIXME("LVM_GETWORKAREAS: unimplemented\n"); return FALSE; case LVM_HITTEST: return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam); case LVM_INSERTCOLUMNA: return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE); case LVM_INSERTCOLUMNW: return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE); case LVM_INSERTITEMA: return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE); case LVM_INSERTITEMW: return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE); case LVM_REDRAWITEMS: return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam); /* case LVM_SCROLL: */ /* return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */ case LVM_SETBKCOLOR: return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam); /* case LVM_SETBKIMAGE: */ case LVM_SETCALLBACKMASK: return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam); case LVM_SETCOLUMNA: return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE); case LVM_SETCOLUMNW: return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE); case LVM_SETCOLUMNORDERARRAY: return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam); case LVM_SETCOLUMNWIDTH: return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam)); case LVM_SETEXTENDEDLISTVIEWSTYLE: return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam); /* case LVM_SETHOTCURSOR: */ case LVM_SETHOTITEM: return LISTVIEW_SetHotItem(hwnd, (INT)wParam); case LVM_SETHOVERTIME: return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam); case LVM_SETICONSPACING: return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam); case LVM_SETIMAGELIST: return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam); case LVM_SETITEMA: return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE); case LVM_SETITEMW: return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE); case LVM_SETITEMCOUNT: return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam); case LVM_SETITEMPOSITION: return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam), (INT)HIWORD(lParam)); case LVM_SETITEMPOSITION32: return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x, ((POINT*)lParam)->y); case LVM_SETITEMSTATE: return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam); case LVM_SETITEMTEXTA: return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE); case LVM_SETITEMTEXTW: return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE); case LVM_SETSELECTIONMARK: return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam); case LVM_SETTEXTBKCOLOR: return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam); case LVM_SETTEXTCOLOR: return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam); /* case LVM_SETTOOLTIPS: */ /* case LVM_SETUNICODEFORMAT: */ /* case LVM_SETWORKAREAS: */ case LVM_SORTITEMS: return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam); /* case LVM_SUBITEMHITTEST: */ case LVM_UPDATE: return LISTVIEW_Update(hwnd, (INT)wParam); case WM_CHAR: return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam ); case WM_COMMAND: return LISTVIEW_Command(hwnd, wParam, lParam); case WM_CREATE: return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam); case WM_ERASEBKGND: return LISTVIEW_EraseBackground(hwnd, wParam, lParam); case WM_GETDLGCODE: return DLGC_WANTCHARS | DLGC_WANTARROWS; case WM_GETFONT: return LISTVIEW_GetFont(hwnd); case WM_HSCROLL: return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam), (INT)HIWORD(wParam), (HWND)lParam); case WM_KEYDOWN: return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam); case WM_KILLFOCUS: return LISTVIEW_KillFocus(hwnd); case WM_LBUTTONDBLCLK: return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam), HIWORD(lParam)); case WM_LBUTTONDOWN: return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam), HIWORD(lParam)); case WM_LBUTTONUP: return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam), HIWORD(lParam)); case WM_MOUSEMOVE: return LISTVIEW_MouseMove (hwnd, wParam, lParam); case WM_MOUSEHOVER: return LISTVIEW_MouseHover(hwnd, wParam, lParam); case WM_NCCREATE: return LISTVIEW_NCCreate(hwnd, wParam, lParam); case WM_NCDESTROY: return LISTVIEW_NCDestroy(hwnd); case WM_NOTIFY: return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam); case WM_NOTIFYFORMAT: return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam); case WM_PAINT: return LISTVIEW_Paint(hwnd, (HDC)wParam); case WM_RBUTTONDBLCLK: return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam), HIWORD(lParam)); case WM_RBUTTONDOWN: return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam), HIWORD(lParam)); case WM_RBUTTONUP: return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam), HIWORD(lParam)); case WM_SETFOCUS: return LISTVIEW_SetFocus(hwnd, (HWND)wParam); case WM_SETFONT: return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam); case WM_SETREDRAW: return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam); case WM_SIZE: return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam)); case WM_STYLECHANGED: return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam); /* case WM_TIMER: */ case WM_VSCROLL: return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam), (INT)HIWORD(wParam), (HWND)lParam); case WM_MOUSEWHEEL: if (wParam & (MK_SHIFT | MK_CONTROL)) return DefWindowProcW( hwnd, uMsg, wParam, lParam ); return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/* case WM_WINDOWPOSCHANGED: */ /* case WM_WININICHANGE: */ default: if (uMsg >= WM_USER) { ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam); } /* call default window procedure */ return DefWindowProcW(hwnd, uMsg, wParam, lParam); } return 0; } /*** * DESCRIPTION: * Registers the window class. * * PARAMETER(S): * None * * RETURN: * None */ VOID LISTVIEW_Register(void) { WNDCLASSW wndClass; ZeroMemory(&wndClass, sizeof(WNDCLASSW)); wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS; wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc; wndClass.cbClsExtra = 0; wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *); wndClass.hCursor = LoadCursorW(0, IDC_ARROWW); wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wndClass.lpszClassName = WC_LISTVIEWW; RegisterClassW(&wndClass); } /*** * DESCRIPTION: * Unregisters the window class. * * PARAMETER(S): * None * * RETURN: * None */ VOID LISTVIEW_Unregister(void) { UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL); } /*** * DESCRIPTION: * Handle any WM_COMMAND messages * * PARAMETER(S): * * RETURN: */ static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam) { switch (HIWORD(wParam)) { case EN_UPDATE: { /* * Adjust the edit window size */ WCHAR buffer[1024]; LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); HDC hdc = GetDC(infoPtr->hwndEdit); HFONT hFont, hOldFont = 0; RECT rect; SIZE sz; int len; len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0])); GetWindowRect(infoPtr->hwndEdit, &rect); /* Select font to get the right dimension of the string */ hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0); if(hFont != 0) { hOldFont = SelectObject(hdc, hFont); } if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz)) { TEXTMETRICW textMetric; /* Add Extra spacing for the next character */ GetTextMetricsW(hdc, &textMetric); sz.cx += (textMetric.tmMaxCharWidth * 2); SetWindowPos ( infoPtr->hwndEdit, HWND_TOP, 0, 0, sz.cx, rect.bottom - rect.top, SWP_DRAWFRAME|SWP_NOMOVE); } if(hFont != 0) SelectObject(hdc, hOldFont); ReleaseDC(hwnd, hdc); break; } default: return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam); } return 0; } /*** * DESCRIPTION: * Subclassed edit control windproc function * * PARAMETER(S): * * RETURN: */ static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0); EDITLABEL_ITEM *einfo = infoPtr->pedititem; static BOOL bIgnoreKillFocus = FALSE; BOOL cancel = FALSE; TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n", hwnd, uMsg, wParam, lParam, isW); switch (uMsg) { case WM_GETDLGCODE: return DLGC_WANTARROWS | DLGC_WANTALLKEYS; case WM_KILLFOCUS: if(bIgnoreKillFocus) return TRUE; break; case WM_DESTROY: { WNDPROC editProc = einfo->EditWndProc; SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc); COMCTL32_Free(einfo); infoPtr->pedititem = NULL; return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW); } case WM_KEYDOWN: if (VK_ESCAPE == (INT)wParam) { cancel = TRUE; break; } else if (VK_RETURN == (INT)wParam) break; default: return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW); } if (einfo->EditLblCb) { LPWSTR buffer = NULL; if (!cancel) { DWORD len = 1 + isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd); if (len) { if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) ) { if (isW) GetWindowTextW(hwnd, buffer, len); else GetWindowTextA(hwnd, (CHAR*)buffer, len); } } } /* Processing LVN_ENDLABELEDIT message could kill the focus */ /* eg. Using a messagebox */ bIgnoreKillFocus = TRUE; einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param); if (buffer) COMCTL32_Free(buffer); einfo->EditLblCb = NULL; bIgnoreKillFocus = FALSE; } SendMessageW(hwnd, WM_CLOSE, 0, 0); return TRUE; } /*** * DESCRIPTION: * Subclassed edit control windproc function * * PARAMETER(S): * * RETURN: */ LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE); } /*** * DESCRIPTION: * Subclassed edit control windproc function * * PARAMETER(S): * * RETURN: */ LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE); } /*** * DESCRIPTION: * Creates a subclassed edit cotrol * * PARAMETER(S): * * RETURN: */ HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y, INT width, INT height, HWND parent, HINSTANCE hinst, EditlblCallbackW EditLblCb, DWORD param, BOOL isW) { LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0); WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' }; HWND hedit; SIZE sz; HDC hdc; HDC hOldFont=0; TEXTMETRICW textMetric; TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW); if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM)))) return 0; style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER; hdc = GetDC(parent); /* Select the font to get appropriate metric dimensions */ if(infoPtr->hFont != 0) hOldFont = SelectObject(hdc, infoPtr->hFont); /*Get String Lenght in pixels */ GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz); /*Add Extra spacing for the next character */ GetTextMetricsW(hdc, &textMetric); sz.cx += (textMetric.tmMaxCharWidth * 2); if(infoPtr->hFont != 0) SelectObject(hdc, hOldFont); ReleaseDC(parent, hdc); if (isW) hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0); else hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0); if (!hedit) { COMCTL32_Free(infoPtr->pedititem); return 0; } infoPtr->pedititem->param = param; infoPtr->pedititem->EditLblCb = EditLblCb; infoPtr->pedititem->EditWndProc = (WNDPROC) (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) : SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) ); SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE); return hedit; }