1974 lines
53 KiB
C++
1974 lines
53 KiB
C++
// Controls.cpp : implementation file
|
|
//
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1999
|
|
//
|
|
// File: Controls.cpp
|
|
//
|
|
// Contents: General window controls used in the slate AMC console
|
|
//
|
|
// History: 19-Dec-96 WayneSc Created
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
#include "stdafx.h"
|
|
#include "docksite.h"
|
|
#include "Controls.h"
|
|
#include "resource.h"
|
|
#include "amc.h"
|
|
#include "tbtrack.h"
|
|
#include "mainfrm.h"
|
|
#include "fontlink.h"
|
|
#include "menubar.h"
|
|
#include <oleacc.h>
|
|
#include "guidhelp.h"
|
|
#include "util.h" // StripTrailingWhitespace
|
|
|
|
/*
|
|
* if we're supporting old platforms, we need to get MSAA definitions
|
|
* from somewhere other than winuser.h
|
|
*/
|
|
#if (_WINNT_WIN32 < 0x0500)
|
|
#include <winable.h>
|
|
#endif
|
|
|
|
|
|
#ifdef DBG
|
|
CTraceTag tagToolbarAccessibility (_T("Accessibility"), _T("Toolbar"));
|
|
#endif
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarAccServer
|
|
*
|
|
* Proxy for the accessibility interface IAccPropServer for CMMCToolBarCtrlEx.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
class CMMCToolBarAccServer :
|
|
public IAccPropServer,
|
|
public CComObjectRoot,
|
|
public CComObjectObserver,
|
|
public CTiedComObject<CMMCToolBarCtrlEx>
|
|
{
|
|
typedef CMMCToolBarAccServer ThisClass;
|
|
typedef CMMCToolBarCtrlEx CMyTiedObject;
|
|
|
|
protected:
|
|
CMMCToolBarAccServer()
|
|
{
|
|
Trace (tagToolbarAccessibility, _T("Creating CMMCToolBarAccServer (0x%p)"), this);
|
|
|
|
// add itself as an observer for com object events
|
|
GetComObjectEventSource().AddObserver(*static_cast<CComObjectObserver*>(this));
|
|
}
|
|
|
|
~CMMCToolBarAccServer()
|
|
{
|
|
Trace (tagToolbarAccessibility, _T("Destroying CMMCToolBarAccServer (0x%p)"), this);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: ScOnDisconnectObjects
|
|
*
|
|
* PURPOSE: invoked when observed event (request to disconnect) occures
|
|
* Disconnects from external connections
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
virtual ::SC ScOnDisconnectObjects()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CMMCIDispatchImpl<_ComInterface>::ScOnDisconnectObjects"));
|
|
|
|
// QI for IUnknown
|
|
IUnknownPtr spUnknown = this;
|
|
|
|
// sanity check
|
|
sc = ScCheckPointers( spUnknown, E_UNEXPECTED );
|
|
if (sc)
|
|
return sc;
|
|
|
|
// cutt own references
|
|
sc = CoDisconnectObject( spUnknown, 0/*dwReserved*/ );
|
|
if (sc)
|
|
return sc;
|
|
|
|
return sc;
|
|
}
|
|
|
|
public:
|
|
BEGIN_COM_MAP(ThisClass)
|
|
COM_INTERFACE_ENTRY(IAccPropServer)
|
|
END_COM_MAP()
|
|
|
|
DECLARE_NOT_AGGREGATABLE(ThisClass)
|
|
|
|
public:
|
|
// *** IAccPropServer methods ***
|
|
MMC_METHOD5 (GetPropValue, const BYTE* /*pIDString*/, DWORD /*dwIDStringLen*/, MSAAPROPID /*idProp*/, VARIANT* /*pvarValue*/, BOOL* /*pfGotProp*/)
|
|
};
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CDescriptionCtrl
|
|
|
|
CDescriptionCtrl::CDescriptionCtrl() :
|
|
m_cxMargin (0),
|
|
m_cyText (0),
|
|
m_cyRequired (0)
|
|
{
|
|
}
|
|
|
|
CDescriptionCtrl::~CDescriptionCtrl()
|
|
{
|
|
}
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CDescriptionCtrl, CStatic)
|
|
//{{AFX_MSG_MAP(CDescriptionCtrl)
|
|
ON_WM_NCHITTEST()
|
|
ON_WM_CREATE()
|
|
ON_WM_SETTINGCHANGE()
|
|
ON_WM_DESTROY()
|
|
ON_WM_SIZE()
|
|
//}}AFX_MSG_MAP
|
|
|
|
ON_WM_DRAWITEM_REFLECT()
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CDescriptionCtrl message handlers
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CDescriptionCtrl::PreCreateWindow
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
BOOL CDescriptionCtrl::PreCreateWindow(CREATESTRUCT& cs)
|
|
{
|
|
cs.style |= SS_NOPREFIX | SS_CENTERIMAGE | SS_ENDELLIPSIS |
|
|
SS_LEFTNOWORDWRAP | SS_OWNERDRAW | WS_CLIPSIBLINGS;
|
|
cs.dwExStyle |= WS_EX_STATICEDGE;
|
|
|
|
return (CStatic::PreCreateWindow (cs));
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CDescriptionCtrl::OnCreate
|
|
*
|
|
* WM_CREATE handler for CDescriptionCtrl.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
int CDescriptionCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
|
|
{
|
|
if (CStatic::OnCreate(lpCreateStruct) == -1)
|
|
return -1;
|
|
|
|
CreateFont();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CDescriptionCtrl::OnDestroy
|
|
*
|
|
* WM_DESTROY handler for CDescriptionCtrl.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CDescriptionCtrl::OnDestroy()
|
|
{
|
|
CStatic::OnDestroy();
|
|
DeleteFont();
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CDescriptionCtrl::OnNcHitTest
|
|
*
|
|
* WM_NCHITTEST handler for CDescriptionCtrl.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
UINT CDescriptionCtrl::OnNcHitTest(CPoint point)
|
|
{
|
|
return (HTCLIENT);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CDescriptionCtrl::OnSettingChange
|
|
*
|
|
* WM_SETTINGCHANGE handler for CDescriptionCtrl.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CDescriptionCtrl::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
|
|
{
|
|
CStatic::OnSettingChange(uFlags, lpszSection);
|
|
|
|
if (uFlags == SPI_SETNONCLIENTMETRICS)
|
|
{
|
|
DeleteFont ();
|
|
CreateFont ();
|
|
}
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CDescriptionCtrl::OnSize
|
|
*
|
|
* WM_SIZE handler for CDescriptionCtrl.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CDescriptionCtrl::OnSize(UINT nType, int cx, int cy)
|
|
{
|
|
CStatic::OnSize(nType, cx, cy);
|
|
|
|
/*
|
|
* Completely redraw when the size changes, so the ellipsis will
|
|
* be drawn correctly. Another way to do this would be to use
|
|
* CS_HREDRAW | CS_VREDRAW, but it's too big a pain to re-register
|
|
* the static control. This'll do just fine.
|
|
*/
|
|
InvalidateRect (NULL);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CDescriptionCtrl::DrawItem
|
|
*
|
|
* WM_DRAWITEM handler for CDescriptionCtrl.
|
|
*
|
|
* The description control needs to be ownerdraw for a couple of reasons:
|
|
*
|
|
* 1. If any of the text contains characters that can't be rendered by
|
|
* the default font, we won't draw them correctly.
|
|
*
|
|
* 2. If any of the text contains right-to-left reading text (e.g. Arabic
|
|
* or Hebrew), system mirroring code will incorrectly mix the console
|
|
* and snap-in text (bug 365469). The static control will draw the text
|
|
* in one fell swoop, but we can get around the problem if we draw
|
|
* the text ourselves in two steps: first console text, then snap-in
|
|
* text.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CDescriptionCtrl::DrawItem(LPDRAWITEMSTRUCT lpdis)
|
|
{
|
|
/*
|
|
* Bug 410450: When the system is under stress, we might not get a
|
|
* valid DC. If we don't there's nothing we can do, so short out (and
|
|
* avoid an AV when we later dereference a NULL CDC*).
|
|
*/
|
|
CDC* pdcWindow = CDC::FromHandle (lpdis->hDC);
|
|
if (pdcWindow == NULL)
|
|
return;
|
|
|
|
/*
|
|
* if we don't have any text, just clear the background
|
|
*/
|
|
bool fHasConsoleText = !m_strConsoleText.IsEmpty();
|
|
bool fHasSnapinText = !m_strSnapinText.IsEmpty();
|
|
|
|
if (!fHasConsoleText && !fHasSnapinText)
|
|
{
|
|
FillRect (lpdis->hDC, &lpdis->rcItem, GetSysColorBrush (COLOR_3DFACE));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* figure out the text rectangle; it will be one line high, vertically
|
|
* centered within the window
|
|
*/
|
|
CRect rectClient;
|
|
GetClientRect (rectClient);
|
|
const int cxClient = rectClient.Width();
|
|
const int cyClient = rectClient.Height();
|
|
|
|
CRect rectText = rectClient;
|
|
rectText.left += m_cxMargin;
|
|
rectText.bottom = m_cyText;
|
|
|
|
rectText.OffsetRect (0, (cyClient - rectText.Height()) / 2);
|
|
|
|
const DWORD dwFlags = DT_LEFT | DT_TOP | DT_SINGLELINE |
|
|
DT_NOPREFIX | DT_END_ELLIPSIS;
|
|
|
|
USES_CONVERSION;
|
|
CFontLinker fl;
|
|
|
|
/*
|
|
* double-buffer drawing for flicker-free redraw
|
|
*/
|
|
CDC dcMem;
|
|
dcMem.CreateCompatibleDC (pdcWindow);
|
|
if (dcMem.GetSafeHdc() == NULL)
|
|
return;
|
|
|
|
CBitmap bmpMem;
|
|
bmpMem.CreateCompatibleBitmap (&dcMem, cxClient, cyClient);
|
|
if (bmpMem.GetSafeHandle() == NULL)
|
|
return;
|
|
|
|
/*
|
|
* put the right font in the DC, and clear it out
|
|
*/
|
|
CFont* pOldFont = dcMem.SelectObject (&m_font);
|
|
CBitmap* pOldBitmap = dcMem.SelectObject (&bmpMem);
|
|
dcMem.PatBlt (0, 0, cxClient, cyClient, WHITENESS);
|
|
|
|
/*
|
|
* if we have console text draw it and update the text rectangle
|
|
* so snap-in text (if any) will be drawn in the right place
|
|
*/
|
|
if (fHasConsoleText)
|
|
{
|
|
/*
|
|
* create a CRichText object for the console text and let
|
|
* the font linker parse it into bite-size chunks
|
|
*/
|
|
CRichText rt (dcMem, T2CW (static_cast<LPCTSTR>(m_strConsoleText)));
|
|
bool fComposed = fl.ComposeRichText (rt);
|
|
|
|
/*
|
|
* draw the console text and adjust the text rectancle in
|
|
* preparation for drawing the snap-in text
|
|
*/
|
|
if (fComposed && !rt.IsDefaultFontSufficient())
|
|
{
|
|
CRect rectRemaining;
|
|
rt.Draw (rectText, dwFlags, rectRemaining);
|
|
|
|
rectText.left = rectRemaining.left;
|
|
}
|
|
else
|
|
{
|
|
dcMem.DrawText (m_strConsoleText, rectText, dwFlags | DT_CALCRECT);
|
|
dcMem.DrawText (m_strConsoleText, rectText, dwFlags);
|
|
|
|
rectText.left = rectText.right;
|
|
rectText.right = rectClient.right;
|
|
}
|
|
|
|
/*
|
|
* leave some space between the console text and the snap-in text
|
|
*/
|
|
rectText.left += 2*m_cxMargin;
|
|
}
|
|
|
|
/*
|
|
* draw the snap-in text, if any
|
|
*/
|
|
if (fHasSnapinText)
|
|
{
|
|
/*
|
|
* create a CRichText object for the console text and let
|
|
* the font linker parse it into bite-size chunks
|
|
*/
|
|
CRichText rt (dcMem, T2CW (static_cast<LPCTSTR>(m_strSnapinText)));
|
|
bool fComposed = fl.ComposeRichText (rt);
|
|
|
|
/*
|
|
* draw the snap-in text
|
|
*/
|
|
if (fComposed && !rt.IsDefaultFontSufficient())
|
|
rt.Draw (rectText, dwFlags);
|
|
else
|
|
dcMem.DrawText (m_strSnapinText, rectText, dwFlags);
|
|
}
|
|
|
|
/*
|
|
* blt to the screen
|
|
*/
|
|
pdcWindow->BitBlt (0, 0, cxClient, cyClient, &dcMem, 0, 0, SRCCOPY);
|
|
|
|
/*
|
|
* restore the original font
|
|
*/
|
|
dcMem.SelectObject (pOldFont);
|
|
dcMem.SelectObject (pOldBitmap);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CDescriptionCtrl::CreateFont
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CDescriptionCtrl::CreateFont ()
|
|
{
|
|
/*
|
|
* create a copy of the icon title font
|
|
*/
|
|
LOGFONT lf;
|
|
SystemParametersInfo (SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, false);
|
|
|
|
m_font.CreateFontIndirect (&lf);
|
|
|
|
/*
|
|
* figure out how much space we need to fully display our text
|
|
*/
|
|
TCHAR ch = _T('0');
|
|
CWindowDC dc(this);
|
|
CFont* pFont = dc.SelectObject (&m_font);
|
|
m_cyText = dc.GetTextExtent(&ch, 1).cy;
|
|
|
|
ch = _T(' ');
|
|
m_cxMargin = 2 * dc.GetTextExtent(&ch, 1).cx;
|
|
|
|
CRect rectRequired (0, 0, 0, m_cyText + 2*GetSystemMetrics(SM_CYEDGE));
|
|
AdjustWindowRectEx (rectRequired, GetStyle(), false, GetExStyle());
|
|
m_cyRequired = rectRequired.Height();
|
|
|
|
dc.SelectObject (pFont);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CDescriptionCtrl::DeleteFont
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CDescriptionCtrl::DeleteFont ()
|
|
{
|
|
m_font.DeleteObject();
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CDescriptionCtrl::ScOnSelectedItemTextChanged
|
|
*
|
|
* This method observes the text of the selected item in the tree control.
|
|
* The text is reflected in the description bar.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
SC CDescriptionCtrl::ScOnSelectedItemTextChanged (LPCTSTR pszSelectedItemText)
|
|
{
|
|
if (m_strConsoleText != pszSelectedItemText)
|
|
{
|
|
m_strConsoleText = pszSelectedItemText;
|
|
Invalidate();
|
|
}
|
|
|
|
return (S_OK);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CDescriptionCtrl::SetSnapinText
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CDescriptionCtrl::SetSnapinText (const CString& strSnapinText)
|
|
{
|
|
if (m_strSnapinText != strSnapinText)
|
|
{
|
|
m_strSnapinText = strSnapinText;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CToolBarCtrlEx
|
|
|
|
CToolBarCtrlEx::CToolBarCtrlEx()
|
|
{
|
|
m_sizeBitmap.cx = 16;
|
|
m_sizeBitmap.cy = 16; // docs say 15, but code in toolbar.c says 16
|
|
|
|
m_fMirrored = false;
|
|
|
|
m_pRebar = NULL;
|
|
m_cx = 0;
|
|
}
|
|
|
|
CToolBarCtrlEx::~CToolBarCtrlEx()
|
|
{
|
|
}
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CToolBarCtrlEx, CToolBarCtrlEx::BaseClass)
|
|
//{{AFX_MSG_MAP(CToolBarCtrlEx)
|
|
ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CToolBarCtrlEx message handlers
|
|
|
|
BOOL CToolBarCtrlEx::Create(LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext)
|
|
{
|
|
BOOL bRtn=FALSE;
|
|
|
|
if (!pParentWnd)
|
|
{
|
|
ASSERT(pParentWnd); // Invalid Parent
|
|
}
|
|
else
|
|
{
|
|
// Initialise the new common controls
|
|
INITCOMMONCONTROLSEX icex;
|
|
|
|
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
|
|
icex.dwICC = ICC_BAR_CLASSES;
|
|
|
|
if (InitCommonControlsEx(&icex))
|
|
{
|
|
// Add toolbar styles to the dwstyle
|
|
dwStyle |= WS_CHILD | TBSTYLE_FLAT | WS_CLIPCHILDREN |
|
|
WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE;
|
|
|
|
|
|
if (CWnd::CreateEx(WS_EX_TOOLWINDOW | WS_EX_NOPARENTNOTIFY,
|
|
TOOLBARCLASSNAME,
|
|
lpszWindowName,
|
|
dwStyle,
|
|
rect,
|
|
pParentWnd,
|
|
nID))
|
|
{
|
|
bRtn=TRUE;
|
|
|
|
//See if toolbar is mirrored or not
|
|
m_fMirrored = GetExStyle() & WS_EX_LAYOUTRTL;
|
|
|
|
// Tells the toolbar what version we are.
|
|
SetButtonStructSize(sizeof(TBBUTTON));
|
|
|
|
//REVIEW This may not need to be here. I'm defaulting buttons w/ text
|
|
//to only have one row. This may need to be configurable.
|
|
SetMaxTextRows(1);
|
|
|
|
CRebarDockWindow* pRebarDock = (CRebarDockWindow*) pParentWnd;
|
|
if (pRebarDock)
|
|
m_pRebar = pRebarDock->GetRebar();
|
|
}
|
|
}
|
|
}
|
|
|
|
return bRtn;
|
|
}
|
|
|
|
|
|
void CToolBarCtrlEx::UpdateToolbarSize(void)
|
|
{
|
|
/*
|
|
* get the right edge of the right-most visible button
|
|
*/
|
|
int cx = 0;
|
|
for (int i = GetButtonCount()-1; i >= 0; i--)
|
|
{
|
|
RECT rcButton;
|
|
|
|
if (GetItemRect (i, &rcButton))
|
|
{
|
|
cx = rcButton.right;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT (IsWindow (m_pRebar->GetSafeHwnd()));
|
|
|
|
/*
|
|
* if the width has changed, update the band
|
|
*/
|
|
if (m_cx != cx)
|
|
{
|
|
m_cx = cx;
|
|
|
|
// Set values unique to the band with the toolbar;
|
|
REBARBANDINFO rbBand;
|
|
rbBand.cbSize = sizeof (rbBand);
|
|
rbBand.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE;
|
|
rbBand.cx = m_cx;
|
|
rbBand.cxMinChild = m_cx;
|
|
rbBand.cyMinChild = HIWORD (GetButtonSize());
|
|
|
|
int iBand = GetBandIndex();
|
|
if (iBand != -1)
|
|
m_pRebar->SetBandInfo (iBand, &rbBand);
|
|
}
|
|
}
|
|
|
|
|
|
bool CToolBarCtrlEx::IsBandVisible()
|
|
{
|
|
return (GetBandIndex() != -1);
|
|
}
|
|
|
|
int CToolBarCtrlEx::GetBandIndex()
|
|
{
|
|
REBARBANDINFO rbBand;
|
|
rbBand.cbSize = sizeof(REBARBANDINFO);
|
|
rbBand.fMask = RBBIM_CHILD;
|
|
|
|
if ( m_pRebar == NULL )
|
|
return (-1);
|
|
|
|
int nBands = m_pRebar->GetBandCount ();
|
|
|
|
for (int i = 0; i < nBands; i++)
|
|
{
|
|
if (m_pRebar->GetBandInfo (i, &rbBand) && (rbBand.hwndChild == m_hWnd))
|
|
return (i);
|
|
}
|
|
|
|
return (-1);
|
|
}
|
|
|
|
|
|
void CToolBarCtrlEx::Show(BOOL bShow, bool bAddToolbarInNewLine)
|
|
{
|
|
if ((m_pRebar == NULL) || !::IsWindow(m_pRebar->m_hWnd))
|
|
{
|
|
ASSERT(FALSE); // Invalid rebar window handle
|
|
return;
|
|
}
|
|
|
|
|
|
if (bShow)
|
|
{
|
|
if (false == IsBandVisible())
|
|
{
|
|
REBARBANDINFO rbBand;
|
|
ZeroMemory(&rbBand, sizeof(rbBand));
|
|
rbBand.cbSize = sizeof(REBARBANDINFO);
|
|
rbBand.fMask = RBBIM_CHILD | RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_ID | RBBIM_STYLE;
|
|
rbBand.hwndChild = m_hWnd;
|
|
rbBand.wID = GetWindowLong (m_hWnd, GWL_ID);
|
|
rbBand.cx = m_cx;
|
|
rbBand.cxMinChild = m_cx;
|
|
rbBand.cyMinChild = HIWORD (GetButtonSize());
|
|
rbBand.fStyle = RBBS_NOGRIPPER;
|
|
|
|
if (bAddToolbarInNewLine)
|
|
{
|
|
// Insert this toolbar in new line.
|
|
rbBand.fStyle |= RBBS_BREAK;
|
|
}
|
|
|
|
m_pRebar->InsertBand (&rbBand);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int iBand = GetBandIndex();
|
|
ASSERT(iBand != -1);
|
|
if (iBand != -1)
|
|
m_pRebar->DeleteBand (iBand);
|
|
}
|
|
}
|
|
|
|
|
|
void CToolBarCtrlEx::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler)
|
|
{
|
|
CToolCmdUIEx state;
|
|
state.m_pOther = this;
|
|
|
|
state.m_nIndexMax = (UINT)DefWindowProc(TB_BUTTONCOUNT, 0, 0);
|
|
for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++)
|
|
{
|
|
// get buttons state
|
|
TBBUTTON button;
|
|
memset(&button, 0, sizeof(TBBUTTON));
|
|
GetButton(state.m_nIndex, &button);
|
|
state.m_nID = button.idCommand;
|
|
|
|
// ignore separators
|
|
if (!(button.fsStyle & TBSTYLE_SEP))
|
|
{
|
|
// allow the toolbar itself to have update handlers
|
|
if (CWnd::OnCmdMsg(state.m_nID, CN_UPDATE_COMMAND_UI, &state, NULL))
|
|
continue;
|
|
|
|
// allow the owner to process the update
|
|
state.DoUpdate(pTarget, bDisableIfNoHndler);
|
|
}
|
|
}
|
|
|
|
// update the dialog controls added to the toolbar
|
|
UpdateDialogControls(pTarget, bDisableIfNoHndler);
|
|
}
|
|
|
|
|
|
LRESULT CToolBarCtrlEx::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM)
|
|
{
|
|
/*
|
|
// handle delay hide/show
|
|
BOOL bVis = GetStyle() & WS_VISIBLE;
|
|
UINT swpFlags = 0;
|
|
if ((m_nStateFlags & delayHide) && bVis)
|
|
swpFlags = SWP_HIDEWINDOW;
|
|
else if ((m_nStateFlags & delayShow) && !bVis)
|
|
swpFlags = SWP_SHOWWINDOW;
|
|
m_nStateFlags &= ~(delayShow|delayHide);
|
|
if (swpFlags != 0)
|
|
{
|
|
SetWindowPos(NULL, 0, 0, 0, 0, swpFlags|
|
|
SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
|
|
}
|
|
*/
|
|
|
|
// The code below updates the menus (especially the child-frame
|
|
// system menu when the frame is maximized).
|
|
|
|
// the style must be visible and if it is docked
|
|
// the dockbar style must also be visible
|
|
if ((GetStyle() & WS_VISIBLE))
|
|
{
|
|
CFrameWnd* pTarget = (CFrameWnd*)GetOwner();
|
|
if (pTarget == NULL || !pTarget->IsFrameWnd())
|
|
pTarget = GetParentFrame();
|
|
if (pTarget != NULL)
|
|
OnUpdateCmdUI(pTarget, (BOOL)wParam);
|
|
}
|
|
|
|
return 0L;
|
|
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CRebarWnd
|
|
|
|
CRebarWnd::CRebarWnd() : m_fRedraw(true)
|
|
{
|
|
}
|
|
|
|
CRebarWnd::~CRebarWnd()
|
|
{
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CRebarWnd, CWnd)
|
|
//{{AFX_MSG_MAP(CRebarWnd)
|
|
ON_WM_CREATE()
|
|
ON_WM_SYSCOLORCHANGE()
|
|
ON_WM_ERASEBKGND()
|
|
//}}AFX_MSG_MAP
|
|
ON_MESSAGE (WM_SETREDRAW, OnSetRedraw)
|
|
ON_NOTIFY_REFLECT(RBN_AUTOSIZE, OnRebarAutoSize)
|
|
ON_NOTIFY_REFLECT(RBN_HEIGHTCHANGE, OnRebarHeightChange)
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CRebarWnd message handlers
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CRebarWnd::OnCreate
|
|
*
|
|
* WM_CREATE handler for CRebarWnd.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
int CRebarWnd::OnCreate(LPCREATESTRUCT lpCreateStruct)
|
|
{
|
|
if (CWnd::OnCreate(lpCreateStruct) == -1)
|
|
return -1;
|
|
|
|
SetTextColor (GetSysColor (COLOR_BTNTEXT));
|
|
SetBkColor (GetSysColor (COLOR_BTNFACE));
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CRebarWnd::OnSysColorChange
|
|
*
|
|
* WM_SYSCOLORCHANGE handler for CRebarWnd.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CRebarWnd::OnSysColorChange()
|
|
{
|
|
CWnd::OnSysColorChange();
|
|
|
|
SetTextColor (GetSysColor (COLOR_BTNTEXT));
|
|
SetBkColor (GetSysColor (COLOR_BTNFACE));
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CRebarWnd::OnEraseBkgnd
|
|
*
|
|
* WM_ERASEBKGND handler for CRebarWnd.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
BOOL CRebarWnd::OnEraseBkgnd(CDC* pDC)
|
|
{
|
|
/*
|
|
* If redraw is turned on, forward this on the the window. If it's
|
|
* not on, we want to prevent erasing the background to minimize
|
|
* flicker.
|
|
*/
|
|
if (m_fRedraw)
|
|
return CWnd::OnEraseBkgnd(pDC);
|
|
|
|
return (true);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CRebarWnd::OnSetRedraw
|
|
*
|
|
* WM_SETREDRAW handler for CRebarWnd.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
LRESULT CRebarWnd::OnSetRedraw (WPARAM wParam, LPARAM)
|
|
{
|
|
m_fRedraw = (wParam != FALSE);
|
|
return (Default ());
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CRebarWnd::OnRebarAutoSize
|
|
*
|
|
* RBN_REBARAUTOSIZE handler for CRebarWnd.
|
|
*
|
|
* We want to keep the menu band on a row by itself, without any other
|
|
* toolbars. To do this, each time the rebar resizes we'll make sure that
|
|
* the first visible band after the menu band starts a new row.
|
|
*
|
|
* A more foolproof way to do this would be to have a separate rebar
|
|
* for the menu. If we do that, we'll need to insure that tabbing between
|
|
* toolbars (Ctrl+Tab) still works.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CRebarWnd::OnRebarAutoSize(NMHDR* pNotify, LRESULT* result)
|
|
{
|
|
/*
|
|
* insure that the band following the menu is on a new line
|
|
*/
|
|
CMainFrame* pFrame = AMCGetMainWnd();
|
|
if (pFrame == NULL)
|
|
return;
|
|
|
|
CToolBarCtrlEx* pMenuBar = pFrame->GetMenuBar();
|
|
if (pMenuBar == NULL)
|
|
return;
|
|
|
|
int iMenuBand = pMenuBar->GetBandIndex();
|
|
if (iMenuBand == -1)
|
|
return;
|
|
|
|
/*
|
|
* if the menu band is the last band on the rebar, we're done
|
|
*/
|
|
int cBands = GetBandCount();
|
|
if (iMenuBand == cBands-1)
|
|
return;
|
|
|
|
REBARBANDINFO rbbi;
|
|
rbbi.cbSize = sizeof (rbbi);
|
|
rbbi.fMask = RBBIM_STYLE;
|
|
|
|
/*
|
|
* if the first visible band following the menu band isn't
|
|
* on a new line, make it so
|
|
*/
|
|
for (int iBand = iMenuBand+1; iBand < cBands; iBand++)
|
|
{
|
|
if (GetBandInfo (iBand, &rbbi) && !(rbbi.fStyle & RBBS_HIDDEN))
|
|
{
|
|
if (!(rbbi.fStyle & RBBS_BREAK))
|
|
{
|
|
rbbi.fStyle |= RBBS_BREAK;
|
|
SetBandInfo (iBand, &rbbi);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: OnRebarHeightChange
|
|
//
|
|
// Synopsis: RBN_HEIGHTCHANGE notification handler.
|
|
//
|
|
// When the rebar changes its height, we need to allow the
|
|
// docking host to resize to accomodate it.
|
|
//
|
|
// Arguments: Not used.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
void CRebarWnd::OnRebarHeightChange(NMHDR* pNotify, LRESULT* result)
|
|
{
|
|
CRebarDockWindow* pRebarWnd = (CRebarDockWindow*) GetParent();
|
|
if (pRebarWnd && IsWindow(pRebarWnd->m_hWnd))
|
|
pRebarWnd->UpdateWindowSize();
|
|
}
|
|
|
|
|
|
CRect CRebarWnd::CalculateSize(CRect maxRect)
|
|
{
|
|
CRect rect;
|
|
GetClientRect(&rect);
|
|
// TRACE(_T("rc.bottom=%d\n"),rect.bottom);
|
|
|
|
rect.right=maxRect.Width();
|
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
BOOL CRebarWnd::Create(LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
ASSERT_VALID(pParentWnd); // must have a parent
|
|
|
|
INITCOMMONCONTROLSEX icex;
|
|
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
|
|
icex.dwICC = ICC_COOL_CLASSES;
|
|
|
|
if (!InitCommonControlsEx(&icex))
|
|
return bResult;
|
|
|
|
dwStyle |= WS_BORDER | /*WS_CLIPCHILDREN | WS_CLIPSIBLINGS |*/
|
|
CCS_NODIVIDER | CCS_NOPARENTALIGN |
|
|
RBS_VARHEIGHT | RBS_BANDBORDERS;
|
|
|
|
if (CWnd::CreateEx (WS_EX_TOOLWINDOW,
|
|
REBARCLASSNAME,
|
|
lpszWindowName,
|
|
dwStyle,
|
|
rect,
|
|
pParentWnd,
|
|
nID))
|
|
{
|
|
// Initialize and send the REBARINFO structure.
|
|
REBARINFO rbi;
|
|
rbi.cbSize = sizeof(REBARINFO);
|
|
rbi.fMask = 0;
|
|
rbi.himl = NULL;
|
|
|
|
if (SetBarInfo (&rbi))
|
|
bResult=TRUE;
|
|
}
|
|
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
BOOL CRebarWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
|
|
{
|
|
if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
|
|
return (TRUE);
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
LRESULT CRebarWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// this is required for Action, View drop-downs
|
|
if ((WM_COMMAND == message) && IsWindow ((HWND) lParam))
|
|
return ::SendMessage((HWND)lParam, message, wParam, lParam);
|
|
|
|
return CWnd::WindowProc(message, wParam, lParam);
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CToolBar idle update through CToolCmdUI class
|
|
|
|
|
|
void CToolCmdUIEx::Enable(BOOL bOn)
|
|
{
|
|
m_bEnableChanged = TRUE;
|
|
CToolBarCtrlEx* pToolBar = (CToolBarCtrlEx*)m_pOther;
|
|
ASSERT(pToolBar != NULL);
|
|
ASSERT(m_nIndex < m_nIndexMax);
|
|
|
|
UINT nNewStyle = pToolBar->GetState(m_nID);
|
|
if (!bOn)
|
|
{
|
|
nNewStyle &= ~TBSTATE_ENABLED;
|
|
// WINBUG: If a button is currently pressed and then is disabled
|
|
// COMCTL32.DLL does not unpress the button, even after the mouse
|
|
// button goes up! We work around this bug by forcing TBBS_PRESSED
|
|
// off when a button is disabled.
|
|
nNewStyle &= ~TBBS_PRESSED;
|
|
}
|
|
else
|
|
{
|
|
nNewStyle |= TBSTATE_ENABLED;
|
|
}
|
|
//ASSERT(!(nNewStyle & TBBS_SEPARATOR));
|
|
pToolBar->SetState(m_nID, nNewStyle);
|
|
}
|
|
|
|
void CToolCmdUIEx::SetCheck(int nCheck)
|
|
{
|
|
ASSERT(nCheck >= 0 && nCheck <= 2); // 0=>off, 1=>on, 2=>indeterminate
|
|
CToolBarCtrlEx* pToolBar = (CToolBarCtrlEx*)m_pOther;
|
|
ASSERT(pToolBar != NULL);
|
|
ASSERT(m_nIndex < m_nIndexMax);
|
|
|
|
UINT nNewStyle = pToolBar->GetState(m_nID) &
|
|
~(TBBS_CHECKED | TBBS_INDETERMINATE | TBBS_CHECKBOX);
|
|
|
|
|
|
if (nCheck == 1)
|
|
nNewStyle |= TBBS_CHECKED | TBBS_CHECKBOX;
|
|
else if (nCheck == 2)
|
|
nNewStyle |= TBBS_INDETERMINATE;
|
|
|
|
pToolBar->SetState(m_nID, nNewStyle);
|
|
}
|
|
|
|
void CToolCmdUIEx::SetText(LPCTSTR)
|
|
{
|
|
|
|
}
|
|
|
|
void CToolCmdUIEx::SetHidden(BOOL bHidden)
|
|
{
|
|
|
|
m_bEnableChanged = TRUE;
|
|
CToolBarCtrlEx* pToolBar = (CToolBarCtrlEx*)m_pOther;
|
|
ASSERT(pToolBar != NULL);
|
|
ASSERT(m_nIndex < m_nIndexMax);
|
|
|
|
pToolBar->HideButton(m_nID, bHidden);
|
|
|
|
pToolBar->UpdateToolbarSize();
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CMMCToolBarCtrlEx
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::GetTrackAccel
|
|
*
|
|
* Manages the accelerator table singleton for CMMCToolBarCtrlEx
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
const CAccel& CMMCToolBarCtrlEx::GetTrackAccel ()
|
|
{
|
|
static ACCEL aaclTrack[] = {
|
|
{ FVIRTKEY, VK_RETURN, CMMCToolBarCtrlEx::ID_MTBX_PRESS_HOT_BUTTON },
|
|
{ FVIRTKEY, VK_RIGHT, CMMCToolBarCtrlEx::ID_MTBX_NEXT_BUTTON },
|
|
{ FVIRTKEY, VK_LEFT, CMMCToolBarCtrlEx::ID_MTBX_PREV_BUTTON },
|
|
{ FVIRTKEY, VK_ESCAPE, CMMCToolBarCtrlEx::ID_MTBX_END_TRACKING },
|
|
{ FVIRTKEY, VK_TAB, CMMCToolBarCtrlEx::ID_MTBX_NEXT_BUTTON },
|
|
{ FVIRTKEY | FSHIFT, VK_TAB, CMMCToolBarCtrlEx::ID_MTBX_PREV_BUTTON },
|
|
};
|
|
|
|
static const CAccel TrackAccel (aaclTrack, countof (aaclTrack));
|
|
return (TrackAccel);
|
|
}
|
|
|
|
|
|
CMMCToolBarCtrlEx::CMMCToolBarCtrlEx()
|
|
{
|
|
m_fTrackingToolBar = false;
|
|
m_fFakeFocusApplied = false;
|
|
}
|
|
|
|
CMMCToolBarCtrlEx::~CMMCToolBarCtrlEx()
|
|
{
|
|
}
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CMMCToolBarCtrlEx, CToolBarCtrlEx)
|
|
//{{AFX_MSG_MAP(CMMCToolBarCtrlEx)
|
|
ON_NOTIFY_REFLECT(TBN_HOTITEMCHANGE, OnHotItemChange)
|
|
ON_WM_LBUTTONDOWN()
|
|
ON_WM_MBUTTONDOWN()
|
|
ON_WM_RBUTTONDOWN()
|
|
ON_WM_DESTROY()
|
|
ON_COMMAND(ID_MTBX_NEXT_BUTTON, OnNextButton)
|
|
ON_COMMAND(ID_MTBX_PREV_BUTTON, OnPrevButton)
|
|
ON_COMMAND(ID_MTBX_END_TRACKING, EndTracking)
|
|
ON_COMMAND(ID_MTBX_PRESS_HOT_BUTTON, OnPressHotButton)
|
|
ON_WM_CREATE()
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CMMCToolBarCtrlEx message handlers
|
|
|
|
BOOL CMMCToolBarCtrlEx::PreTranslateMessage(MSG* pMsg)
|
|
{
|
|
if (CToolBarCtrlEx::PreTranslateMessage (pMsg))
|
|
return (TRUE);
|
|
|
|
if ((pMsg->message >= WM_KEYFIRST) && (pMsg->message <= WM_KEYLAST))
|
|
{
|
|
const CAccel& TrackAccel = GetTrackAccel();
|
|
ASSERT (TrackAccel != NULL);
|
|
|
|
// ...or try to handle it here.
|
|
if (m_fTrackingToolBar && TrackAccel.TranslateAccelerator (m_hWnd, pMsg))
|
|
return (TRUE);
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::OnCreate
|
|
*
|
|
* WM_CREATE handler for CMMCToolBarCtrlEx.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
int CMMCToolBarCtrlEx::OnCreate(LPCREATESTRUCT lpCreateStruct)
|
|
{
|
|
DECLARE_SC (sc, _T("CMMCToolBarCtrlEx::OnCreate"));
|
|
|
|
if (CToolBarCtrlEx::OnCreate(lpCreateStruct) == -1)
|
|
return -1;
|
|
|
|
/*
|
|
* Create our accessibility object so accessibility tools can follow
|
|
* keyboard access to the toolbar. If we can't, some accessibility
|
|
* tools (those that are super paranoid about confirming that objects
|
|
* for which they receive EVENT_OBJECT_FOCUS have a state of
|
|
* STATE_SYSTEM_FOCUSED) may not be able to follow along with toolbar
|
|
* tracking, but we can continue.
|
|
*/
|
|
sc = ScInitAccessibility();
|
|
if (sc)
|
|
sc.TraceAndClear();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::OnDestroy
|
|
*
|
|
* WM_DESTROY handler for CMMCToolBarCtrlEx.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMMCToolBarCtrlEx::OnDestroy()
|
|
{
|
|
CToolBarCtrlEx::OnDestroy();
|
|
|
|
/*
|
|
* if we provided an IAccPropServer to oleacc.dll, revoke it now
|
|
*/
|
|
if (m_spAccPropServices != NULL)
|
|
{
|
|
m_spAccPropServices->ClearHwndProps (m_hWnd, OBJID_CLIENT, CHILDID_SELF,
|
|
&PROPID_ACC_STATE, 1);
|
|
m_spAccPropServices.Release();
|
|
}
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::OnLButtonDown
|
|
*
|
|
* Allows the tracker to turn off when someone clicks elsewhere
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMMCToolBarCtrlEx::OnLButtonDown(UINT nFlags, CPoint point)
|
|
{
|
|
if (GetCapture() == this)
|
|
EndTracking();
|
|
else
|
|
CToolBarCtrlEx::OnLButtonDown(nFlags, point );
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::OnMButtonDown
|
|
*
|
|
* Allows the tracker to turn off when someone clicks elsewhere
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMMCToolBarCtrlEx::OnMButtonDown(UINT nFlags, CPoint point)
|
|
{
|
|
if (GetCapture() == this)
|
|
EndTracking();
|
|
else
|
|
CToolBarCtrlEx::OnMButtonDown(nFlags, point );
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::OnRButtonDown
|
|
*
|
|
* Allows the tracker to turn off when someone clicks elsewhere
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMMCToolBarCtrlEx::OnRButtonDown(UINT nFlags, CPoint point)
|
|
{
|
|
if (GetCapture() == this)
|
|
EndTracking();
|
|
else
|
|
CToolBarCtrlEx::OnRButtonDown(nFlags, point );
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::OnNextButton
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMMCToolBarCtrlEx::OnNextButton ()
|
|
{
|
|
// In a mirrored toolbar swap left and right keys.
|
|
if (m_fMirrored)
|
|
SetHotItem (GetPrevButtonIndex (GetHotItem ()));
|
|
else
|
|
SetHotItem (GetNextButtonIndex (GetHotItem ()));
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::OnPrevButton
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMMCToolBarCtrlEx::OnPrevButton ()
|
|
{
|
|
// In a mirrored toolbar swap left and right keys.
|
|
if (m_fMirrored)
|
|
SetHotItem (GetNextButtonIndex (GetHotItem ()));
|
|
else
|
|
SetHotItem (GetPrevButtonIndex (GetHotItem ()));
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::BeginTracking
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMMCToolBarCtrlEx::BeginTracking ()
|
|
{
|
|
BeginTracking2 (GetMainAuxWnd());
|
|
}
|
|
|
|
void CMMCToolBarCtrlEx::BeginTracking2 (CToolbarTrackerAuxWnd* pAuxWnd)
|
|
{
|
|
if (!m_fTrackingToolBar)
|
|
{
|
|
m_fTrackingToolBar = true;
|
|
SetHotItem (GetFirstButtonIndex ());
|
|
|
|
// Captures the mouse
|
|
// This prevents the mouse from activating something else without
|
|
// first giving us a chance to deactivate the tool bar.
|
|
SetCapture();
|
|
// make sure to set standard corsor since we've stolen the mouse
|
|
// see BUG 28458 MMC: Mouse icon does not refresh when menu is activated by pressing ALT key
|
|
::SetCursor( ::LoadCursor( NULL, IDC_ARROW ) );
|
|
|
|
if (pAuxWnd != NULL)
|
|
pAuxWnd->TrackToolbar (this);
|
|
}
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::EndTracking
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMMCToolBarCtrlEx::EndTracking ()
|
|
{
|
|
EndTracking2 (GetMainAuxWnd());
|
|
}
|
|
|
|
void CMMCToolBarCtrlEx::EndTracking2 (CToolbarTrackerAuxWnd* pAuxWnd)
|
|
{
|
|
DECLARE_SC (sc, _T("CMMCToolBarCtrlEx::EndTracking2"));
|
|
|
|
/*
|
|
* tell accessibility tools that the "focus" went back to the real
|
|
* focus window
|
|
*/
|
|
sc = ScRestoreAccFocus();
|
|
if (sc)
|
|
sc.TraceAndClear();
|
|
|
|
if (m_fTrackingToolBar)
|
|
{
|
|
SetHotItem (-1);
|
|
m_fTrackingToolBar = false;
|
|
|
|
// Releases the mouse This gives us a chance to deactivate the tool bar
|
|
// before anything else is activated.
|
|
ReleaseCapture();
|
|
|
|
if (pAuxWnd != NULL)
|
|
pAuxWnd->TrackToolbar (NULL);
|
|
}
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::OnPressHotButton
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMMCToolBarCtrlEx::OnPressHotButton ()
|
|
{
|
|
int nHotIndex = GetHotItem();
|
|
ASSERT (m_fTrackingToolBar);
|
|
ASSERT (nHotIndex != -1);
|
|
|
|
TBBUTTON tb;
|
|
GetButton (nHotIndex, &tb);
|
|
|
|
// press the button and pause to show the press
|
|
PressButton (tb.idCommand, true);
|
|
UpdateWindow ();
|
|
Sleep (50);
|
|
|
|
// EndTracking for surrogate windows will detach the window,
|
|
// so remember everything that we'll need later
|
|
HWND hwnd = m_hWnd;
|
|
CWnd* pwndOwner = SetOwner (NULL);
|
|
SetOwner (pwndOwner);
|
|
|
|
// release the button
|
|
PressButton (tb.idCommand, false);
|
|
EndTracking ();
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
/* WARNING: don't use any members of this class beyond this point */
|
|
/*-----------------------------------------------------------------*/
|
|
|
|
// make sure drawing is completed
|
|
::UpdateWindow (hwnd);
|
|
|
|
// send a command to our owner
|
|
pwndOwner->SendMessage (WM_COMMAND, MAKEWPARAM (tb.idCommand, BN_CLICKED),
|
|
(LPARAM) hwnd);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::GetFirstButtonIndex
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
int CMMCToolBarCtrlEx::GetFirstButtonIndex ()
|
|
{
|
|
return (GetNextButtonIndex (-1));
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::GetNextButtonIndex
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
int CMMCToolBarCtrlEx::GetNextButtonIndex (
|
|
int nStartIndex,
|
|
int nCount /* = 1 */)
|
|
{
|
|
return (GetNextButtonIndexWorker (nStartIndex, nCount, true));
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::GetPrevButtonIndex
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
int CMMCToolBarCtrlEx::GetPrevButtonIndex (
|
|
int nStartIndex,
|
|
int nCount /* = 1 */)
|
|
{
|
|
return (GetNextButtonIndexWorker (nStartIndex, nCount, false));
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::GetNextButtonIndexWorker
|
|
*
|
|
*
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
int CMMCToolBarCtrlEx::GetNextButtonIndexWorker (
|
|
int nStartIndex,
|
|
int nCount,
|
|
bool fAdvance)
|
|
{
|
|
ASSERT (nCount >= 0);
|
|
|
|
if (!fAdvance)
|
|
nCount = -nCount;
|
|
|
|
int cButtons = GetButtonCount ();
|
|
int nNextIndex = nStartIndex;
|
|
bool fIgnorable;
|
|
|
|
if (0 == cButtons)
|
|
return nStartIndex;
|
|
|
|
/*
|
|
* loop until we find a next index that we don't want to
|
|
* ignore, or until we've checked each of the buttons
|
|
*/
|
|
do
|
|
{
|
|
nNextIndex = (nNextIndex + cButtons + nCount) % cButtons;
|
|
fIgnorable = IsIgnorableButton (nNextIndex);
|
|
|
|
if (fIgnorable)
|
|
nCount = fAdvance ? 1 : -1;
|
|
|
|
// prevent an infinite loop finding the first button
|
|
if ((nStartIndex == -1) && (nNextIndex == cButtons-1))
|
|
nNextIndex = nStartIndex;
|
|
|
|
} while (fIgnorable && (nNextIndex != nStartIndex));
|
|
|
|
return (nNextIndex);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* IsIgnorableButton
|
|
*
|
|
* Determines if a toolbar button is "ignorable" from a UI perspective,
|
|
* i.e. whether it is hidden, disabled, or a separator.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
bool CMMCToolBarCtrlEx::IsIgnorableButton (int nButtonIndex)
|
|
{
|
|
TBBUTTON tb;
|
|
GetButton (nButtonIndex, &tb);
|
|
|
|
return (::IsIgnorableButton (tb));
|
|
}
|
|
|
|
bool IsIgnorableButton (const TBBUTTON& tb)
|
|
{
|
|
if (tb.fsStyle & TBSTYLE_SEP)
|
|
return (true);
|
|
|
|
if (tb.fsState & TBSTATE_HIDDEN)
|
|
return (true);
|
|
|
|
if (!(tb.fsState & TBSTATE_ENABLED))
|
|
return (true);
|
|
|
|
return (false);
|
|
}
|
|
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::OnHotItemChange
|
|
*
|
|
* Reflected TBN_HOTITEMCHANGE handler for void CMMCToolBarCtrlEx.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
void CMMCToolBarCtrlEx::OnHotItemChange (
|
|
NMHDR * pHdr,
|
|
LRESULT * pResult)
|
|
{
|
|
ASSERT (CWnd::FromHandle (pHdr->hwndFrom) == this);
|
|
CToolbarTrackerAuxWnd* pAuxWnd = GetMainAuxWnd();
|
|
LPNMTBHOTITEM ptbhi = (LPNMTBHOTITEM) pHdr;
|
|
|
|
Trace (tagToolbarAccessibility, _T("TBN_HOTITEMCHANGE: idOld=%d idNew=%d"), ptbhi->idOld, ptbhi->idNew);
|
|
|
|
/*
|
|
* if we're not in tracking mode, hot item change is OK
|
|
*/
|
|
if (pAuxWnd == NULL)
|
|
*pResult = 0;
|
|
|
|
/*
|
|
* if we're tracking, but this isn't the tracked toolbar,
|
|
* the hot item change isn't OK
|
|
*/
|
|
else if (!IsTrackingToolBar())
|
|
*pResult = 1;
|
|
|
|
/*
|
|
* prevent mouse movement over empty portions of
|
|
* the bar from changing the hot item
|
|
*/
|
|
else
|
|
{
|
|
const DWORD dwIgnoreFlags = (HICF_MOUSE | HICF_LEAVING);
|
|
*pResult = ((ptbhi->dwFlags & dwIgnoreFlags) == dwIgnoreFlags);
|
|
}
|
|
|
|
/*
|
|
* If we're allowing the hot item change while we're keyboard tracking
|
|
* the toolbar (to exclude changes due to mouse tracking), send a focus
|
|
* event so accessibility tools like Magnifier and Narrator can follow
|
|
* the change. This fake-focus effect is undone in ScRestoreAccFocus.
|
|
*/
|
|
int idChild;
|
|
if (IsTrackingToolBar() &&
|
|
(*pResult == 0) &&
|
|
(ptbhi->idNew != 0) &&
|
|
((idChild = CommandToIndex(ptbhi->idNew)) != -1))
|
|
{
|
|
Trace (tagToolbarAccessibility, _T("Sending focus event for button %d"), idChild+1);
|
|
NotifyWinEvent (EVENT_OBJECT_FOCUS, m_hWnd, OBJID_CLIENT, idChild+1 /*1-based*/);
|
|
m_fFakeFocusApplied = true;
|
|
}
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::ScInitAccessibility
|
|
*
|
|
* Creates the IAccPropServer object that will fool accessibility tools into
|
|
* thinking that this toolbar really has the focus when we're in tracking
|
|
* mode, even though it really doesn't
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
SC CMMCToolBarCtrlEx::ScInitAccessibility ()
|
|
{
|
|
DECLARE_SC (sc, _T("CMMCToolBarCtrlEx::ScInitAccessibility"));
|
|
|
|
/*
|
|
* if we've already initialized, just return
|
|
*/
|
|
if (m_spAccPropServices != NULL)
|
|
return (sc);
|
|
|
|
/*
|
|
* create a CLSID_AccPropServices provided by the MSAA runtime (oleacc.dll)
|
|
* This is a new feature in oleacc.dll, so trace failure as an informational
|
|
* message rather than an error.
|
|
*/
|
|
SC scNoTrace = m_spAccPropServices.CoCreateInstance (CLSID_AccPropServices);
|
|
if (scNoTrace)
|
|
{
|
|
#ifdef DBG
|
|
TCHAR szErrorText[256];
|
|
sc.GetErrorMessage (countof(szErrorText), szErrorText);
|
|
StripTrailingWhitespace (szErrorText);
|
|
|
|
Trace (tagToolbarAccessibility, _T("Failed to create CLSID_AccPropServices"));
|
|
Trace (tagToolbarAccessibility, _T("SC = 0x%08X = %d = \"%s\""),
|
|
sc.GetCode(), LOWORD(sc.GetCode()), szErrorText);
|
|
#endif // DBG
|
|
|
|
return (sc);
|
|
}
|
|
|
|
sc = ScCheckPointers (m_spAccPropServices, E_UNEXPECTED);
|
|
if (sc)
|
|
return (sc);
|
|
|
|
/*
|
|
* create the property server
|
|
*/
|
|
sc = CTiedComObjectCreator<CMMCToolBarAccServer>::ScCreateAndConnect(*this, m_spAccPropServer);
|
|
if (sc)
|
|
return (sc);
|
|
|
|
sc = ScCheckPointers (m_spAccPropServer, E_UNEXPECTED);
|
|
if (sc)
|
|
return (sc);
|
|
|
|
/*
|
|
* collect the properties we'll be providing, insuring there
|
|
* are no duplicates
|
|
*/
|
|
sc = ScInsertAccPropIDs (m_vPropIDs);
|
|
if (sc)
|
|
return (sc);
|
|
|
|
std::sort (m_vPropIDs.begin(), m_vPropIDs.end()); // std::unique needs a sorted range
|
|
m_vPropIDs.erase (std::unique (m_vPropIDs.begin(), m_vPropIDs.end()),
|
|
m_vPropIDs.end());
|
|
|
|
/*
|
|
* insure m_vPropIDs contains no duplicates (IAccPropServices::SetHwndPropServer
|
|
* depends on it)
|
|
*/
|
|
#ifdef DBG
|
|
for (int i = 0; i < m_vPropIDs.size()-1; i++)
|
|
{
|
|
ASSERT (m_vPropIDs[i] < m_vPropIDs[i+1]);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Enable our property server for this window. We should be able to
|
|
* hook all properties in one fell swoop, but there's a bug in oleacc.dll
|
|
* that prevents this. Hooking the properties one at a time works fine.
|
|
*/
|
|
#if 0
|
|
sc = m_spAccPropServices->SetHwndPropServer (m_hWnd,
|
|
OBJID_CLIENT,
|
|
CHILDID_SELF,
|
|
m_vPropIDs.begin(),
|
|
m_vPropIDs.size(),
|
|
m_spAccPropServer,
|
|
ANNO_CONTAINER);
|
|
if (sc)
|
|
{
|
|
Trace (tagToolbarAccessibility, _T("SetHwndPropServer failed"));
|
|
return (sc);
|
|
}
|
|
#else
|
|
for (int i = 0; i < m_vPropIDs.size(); i++)
|
|
{
|
|
sc = m_spAccPropServices->SetHwndPropServer (m_hWnd,
|
|
OBJID_CLIENT,
|
|
CHILDID_SELF,
|
|
&m_vPropIDs[i],
|
|
1,
|
|
m_spAccPropServer,
|
|
ANNO_CONTAINER);
|
|
if (sc)
|
|
{
|
|
#ifdef DBG
|
|
USES_CONVERSION;
|
|
WCHAR wzPropID[40];
|
|
StringFromGUID2 (m_vPropIDs[i], wzPropID, countof(wzPropID));
|
|
Trace (tagToolbarAccessibility, _T("SetHwndPropServer failed for %s"), W2T(wzPropID));
|
|
#endif
|
|
sc.TraceAndClear();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return (sc);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::ScInsertAccPropIDs
|
|
*
|
|
* Inserts the IDs of the accessibility properties supported by
|
|
* CMMCToolBarCtrlEx (see ScGetPropValue).
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
SC CMMCToolBarCtrlEx::ScInsertAccPropIDs (PropIDCollection& v)
|
|
{
|
|
DECLARE_SC (sc, _T("CMMCToolBarCtrlEx::ScInsertAccPropIDs"));
|
|
v.push_back (PROPID_ACC_STATE);
|
|
return (sc);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::ScGetPropValue
|
|
*
|
|
* Implements IAccPropServer::GetPropValue for CMMCToolBarCtrlEx. If this
|
|
* funtion is asked for PROPID_ACC_STATE for the currently hot button while
|
|
* we're in tracking mode, it'll return a state that mimics the state
|
|
* returned by a plain toolbar when it really has the focus.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
SC CMMCToolBarCtrlEx::ScGetPropValue (
|
|
const BYTE* pIDString,
|
|
DWORD dwIDStringLen,
|
|
MSAAPROPID idProp,
|
|
VARIANT * pvarValue,
|
|
BOOL * pfGotProp)
|
|
{
|
|
DECLARE_SC (sc, _T("CMMCToolBarCtrlEx::ScGetPropValue"));
|
|
|
|
sc = ScCheckPointers (pIDString, pvarValue, pfGotProp);
|
|
if (sc)
|
|
return (sc);
|
|
|
|
/*
|
|
* assume no prop returned
|
|
*/
|
|
*pfGotProp = false;
|
|
V_VT(pvarValue) = VT_EMPTY;
|
|
|
|
sc = ScCheckPointers (m_spAccPropServer, E_UNEXPECTED);
|
|
if (sc)
|
|
return (sc);
|
|
|
|
/*
|
|
* extract the child ID from the identity string
|
|
*/
|
|
HWND hwnd;
|
|
DWORD idObject, idChild;
|
|
sc = m_spAccPropServices->DecomposeHwndIdentityString (pIDString, dwIDStringLen,
|
|
&hwnd, &idObject, &idChild);
|
|
if (sc)
|
|
return (sc);
|
|
|
|
#ifdef DBG
|
|
#define DEFINE_PDI(p) PropDebugInfo (p, _T(#p))
|
|
|
|
static const struct PropDebugInfo {
|
|
// constructor used to get around nested structure initialization weirdness
|
|
PropDebugInfo(const MSAAPROPID& id, LPCTSTR psz) : idProp(id), pszProp(psz) {}
|
|
|
|
const MSAAPROPID& idProp;
|
|
LPCTSTR pszProp;
|
|
} rgpdi[] = {
|
|
DEFINE_PDI (PROPID_ACC_NAME ),
|
|
DEFINE_PDI (PROPID_ACC_VALUE ),
|
|
DEFINE_PDI (PROPID_ACC_DESCRIPTION ),
|
|
DEFINE_PDI (PROPID_ACC_ROLE ),
|
|
DEFINE_PDI (PROPID_ACC_STATE ),
|
|
DEFINE_PDI (PROPID_ACC_HELP ),
|
|
DEFINE_PDI (PROPID_ACC_KEYBOARDSHORTCUT),
|
|
DEFINE_PDI (PROPID_ACC_DEFAULTACTION ),
|
|
DEFINE_PDI (PROPID_ACC_HELPTOPIC ),
|
|
DEFINE_PDI (PROPID_ACC_FOCUS ),
|
|
DEFINE_PDI (PROPID_ACC_SELECTION ),
|
|
DEFINE_PDI (PROPID_ACC_PARENT ),
|
|
DEFINE_PDI (PROPID_ACC_NAV_UP ),
|
|
DEFINE_PDI (PROPID_ACC_NAV_DOWN ),
|
|
DEFINE_PDI (PROPID_ACC_NAV_LEFT ),
|
|
DEFINE_PDI (PROPID_ACC_NAV_RIGHT ),
|
|
DEFINE_PDI (PROPID_ACC_NAV_PREV ),
|
|
DEFINE_PDI (PROPID_ACC_NAV_NEXT ),
|
|
DEFINE_PDI (PROPID_ACC_NAV_FIRSTCHILD ),
|
|
DEFINE_PDI (PROPID_ACC_NAV_LASTCHILD ),
|
|
DEFINE_PDI (PROPID_ACC_VALUEMAP ),
|
|
DEFINE_PDI (PROPID_ACC_ROLEMAP ),
|
|
DEFINE_PDI (PROPID_ACC_STATEMAP ),
|
|
};
|
|
|
|
/*
|
|
* dump the requested property
|
|
*/
|
|
for (int i = 0; i < countof(rgpdi); i++)
|
|
{
|
|
if (rgpdi[i].idProp == idProp)
|
|
{
|
|
Trace (tagToolbarAccessibility, _T("GetPropValue: %s requested for child %d"), rgpdi[i].pszProp, idChild);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == countof(rgpdi))
|
|
{
|
|
USES_CONVERSION;
|
|
WCHAR wzPropID[40];
|
|
StringFromGUID2 (idProp, wzPropID, countof(wzPropID));
|
|
Trace (tagToolbarAccessibility, _T("GetPropValue: Unknown property ID %s"), W2T(wzPropID));
|
|
}
|
|
|
|
/*
|
|
* insure m_vPropIDs is sorted (std::lower_bound depends on it)
|
|
*/
|
|
for (int i = 0; i < m_vPropIDs.size()-1; i++)
|
|
{
|
|
ASSERT (m_vPropIDs[i] < m_vPropIDs[i+1]);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* if we're asked for a property we didn't claim to support, don't return
|
|
* anything
|
|
*/
|
|
if (m_vPropIDs.end() == std::lower_bound (m_vPropIDs.begin(), m_vPropIDs.end(), idProp))
|
|
{
|
|
Trace (tagToolbarAccessibility, _T("GetPropValue: Unexpected property request"));
|
|
return (sc);
|
|
}
|
|
|
|
/*
|
|
* get the property
|
|
*/
|
|
sc = ScGetPropValue (hwnd, idObject, idChild, idProp, *pvarValue, *pfGotProp);
|
|
if (sc)
|
|
return (sc);
|
|
|
|
return (sc);
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::ScGetPropValue
|
|
*
|
|
* Returns accessibility properties supported by CMMCToolBarCtrlEx.
|
|
*
|
|
* If a property is returned, fGotProp is set to true. If it is not
|
|
* returned, the value of fGotProp is unchanged, since the property might
|
|
* have been provided by a base/derived class.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
SC CMMCToolBarCtrlEx::ScGetPropValue (
|
|
HWND hwnd, // I:accessible window
|
|
DWORD idObject, // I:accessible object
|
|
DWORD idChild, // I:accessible child object
|
|
const MSAAPROPID& idProp, // I:property requested
|
|
VARIANT& varValue, // O:returned property value
|
|
BOOL& fGotProp) // O:was a property returned?
|
|
{
|
|
DECLARE_SC (sc, _T("CMMCToolBarCtrlEx::ScGetPropValue"));
|
|
|
|
/*
|
|
* handle requests for state
|
|
*/
|
|
if (idProp == PROPID_ACC_STATE)
|
|
{
|
|
/*
|
|
* only override the property for child elements, not the control itself;
|
|
* don't return a property
|
|
*/
|
|
if (idChild == CHILDID_SELF)
|
|
{
|
|
Trace (tagToolbarAccessibility, _T("GetPropValue: no state for CHILDID_SELF"));
|
|
return (sc);
|
|
}
|
|
|
|
/*
|
|
* if we're not in tracking mode, don't return a property
|
|
*/
|
|
if (!IsTrackingToolBar())
|
|
{
|
|
Trace (tagToolbarAccessibility, _T("GetPropValue: not in tracking mode, no state returned"));
|
|
return (sc);
|
|
}
|
|
|
|
/*
|
|
* if the current hot item isn't the child we're asked for, don't return a property
|
|
*/
|
|
int nHotItem = GetHotItem();
|
|
if (nHotItem != (idChild-1) /*0-based*/)
|
|
{
|
|
Trace (tagToolbarAccessibility, _T("GetPropValue: hot item is %d, no state returned"), nHotItem);
|
|
return (sc);
|
|
}
|
|
|
|
/*
|
|
* if we get here, we're asked for state for the current hot item;
|
|
* return STATE_SYSTEM_FOCUSED | STATE_SYSTEM_HOTTRACKED to match
|
|
* what a truly focused toolbar would return
|
|
*/
|
|
V_VT(&varValue) = VT_I4;
|
|
V_I4(&varValue) = STATE_SYSTEM_FOCUSED | STATE_SYSTEM_HOTTRACKED | STATE_SYSTEM_FOCUSABLE;
|
|
fGotProp = true;
|
|
Trace (tagToolbarAccessibility, _T("GetPropValue: Returning 0x%08x"), V_I4(&varValue));
|
|
}
|
|
|
|
return (sc);
|
|
}
|
|
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CMMCToolBarCtrlEx::ScRestoreAccFocus
|
|
*
|
|
* Sends a fake EVENT_OBJECT_FOCUS event to send accessibility tools back
|
|
* to the true focus window, undoing the effect of our fake focus events
|
|
* in OnHotItemChange.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
SC CMMCToolBarCtrlEx::ScRestoreAccFocus()
|
|
{
|
|
DECLARE_SC (sc, _T("CMMCToolBarCtrlEx::ScRestoreAccFocus"));
|
|
|
|
/*
|
|
* if we haven't applied fake-focus, we don't need to restore anything
|
|
*/
|
|
if (!m_fFakeFocusApplied)
|
|
return (sc);
|
|
|
|
/*
|
|
* who has the focus now?
|
|
*/
|
|
HWND hwndFocus = ::GetFocus();
|
|
if (hwndFocus == NULL)
|
|
return (sc);
|
|
|
|
/*
|
|
* default to sending the focus for CHILDID_SELF
|
|
*/
|
|
int idChild = CHILDID_SELF;
|
|
|
|
/*
|
|
* get the accessible object for the focus window (don't abort on
|
|
* failure -- don't convert this HRESULT to SC)
|
|
*/
|
|
CComPtr<IAccessible> spAccessible;
|
|
HRESULT hr = AccessibleObjectFromWindow (hwndFocus, OBJID_CLIENT,
|
|
IID_IAccessible,
|
|
(void**) &spAccessible);
|
|
|
|
if (hr == S_OK) // not "SUCCEEDED(hr)", per Accessibility spec
|
|
{
|
|
/*
|
|
* ask the accessible object which
|
|
*/
|
|
CComVariant varFocusID;
|
|
hr = spAccessible->get_accFocus (&varFocusID);
|
|
|
|
if (hr == S_OK) // not "SUCCEEDED(hr)", per Accessibility spec
|
|
{
|
|
switch (V_VT(&varFocusID))
|
|
{
|
|
case VT_I4:
|
|
idChild = V_I4(&varFocusID);
|
|
break;
|
|
|
|
case VT_EMPTY:
|
|
/*
|
|
* Windows thinks the window has the focus, but its
|
|
* IAccessible thinks it doesn't. Trust Windows.
|
|
*/
|
|
Trace (tagToolbarAccessibility, _T("Windows and IAccessible::get_accFocus don't agree on who has the focus"));
|
|
break;
|
|
|
|
case VT_DISPATCH:
|
|
Trace (tagToolbarAccessibility, _T("IAccessible::get_accFocus returned VT_DISPATCH, ignoring"));
|
|
break;
|
|
|
|
default:
|
|
Trace (tagToolbarAccessibility, _T("IAccessible::get_accFocus returned unexpected VARIANT type (%d)"), V_VT(&varFocusID));
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Trace (tagToolbarAccessibility, _T("IAccessible::get_accFocus failed, hr=0x%08x"), hwndFocus, hr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Trace (tagToolbarAccessibility, _T("Can't get IAccessible from hwnd=0x%p (hr=0x%08x)"), hwndFocus, hr);
|
|
}
|
|
|
|
Trace (tagToolbarAccessibility, _T("Sending focus event back to hwnd=0x%p, idChild=%d"), hwndFocus, idChild);
|
|
NotifyWinEvent (EVENT_OBJECT_FOCUS, hwndFocus, OBJID_CLIENT, idChild);
|
|
m_fFakeFocusApplied = false;
|
|
|
|
return (sc);
|
|
}
|