WindowsXP-SP1/windows/oleacc/oleacc/listview.cpp
2020-09-30 16:53:49 +02:00

1551 lines
42 KiB
C++

// Copyright (c) 1996-1999 Microsoft Corporation
// --------------------------------------------------------------------------
//
// LISTVIEW.CPP
//
// Wrapper for COMCTL32's listview control
//
// --------------------------------------------------------------------------
#include "oleacc_p.h"
#include "default.h"
#include "client.h"
#include "listview.h"
#include "RemoteProxy6432.h"
#include "propmgr_util.h"
#define NOSTATUSBAR
#define NOUPDOWN
#define NOMENUHELP
#define NOTRACKBAR
#define NODRAGLIST
#define NOTOOLBAR
#define NOHOTKEY
#define NOPROGRESS
#define NOTREEVIEW
#define NOANIMATE
#include <commctrl.h>
#include "Win64Helper.h"
#ifndef LVM_GETSELECTEDCOLUMN
#define LVM_GETVIEW (LVM_FIRST + 143)
#define ListView_GetView(hwnd) \
SNDMSG((hwnd), LVM_GETVIEW, 0, 0)
#define LVM_GETSELECTEDCOLUMN (LVM_FIRST + 174)
#define ListView_GetSelectedColumn(hwnd) \
(UINT)SNDMSG((hwnd), LVM_GETSELECTEDCOLUMN, 0, 0)
#define LV_VIEW_ICON 0x0000
#define LV_VIEW_DETAILS 0x0001
#define LV_VIEW_SMALLICON 0x0002
#define LV_VIEW_LIST 0x0003
#define LV_VIEW_TILE 0x0004
#endif
#define MAX_NAME_TEXT 256
enum
{
LV_IMGIDX_Image,
LV_IMGIDX_State,
LV_IMGIDX_Overlay,
LV_IMGIDX_COUNT
};
BOOL LVGetImageIndex( HWND hwnd, int id, int aKeys[ LV_IMGIDX_COUNT ] );
HRESULT LVBuildDescriptionString( HWND hwnd, int iItem, int * pCols, int cCols, BSTR * pszDesc );
HRESULT LVGetDescription_ReportView( HWND hwnd, int iItem, BSTR * pszDesc );
HRESULT LVGetDescription_TileView( HWND hwnd, int iItem, BSTR * pszDesc );
extern "C" {
// in outline.cpp...
BOOL GetRoleFromStateImageMap( HWND hwnd, int iImage, DWORD * pdwRole );
BOOL GetStateFromStateImageMap( HWND hwnd, int iImage, DWORD * pdwState );
}
// --------------------------------------------------------------------------
//
// CreateListViewClient()
//
// --------------------------------------------------------------------------
HRESULT CreateListViewClient(HWND hwnd, long idChildCur, REFIID riid,
void** ppvList)
{
CListView32 * plist;
HRESULT hr;
InitPv(ppvList);
plist = new CListView32(hwnd, idChildCur);
if (!plist)
return(E_OUTOFMEMORY);
hr = plist->QueryInterface(riid, ppvList);
if (!SUCCEEDED(hr))
delete plist;
return(hr);
}
// --------------------------------------------------------------------------
//
// CListView32::CListView32()
//
// --------------------------------------------------------------------------
CListView32::CListView32(HWND hwnd, long idChildCur)
: CClient( CLASS_ListViewClient )
{
Initialize(hwnd, idChildCur);
m_fUseLabel = TRUE;
}
// --------------------------------------------------------------------------
//
// CListView32::SetupChildren()
//
// --------------------------------------------------------------------------
void CListView32::SetupChildren(void)
{
m_cChildren = SendMessageINT(m_hwnd, LVM_GETITEMCOUNT, 0, 0L);
}
// --------------------------------------------------------------------------
//
// CListView32::get_accName()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListView32::get_accName(VARIANT varChild, BSTR* pszName)
{
InitPv(pszName);
if (!ValidateChild(&varChild))
return(E_INVALIDARG);
if (!varChild.lVal)
{
if (InTheShell(m_hwnd, SHELL_DESKTOP))
return(HrCreateString(STR_DESKTOP_NAME, pszName));
else
return(CClient::get_accName(varChild, pszName));
}
TCHAR tchText[MAX_NAME_TEXT + 1] = {0};
LVITEM lvi;
memset(&lvi, 0, sizeof(LVITEM));
lvi.mask = LVIF_TEXT;
lvi.pszText = tchText;
lvi.cchTextMax = MAX_NAME_TEXT;
lvi.iItem = varChild.lVal - 1;
if (SUCCEEDED(XSend_ListView_GetItem(m_hwnd, LVM_GETITEM, 0, &lvi)))
{
if (*lvi.pszText)
*pszName = TCharSysAllocString(lvi.pszText);
}
return(*pszName ? S_OK : S_FALSE);
}
// --------------------------------------------------------------------------
//
// CListView32::get_accDescription()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListView32::get_accDescription(VARIANT varChild, BSTR* pszDesc)
{
InitPv(pszDesc);
if (!ValidateChild(&varChild))
return E_INVALIDARG;
if (!varChild.lVal)
return CClient::get_accDescription(varChild, pszDesc);
// Special cases for details (report) and tile views.
DWORD dwView = ListView_GetView( m_hwnd );
DWORD dwStyle = GetWindowLong( m_hwnd, GWL_STYLE );
// Have to check for report/details view in two ways:
// - check the style for LVS_REPORT (pre-V6)
// - check LVM_GETVIEW for LV_VIEW_DETAILS (V6+)
if( ( dwStyle & LVS_TYPEMASK ) == LVS_REPORT
|| dwView == LV_VIEW_DETAILS )
{
return LVGetDescription_ReportView( m_hwnd, varChild.lVal - 1, pszDesc );
}
if( dwView == LV_VIEW_TILE )
{
return LVGetDescription_TileView( m_hwnd, varChild.lVal - 1, pszDesc );
}
return E_NOT_APPLICABLE;
}
// --------------------------------------------------------------------------
//
// CListView32::get_accHelp()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListView32::get_accHelp(VARIANT varChild, BSTR* pszHelp)
{
if ( pszHelp == NULL )
return E_POINTER;
InitPv(pszHelp);
if (!ValidateChild(&varChild))
return E_INVALIDARG;
if (!varChild.lVal)
return(S_FALSE);
LVITEM_V6 lvi;
lvi.iItem = varChild.lVal -1;
lvi.iSubItem = 0;
lvi.mask = LVIF_GROUPID;
lvi.cColumns = 0;
lvi.puColumns = NULL;
HRESULT hr;
hr = XSend_ListView_V6_GetItem( m_hwnd, LVM_GETITEM, 0, &lvi );
if( hr != S_OK || lvi.iGroupId <= 0 )
{
DBPRINTF( TEXT("XSend_ListView_V6_GetItem hr = %x, lvi.iGroupId = %d\r\n"), hr, lvi.iGroupId );
return E_NOT_APPLICABLE;
}
LVGROUP_V6 grp;
memset(&grp, 0, sizeof(LVGROUP_V6));
TCHAR szHeader[MAX_NAME_TEXT + 1] = {0};
grp.cbSize = sizeof(LVGROUP_V6);
grp.mask = LVGF_HEADER;
grp.pszHeader = szHeader;
grp.cchHeader = MAX_NAME_TEXT;
grp.iGroupId = lvi.iGroupId;
hr = XSend_ListView_V6_GetGroupInfo( m_hwnd, LVM_GETGROUPINFO, lvi.iGroupId, &grp );
if( FAILED( hr ) )
return hr;
*pszHelp = TCharSysAllocString( grp.pszHeader );
return S_OK;
}
// --------------------------------------------------------------------------
//
// CListView32::get_accRole()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListView32::get_accRole(VARIANT varChild, VARIANT* pvarRole)
{
InitPvar(pvarRole);
if (!ValidateChild(&varChild))
return(E_INVALIDARG);
pvarRole->vt = VT_I4;
if (varChild.lVal)
{
DWORD dwRole;
BOOL fGotRole = FALSE;
int aKeys[ LV_IMGIDX_COUNT ];
if( LVGetImageIndex( m_hwnd, varChild.lVal - 1, aKeys ) )
{
if( CheckDWORDMap( m_hwnd, OBJID_CLIENT, CHILDID_SELF,
PROPINDEX_ROLEMAP,
aKeys, ARRAYSIZE( aKeys ),
& dwRole ) )
{
pvarRole->lVal = dwRole;
fGotRole = TRUE;
}
else if( GetRoleFromStateImageMap( m_hwnd, aKeys[ LV_IMGIDX_Image ], & dwRole ) )
{
pvarRole->lVal = dwRole;
fGotRole = TRUE;
}
}
if( ! fGotRole )
{
//
// Note that just because the listview has LVS_EX_CHECKBOXES
// doesn't mean that every item is itself a checkbox. We
// need to sniff at the item, too, to see if it has a state
// image.
//
DWORD dwExStyle = SendMessageINT(m_hwnd, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
if ((dwExStyle & LVS_EX_CHECKBOXES) &&
ListView_GetItemState(m_hwnd, varChild.lVal-1, LVIS_STATEIMAGEMASK))
{
pvarRole->lVal = ROLE_SYSTEM_CHECKBUTTON;
}
else
{
pvarRole->lVal = ROLE_SYSTEM_LISTITEM;
}
}
}
else
pvarRole->lVal = ROLE_SYSTEM_LIST;
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CListView32::get_accState()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListView32::get_accState(VARIANT varChild, VARIANT* pvarState)
{
long lState;
DWORD dwStyle;
DWORD dwExStyle;
InitPvar(pvarState);
if (!ValidateChild(&varChild))
return(E_INVALIDARG);
if (!varChild.lVal)
return(CClient::get_accState(varChild, pvarState));
lState = SendMessageINT(m_hwnd, LVM_GETITEMSTATE, varChild.lVal-1, 0xFFFFFFFF);
pvarState->vt = VT_I4;
pvarState->lVal = 0;
if (MyGetFocus() == m_hwnd)
{
pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
if (lState & LVIS_FOCUSED)
pvarState->lVal |= STATE_SYSTEM_FOCUSED;
}
pvarState->lVal |= STATE_SYSTEM_SELECTABLE;
dwStyle = GetWindowLong (m_hwnd,GWL_STYLE);
if (!(dwStyle & LVS_SINGLESEL))
pvarState->lVal |= STATE_SYSTEM_MULTISELECTABLE;
if (lState & LVIS_SELECTED)
pvarState->lVal |= STATE_SYSTEM_SELECTED;
if (lState & LVIS_DROPHILITED)
pvarState->lVal |= STATE_SYSTEM_HOTTRACKED;
// If this is a checkbox listview, then look at the checkbox state.
// State 0 = no checkbox, State 1 = unchecked, State 2 = checked
dwExStyle = SendMessageINT(m_hwnd, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
if ((dwExStyle & LVS_EX_CHECKBOXES) &&
(lState & LVIS_STATEIMAGEMASK) == INDEXTOSTATEIMAGEMASK(2))
pvarState->lVal |= STATE_SYSTEM_CHECKED;
if( IsClippedByWindow( this, varChild, m_hwnd ) )
{
pvarState->lVal |= STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_OFFSCREEN;
}
int aKeys[ LV_IMGIDX_COUNT ];
if( LVGetImageIndex( m_hwnd, varChild.lVal - 1, aKeys ) )
{
DWORD dwState;
if( CheckDWORDMap( m_hwnd, OBJID_CLIENT, CHILDID_SELF,
PROPINDEX_STATEMAP,
aKeys, ARRAYSIZE( aKeys ),
& dwState ) )
{
pvarState->lVal |= dwState;
}
else if( GetStateFromStateImageMap( m_hwnd, aKeys[ LV_IMGIDX_Image ], & dwState ) )
{
pvarState->lVal |= dwState;
}
}
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CListView32::get_accFocus()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListView32::get_accFocus(VARIANT* pvarFocus)
{
long lFocus;
HRESULT hr;
//
// Do we have the focus?
//
hr = CClient::get_accFocus(pvarFocus);
if (!SUCCEEDED(hr) || (pvarFocus->vt != VT_I4) || (pvarFocus->lVal != 0))
return(hr);
//
// We do. What item is focused?
//
lFocus = SendMessageINT(m_hwnd, LVM_GETNEXTITEM, 0xFFFFFFFF, LVNI_FOCUSED);
if (lFocus != -1)
pvarFocus->lVal = lFocus+1;
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CListView32::get_accDefaultAction()
//
// Since the default action for a listview item is really determined by the
// creator of the listview control, the best we can do is double click on
// the thing, and return "double click" as the default action string.
//
// --------------------------------------------------------------------------
STDMETHODIMP CListView32::get_accDefaultAction(VARIANT varChild, BSTR* pszDefAction)
{
InitPv(pszDefAction);
//
// Validate.
//
if (!ValidateChild(&varChild))
return(E_INVALIDARG);
DWORD dwExStyle = ListView_GetExtendedListViewStyle( m_hwnd );
if (varChild.lVal)
{
if ( dwExStyle & LVS_EX_ONECLICKACTIVATE )
return HrCreateString(STR_CLICK, pszDefAction);
else
return HrCreateString(STR_DOUBLE_CLICK, pszDefAction);
}
return(E_NOT_APPLICABLE);
}
// --------------------------------------------------------------------------
//
// CListView32::accDoDefaultAction()
//
// As noted above, we really don't know what the default action for a list
// view item is, so unless the parent overrides us, we'll just do a double
// click on the thing.
//
// --------------------------------------------------------------------------
STDMETHODIMP CListView32::accDoDefaultAction(VARIANT varChild)
{
LPRECT lprcLoc;
RECT rcLocal;
HANDLE hProcess;
//
// Validate
//
if (!ValidateChild(&varChild))
return(E_INVALIDARG);
if (varChild.lVal)
{
// Can't just use accLocation, since that gives back the rectangle
// for the whole line in details view, but you can only click on
// a certain part - icon and text. So we'll just ask the control
// for that rectangle.
lprcLoc = (LPRECT)SharedAlloc(sizeof(RECT),m_hwnd,&hProcess);
if (!lprcLoc)
return(E_OUTOFMEMORY);
//lprcLoc->left = LVIR_ICON;
rcLocal.left = LVIR_ICON;
SharedWrite (&rcLocal,lprcLoc,sizeof(RECT),hProcess);
if (SendMessage(m_hwnd, LVM_GETITEMRECT, varChild.lVal-1, (LPARAM)lprcLoc))
{
SharedRead (lprcLoc,&rcLocal,sizeof(RECT),hProcess);
MapWindowPoints(m_hwnd, NULL, (LPPOINT)&rcLocal, 2);
// convert to width and height
rcLocal.right = rcLocal.right - rcLocal.left;
rcLocal.bottom = rcLocal.bottom - rcLocal.top;
BOOL fDoubleClick = TRUE;
DWORD dwExStyle = ListView_GetExtendedListViewStyle( m_hwnd );
if ( dwExStyle & LVS_EX_ONECLICKACTIVATE )
fDoubleClick = FALSE;
// this will check if WindowFromPoint at the click point is the same
// as m_hwnd, and if not, it won't click. Cool!
if ( ClickOnTheRect( &rcLocal, m_hwnd, fDoubleClick ) )
{
SharedFree(lprcLoc,hProcess);
return (S_OK);
}
}
SharedFree(lprcLoc,hProcess);
}
return(E_NOT_APPLICABLE);
}
// --------------------------------------------------------------------------
//
// CListView32::get_accSelection()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListView32::get_accSelection(VARIANT* pvarSelection)
{
return(GetListViewSelection(m_hwnd, pvarSelection));
}
// --------------------------------------------------------------------------
//
// CListView32::accSelect()
//
// Selection Flags can be OR'ed together, with certain limitations. So we
// need to check each flag and do appropriate action.
//
// Selection flags:
// SELFLAG_TAKEFOCUS
// SELFLAG_TAKESELECTION
// SELFLAG_EXTENDSELECTION
// SELFLAG_ADDSELECTION
// SELFLAG_REMOVESELECTION
// --------------------------------------------------------------------------
STDMETHODIMP CListView32::accSelect(long selFlags, VARIANT varChild)
{
long lState;
long lStateMask;
long lFocusedItem;
if (!ValidateChild(&varChild) || !ValidateSelFlags(selFlags))
return(E_INVALIDARG);
if (!varChild.lVal)
return(CClient::accSelect(selFlags, varChild));
if (selFlags & SELFLAG_TAKEFOCUS)
{
MySetFocus(m_hwnd);
}
// get the thing with focus (anchor point)
// if no focus, use first one
// have to get it here because we might clear it b4 we need it.
lFocusedItem = ListView_GetNextItem(m_hwnd, -1,LVNI_FOCUSED);
if (lFocusedItem == -1)
lFocusedItem = 0;
varChild.lVal--;
// First check if there can be more than one item selected.
if ((selFlags & SELFLAG_ADDSELECTION) ||
(selFlags & SELFLAG_REMOVESELECTION) ||
(selFlags & SELFLAG_EXTENDSELECTION))
{
// LVM_GETITEMSTATE doesn't compare 0xFFFFFFFF so don't worry about sign extension
if (SendMessage(m_hwnd, LVM_GETITEMSTATE, varChild.lVal, 0xFFFFFFFF) & LVS_SINGLESEL)
return (E_NOT_APPLICABLE);
}
// If the take focus flag is set, check if it can get focus &
// remove focus from other items
if (selFlags & SELFLAG_TAKEFOCUS)
{
if (MyGetFocus() != m_hwnd)
{
return(S_FALSE);
}
RemoveCurrentSelFocus(SELFLAG_TAKEFOCUS);
}
// If the take selection flag is set, remove selection from other items
if (selFlags & SELFLAG_TAKESELECTION)
RemoveCurrentSelFocus(SELFLAG_TAKESELECTION);
lState = 0;
lStateMask = 0;
LVITEM lvi;
memset(&lvi, 0, sizeof(LVITEM));
lvi.mask = LVM_SETITEMSTATE;
// now is where the real work starts. If they are just taking
// selection, adding a selection, or removing a selection, it is
// pretty easy. But if they are extending the selection, we'll have
// to loop through from where the focus is to this one and select or
// deselect each one.
if ((selFlags & SELFLAG_EXTENDSELECTION) == 0) // not extending (easy)
{
if (selFlags & SELFLAG_ADDSELECTION ||
selFlags & SELFLAG_TAKESELECTION)
{
lState |= LVIS_SELECTED;
lStateMask |= LVIS_SELECTED;
}
if (selFlags & SELFLAG_REMOVESELECTION)
lStateMask |= LVIS_SELECTED;
if (selFlags & SELFLAG_TAKEFOCUS)
{
lState |= LVIS_FOCUSED;
lStateMask |= LVIS_FOCUSED;
}
lvi.state = lState;
lvi.stateMask = lStateMask;
// TODO (micw) Dumpty doesn't test this function
XSend_ListView_SetItem(m_hwnd, LVM_SETITEMSTATE, varChild.lVal, &lvi);
}
else // we are extending the selection (hard work)
{
long i;
long nIncrement;
// we are always selecting or deselecting, so statemask
// always has LVIS_SELECTED.
lStateMask = LVIS_SELECTED;
// if neither ADDSELECTION or REMOVESELECTION is set, then we are
// supposed to do something based on the selection state of whatever
// has the focus.
if (selFlags & SELFLAG_ADDSELECTION)
lState |= LVIS_SELECTED;
if (((selFlags & SELFLAG_REMOVESELECTION) == 0) &&
((selFlags & SELFLAG_ADDSELECTION) == 0))
{
// if focused item is selected, lState to have selected also
if (SendMessage(m_hwnd, LVM_GETITEMSTATE, lFocusedItem, 0xFFFFFFFF)
& LVIS_SELECTED)
lState |= LVIS_SELECTED;
}
lvi.state = lState;
lvi.stateMask = lStateMask;
// Now walk through from focused to current, setting the state.
// Set increment and last one depending on direction
if (lFocusedItem > varChild.lVal)
{
nIncrement = -1;
varChild.lVal--;
}
else
{
nIncrement = 1;
varChild.lVal++;
}
for (i=lFocusedItem; i!=varChild.lVal; i+=nIncrement)
XSend_ListView_SetItem(m_hwnd, LVM_SETITEMSTATE, i, &lvi);
// focus the last one if needed
if (selFlags & SELFLAG_TAKEFOCUS)
{
lStateMask |= LVIS_FOCUSED;
lState |= LVIS_FOCUSED;
lvi.state = lState;
lvi.stateMask = lStateMask;
XSend_ListView_SetItem(m_hwnd, LVM_SETITEMSTATE, i-nIncrement, &lvi);
}
}
return (S_OK);
}
// --------------------------------------------------------------------------
//
// CListView32::accLocation()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListView32::accLocation(long* pxLeft, long* pyTop, long* pcxWidth,
long* pcyHeight, VARIANT varChild)
{
LPRECT lprc;
RECT rcLocal;
HANDLE hProcess;
InitAccLocation(pxLeft, pyTop, pcxWidth, pcyHeight);
if (!ValidateChild(&varChild))
return(E_INVALIDARG);
if (!varChild.lVal)
return(CClient::accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild));
// Get the listview item rect.
lprc = (LPRECT)SharedAlloc(sizeof(RECT),m_hwnd,&hProcess);
if (!lprc)
return(E_OUTOFMEMORY);
rcLocal.left = LVIR_BOUNDS;
SharedWrite (&rcLocal,lprc,sizeof(RECT),hProcess);
if (SendMessage(m_hwnd, LVM_GETITEMRECT, varChild.lVal-1, (LPARAM)lprc))
{
SharedRead (lprc,&rcLocal,sizeof(RECT),hProcess);
MapWindowPoints(m_hwnd, NULL, (LPPOINT)&rcLocal, 2);
*pxLeft = rcLocal.left;
*pyTop = rcLocal.top;
*pcxWidth = rcLocal.right - rcLocal.left;
*pcyHeight = rcLocal.bottom - rcLocal.top;
}
SharedFree(lprc,hProcess);
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CListView32::accNavigate()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListView32::accNavigate(long dwNavDir, VARIANT varStart,
VARIANT* pvarEnd)
{
long lEnd = 0;
int lvFlags;
InitPvar(pvarEnd);
if (!ValidateChild(&varStart) ||
!ValidateNavDir(dwNavDir, varStart.lVal))
return(E_INVALIDARG);
if (dwNavDir == NAVDIR_FIRSTCHILD)
dwNavDir = NAVDIR_NEXT;
else if (dwNavDir == NAVDIR_LASTCHILD)
{
varStart.lVal = m_cChildren + 1;
dwNavDir = NAVDIR_PREVIOUS;
}
else if (!varStart.lVal)
return(CClient::accNavigate(dwNavDir, varStart, pvarEnd));
DWORD dwStyle = GetWindowLong(m_hwnd, GWL_STYLE);
//
// Gotta love those listview dudes! They have all the messages we need
// to do hittesting, location, and navigation easily. And those are
// by far the hardest things to manually implement.
//
switch (dwNavDir)
{
case NAVDIR_NEXT:
lEnd = varStart.lVal + 1;
if (lEnd > m_cChildren)
lEnd = 0;
break;
case NAVDIR_PREVIOUS:
lEnd = varStart.lVal - 1;
break;
case NAVDIR_LEFT:
if( ( dwStyle & LVS_TYPEMASK ) == LVS_REPORT
|| ListView_GetView( m_hwnd ) == LV_VIEW_DETAILS )
{
break; // in report view there is nothing to the left
}
lvFlags = LVNI_TOLEFT;
goto Navigate;
case NAVDIR_RIGHT:
if( ( dwStyle & LVS_TYPEMASK ) == LVS_REPORT
|| ListView_GetView( m_hwnd ) == LV_VIEW_DETAILS )
{
break; // in report view there is nothing to the right
}
lvFlags = LVNI_TORIGHT;
goto Navigate;
case NAVDIR_UP:
lvFlags = LVNI_ABOVE;
goto Navigate;
case NAVDIR_DOWN:
lvFlags = LVNI_BELOW;
Navigate:
// Note that if nothing is there, COMCTL32 will return -1, and -1+1 is
// zero, meaning nothing in our land also.
lEnd = SendMessageINT(m_hwnd, LVM_GETNEXTITEM, varStart.lVal-1, lvFlags);
++lEnd;
break;
}
if (lEnd)
{
pvarEnd->vt = VT_I4;
pvarEnd->lVal = lEnd;
return(S_OK);
}
else
return(S_FALSE);
}
// --------------------------------------------------------------------------
//
// CListView32::accHitTest()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListView32::accHitTest(long x, long y, VARIANT* pvarHit)
{
HRESULT hr;
HANDLE hProcess;
int nSomeInt;
POINT ptLocal;
LPLVHITTESTINFO lpht;
SetupChildren();
//
// Is the point in the listview at all?
//
hr = CClient::accHitTest(x, y, pvarHit);
// #11150, CWO, 1/27/97, Replaced !SUCCEEDED with !S_OK
if ((hr != S_OK) || (pvarHit->vt != VT_I4) || (pvarHit->lVal != 0))
return(hr);
//
// Now find out what item this point is on.
//
lpht = (LPLVHITTESTINFO)SharedAlloc(sizeof(LVHITTESTINFO),m_hwnd,&hProcess);
if (!lpht)
return(E_OUTOFMEMORY);
//lpht->iItem = -1;
nSomeInt = -1;
SharedWrite (&nSomeInt,&lpht->iItem,sizeof(int),hProcess);
ptLocal.x = x;
ptLocal.y = y;
ScreenToClient(m_hwnd, &ptLocal);
SharedWrite (&ptLocal,&lpht->pt,sizeof(POINT),hProcess);
//
// LVM_SUBHITTEST will return -1 if the point isn't over an item. And -1
// + 1 is zero, which is self. So that works great for us.
//
SendMessage(m_hwnd, LVM_SUBITEMHITTEST, 0, (LPARAM)lpht);
SharedRead (&lpht->iItem,&pvarHit->lVal,sizeof(int),hProcess);
pvarHit->lVal++;
SharedFree(lpht,hProcess);
return(S_OK);
}
// --------------------------------------------------------------------------
//
// RemoveCurrentSelFocus()
//
// This removes all selected/focused items.
//
// -------------------------------------------------------------------------
void CListView32::RemoveCurrentSelFocus(long lState)
{
// Set up LVITEM struct
LVITEM lvi;
memset(&lvi, 0, sizeof(LVITEM));
lvi.stateMask = lState;
lvi.state = 0;
//
// Loop through all focused/selected items.
//
long lNext = ListView_GetNextItem(m_hwnd, -1,
((lState == LVIS_FOCUSED) ? LVNI_FOCUSED : LVNI_SELECTED));
while (lNext != -1)
{
// TODO (micw) Dumpty doesn't call this function
if (FAILED(XSend_ListView_SetItem(m_hwnd, LVM_SETITEMSTATE, lNext, &lvi)))
return;
lNext = ListView_GetNextItem(m_hwnd, lNext,
((lState == LVIS_FOCUSED) ? LVNI_FOCUSED : LVNI_SELECTED));
}
}
/////////////////////////////////////////////////////////////////////////////
//
// MULTIPLE SELECTION LISTVIEW SUPPORT
//
// If a listview has more than one item selected, we create an object that
// is a clone. It supports merely IUnknown and IEnumVARIANT, and is a
// collection. The caller should take the returned item IDs and pass them
// in a VARIANT (VT_I4, ID as lVal) to the parent object.
//
/////////////////////////////////////////////////////////////////////////////
// --------------------------------------------------------------------------
//
// GetListViewSelection()
//
// --------------------------------------------------------------------------
HRESULT GetListViewSelection(HWND hwnd, VARIANT* pvarSelection)
{
int cSelected;
LPINT lpSelected;
long lRet;
int iSelected;
CListViewSelection * plvs;
InitPvar(pvarSelection);
cSelected = SendMessageINT(hwnd, LVM_GETSELECTEDCOUNT, 0, 0L);
//
// No selection.
//
if (!cSelected)
return(S_FALSE);
//
// Single item.
//
if (cSelected == 1)
{
pvarSelection->vt = VT_I4;
pvarSelection->lVal = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED) + 1;
return(S_OK);
}
//
// Multiple items, must make a collection object.
//
// Allocate the list.
lpSelected = (LPINT)LocalAlloc(LPTR, cSelected*sizeof(INT));
if (!lpSelected)
return(E_OUTOFMEMORY);
plvs = NULL;
// Get the list of selected items.
lRet = -1;
for (iSelected = 0; iSelected < cSelected; iSelected++)
{
lRet = ListView_GetNextItem(hwnd, lRet, LVNI_SELECTED);
if (lRet == -1)
break;
lpSelected[iSelected] = lRet;
}
//
// Did something go wrong in the middle?
//
cSelected = iSelected;
if (cSelected)
{
plvs = new CListViewSelection(0, cSelected, lpSelected);
if (plvs)
{
pvarSelection->vt = VT_UNKNOWN;
plvs->QueryInterface(IID_IUnknown, (void**)&(pvarSelection->punkVal));
}
}
//
// Free the list memory no matter what, the constructor will make a copy.
//
if (lpSelected)
LocalFree((HANDLE)lpSelected);
if (!plvs)
return(E_OUTOFMEMORY);
else
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CListViewSelection::CListViewSelection()
//
// --------------------------------------------------------------------------
CListViewSelection::CListViewSelection(int iChildCur, int cTotal, LPINT lpItems)
{
m_idChildCur = iChildCur;
m_lpSelected = (LPINT)LocalAlloc(LPTR, cTotal*sizeof(int));
if (!m_lpSelected)
m_cSelected = 0;
else
{
m_cSelected = cTotal;
CopyMemory(m_lpSelected, lpItems, cTotal*sizeof(int));
}
}
// --------------------------------------------------------------------------
//
// CListViewSelection::~CListViewSelection()
//
// --------------------------------------------------------------------------
CListViewSelection::~CListViewSelection()
{
//
// Free selection list
//
if (m_lpSelected)
{
LocalFree((HANDLE)m_lpSelected);
m_cSelected = 0;
m_lpSelected = NULL;
}
}
// --------------------------------------------------------------------------
//
// CListViewSelection::QueryInterface()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListViewSelection::QueryInterface(REFIID riid, void** ppunk)
{
InitPv(ppunk);
if ((riid == IID_IUnknown) ||
(riid == IID_IEnumVARIANT))
{
*ppunk = this;
}
else
return(E_NOINTERFACE);
((LPUNKNOWN) *ppunk)->AddRef();
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CListViewSelection::AddRef()
//
// --------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CListViewSelection::AddRef(void)
{
return(++m_cRef);
}
// --------------------------------------------------------------------------
//
// CListViewSelection::Release()
//
// --------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CListViewSelection::Release(void)
{
if ((--m_cRef) == 0)
{
delete this;
return 0;
}
return(m_cRef);
}
// --------------------------------------------------------------------------
//
// CListViewSelection::Next()
//
// This returns a VT_I4 which is the child ID for the parent ListView that
// returned this object for the selection collection. The caller turns
// around and passes this variant to the ListView object to get acc info
// about it.
//
// --------------------------------------------------------------------------
STDMETHODIMP CListViewSelection::Next(ULONG celt, VARIANT* rgvar, ULONG *pceltFetched)
{
VARIANT* pvar;
long cFetched;
long iCur;
// Can be NULL
if (pceltFetched)
*pceltFetched = 0;
//
// Initialize VARIANTs
// This is so bogus
//
pvar = rgvar;
for (iCur = 0; iCur < (long)celt; iCur++, pvar++)
VariantInit(pvar);
pvar = rgvar;
cFetched = 0;
iCur = m_idChildCur;
//
// Loop through our items
//
while ((cFetched < (long)celt) && (iCur < m_cSelected))
{
pvar->vt = VT_I4;
pvar->lVal = m_lpSelected[iCur] + 1;
++cFetched;
++iCur;
++pvar;
}
//
// Advance the current position
//
m_idChildCur = iCur;
//
// Fill in the number fetched
//
if (pceltFetched)
*pceltFetched = cFetched;
//
// Return S_FALSE if we grabbed fewer items than requested
//
return((cFetched < (long)celt) ? S_FALSE : S_OK);
}
// --------------------------------------------------------------------------
//
// CListViewSelection::Skip()
//
// -------------------------------------------------------------------------
STDMETHODIMP CListViewSelection::Skip(ULONG celt)
{
m_idChildCur += celt;
if (m_idChildCur > m_cSelected)
m_idChildCur = m_cSelected;
//
// We return S_FALSE if at the end.
//
return((m_idChildCur >= m_cSelected) ? S_FALSE : S_OK);
}
// --------------------------------------------------------------------------
//
// CListViewSelection::Reset()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListViewSelection::Reset(void)
{
m_idChildCur = 0;
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CListViewSelection::Clone()
//
// --------------------------------------------------------------------------
STDMETHODIMP CListViewSelection::Clone(IEnumVARIANT **ppenum)
{
CListViewSelection * plistselnew;
InitPv(ppenum);
plistselnew = new CListViewSelection(m_idChildCur, m_cSelected, m_lpSelected);
if (!plistselnew)
return(E_OUTOFMEMORY);
return(plistselnew->QueryInterface(IID_IEnumVARIANT, (void**)ppenum));
}
BOOL LVGetImageIndex( HWND hwnd, int id, int aKeys[ LV_IMGIDX_COUNT ] )
{
LVITEM lvi;
memset(&lvi, 0, sizeof(LVITEM));
lvi.mask = LVIF_IMAGE | LVIF_STATE;
lvi.iItem = id;
// TODO (micw) Dumpty doesn't call this function
if (SUCCEEDED(XSend_ListView_GetItem(hwnd, LVM_GETITEM, 0, &lvi)))
{
aKeys[ LV_IMGIDX_Image ] = lvi.iImage;
aKeys[ LV_IMGIDX_Overlay ] = ( lvi.state >> 8 ) & 0xF;
aKeys[ LV_IMGIDX_State ] = ( lvi.state >> 12 ) & 0xF;
return TRUE;
}
else
{
return FALSE;
}
}
#define COLONSEP TEXT(": ")
HRESULT LVBuildDescriptionString( HWND hwnd, int iItem, int * pCols, int cCols, BSTR * pszDesc )
{
// Declare ListView Structure plus a string to hold description.
TCHAR tchText[81];
LVITEM lvi;
memset(&lvi, 0, sizeof(LVITEM));
lvi.mask = LVIF_TEXT;
lvi.pszText = tchText;
lvi.cchTextMax = ARRAYSIZE( tchText ) - 1; // -1 for NUL
lvi.iItem = iItem;
TCHAR tchColText[81];
LVCOLUMN lvc;
lvc.mask = LVCF_TEXT;
lvc.pszText = tchColText;
lvc.cchTextMax = ARRAYSIZE( tchColText ) - 1; // -1 for NUL
// Space for the locale-specific separator. (Usually ", " for English)
TCHAR szSep[ 16 ];
// Now allocate a local string to hold everything. Its length will be:
// number of cols * ( coltext + ": " + text + separator )
//
// sizeof(COLONSEP) incluses the terminating NUL in that string; but it's ok
// to overestimate. Because we use sizeof, we don't need to multiply by sizeof(TCHAR).
int len = cCols * ( sizeof( tchColText ) + sizeof( COLONSEP )
+ sizeof( tchText ) + sizeof( szSep ) );
LPTSTR lpszLocal = (LPTSTR)LocalAlloc ( LPTR, len );
if (!lpszLocal)
{
return E_OUTOFMEMORY;
}
// This points to the 'current write position' as we build up the string
LPTSTR lpszTempLocal = lpszLocal;
// Get the list separator string. The -1 allows us to append
// a space char if we need it.
int nSepLen = GetLocaleInfo( GetThreadLocale(), LOCALE_SLIST, szSep, ARRAYSIZE( szSep ) - 1 );
if( ! nSepLen || szSep[ 0 ] == '\0' )
{
// Default to using ", "...
lstrcpy( szSep, TEXT(", ") );
nSepLen = 2;
}
else
{
// GetLocalInfo return value includes terminating NUL... don't want
// to include that in our length.
nSepLen = lstrlen( szSep );
// Add extra space at end, if necessary.
if( szSep[ nSepLen - 1 ] != ' ' )
{
lstrcat( szSep, TEXT(" ") );
nSepLen++;
}
}
//
// Traverse the description order array sequentially to get each item
//
// Flag used to remember not to add separator when adding first item
BOOL fFirstItem = TRUE;
for ( int iOrder = 0; iOrder < cCols; iOrder++ )
{
INT iCol = pCols[iOrder];
// Skip subitem 0, that is the 'name'.
// Also skip negative numbers, just in case.
if ( iCol <= 0 )
continue;
// Try and get the column value text...
lvi.iSubItem = iCol;
*lvi.pszText = '\0';
if( FAILED(XSend_ListView_GetItem( hwnd, LVM_GETITEM, 0, &lvi ) ) )
continue;
// Skip empty strings...
if( *lvi.pszText == '\0' )
continue;
// Add separator if necessary...
if( ! fFirstItem )
{
lstrcpy(lpszTempLocal, szSep);
lpszTempLocal += nSepLen;
}
else
{
fFirstItem = FALSE;
}
// Try to get column header string...
lvc.iSubItem = iCol;
*lvc.pszText = '\0';
if( SUCCEEDED(XSend_ListView_GetColumn( hwnd, LVM_GETCOLUMN, iCol, &lvc ) )
&& *lvc.pszText != '\0' )
{
lstrcpy(lpszTempLocal, lvc.pszText);
lpszTempLocal += lstrlen(lpszTempLocal);
lstrcpy(lpszTempLocal, TEXT(": "));
lpszTempLocal += 2;
}
// Now add the column value to string...
lstrcpy(lpszTempLocal, lvi.pszText);
lpszTempLocal += lstrlen(lpszTempLocal);
}
// Convert to BSTR...
if (lpszTempLocal != lpszLocal)
{
*pszDesc = TCharSysAllocString(lpszLocal);
}
LocalFree (lpszLocal);
return *pszDesc ? S_OK : S_FALSE;
}
HRESULT LVGetDescription_ReportView( HWND hwnd, int iItem, BSTR * pszDesc )
{
//
// Is there a header control?
//
HWND hwndHeader = ListView_GetHeader(hwnd);
if (!hwndHeader)
return E_NOT_APPLICABLE ;
//
// Is there more than one column?
//
int cColumns = SendMessageINT(hwndHeader, HDM_GETITEMCOUNT, 0, 0L);
if (cColumns < 2)
return E_NOT_APPLICABLE;
//
// Get the order to traverse these columns in.
//
HANDLE hProcess;
LPINT lpColumnOrderShared = (LPINT)SharedAlloc( 2 * cColumns * sizeof(INT),
hwnd, & hProcess );
if (!lpColumnOrderShared)
return E_OUTOFMEMORY;
// Now allocate a local array twice as big, so we can do our sorting
// in the second half.
LPINT lpColumnOrder = (LPINT)LocalAlloc (LPTR,2 * cColumns * sizeof(INT));
if (!lpColumnOrder)
{
SharedFree (lpColumnOrderShared,hProcess);
return E_OUTOFMEMORY;
}
LPINT lpDescOrder = lpColumnOrder + cColumns;
if (!SendMessage(hwnd, LVM_GETCOLUMNORDERARRAY, cColumns, (LPARAM)lpColumnOrderShared))
{
SharedFree(lpColumnOrderShared,hProcess);
LocalFree (lpColumnOrder);
return(E_OUTOFMEMORY);
}
SharedRead (lpColumnOrderShared,lpColumnOrder,cColumns*sizeof(INT),hProcess);
//
// lpColumnOrder is currently an array where index == iSubItem, value == order.
// Change this into an array where index == order, value == iSubItem.
// That way we can sit in a loop using the value as the iSubItem,
// knowing we are composing the pieces of the description in the proper
// order.
//
for (int iOrder = 0; iOrder < cColumns; iOrder++)
{
lpDescOrder[lpColumnOrder[iOrder]] = iOrder;
}
HRESULT hr = LVBuildDescriptionString( hwnd, iItem, lpDescOrder, cColumns, pszDesc );
SharedFree(lpColumnOrderShared,hProcess);
LocalFree (lpColumnOrder);
return hr;
}
HRESULT LVGetDescription_TileView( HWND hwnd, int iItem, BSTR * pszDesc )
{
// Get the 'sorted' column...
int iColSorted = ListView_GetSelectedColumn( hwnd );
// Normalize to 0 if negative. We don't use col 0, since that's the name.
if( iColSorted < 0 )
iColSorted = 0;
// First, get number of cols...
LVITEM_V6 lvi;
lvi.iItem = iItem;
lvi.iSubItem = 0;
lvi.mask = LVIF_COLUMNS;
lvi.cColumns = 0;
lvi.puColumns = NULL;
HRESULT hr = XSend_ListView_V6_GetItem( hwnd, LVM_GETITEM, 0, &lvi );
if( FAILED( hr ) )
return hr;
int cCols = lvi.cColumns;
if( cCols < 0 )
cCols = 0;
// If we get back 0 columns, we still have to display the sorted column, if there is one.
// But if there are no cols, and no sorted col, then there's no description.
if( cCols == 0 && iColSorted == 0 )
return S_FALSE;
// Allocate space for those cols - with space for the sorted column at the head.
int * pCols = new int [ cCols + 1 ];
if( ! pCols )
return E_OUTOFMEMORY;
pCols [ 0 ] = iColSorted;
if( cCols )
{
// Now get them...
lvi.puColumns = (UINT *)(pCols + 1);
hr = XSend_ListView_V6_GetItem( hwnd, LVM_GETITEM, 0, &lvi );
if( FAILED( hr ) )
{
delete [ ] pCols;
return hr;
}
// Scan remainder of columns for the sorted column - if found, set that
// entry to 0, so it will be skipped when building the string.
// (Neater than moving all the entries down by one.)
for( int iScan = 1 ; iScan < cCols + 1 ; iScan++ )
{
if( pCols[ iScan ] == iColSorted )
{
pCols[ iScan ] = 0;
}
}
}
// Finally, build the description string using those columns.
// If we didn't get any cols above, this will end up using just the
// sorted col - if there is one.
hr = LVBuildDescriptionString( hwnd, iItem, pCols, cCols + 1, pszDesc );
delete [ ] pCols;
return S_OK;
}