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

952 lines
27 KiB
C++

// Copyright (c) 1996-1999 Microsoft Corporation
// --------------------------------------------------------------------------
//
// TOOLBAR.CPP
//
// This knows how to talk to COMCTL32's tool bar control.
//
// --------------------------------------------------------------------------
#include "oleacc_p.h"
#include "default.h"
#include "client.h"
#define NOSTATUSBAR
#define NOUPDOWN
#define NOMENUHELP
#define NOTRACKBAR
#define NODRAGLIST
#define NOPROGRESS
#define NOHOTKEY
#define NOTREEVIEW
#define NOANIMATE
#include <commctrl.h>
#include "Win64Helper.h"
#include <tchar.h>
#include "toolbar.h"
#define MAX_NAME_SIZE 128
#ifndef I_IMAGENONE
#define I_IMAGENONE (-2)
#endif
// --------------------------------------------------------------------------
//
// CreateToolBarClient()
//
// EXTERNAL for CreateClientObject()
//
// --------------------------------------------------------------------------
HRESULT CreateToolBarClient(HWND hwnd, long idChildCur, REFIID riid, void** ppvTool)
{
HRESULT hr;
CToolBar32* ptool;
InitPv(ppvTool);
ptool = new CToolBar32(hwnd, idChildCur);
if (! ptool)
return(E_OUTOFMEMORY);
hr = ptool->QueryInterface(riid, ppvTool);
if (!SUCCEEDED(hr))
delete ptool;
return(hr);
}
// --------------------------------------------------------------------------
//
// CToolBar32::CToolBar32()
//
// --------------------------------------------------------------------------
CToolBar32::CToolBar32(HWND hwnd, long idChildCur)
: CClient( CLASS_ToolBarClient )
{
Initialize(hwnd, idChildCur);
}
// --------------------------------------------------------------------------
//
// CToolBar32::SetupChildren()
//
// We need the # of buttons, plus 1 if there is a window child.
//
// --------------------------------------------------------------------------
void CToolBar32::SetupChildren()
{
m_cChildren = SendMessageINT(m_hwnd, TB_BUTTONCOUNT, 0, 0);
if (::GetWindow(m_hwnd,GW_CHILD))
m_cChildren++;
}
// --------------------------------------------------------------------------
//
// CToolBar32::GetItemData()
//
// This gets the data from a button in the toolbar, the command ID, the
// state, the style, etc. We need the command ID for example to pass to
// most TB_ messages instead of the index.
//
// --------------------------------------------------------------------------
BOOL CToolBar32::GetItemData(int itemID, LPTBBUTTON lptbResult)
{
LPTBBUTTON lptbShared;
BOOL fReturn;
HANDLE hProcess;
fReturn = FALSE;
// Allocate a TBBUTTON struct from shared memory. The last member in
// TBBUTTON is an INT_PTR (iString) which isn't used by OLEACC. For
// 32b, in case we're making a cross-proc call to a 64b server, we'll
// add an extra DWORD onto the end of the memory we allocate so the struct
// is correctly sized for a 64b server. When reading back the lower
// DWORD is lopped off.
UINT ccbTButton = sizeof(TBBUTTON);
#ifdef _WIN32
ccbTButton += sizeof(DWORD);
#endif
lptbShared = (LPTBBUTTON)SharedAlloc(ccbTButton,m_hwnd,&hProcess);
if (lptbShared)
{
if (SendMessage(m_hwnd, TB_GETBUTTON, itemID-1, (LPARAM)lptbShared))
{
SharedRead (lptbShared,lptbResult,sizeof(TBBUTTON),hProcess);
fReturn = TRUE;
}
SharedFree(lptbShared,hProcess);
}
return(fReturn);
}
// --------------------------------------------------------------------------
//
// CToolBar32::get_accName()
//
// --------------------------------------------------------------------------
STDMETHODIMP CToolBar32::get_accName(VARIANT varChild, BSTR* pszName)
{
LPTSTR lpszName = NULL;
HRESULT hr;
InitPv(pszName);
if (!ValidateChild(&varChild))
return(E_INVALIDARG);
if (! varChild.lVal)
return(CClient::get_accName(varChild, pszName));
// if child id is not zero (CHILDID_SELF)...
hr = GetToolbarString (varChild.lVal,&lpszName);
if( ! lpszName )
return (hr); // will be S_FALSE or an E_error_code
if (*lpszName)
{
StripMnemonic(lpszName);
*pszName = TCharSysAllocString(lpszName);
}
LocalFree (lpszName);
return(*pszName ? S_OK : S_FALSE);
}
// --------------------------------------------------------------------------
//
// CToolBar32::get_accKeyboardShortcut()
//
// --------------------------------------------------------------------------
STDMETHODIMP CToolBar32::get_accKeyboardShortcut(VARIANT varChild, BSTR* pszShortcut)
{
TCHAR chMnemonic = 0;
LPTSTR lpszName = NULL;
HRESULT hr;
InitPv(pszShortcut);
if (!ValidateChild(&varChild))
return(E_INVALIDARG);
if (! varChild.lVal)
return(CClient::get_accKeyboardShortcut(varChild, pszShortcut));
// if child id is not zero (CHILDID_SELF)...
hr = GetToolbarString (varChild.lVal,&lpszName);
if ( ! lpszName )
return (hr); // will be S_FALSE or E_error_code...
if (*lpszName)
chMnemonic = StripMnemonic(lpszName);
LocalFree (lpszName);
//
// Is there a mnemonic?
//
if (chMnemonic)
{
//
// Make a string of the form "Alt+ch".
//
TCHAR szKey[2];
*szKey = chMnemonic;
*(szKey+1) = 0;
return(HrMakeShortcut(szKey, pszShortcut));
}
return(S_FALSE);
}
// --------------------------------------------------------------------------
//
// CToolBar32::GetToolbarString()
//
// Get the name of the item on the toolbar. There are two ways to do this -
// You can just ask using the standard messages, or if that fails, you
// can try to get it from a tooltip. Since we need to do this for both name
// and keyboard shortcut, we'll write a private method to get the
// unstripped name.
//
// Parameters:
// int ChildId - the Child ID (1 based) of the item we want to get
// LPTSTR* ppszName - pointer that will be LocalAlloc'ed and filled
// in with the name. Caller must LocalFree it.
//
// Returns:
//
// On Success:
// returns S_TRUE, *ppszName will be non-NULL, caller must LocalFree() it.
//
// On Failure:
// returns S_FALSE - no name available. *ppszName set to NULL.
// ...or...
// returns COM Failure code (including E_OUTOFMEMORY) - com/memory error.
// *ppszName set to NULL.
//
// Note: caller should take care if using "FAILED( hr )" to examine the return
// value of this method, since it does treats both S_OK and S_FALSE as 'success'.
// It may be better to check that *ppszName is non-NULL.
//
// --------------------------------------------------------------------------
STDMETHODIMP CToolBar32::GetToolbarString(int ChildId, LPTSTR* ppszName)
{
LPTSTR lpszTextShared;
int cchText;
int nSomeInt;
TBBUTTON tb;
HANDLE hProcess;
LPTSTR pszName = NULL;
// Set this to NULL now, in case we return an error code (or S_FALSE) later...
// (We'll set it to a valid return value later if we succeed...)
*ppszName = NULL;
// Get the button ID
if (!GetItemData(ChildId, &tb))
return(S_FALSE);
//
// Get the button text length. NOTE: If this is a separator item
// then just return empty now.
//
if (tb.fsStyle & TBSTYLE_SEP)
return(S_FALSE);
cchText = SendMessageINT(m_hwnd, TB_GETBUTTONTEXT, tb.idCommand, 0);
if (cchText && (cchText != -1))
{
// Allocate a buffer to hold it
lpszTextShared = (LPTSTR)SharedAlloc((cchText+1)*sizeof(TCHAR),
m_hwnd,&hProcess);
if (! lpszTextShared)
return(E_OUTOFMEMORY);
pszName = (LPTSTR)LocalAlloc(LPTR,(cchText+1)*sizeof(TCHAR));
if (! pszName)
{
SharedFree (lpszTextShared,hProcess);
return(E_OUTOFMEMORY);
}
// Get the button text
nSomeInt = 0;
SharedWrite (&nSomeInt,lpszTextShared,sizeof(int),hProcess);
SendMessage(m_hwnd, TB_GETBUTTONTEXT, tb.idCommand, (LPARAM)lpszTextShared);
SharedRead (lpszTextShared,pszName,(cchText+1)*sizeof(TCHAR),hProcess);
SharedFree(lpszTextShared,hProcess);
}
else // Button has no text, so use tooltip method.
{
if ( ! GetTooltipStringForControl( m_hwnd, TB_GETTOOLTIPS, tb.idCommand, & pszName ) )
{
return S_FALSE;
}
}
// At this stage, local var pszName points to a (possibly) empty string.
// We deal with that next...
// Paranoia...
if( ! pszName )
{
return S_FALSE;
}
// do we have a non-empty string?
if( *pszName )
{
*ppszName = pszName;
return S_OK;
}
else
{
// Free the 'empty' pszName...
LocalFree( pszName );
return S_FALSE;
}
}
// --------------------------------------------------------------------------
//
// CToolBar32::get_accRole()
//
// --------------------------------------------------------------------------
STDMETHODIMP CToolBar32::get_accRole(VARIANT varChild, VARIANT* pvarRole)
{
InitPvar(pvarRole);
if (! ValidateChild(&varChild))
return(E_INVALIDARG);
if (!varChild.lVal)
{
pvarRole->vt = VT_I4;
pvarRole->lVal = ROLE_SYSTEM_TOOLBAR;
}
else
{
TBBUTTON tb;
// Get the button type (checkbox, radio, or push).
if (!GetItemData(varChild.lVal, &tb))
return(S_FALSE);
pvarRole->vt = VT_I4;
BOOL bHasImageList = SendMessage( m_hwnd, TB_GETIMAGELIST, 0, 0 ) != 0;
DWORD dwExStyle = SendMessageINT( m_hwnd, TB_GETEXTENDEDSTYLE, 0, 0 );
// If a separator, say so
if (tb.fsStyle & TBSTYLE_SEP)
pvarRole->lVal = ROLE_SYSTEM_SEPARATOR;
else if (tb.fsStyle & TBSTYLE_CHECK)
{
// Special case for task list - they use the checked style, but only for visuals...
TCHAR szClassName[ 64 ];
HWND hwndParent = GetParent( m_hwnd );
if ( hwndParent != NULL
&& GetClassName( hwndParent, szClassName, ARRAYSIZE( szClassName ) )
&& ( lstrcmp( szClassName, TEXT("MSTaskSwWClass") ) == 0 ) )
{
pvarRole->lVal = ROLE_SYSTEM_PUSHBUTTON;
}
else
{
// Check other possible styles
if (tb.fsStyle & TBSTYLE_GROUP)
pvarRole->lVal = ROLE_SYSTEM_RADIOBUTTON;
else
pvarRole->lVal = ROLE_SYSTEM_CHECKBUTTON;
}
}
else if (!bHasImageList || tb.iBitmap == I_IMAGENONE )
{
// TODO - check that it's not a standard image (since they don't require an
// imagelist?
// Text-only, no bitmap, so it's effectively a menu item.
// (eg. as used in MMC)
pvarRole->lVal = ROLE_SYSTEM_MENUITEM;
}
else if ( ( tb.fsStyle & TBSTYLE_DROPDOWN ) && ( dwExStyle & TBSTYLE_EX_DRAWDDARROWS ) )
{
// if its a drop down and it has an arrow its a split button
pvarRole->lVal = ROLE_SYSTEM_SPLITBUTTON;
}
else
{
pvarRole->lVal = ROLE_SYSTEM_PUSHBUTTON;
}
}
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CToolBar32::get_accState()
//
// --------------------------------------------------------------------------
STDMETHODIMP CToolBar32::get_accState(VARIANT varChild, VARIANT* pvarState)
{
InitPvar(pvarState);
if (! ValidateChild(&varChild))
return(E_INVALIDARG);
if (! varChild.lVal)
{
return CClient::get_accState( varChild, pvarState );
}
else
{
TBBUTTON tb;
if (! GetItemData(varChild.lVal, &tb))
{
pvarState->vt = VT_I4;
pvarState->lVal = STATE_SYSTEM_INVISIBLE;
return(S_OK);
}
pvarState->vt = VT_I4;
pvarState->lVal = 0;
if (tb.fsState & TBSTATE_CHECKED)
pvarState->lVal |= STATE_SYSTEM_CHECKED;
if (tb.fsState & TBSTATE_PRESSED)
pvarState->lVal |= STATE_SYSTEM_PRESSED;
if (!(tb.fsState & TBSTATE_ENABLED))
pvarState->lVal |= STATE_SYSTEM_UNAVAILABLE;
if (tb.fsState & TBSTATE_HIDDEN)
pvarState->lVal |= STATE_SYSTEM_INVISIBLE;
else
{
if( IsClippedByWindow( this, varChild, m_hwnd ) )
{
pvarState->lVal |= STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_OFFSCREEN;
}
}
if (tb.fsState & TBSTATE_INDETERMINATE)
pvarState->lVal |= STATE_SYSTEM_MIXED;
if (tb.fsStyle & TBSTYLE_ALTDRAG)
pvarState->lVal |= STATE_SYSTEM_MOVEABLE;
// Special case for task list - they use the checked style, but only for visuals...
TCHAR szClassName[ 64 ];
HWND hwndParent = GetParent( m_hwnd );
if ( hwndParent != NULL
&& GetClassName( hwndParent, szClassName, ARRAYSIZE( szClassName ) )
&& ( lstrcmp( szClassName, TEXT("MSTaskSwWClass") ) == 0 ) )
{
// Change the checked state into pressed instead...
if( pvarState->lVal & STATE_SYSTEM_CHECKED )
{
pvarState->lVal &= ~ STATE_SYSTEM_CHECKED;
pvarState->lVal |= STATE_SYSTEM_PRESSED;
}
}
// idChild-1 will never == -1 here, since we handle that case (id==CHILDID_SELF)
// in the first branch of this if. (TB_GETHOTITEM returns -1 if there's no hot item.)
if( SendMessage( m_hwnd, TB_GETHOTITEM, 0, 0 ) == varChild.lVal - 1 )
{
pvarState->lVal |= STATE_SYSTEM_HOTTRACKED;
// This doesn't quite work...
// Some apps - notably MMCs - don't actually focus their toolbar -
// they leave their main MDI child focused. So we have no way of
// telling whether the menu is hot because it's in 'focus' mode
// or whether it's just because the mouse is over it (not really
// focused).
//
// But at least this finally gets us the correct state for the quick
// launch toolbar.
if( MyGetFocus() == m_hwnd )
{
pvarState->lVal |= STATE_SYSTEM_FOCUSED;
}
}
// What about separators?
// CWO, 4/22/97, Separators have a state of TBSTATE_ENABLED
return(S_OK);
}
}
// --------------------------------------------------------------------------
//
// CToolBar32::get_accDefaultAction()
//
// Default action is same as the name of the button.
//
// --------------------------------------------------------------------------
STDMETHODIMP CToolBar32::get_accDefaultAction(VARIANT varChild, BSTR* pszDef)
{
InitPv(pszDef);
if (! ValidateChild(&varChild))
return(E_INVALIDARG);
if (! varChild.lVal)
return(CClient::get_accDefaultAction(varChild, pszDef));
else
{
TBBUTTON tb;
// TBSTYLE_DROP has a different default action than the name
if (GetItemData(varChild.lVal, &tb) && (tb.fsStyle & TBSTYLE_DROPDOWN))
return(HrCreateString(STR_DROPDOWN_SHOW, pszDef));
else
return(HrCreateString(STR_BUTTON_PUSH, pszDef));
}
}
// --------------------------------------------------------------------------
//
// CToolBar32::accLocation()
//
// --------------------------------------------------------------------------
STDMETHODIMP CToolBar32::accLocation(long* pxLeft, long* pyTop,
long* pcxWidth, long* pcyHeight, VARIANT varChild)
{
InitAccLocation(pxLeft, pyTop, pcxWidth, pcyHeight);
if (! ValidateChild(&varChild))
return(E_INVALIDARG);
if (! varChild.lVal)
return(CClient::accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild));
else
{
LPRECT prcShared;
HRESULT hr;
RECT rcLocal;
HANDLE hProcess;
// Allocate a Shared RECT
prcShared = (LPRECT)SharedAlloc(sizeof(RECT),m_hwnd,&hProcess);
if (! prcShared)
return(E_OUTOFMEMORY);
// This returns FALSE if the button is hidden
if (SendMessage(m_hwnd, TB_GETITEMRECT, varChild.lVal-1, (LPARAM)prcShared))
{
hr = S_OK;
SharedRead (prcShared,&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;
}
else
hr = S_FALSE;
SharedFree(prcShared,hProcess);
return(hr);
}
}
// --------------------------------------------------------------------------
//
// CToolBar32::accNavigate()
//
// Toolbar clients can only set an indent on the left side. Hence all
// child window objects are on the left, buttons are on the right.
//
// BOGUS! Doesn't deal with wrapped toolbars yet.
//
// --------------------------------------------------------------------------
STDMETHODIMP CToolBar32::accNavigate(long dwNavDir, VARIANT varStart,
VARIANT* pvarEnd)
{
int lEnd = 0;
TBBUTTON tb;
InitPvar(pvarEnd);
if ((!ValidateChild(&varStart) && !ValidateHwnd(&varStart)) ||
!ValidateNavDir(dwNavDir, varStart.lVal))
return(E_INVALIDARG);
// Peer-to-peer navigation in nonclient
if (!varStart.lVal && (dwNavDir < NAVDIR_FIRSTCHILD))
return(CClient::accNavigate(dwNavDir, varStart, pvarEnd));
//
// Nav is strange, the first items on the left have the highest IDs.
//
if (dwNavDir == NAVDIR_FIRSTCHILD)
dwNavDir = NAVDIR_NEXT;
else if (dwNavDir == NAVDIR_LASTCHILD)
{
dwNavDir = NAVDIR_PREVIOUS;
varStart.lVal = m_cChildren + 1;
}
switch (dwNavDir)
{
case NAVDIR_NEXT:
case NAVDIR_RIGHT:
lEnd = varStart.lVal;
// Toolbars can't really have windows as children - it is
// a hack that explorer does. Because it is a hack, we know
// that when we get starting point that is a window, it is
// the first child, so to naviagte next, we just move to the
// first 'real' child.
// If we are trying to navigate next from 0 (the toolbar itself)
// we just check if the toolbar window has a child window, and
// return a dispatch interface to that child.
if (lEnd == CHILDID_SELF)
{
HWND hwndChild;
if (hwndChild = ::GetWindow(m_hwnd,GW_CHILD))
{
pvarEnd->vt=VT_DISPATCH;
return (AccessibleObjectFromWindow(hwndChild,OBJID_WINDOW,
IID_IDispatch, (void**)&pvarEnd->pdispVal));
}
}
// just set lEnd to 0 so we get the first 'real' child
// of the toolbar - the first button.
if (IsHWNDID(lEnd))
lEnd = 0;
while (++lEnd <= m_cChildren)
{
//
// Is this a visible child?
// CWO, 4/22/97, removed separate clause
//
if (GetItemData(lEnd, &tb) && !(tb.fsState & TBSTATE_HIDDEN))
break; // out of while loop
}
if (lEnd > m_cChildren)
lEnd = 0;
break; // out of switch
case NAVDIR_PREVIOUS:
case NAVDIR_LEFT:
lEnd = varStart.lVal;
// Navigating previous is similar to next when dealing with
// children that are windows. If the start point is a child
// window, then the end point is 0, the toolbar itself. If
// the end point (after doing normal children) is 0, then
// check if the toolbar has a child window and if so, return
// a dispatch interface to that object.
if (IsHWNDID(lEnd))
{
lEnd = 0;
break; // out of switch
}
while (--lEnd > 0)
{
//
// Is this a visible child?
// CWO, 4/22/97, removed separate clause
//
if (GetItemData(lEnd, &tb) && !(tb.fsState & TBSTATE_HIDDEN))
break; // out of while
}
if (lEnd == CHILDID_SELF)
{
HWND hwndChild;
if (hwndChild = ::GetWindow(m_hwnd,GW_CHILD))
{
pvarEnd->vt=VT_DISPATCH;
return (AccessibleObjectFromWindow(hwndChild,OBJID_WINDOW,
IID_IDispatch, (void**)&pvarEnd->pdispVal));
}
}
break; // out of switch
case NAVDIR_UP:
case NAVDIR_DOWN:
lEnd = 0;
// Don't handle wrapping toolbars yet.
break; // out of switch
}
if (lEnd)
{
pvarEnd->vt = VT_I4;
pvarEnd->lVal = lEnd;
return(S_OK);
}
return(S_FALSE);
}
// --------------------------------------------------------------------------
//
// CToolBar32::accHitTest()
//
// First, ask the client window what is here. If itself, then try the
// buttons. If nothing/child window, return that thing.
//
// --------------------------------------------------------------------------
STDMETHODIMP CToolBar32::accHitTest(long x, long y, VARIANT* pvarHit)
{
POINT pt;
LPRECT lprcShared;
int iButton;
HRESULT hr;
RECT rcLocal;
HANDLE hProcess;
SetupChildren();
//
// Is this point in our client, not in any child window?
//
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);
pt.x = x;
pt.y = y;
ScreenToClient(m_hwnd, &pt);
//
// Figure out what button this point is over. We have to do this the
// hard way, by looping through the buttons asking for location.
//
lprcShared = (LPRECT)SharedAlloc(sizeof(RECT),m_hwnd,&hProcess);
if (!lprcShared)
return(E_OUTOFMEMORY);
for (iButton = 0; iButton < m_cChildren; iButton++)
{
if (SendMessage(m_hwnd, TB_GETITEMRECT, iButton, (LPARAM)lprcShared))
{
SharedRead (lprcShared,&rcLocal,sizeof(RECT),hProcess);
if (PtInRect(&rcLocal, pt))
{
pvarHit->vt = VT_I4;
pvarHit->lVal = iButton+1;
SharedFree(lprcShared,hProcess);
return(S_OK);
}
}
}
//
// If we got here, the point is not over any toolbar item. It must be
// over ourself.
//
SharedFree(lprcShared,hProcess);
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CToolBar32::accDoDefaultAction()
//
// This sends the command that the button represents. We can't fake a click
// because that won't work if the window isn't active.
//
// We have to send a WM_COMMAND, BN_CLICKED to the toolbar parent. Problem
// is, no easy way to get the parent. So we set it (which returns the old
// one, then set it back).
//
// --------------------------------------------------------------------------
STDMETHODIMP CToolBar32::accDoDefaultAction(VARIANT varChild)
{
HWND hwndToolBarParent;
TBBUTTON tb;
if (! ValidateChild(&varChild))
return(E_INVALIDARG);
if (! varChild.lVal)
return(CClient::accDoDefaultAction(varChild));
//
// Get the toolbar parent in a hacky way, by setting it then setting
// it back. THIS CODE ASSUMES THAT THE HANDLING IS MINIMAL IN COMCTL32.
//
hwndToolBarParent = (HWND)SendMessage(m_hwnd, TB_SETPARENT, 0, 0);
SendMessage(m_hwnd, TB_SETPARENT, (WPARAM)hwndToolBarParent, 0);
if (! hwndToolBarParent)
return(S_FALSE);
//
// Get the command ID of this button, and generate a BN_CLICK if it
// isn't a separator.
//
if (GetItemData(varChild.lVal, &tb) &&
!(tb.fsStyle & TBSTYLE_SEP) &&
(tb.fsState & TBSTATE_ENABLED) &&
!(tb.fsState & TBSTATE_HIDDEN))
{
PostMessage(hwndToolBarParent, WM_COMMAND, MAKEWPARAM(tb.idCommand, BN_CLICKED), (LPARAM)m_hwnd);
return(S_OK);
}
else
return(S_FALSE);
}
// --------------------------------------------------------------------------
//
// CToolBar32::Next()
//
// This knows that the first child might be an HWND.
//
// --------------------------------------------------------------------------
STDMETHODIMP CToolBar32::Next(ULONG celt, VARIANT *rgvar, ULONG* pceltFetched)
{
HWND hwndChild;
VARIANT* pvar;
long cFetched;
HRESULT hr;
long iCur;
long cChildTemp;
SetupChildren();
// Can be NULL
if (pceltFetched)
*pceltFetched = 0;
cFetched = 0;
// check for window handle child first
if (m_idChildCur == CHILDID_SELF)
{
if (hwndChild = ::GetWindow(m_hwnd,GW_CHILD))
{
rgvar->vt=VT_DISPATCH;
hr = AccessibleObjectFromWindow(hwndChild,OBJID_WINDOW,
IID_IDispatch, (void**)&rgvar->pdispVal);
if (!SUCCEEDED(hr))
return(hr);
// decrement how many left to get
celt--;
cFetched = 1;
// increment to next variant in array
rgvar++;
// increment count of fetched
if (pceltFetched)
(*pceltFetched)++;
// remember current child
m_idChildCur = HWNDIDFromHwnd(m_hwnd, hwndChild);
// if no more to get, return
if (!celt)
return(S_OK);
} // end if there is a child window
} // end if (started at 0)
// now get any non-window children
pvar = rgvar;
iCur = m_idChildCur;
if (IsHWNDID(iCur))
iCur = 0;
//
// Loop through our items. Need to do different if there is a
// window child, because m_cChildren will be +1.
//
cChildTemp = m_cChildren;
if (::GetWindow(m_hwnd,GW_CHILD))
cChildTemp--;
while ((cFetched < (long)celt) && (iCur < cChildTemp))
{
cFetched++;
iCur++;
//
// Note this gives us (index)+1 because we incremented iCur
//
pvar->vt = VT_I4;
pvar->lVal = 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);
}