2216 lines
59 KiB
C++
2216 lines
59 KiB
C++
|
/*--------------------------------------------------------------------------*
|
||
|
*
|
||
|
* Microsoft Windows
|
||
|
* Copyright (C) Microsoft Corporation, 1992 - 1999
|
||
|
*
|
||
|
* File: ftab.h
|
||
|
*
|
||
|
* Contents: Implementation file for CFolderTab, CFolderTabView
|
||
|
*
|
||
|
* History: 06-May-99 vivekj Created
|
||
|
*
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#include "ftab.h"
|
||
|
#include "amcview.h"
|
||
|
#include <oleacc.h>
|
||
|
|
||
|
/*
|
||
|
* if we're supporting old platforms, we need to build MSAA stubs
|
||
|
*/
|
||
|
#if (_WINNT_WIN32 < 0x0500)
|
||
|
#include <winable.h>
|
||
|
|
||
|
#define COMPILE_MSAA_STUBS
|
||
|
#include "msaastub.h"
|
||
|
|
||
|
#define WM_GETOBJECT 0x003D
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#ifdef DBG
|
||
|
CTraceTag tagTabAccessibility (_T("Accessibility"), _T("Tab Control"));
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* ValueOf
|
||
|
*
|
||
|
* Returns the value contained in the given variant. The variant is
|
||
|
* expected to be of type VT_I4.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
inline LONG ValueOf (VARIANT& var)
|
||
|
{
|
||
|
ASSERT (V_VT (&var) == VT_I4); // prevalidation is expected
|
||
|
return (V_I4 (&var));
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CTabAccessible
|
||
|
*
|
||
|
* Implements the accessibility interface IAccessible for CFolderTabView.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
class CTabAccessible :
|
||
|
public CMMCIDispatchImpl<IAccessible, &GUID_NULL, &LIBID_Accessibility>,
|
||
|
public CTiedComObject<CFolderTabView>
|
||
|
{
|
||
|
typedef CTabAccessible ThisClass;
|
||
|
typedef CFolderTabView CMyTiedObject;
|
||
|
|
||
|
public:
|
||
|
BEGIN_MMC_COM_MAP(ThisClass)
|
||
|
END_MMC_COM_MAP()
|
||
|
|
||
|
DECLARE_NOT_AGGREGATABLE(ThisClass)
|
||
|
|
||
|
public:
|
||
|
// *** IAccessible methods ***
|
||
|
MMC_METHOD1 (get_accParent, IDispatch** /*ppdispParent*/);
|
||
|
MMC_METHOD1 (get_accChildCount, long* /*pChildCount*/);
|
||
|
MMC_METHOD2 (get_accChild, VARIANT /*varChildID*/, IDispatch ** /*ppdispChild*/);
|
||
|
MMC_METHOD2 (get_accName, VARIANT /*varChildID*/, BSTR* /*pszName*/);
|
||
|
MMC_METHOD2 (get_accValue, VARIANT /*varChildID*/, BSTR* /*pszValue*/);
|
||
|
MMC_METHOD2 (get_accDescription, VARIANT /*varChildID*/, BSTR* /*pszDescription*/);
|
||
|
MMC_METHOD2 (get_accRole, VARIANT /*varChildID*/, VARIANT */*pvarRole*/);
|
||
|
MMC_METHOD2 (get_accState, VARIANT /*varChildID*/, VARIANT */*pvarState*/);
|
||
|
MMC_METHOD2 (get_accHelp, VARIANT /*varChildID*/, BSTR* /*pszHelp*/);
|
||
|
MMC_METHOD3 (get_accHelpTopic, BSTR* /*pszHelpFile*/, VARIANT /*varChildID*/, long* /*pidTopic*/);
|
||
|
MMC_METHOD2 (get_accKeyboardShortcut, VARIANT /*varChildID*/, BSTR* /*pszKeyboardShortcut*/);
|
||
|
MMC_METHOD1 (get_accFocus, VARIANT * /*pvarFocusChild*/);
|
||
|
MMC_METHOD1 (get_accSelection, VARIANT * /*pvarSelectedChildren*/);
|
||
|
MMC_METHOD2 (get_accDefaultAction, VARIANT /*varChildID*/, BSTR* /*pszDefaultAction*/);
|
||
|
MMC_METHOD2 (accSelect, long /*flagsSelect*/, VARIANT /*varChildID*/);
|
||
|
MMC_METHOD5 (accLocation, long* /*pxLeft*/, long* /*pyTop*/, long* /*pcxWidth*/, long* /*pcyHeight*/, VARIANT /*varChildID*/);
|
||
|
MMC_METHOD3 (accNavigate, long /*navDir*/, VARIANT /*varStart*/, VARIANT * /*pvarEndUpAt*/);
|
||
|
MMC_METHOD3 (accHitTest, long /*xLeft*/, long /*yTop*/, VARIANT * /*pvarChildAtPoint*/);
|
||
|
MMC_METHOD1 (accDoDefaultAction, VARIANT /*varChildID*/);
|
||
|
MMC_METHOD2 (put_accName, VARIANT /*varChildID*/, BSTR /*szName*/);
|
||
|
MMC_METHOD2 (put_accValue, VARIANT /*varChildID*/, BSTR /*pszValue*/);
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
//############################################################################
|
||
|
//############################################################################
|
||
|
//
|
||
|
// Implementation of class CFolderTabMetrics
|
||
|
//
|
||
|
//############################################################################
|
||
|
//############################################################################
|
||
|
|
||
|
CFolderTabMetrics::CFolderTabMetrics()
|
||
|
: m_dwStyle(0), m_textHeight(0)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
int CFolderTabMetrics::GetXOffset() const {return 8;}
|
||
|
int CFolderTabMetrics::GetXMargin() const {return 2;}
|
||
|
int CFolderTabMetrics::GetYMargin() const {return 1;}
|
||
|
int CFolderTabMetrics::GetYBorder() const {return 1;}
|
||
|
int CFolderTabMetrics::GetExtraYSpace() const {return 0;}
|
||
|
int CFolderTabMetrics::GetTabHeight() const {return GetTextHeight() + 2 * GetYMargin() + 2 * GetYBorder();}
|
||
|
int CFolderTabMetrics::GetUpDownWidth() const {return 2*GetTabHeight();} //for nice square buttons
|
||
|
int CFolderTabMetrics::GetUpDownHeight()const {return GetTabHeight();} // the up-down control is as tall as the tabs
|
||
|
|
||
|
|
||
|
//############################################################################
|
||
|
//############################################################################
|
||
|
//
|
||
|
// Implementation of class CFolderTab
|
||
|
//
|
||
|
//############################################################################
|
||
|
//############################################################################
|
||
|
|
||
|
CFolderTab::CFolderTab()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CFolderTab::CFolderTab(const CFolderTab &other)
|
||
|
{
|
||
|
*this = other;
|
||
|
}
|
||
|
|
||
|
CFolderTab &
|
||
|
CFolderTab::operator = (const CFolderTab &other)
|
||
|
{
|
||
|
if((CFolderTab *) this == (CFolderTab *) &other)
|
||
|
return *this;
|
||
|
|
||
|
m_sText = other.m_sText;
|
||
|
m_rect = other.m_rect;
|
||
|
m_clsid = other.m_clsid;
|
||
|
m_dwStyle = other.m_dwStyle;
|
||
|
m_textHeight = other.m_textHeight;
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTab::GetWidth
|
||
|
*
|
||
|
* PURPOSE: Returns the width of the tab.
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* int
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
int
|
||
|
CFolderTab::GetWidth() const
|
||
|
{
|
||
|
return m_rect.Width() + 1; // rect.Width() returns right-left, need to add 1 for inclusive width.
|
||
|
}
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTab::SetWidth
|
||
|
*
|
||
|
* PURPOSE: Sets the width of the tab.
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* int nWidth :
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* void
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
void
|
||
|
CFolderTab::SetWidth(int nWidth)
|
||
|
{
|
||
|
ASSERT(nWidth > 0);
|
||
|
ASSERT(GetWidth() >= nWidth);
|
||
|
|
||
|
int delta = nWidth - (m_rect.Width() + 1);
|
||
|
m_rect.right = m_rect.left + nWidth -1;
|
||
|
|
||
|
m_rgPts[2].x+=delta;
|
||
|
m_rgPts[3].x+=delta;
|
||
|
SetRgn();
|
||
|
}
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTab::Offset
|
||
|
*
|
||
|
* PURPOSE: Adds a certain offset to the internal array of points.
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* const CPoint :
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* void
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
void
|
||
|
CFolderTab::Offset(const CPoint &point)
|
||
|
{
|
||
|
m_rect.OffsetRect(point);
|
||
|
m_rgPts[0].Offset(point);
|
||
|
m_rgPts[1].Offset(point);
|
||
|
m_rgPts[2].Offset(point);
|
||
|
m_rgPts[3].Offset(point);
|
||
|
m_rgn.OffsetRgn(point);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
CFolderTab::SetRgn()
|
||
|
{
|
||
|
m_rgn.DeleteObject();
|
||
|
m_rgn.CreatePolygonRgn(m_rgPts, 4, WINDING);
|
||
|
}
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTab::ComputeRgn
|
||
|
*
|
||
|
* PURPOSE: Compute the the points, rect and region for a tab.
|
||
|
* Input x is starting x pos.
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* CDC& dc :
|
||
|
* int x :
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* int: The actual width of the tab
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
int
|
||
|
CFolderTab::ComputeRgn(CDC& dc, int x)
|
||
|
{
|
||
|
|
||
|
CRect& rc = m_rect;
|
||
|
rc.SetRectEmpty();
|
||
|
|
||
|
// calculate desired text rectangle
|
||
|
dc.DrawText(m_sText, &rc, DT_CALCRECT);
|
||
|
rc.right += 2*GetXOffset() + 2*GetXMargin(); // add margins
|
||
|
rc.bottom = rc.top + GetTabHeight();
|
||
|
rc += CPoint(x,0); // shift right
|
||
|
|
||
|
// create region
|
||
|
GetTrapezoid(rc, m_rgPts);
|
||
|
SetRgn();
|
||
|
|
||
|
return rc.Width();
|
||
|
}
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTab::GetTrapezoid
|
||
|
*
|
||
|
* PURPOSE: Given the bounding rect, compute trapezoid region.
|
||
|
* Note that the right and bottom edges not included in rect or
|
||
|
* trapezoid; these are normal rules of geometry.
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* const CRect :
|
||
|
* CPoint* pts :
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* void
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
void CFolderTab::GetTrapezoid(const CRect& rc, CPoint* pts) const
|
||
|
{
|
||
|
pts[0] = CPoint(rc.left, rc.top );
|
||
|
pts[1] = CPoint(rc.left + GetXOffset(), rc.bottom );
|
||
|
pts[2] = CPoint(rc.right- GetXOffset()-1, rc.bottom );
|
||
|
pts[3] = CPoint(rc.right-1, rc.top );
|
||
|
}
|
||
|
|
||
|
//////////////////
|
||
|
// Draw tab in normal or highlighted state
|
||
|
//
|
||
|
int CFolderTab::Draw(CDC& dc, CFont& font, BOOL bSelected, bool bFocused)
|
||
|
{
|
||
|
return DrawTrapezoidal(dc, font, bSelected, bFocused);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTab::DrawTrapezoidal
|
||
|
*
|
||
|
* PURPOSE: Draws a trapezoidal tab.
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* CDC& dc :
|
||
|
* CFont& font :
|
||
|
* BOOL bSelected :
|
||
|
* bool bFocused :
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* int
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
int CFolderTab::DrawTrapezoidal(CDC& dc, CFont& font, BOOL bSelected, bool bFocused)
|
||
|
{
|
||
|
COLORREF bgColor = GetSysColor(bSelected ? COLOR_WINDOW : COLOR_3DFACE);
|
||
|
COLORREF fgColor = GetSysColor(bSelected ? COLOR_WINDOWTEXT : COLOR_BTNTEXT);
|
||
|
|
||
|
CBrush brush(bgColor); // background brush
|
||
|
dc.SetBkColor(bgColor); // text background
|
||
|
dc.SetTextColor(fgColor); // text color = fg color
|
||
|
|
||
|
CPen blackPen (PS_SOLID, 1, GetSysColor(COLOR_WINDOWFRAME));
|
||
|
CPen shadowPen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW));
|
||
|
|
||
|
// Fill trapezoid
|
||
|
CPoint pts[4];
|
||
|
CRect rc = m_rect;
|
||
|
GetTrapezoid(rc, pts);
|
||
|
CPen* pOldPen = dc.SelectObject(&blackPen);
|
||
|
dc.FillRgn(&m_rgn, &brush);
|
||
|
|
||
|
// Draw edges. This is requires two corrections:
|
||
|
// 1) Trapezoid dimensions don't include the right and bottom edges,
|
||
|
// so must use one pixel less on bottom (cybottom)
|
||
|
// 2) the endpoint of LineTo is not included when drawing the line, so
|
||
|
// must add one pixel (cytop)
|
||
|
//
|
||
|
{
|
||
|
pts[1].y--; // correction #1: true bottom edge y-coord
|
||
|
pts[2].y--; // ...ditto
|
||
|
pts[3].y--; // correction #2: extend final LineTo
|
||
|
}
|
||
|
dc.MoveTo(pts[0]); // upper left
|
||
|
dc.LineTo(pts[1]); // bottom left
|
||
|
dc.SelectObject(&shadowPen); // bottom line is shadow color
|
||
|
dc.MoveTo(pts[1]); // line is inside trapezoid bottom
|
||
|
dc.LineTo(pts[2]); // ...
|
||
|
dc.SelectObject(&blackPen); // upstroke is black
|
||
|
dc.LineTo(pts[3]); // y-1 to include endpoint
|
||
|
if(!bSelected)
|
||
|
{
|
||
|
// if not highlighted, upstroke has a 3D shadow, one pixel inside
|
||
|
pts[2].x--; // offset left one pixel
|
||
|
pts[3].x--; // ...ditto
|
||
|
dc.SelectObject(&shadowPen);
|
||
|
dc.MoveTo(pts[2]);
|
||
|
dc.LineTo(pts[3]);
|
||
|
}
|
||
|
dc.SelectObject(pOldPen);
|
||
|
|
||
|
// draw text
|
||
|
rc.DeflateRect(GetXOffset() + GetXMargin(), GetYMargin());
|
||
|
CFont* pOldFont = dc.SelectObject(&font);
|
||
|
dc.DrawText(m_sText, &rc, DT_CENTER|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS);
|
||
|
dc.SelectObject(pOldFont);
|
||
|
|
||
|
if(bFocused) // draw the focus rectangle
|
||
|
{
|
||
|
// make some more space.
|
||
|
rc.top--;
|
||
|
rc.bottom++;
|
||
|
rc.left--;
|
||
|
rc.right++;
|
||
|
|
||
|
dc.DrawFocusRect(&rc);
|
||
|
}
|
||
|
|
||
|
return m_rect.right;
|
||
|
}
|
||
|
|
||
|
//############################################################################
|
||
|
//############################################################################
|
||
|
//
|
||
|
// Implementation of class CFolderTabView
|
||
|
//
|
||
|
//############################################################################
|
||
|
//############################################################################
|
||
|
IMPLEMENT_DYNAMIC(CFolderTabView, CView)
|
||
|
BEGIN_MESSAGE_MAP(CFolderTabView, CView)
|
||
|
ON_WM_PAINT()
|
||
|
ON_WM_LBUTTONDOWN()
|
||
|
ON_WM_SETFOCUS()
|
||
|
ON_WM_KILLFOCUS()
|
||
|
ON_WM_MOUSEACTIVATE()
|
||
|
ON_WM_KEYDOWN()
|
||
|
ON_WM_SETTINGCHANGE()
|
||
|
ON_WM_SIZE()
|
||
|
ON_WM_HSCROLL()
|
||
|
ON_MESSAGE(WM_GETOBJECT, OnGetObject)
|
||
|
END_MESSAGE_MAP()
|
||
|
|
||
|
CFolderTabView::CFolderTabView(CView *pParentView)
|
||
|
: m_bVisible(false), m_pParentView(pParentView)
|
||
|
{
|
||
|
m_iCurItem = -1; // nothing currently selected
|
||
|
m_dwStyle = 0;
|
||
|
m_textHeight = 0;
|
||
|
m_sizeX = 0;
|
||
|
m_sizeY = 0;
|
||
|
m_hWndUpDown = NULL;
|
||
|
m_nPos = 0; // the first tab is the one drawn
|
||
|
m_fHaveFocus = false;
|
||
|
}
|
||
|
|
||
|
CFolderTabView::~CFolderTabView()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::ScFireAccessibilityEvent
|
||
|
*
|
||
|
* Fires accessibility events for the folder tab view
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::ScFireAccessibilityEvent (
|
||
|
DWORD dwEvent, /* I:event to fire */
|
||
|
LONG idObject) /* I:object generating the event */
|
||
|
{
|
||
|
DECLARE_SC (sc, _T("CFolderTabView::ScFireAccessibilityEvent"));
|
||
|
|
||
|
/*
|
||
|
* Accessibility events are fired after the event takes place (e.g.
|
||
|
* EVENT_OBJECT_CREATE is sent after the child is created, not before).
|
||
|
* Because of this the child ID for EVENT_OBJECT_DESTROY is not
|
||
|
* necessarily valid, so we shouldn't validate in that case.
|
||
|
*/
|
||
|
if (dwEvent != EVENT_OBJECT_DESTROY)
|
||
|
{
|
||
|
sc = ScValidateChildID (idObject);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
NotifyWinEvent (dwEvent, m_hWnd, OBJID_CLIENT, idObject); // returns void
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTabView::OnHScroll
|
||
|
*
|
||
|
* PURPOSE: Called when the position of the scroll bar is changed
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* UINT nSBCode :
|
||
|
* UINT nPos :
|
||
|
* CScrollBar* pScrollBar :
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* void
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
void
|
||
|
CFolderTabView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar )
|
||
|
{
|
||
|
// we're only interested in SB_THUMBPOSITION
|
||
|
if(nSBCode != SB_THUMBPOSITION)
|
||
|
return;
|
||
|
|
||
|
// if the position has not changed, do nothing.
|
||
|
if(nPos == m_nPos)
|
||
|
return;
|
||
|
|
||
|
m_nPos = nPos; // change the position
|
||
|
RecomputeLayout();
|
||
|
InvalidateRect(NULL, true); // redraw everything.
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::OnSetFocus
|
||
|
*
|
||
|
* WM_SETFOCUS handler for CFolderTabView.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
void CFolderTabView::OnSetFocus(CWnd* pOldWnd)
|
||
|
{
|
||
|
m_fHaveFocus = true;
|
||
|
|
||
|
InvalidateRect(NULL);
|
||
|
BC::OnSetFocus(pOldWnd);
|
||
|
|
||
|
/*
|
||
|
* If we have any tabs, one of them will get the focus. Fire the
|
||
|
* focus accessibility event, ignoring errors. We do this after
|
||
|
* calling the base class, so this focus event will override the
|
||
|
* "focus to the window" event sent by the system on our behalf.
|
||
|
*/
|
||
|
if (GetItemCount() > 0)
|
||
|
ScFireAccessibilityEvent (EVENT_OBJECT_FOCUS, m_iCurItem+1 /*1-based*/);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::OnKillFocus
|
||
|
*
|
||
|
* WM_KILLFOCUS handler for CFolderTabView.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
void CFolderTabView::OnKillFocus(CWnd* pNewWnd)
|
||
|
{
|
||
|
m_fHaveFocus = false;
|
||
|
|
||
|
InvalidateRect(NULL);
|
||
|
BC::OnKillFocus(pNewWnd);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::OnMouseActivate
|
||
|
*
|
||
|
* WM_MOUSEACTIVATE handler for CFolderTabView.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
int CFolderTabView::OnMouseActivate( CWnd* pDesktopWnd, UINT nHitTest, UINT message )
|
||
|
{
|
||
|
//short-circuit the MFC base class code, which sets the keyboard focus here as well...
|
||
|
return MA_ACTIVATE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::OnCmdMsg
|
||
|
*
|
||
|
* WM_COMMAND handler for CFolderTabView.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
BOOL CFolderTabView::OnCmdMsg( UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo )
|
||
|
{
|
||
|
// Do normal command routing
|
||
|
if (BC::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
|
||
|
return TRUE;
|
||
|
|
||
|
// if view didn't handle it, give parent view a chance
|
||
|
if (m_pParentView != NULL)
|
||
|
return static_cast<CWnd*>(m_pParentView)->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
|
||
|
else
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::OnKeyDown
|
||
|
*
|
||
|
* WM_KEYDOWN handler for CFolderTabView.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
void CFolderTabView::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
|
||
|
{
|
||
|
int cSize = m_tabList.size();
|
||
|
|
||
|
if( (cSize == 0) || ( (nChar != VK_LEFT) && (nChar != VK_RIGHT) ) )
|
||
|
{
|
||
|
BC::OnKeyDown(nChar, nRepCnt, nFlags);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ASSERT( (nChar == VK_LEFT) || (nChar == VK_RIGHT) );
|
||
|
|
||
|
int iNew = GetSelectedItem() + (nChar==VK_LEFT ? -1 : 1);
|
||
|
if(iNew < 0)
|
||
|
iNew = 0; // does not wrap
|
||
|
|
||
|
if(iNew >= cSize)
|
||
|
iNew = cSize -1; // does not wrap
|
||
|
|
||
|
SelectItem(iNew, true /*bEnsureVisible*/);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::OnSettingChange
|
||
|
*
|
||
|
* WM_SETTINGCHANGE handler for CFolderTabView.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
void CFolderTabView::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
|
||
|
{
|
||
|
CView::OnSettingChange(uFlags, lpszSection);
|
||
|
|
||
|
if (uFlags == SPI_SETNONCLIENTMETRICS)
|
||
|
{
|
||
|
DeleteFonts ();
|
||
|
CreateFonts ();
|
||
|
InvalidateRect(NULL, true); // redraw everything.
|
||
|
RecomputeLayout ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//////////////////
|
||
|
// Create folder tab control from static control.
|
||
|
// Destroys the static control. This is convenient for dialogs
|
||
|
//
|
||
|
BOOL CFolderTabView::CreateFromStatic(UINT nID, CWnd* pParent)
|
||
|
{
|
||
|
CStatic wndStatic;
|
||
|
if(!wndStatic.SubclassDlgItem(nID, pParent))
|
||
|
return FALSE;
|
||
|
CRect rc;
|
||
|
wndStatic.GetWindowRect(&rc);
|
||
|
pParent->ScreenToClient(&rc);
|
||
|
wndStatic.DestroyWindow();
|
||
|
rc.bottom = rc.top + GetDesiredHeight();
|
||
|
return Create(WS_CHILD|WS_VISIBLE, rc, pParent, nID);
|
||
|
}
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTabView::Create
|
||
|
*
|
||
|
* PURPOSE: Creates the folder tab control
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* DWORD dwStyle :
|
||
|
* const RECT :
|
||
|
* CWnd* pParent :
|
||
|
* UINT nID :
|
||
|
* DWORD dwFtabStyle :
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* BOOL
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
BOOL CFolderTabView::Create(DWORD dwStyle, const RECT& rc,
|
||
|
CWnd* pParent, UINT nID, DWORD dwFtabStyle)
|
||
|
{
|
||
|
ASSERT(pParent);
|
||
|
ASSERT(dwStyle & WS_CHILD);
|
||
|
|
||
|
m_dwStyle = dwFtabStyle;
|
||
|
|
||
|
static LPCTSTR lpClassName = _T("AMCCustomTab");
|
||
|
static BOOL bRegistered = FALSE; // registered?
|
||
|
if(!bRegistered)
|
||
|
{
|
||
|
WNDCLASS wc;
|
||
|
memset(&wc, 0, sizeof(wc));
|
||
|
wc.lpfnWndProc = ::DefWindowProc; // will get hooked by MFC
|
||
|
wc.hInstance = AfxGetInstanceHandle();
|
||
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||
|
wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
|
||
|
wc.lpszMenuName = NULL;
|
||
|
wc.lpszClassName = lpClassName;
|
||
|
if(!AfxRegisterClass(&wc))
|
||
|
{
|
||
|
TRACE(_T("*** CFolderTabView::AfxRegisterClass failed!\n"));
|
||
|
return FALSE;
|
||
|
}
|
||
|
bRegistered = TRUE;
|
||
|
}
|
||
|
if(!BC::CreateEx(0, lpClassName, NULL, dwStyle, rc, pParent, nID))
|
||
|
return FALSE;
|
||
|
|
||
|
// initialize fonts
|
||
|
CreateFonts();
|
||
|
|
||
|
/*
|
||
|
* Bug 141015: Create a buddy window for the up-down control. It will
|
||
|
* never be visible, but we need it so UDM_GETPOS sent to the up-down
|
||
|
* will work. Narrator will send this message when the up-down becomes
|
||
|
* visible, but it will fail if there's no buddy (sad, but true). It
|
||
|
* fails by returning an LRESULT with a non-zero high-order word
|
||
|
* (specifically, 0x00010000), so Narrator translates and announces
|
||
|
* "65536" instead of the true value.
|
||
|
*
|
||
|
* This is only required for Narrator support, so if it fails it's
|
||
|
* not sufficient reason to fail CFolderTabView creation altogether.
|
||
|
*/
|
||
|
HWND hwndBuddy = CreateWindow (_T("edit"), NULL, WS_CHILD, 0, 0, 0, 0,
|
||
|
m_hWnd, 0, AfxGetInstanceHandle(), NULL);
|
||
|
|
||
|
// create the up-down control
|
||
|
DWORD dwUpDownStyle = WS_CHILD | WS_BORDER |
|
||
|
UDS_SETBUDDYINT | // for Narrator support
|
||
|
UDS_HORZ /*to display the arrows left to right*/; // NOTE: the control is created invisible on purpose.
|
||
|
m_hWndUpDown = CreateUpDownControl(dwUpDownStyle, 0, 0,
|
||
|
GetUpDownWidth(), //width
|
||
|
GetUpDownHeight(), //height
|
||
|
m_hWnd,
|
||
|
1 /*nID*/,
|
||
|
AfxGetInstanceHandle(),
|
||
|
hwndBuddy,
|
||
|
0 /*nUpper*/,
|
||
|
0 /*nLower*/,
|
||
|
0 /*nPos*/);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void CFolderTabView::CreateFonts ()
|
||
|
{
|
||
|
LOGFONT lf;
|
||
|
SystemParametersInfo (SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, false);
|
||
|
m_fontNormal.CreateFontIndirect(&lf);
|
||
|
|
||
|
// Get the font height (converting from points to pixels)
|
||
|
CClientDC dc(NULL);
|
||
|
TEXTMETRIC tm;
|
||
|
CFont *pFontOld = dc.SelectObject(&m_fontNormal);
|
||
|
dc.GetTextMetrics(&tm);
|
||
|
|
||
|
m_textHeight = tm.tmHeight;
|
||
|
|
||
|
// set the old font back.
|
||
|
dc.SelectObject(pFontOld);
|
||
|
|
||
|
lf.lfWeight = FW_BOLD;
|
||
|
m_fontSelected.CreateFontIndirect(&lf);
|
||
|
}
|
||
|
|
||
|
void CFolderTabView::DeleteFonts ()
|
||
|
{
|
||
|
m_fontNormal.DeleteObject();
|
||
|
m_fontSelected.DeleteObject();
|
||
|
}
|
||
|
|
||
|
//////////////////
|
||
|
// copy a font
|
||
|
//
|
||
|
static void CopyFont(CFont& dst, CFont& src)
|
||
|
{
|
||
|
dst.DeleteObject();
|
||
|
LOGFONT lf;
|
||
|
VERIFY(src.GetLogFont(&lf));
|
||
|
dst.CreateFontIndirect(&lf);
|
||
|
}
|
||
|
|
||
|
//////////////////
|
||
|
// Set normal, selected fonts
|
||
|
//
|
||
|
void CFolderTabView::SetFonts(CFont& fontNormal, CFont& fontSelected)
|
||
|
{
|
||
|
CopyFont(m_fontNormal, fontNormal);
|
||
|
CopyFont(m_fontSelected, fontSelected);
|
||
|
}
|
||
|
|
||
|
//////////////////
|
||
|
// Paint function
|
||
|
//
|
||
|
|
||
|
void CFolderTabView::OnDraw(CDC* pDC)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void CFolderTabView::OnPaint()
|
||
|
{
|
||
|
Paint (m_fHaveFocus);
|
||
|
}
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTabView::EnsureVisible
|
||
|
*
|
||
|
* PURPOSE: Changes the layout to ensure that the specified tab is visible.
|
||
|
*
|
||
|
* NOTE: Does NOT invalidate the rect, for efficiency.
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* UINT iTab :
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* void
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
void
|
||
|
CFolderTabView::EnsureVisible(int iTab)
|
||
|
{
|
||
|
if((iTab < 0) || (iTab > m_tabList.size()))
|
||
|
{
|
||
|
ASSERT(0 && "Should not come here.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(!::IsWindowVisible(m_hWndUpDown))
|
||
|
return; // the up-down control is hidden, meaning that all tabs are visible
|
||
|
|
||
|
RecomputeLayout(); // make sure we have the correct dimensions
|
||
|
|
||
|
if(m_nPos == iTab)
|
||
|
return; // the tab already shows as much as it can.
|
||
|
|
||
|
if(m_nPos > iTab) // the first visible tab is to the right of iTab. Make iTab the first visible tab
|
||
|
{
|
||
|
m_nPos = iTab;
|
||
|
RecomputeLayout();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
iterator iter = m_tabList.begin();
|
||
|
std::advance(iter, iTab); // get the correct item
|
||
|
|
||
|
CRect rcCurTab = iter->GetRect();
|
||
|
|
||
|
// loop: Increase the start tab position until the right edge of iTab fits.
|
||
|
while((m_nPos < iTab) && (rcCurTab.right > m_sizeX))
|
||
|
{
|
||
|
m_nPos++;
|
||
|
RecomputeLayout();
|
||
|
rcCurTab = iter->GetRect();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTabView::Paint
|
||
|
*
|
||
|
* PURPOSE: Completely redraws the tab control.
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* bool bFocused :
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* void
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
void
|
||
|
CFolderTabView::Paint(bool bFocused)
|
||
|
{
|
||
|
CPaintDC dc(this); // device context for painting
|
||
|
|
||
|
CRect rc;
|
||
|
GetClientRect(&rc);
|
||
|
|
||
|
// draw all the normal (non-selected) tabs
|
||
|
iterator iterSelected = m_tabList.end();
|
||
|
int i = 0;
|
||
|
bool bDraw = true;
|
||
|
for(iterator iter= m_tabList.begin(); iter!= m_tabList.end(); ++iter, i++)
|
||
|
{
|
||
|
if(i!=m_iCurItem)
|
||
|
{
|
||
|
if(bDraw && iter->Draw(dc, m_fontNormal, FALSE, false) > rc.right)
|
||
|
bDraw = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iterSelected = iter;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ASSERT(iterSelected != m_tabList.end());
|
||
|
|
||
|
/*
|
||
|
* Bug 350942: selected tab shouldn't be bold
|
||
|
*/
|
||
|
// draw selected tab last so it will be "on top" of the others
|
||
|
iterSelected->Draw(dc, /*m_fontSelected*/ m_fontNormal, TRUE, bFocused);
|
||
|
|
||
|
// draw border: line along the top edge, excluding seleted tab
|
||
|
CPoint pts[4];
|
||
|
CRect rcCurTab = iterSelected->GetRect();
|
||
|
iterSelected->GetTrapezoid(&rcCurTab, pts);
|
||
|
|
||
|
CPen blackPen(PS_SOLID, 1, GetSysColor(COLOR_WINDOWFRAME));
|
||
|
|
||
|
CPen* pOldPen = dc.SelectObject(&blackPen);
|
||
|
int y = pts[0].y;
|
||
|
dc.MoveTo(rc.left, y);
|
||
|
dc.LineTo(pts[0].x, y);
|
||
|
dc.MoveTo(pts[3].x, y);
|
||
|
dc.LineTo(rc.right, y);
|
||
|
|
||
|
dc.SelectObject(pOldPen);
|
||
|
}
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTabView::OnLButtonDown
|
||
|
*
|
||
|
* PURPOSE: Selects the tab pointed to on a left mouse click
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* UINT nFlags :
|
||
|
* CPoint pt :
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* void
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
void
|
||
|
CFolderTabView::OnLButtonDown(UINT nFlags, CPoint pt)
|
||
|
{
|
||
|
int iTab = HitTest(pt);
|
||
|
if(iTab>=0 && iTab!=m_iCurItem)
|
||
|
{
|
||
|
SelectItem(iTab, true /*bEnsureVisible*/);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTabView::HitTest
|
||
|
*
|
||
|
* PURPOSE: Computes which tab is at the specified point.
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* CPoint pt :
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* int: The tab index, or -1 if none.
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
int
|
||
|
CFolderTabView::HitTest(CPoint pt)
|
||
|
{
|
||
|
CRect rc;
|
||
|
GetClientRect(&rc);
|
||
|
if(rc.PtInRect(pt))
|
||
|
{
|
||
|
int i = 0;
|
||
|
for( iterator iter= m_tabList.begin(); iter!= m_tabList.end(); ++iter, i++)
|
||
|
{
|
||
|
if(iter->HitTest(pt))
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTabView::SelectItem
|
||
|
*
|
||
|
* PURPOSE: Selects the iTab'th tab and returns the index of the tab selected,
|
||
|
* or -1 if an error occurred.
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* int iTab :
|
||
|
* bool bEnsureVisible : If true, repositions the tab to make it visible.
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* int
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
int
|
||
|
CFolderTabView::SelectItem(int iTab, bool bEnsureVisible)
|
||
|
{
|
||
|
if(iTab<0 || iTab>=GetItemCount())
|
||
|
return -1; // bad
|
||
|
|
||
|
bool bSendTabChanged = (iTab != m_iCurItem); // send a message only if a different item got selected
|
||
|
|
||
|
// repaint the control
|
||
|
m_iCurItem = iTab; // set new selected tab
|
||
|
if(bEnsureVisible)
|
||
|
EnsureVisible(iTab);
|
||
|
else
|
||
|
RecomputeLayout();
|
||
|
|
||
|
InvalidateRect(NULL, true);
|
||
|
|
||
|
if(bSendTabChanged)
|
||
|
{
|
||
|
/*
|
||
|
* If the selection changed, fire the selection accessibility event.
|
||
|
* We do it before sending FTN_TABCHANGED so that if the FTN_TABCHANGED
|
||
|
* handler selects another item, observers will get the selection
|
||
|
* events in the right order (ignore errors)
|
||
|
*/
|
||
|
ScFireAccessibilityEvent (EVENT_OBJECT_SELECTION, m_iCurItem+1 /*1-based*/);
|
||
|
|
||
|
/*
|
||
|
* if our window has the focus, focus changes with selection,
|
||
|
* so send focus event, too (ignore errors)
|
||
|
*/
|
||
|
if (m_fHaveFocus)
|
||
|
ScFireAccessibilityEvent (EVENT_OBJECT_FOCUS, m_iCurItem+1 /*1-based*/);
|
||
|
|
||
|
// send the FTN_TABCHANGED message
|
||
|
NMFOLDERTAB nm;
|
||
|
nm.hwndFrom = m_hWnd;
|
||
|
nm.idFrom = GetDlgCtrlID();
|
||
|
nm.code = FTN_TABCHANGED;
|
||
|
nm.iItem = iTab;
|
||
|
CWnd* pParent = GetParent();
|
||
|
pParent->SendMessage(WM_NOTIFY, nm.idFrom, (LPARAM)&nm);
|
||
|
}
|
||
|
|
||
|
return m_iCurItem;
|
||
|
}
|
||
|
|
||
|
|
||
|
int
|
||
|
CFolderTabView::SelectItemByClsid(const CLSID& clsid)
|
||
|
{
|
||
|
bool bFound = false;
|
||
|
int i=0;
|
||
|
for(iterator iter= m_tabList.begin(); iter!= m_tabList.end(); ++iter, i++)
|
||
|
{
|
||
|
if(IsEqualGUID(iter->GetClsid(),clsid))
|
||
|
{
|
||
|
bFound = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(!bFound)
|
||
|
{
|
||
|
ASSERT(0 && "Invalid folder tab.");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
return SelectItem(i);
|
||
|
}
|
||
|
|
||
|
|
||
|
CFolderTab &
|
||
|
CFolderTabView::GetItem(int iPos)
|
||
|
{
|
||
|
ASSERT(!(iPos<0 || iPos>=GetItemCount()));
|
||
|
|
||
|
CFolderTabList::iterator iter = m_tabList.begin();
|
||
|
std::advance(iter, iPos);
|
||
|
return *iter;
|
||
|
}
|
||
|
|
||
|
|
||
|
int CFolderTabView::AddItem(LPCTSTR lpszText, const CLSID& clsid)
|
||
|
{
|
||
|
CFolderTab tab;
|
||
|
tab.SetText(lpszText);
|
||
|
tab.SetClsid(clsid);
|
||
|
tab.SetStyle(m_dwStyle);
|
||
|
tab.SetTextHeight(m_textHeight);
|
||
|
|
||
|
m_tabList.push_back(tab);
|
||
|
|
||
|
RecomputeLayout();
|
||
|
InvalidateRect(NULL, true);
|
||
|
|
||
|
int nNewItemIndex = m_tabList.size() - 1; // 0-based
|
||
|
|
||
|
/*
|
||
|
* tell observers we created a new tab, after it's been created (ignore errors)
|
||
|
*/
|
||
|
ScFireAccessibilityEvent (EVENT_OBJECT_CREATE, nNewItemIndex+1 /*1-based*/);
|
||
|
|
||
|
return (nNewItemIndex);
|
||
|
}
|
||
|
|
||
|
BOOL CFolderTabView::RemoveItem(int iPos)
|
||
|
{
|
||
|
if( (iPos < 0) || (iPos>= m_tabList.size()) )
|
||
|
return false;
|
||
|
|
||
|
CFolderTabList::iterator iter = m_tabList.begin();
|
||
|
std::advance(iter, iPos);
|
||
|
m_tabList.erase(iter);
|
||
|
|
||
|
/*
|
||
|
* tell observers we destroyed a tab, after it's been destroyed but before
|
||
|
* we might send selection/focus notifications in SelectItem (ignore errors)
|
||
|
*/
|
||
|
ScFireAccessibilityEvent (EVENT_OBJECT_DESTROY, iPos+1 /*1-based*/);
|
||
|
|
||
|
/*
|
||
|
* If we're deleting the currently selected tab, the selection needs to
|
||
|
* move somewhere else. If there are tabs following the current one,
|
||
|
* we'll move the selection to the next tab; otherwise, we'll move to
|
||
|
* the previous one.
|
||
|
*/
|
||
|
if ((iPos == m_iCurItem) && !m_tabList.empty())
|
||
|
{
|
||
|
/*
|
||
|
* if there are tabs to the following the one we just deleted,
|
||
|
* increment m_iCurItem so the subsequent call to SelectItem
|
||
|
* will recognize that the selection change and send the proper
|
||
|
* notifications.
|
||
|
*/
|
||
|
if (m_iCurItem < m_tabList.size())
|
||
|
m_iCurItem++;
|
||
|
|
||
|
SelectItem (m_iCurItem-1, true /*bEnsureVisible*/);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/*
|
||
|
* if we deleted a tab before the selected tab, decrement the
|
||
|
* selected tab index to keep things in sync
|
||
|
* m_iCurItem will become -1 when the last tab is removed, which is correct
|
||
|
*/
|
||
|
if (iPos <= m_iCurItem)
|
||
|
m_iCurItem--;
|
||
|
|
||
|
InvalidateRect(NULL, true);
|
||
|
RecomputeLayout();
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void CFolderTabView::DeleteAllItems()
|
||
|
{
|
||
|
const int cChildren = m_tabList.size();
|
||
|
m_tabList.clear();
|
||
|
m_iCurItem = -1; // nothing is selected
|
||
|
|
||
|
InvalidateRect(NULL, true);
|
||
|
RecomputeLayout();
|
||
|
|
||
|
/*
|
||
|
* Tell accessibility observers that each tab is destroyed. Notify
|
||
|
* in last-to-first order so IDs remain sane during this process.
|
||
|
*/
|
||
|
for (int idChild = cChildren /*1-based*/; idChild >= 1; idChild--)
|
||
|
{
|
||
|
ScFireAccessibilityEvent (EVENT_OBJECT_DESTROY, idChild);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If we have the focus, tell accessibility observers that the
|
||
|
* control itself has the focus. We do this to be consistent with
|
||
|
* other controls (like the list view)
|
||
|
*/
|
||
|
if (m_fHaveFocus)
|
||
|
ScFireAccessibilityEvent (EVENT_OBJECT_FOCUS, CHILDID_SELF);
|
||
|
}
|
||
|
|
||
|
void CFolderTabView::OnSize(UINT nType, int cx, int cy)
|
||
|
{
|
||
|
m_sizeX = cx;
|
||
|
m_sizeY = cy;
|
||
|
|
||
|
CView::OnSize(nType, cx, cy);
|
||
|
|
||
|
if (nType != SIZE_MINIMIZED)
|
||
|
{
|
||
|
RecomputeLayout();
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTabView::ShowUpDownControl
|
||
|
*
|
||
|
* PURPOSE: Shows or hides the up/down control
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* BOOL bShow : true to show, false to hide.
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* void
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
void
|
||
|
CFolderTabView::ShowUpDownControl(BOOL bShow)
|
||
|
{
|
||
|
BOOL bVisible = (m_hWndUpDown != NULL) && ::IsWindowVisible(m_hWndUpDown); // was the up-down control visible previously?
|
||
|
if(bShow)
|
||
|
{
|
||
|
if(!bVisible)
|
||
|
{
|
||
|
::SendMessage(m_hWndUpDown, UDM_SETRANGE32, (WPARAM) 0 /*iLow*/, (LPARAM) m_tabList.size()-1 /*zero-based*/);
|
||
|
::SendMessage(m_hWndUpDown, UDM_SETPOS, (WPARAM) 0, (LPARAM) m_nPos /*nPos*/);
|
||
|
::ShowWindow(m_hWndUpDown, SW_SHOW);
|
||
|
|
||
|
InvalidateRect(NULL, true);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// hide the updown control
|
||
|
if(m_hWndUpDown)
|
||
|
::ShowWindow(m_hWndUpDown, SW_HIDE);
|
||
|
|
||
|
if(bVisible) // invalidate only on a transition from visible to invisible
|
||
|
InvalidateRect(NULL, true);
|
||
|
|
||
|
m_nPos = 0;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTabView::GetTotalTabWidth
|
||
|
*
|
||
|
* PURPOSE: Computes the total width of all the tabs.
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* CClientDC& dc :
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* int
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
int
|
||
|
CFolderTabView::GetTotalTabWidth(CClientDC& dc)
|
||
|
{
|
||
|
int x = 0;
|
||
|
|
||
|
// compute the width "as is", ie without taking into account the actual space available.
|
||
|
for(iterator iter = m_tabList.begin(); iter!= m_tabList.end(); ++iter)
|
||
|
{
|
||
|
x += iter->ComputeRgn(dc, x) - GetXOffset();
|
||
|
}
|
||
|
|
||
|
return x;
|
||
|
}
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTabView::ComputeRegion
|
||
|
*
|
||
|
* PURPOSE: Computes the location and regions for all the tabs
|
||
|
*
|
||
|
* PARAMETERS:
|
||
|
* CClientDC& dc :
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* int
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
int
|
||
|
CFolderTabView::ComputeRegion(CClientDC& dc)
|
||
|
{
|
||
|
int x = GetTotalTabWidth(dc);
|
||
|
|
||
|
// subtract the top-left x coordinate of the m_nPos'th tab from all x coordinates, thereby creating a shift
|
||
|
iterator iter = m_tabList.begin();
|
||
|
std::advance(iter, m_nPos); // advance to the m_nPos'th tab
|
||
|
|
||
|
int xOffset = iter->GetRect().left;
|
||
|
|
||
|
x = GetUpDownWidth() - xOffset; // shift everything to the left by xOffset
|
||
|
|
||
|
for(iterator iterTemp = m_tabList.begin(); iterTemp!= m_tabList.end(); ++iterTemp)
|
||
|
{
|
||
|
x += iterTemp->ComputeRgn(dc, x) - GetXOffset();
|
||
|
}
|
||
|
|
||
|
return x;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
*
|
||
|
* CFolderTabView::RecomputeLayout
|
||
|
*
|
||
|
* PURPOSE: Determines the location of all the tabs, and whether or not the
|
||
|
* up/down control should be displayed.
|
||
|
*
|
||
|
* RETURNS:
|
||
|
* void
|
||
|
*
|
||
|
*+-------------------------------------------------------------------------*/
|
||
|
void
|
||
|
CFolderTabView::RecomputeLayout()
|
||
|
{
|
||
|
// set the size of the updown control
|
||
|
if(m_hWndUpDown)
|
||
|
::SetWindowPos(m_hWndUpDown, NULL /*hWndInsertAfter*/, 0 /*left*/, 0 /*top*/,
|
||
|
GetUpDownWidth(), GetUpDownHeight(), SWP_NOMOVE| SWP_NOZORDER);
|
||
|
|
||
|
// set the correct text height for the tabs
|
||
|
for(iterator iterTemp = m_tabList.begin(); iterTemp!= m_tabList.end(); ++iterTemp)
|
||
|
iterTemp->SetTextHeight(GetTextHeight());
|
||
|
|
||
|
|
||
|
CClientDC dc(this);
|
||
|
CFont* pOldFont = dc.SelectObject(&m_fontSelected); // use the bold font to compute with.
|
||
|
|
||
|
int totalWidth = GetTotalTabWidth(dc); // the width of ALL tabs
|
||
|
|
||
|
if(totalWidth <= m_sizeX)
|
||
|
{
|
||
|
// there's enough space to show all tabs. Hide the updown control
|
||
|
ShowUpDownControl(false);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// not enough width for all tabs.
|
||
|
BOOL bVisible = ::IsWindowVisible(m_hWndUpDown); // was the up-down control visible previously?
|
||
|
|
||
|
if(!bVisible) // the up-down control was not visible, so make it visible.
|
||
|
{
|
||
|
m_nPos = 0;
|
||
|
ShowUpDownControl(true);
|
||
|
}
|
||
|
|
||
|
ComputeRegion(dc); // make sure we leave space for the tab
|
||
|
|
||
|
}
|
||
|
|
||
|
dc.SelectObject(pOldFont);
|
||
|
}
|
||
|
|
||
|
|
||
|
void CFolderTabView::Layout(CRect& rectTotal, CRect& rectFTab)
|
||
|
{
|
||
|
int cy = GetTabHeight() + GetExtraYSpace();
|
||
|
rectFTab = rectTotal;
|
||
|
if(!IsVisible())
|
||
|
return;
|
||
|
|
||
|
rectFTab.top = rectFTab.bottom - cy;
|
||
|
rectTotal.bottom= rectFTab.top;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::OnGetObject
|
||
|
*
|
||
|
* WM_GETOBJECT handler for CFolderTabView.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
LRESULT CFolderTabView::OnGetObject (WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
DECLARE_SC (sc, _T("CFolderTabView::OnGetObject"));
|
||
|
|
||
|
/*
|
||
|
* ignore requests for objects other than OBJID_CLIENT
|
||
|
*/
|
||
|
if (lParam != OBJID_CLIENT)
|
||
|
{
|
||
|
Trace (tagTabAccessibility, _T("WM_GETOBJECT: (lParam != OBJID_CLIENT), returning 0"));
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* create our accessibility object
|
||
|
*/
|
||
|
if ((sc = CTiedComObjectCreator<CTabAccessible>::ScCreateAndConnect(*this, m_spTabAcc)).IsError() ||
|
||
|
(sc = ScCheckPointers (m_spTabAcc, E_UNEXPECTED)).IsError())
|
||
|
{
|
||
|
sc.TraceAndClear();
|
||
|
Trace (tagTabAccessibility, _T("WM_GETOBJECT: error creating IAccessible object, returning 0"));
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* return a pointer to the IAccessible interface
|
||
|
*/
|
||
|
Trace (tagTabAccessibility, _T("WM_GETOBJECT: returning IAccessible*"));
|
||
|
return (LresultFromObject (IID_IAccessible, wParam, m_spTabAcc));
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scget_accParent
|
||
|
*
|
||
|
* Retrieves the IDispatch interface of the object's parent.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scget_accParent(IDispatch ** ppdispParent)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scget_accParent"));
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::Scget_accParent"));
|
||
|
|
||
|
sc = ScCheckPointers (ppdispParent);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
/*
|
||
|
* return the accessibility interface for the OBJID_WINDOW object
|
||
|
*/
|
||
|
sc = AccessibleObjectFromWindow (m_hWnd, OBJID_WINDOW, IID_IDispatch,
|
||
|
(void **)ppdispParent);
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scget_accChildCount
|
||
|
*
|
||
|
* Retrieves the number of children belonging to this object.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scget_accChildCount(long* pChildCount)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scget_accChildCount"));
|
||
|
|
||
|
sc = ScCheckPointers (pChildCount);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
*pChildCount = GetItemCount();
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::Scget_accChildCount: returning %d"), GetItemCount());
|
||
|
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scget_accChild
|
||
|
*
|
||
|
* Retrieves the address of an IDispatch interface for the specified child.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scget_accChild(VARIANT varChildID, IDispatch ** ppdispChild)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scget_accChild"));
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::Scget_accChild"));
|
||
|
|
||
|
sc = ScCheckPointers (ppdispChild);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
// init out parameter
|
||
|
(*ppdispChild) = NULL;
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
/*
|
||
|
* all children are simple elements exposed through their parent,
|
||
|
* not accessible objects in their own right
|
||
|
*/
|
||
|
sc = S_FALSE;
|
||
|
|
||
|
Trace (tagTabAccessibility, TEXT("returning parent's IDispatch for child %d"), ValueOf(varChildID));
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scget_accName
|
||
|
*
|
||
|
* Retrieves the name of the specified object.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scget_accName(VARIANT varChildID, BSTR* pbstrName)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scget_accName"));
|
||
|
|
||
|
sc = ScCheckPointers (pbstrName);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
// init out parameter
|
||
|
*pbstrName = NULL;
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
/*
|
||
|
* the tab control itself doesn't have a name; otherwise, get the
|
||
|
* name of the requested tab
|
||
|
*/
|
||
|
LONG idChild = ValueOf (varChildID);
|
||
|
if (idChild == CHILDID_SELF)
|
||
|
{
|
||
|
sc = S_FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CFolderTab& tab = GetItem (idChild-1);
|
||
|
CComBSTR bstrName (tab.GetText());
|
||
|
*pbstrName = bstrName.Detach();
|
||
|
}
|
||
|
|
||
|
#ifdef DBG
|
||
|
USES_CONVERSION;
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::Scget_accName: child %d, returning \"%s\""),
|
||
|
idChild,
|
||
|
(*pbstrName) ? W2T(*pbstrName) : _T("<None>"));
|
||
|
#endif
|
||
|
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scget_accValue
|
||
|
*
|
||
|
* Retrieves the value of the specified object. Not all objects have a value.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scget_accValue(VARIANT varChildID, BSTR* pbstrValue)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scget_accValue"));
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::Scget_accValue"));
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
/*
|
||
|
* tabs don't have values
|
||
|
*/
|
||
|
sc = S_FALSE;
|
||
|
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scget_accDescription
|
||
|
*
|
||
|
* Retrieves a string that describes the visual appearance of the specified
|
||
|
* object. Not all objects have a description.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scget_accDescription(VARIANT varChildID, BSTR* pbstrDescription)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scget_accDescription"));
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::Scget_accDescription"));
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
/*
|
||
|
* tabs don't have descriptions
|
||
|
*/
|
||
|
sc = S_FALSE;
|
||
|
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scget_accRole
|
||
|
*
|
||
|
* Retrieves information that describes the role of the specified object.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scget_accRole(VARIANT varChildID, VARIANT *pvarRole)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scget_accRole"));
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::Scget_accRole"));
|
||
|
|
||
|
sc = ScCheckPointers (pvarRole);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
// init out parameter
|
||
|
VariantInit (pvarRole);
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
/*
|
||
|
* the tab control has a "page tab list" role; an individual tab has a
|
||
|
* "page tab" role
|
||
|
*/
|
||
|
V_VT(pvarRole) = VT_I4;
|
||
|
V_I4(pvarRole) = (ValueOf (varChildID) == CHILDID_SELF)
|
||
|
? ROLE_SYSTEM_PAGETABLIST
|
||
|
: ROLE_SYSTEM_PAGETAB;
|
||
|
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scget_accState
|
||
|
*
|
||
|
* Retrieves the current state of the specified object.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scget_accState(VARIANT varChildID, VARIANT *pvarState)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scget_accState"));
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
LONG idChild = ValueOf (varChildID);
|
||
|
|
||
|
/*
|
||
|
* all items are focusable
|
||
|
*/
|
||
|
V_VT(pvarState) = VT_I4;
|
||
|
V_I4(pvarState) = STATE_SYSTEM_FOCUSABLE;
|
||
|
|
||
|
/*
|
||
|
* is this for a tab?
|
||
|
*/
|
||
|
if (idChild != CHILDID_SELF)
|
||
|
{
|
||
|
/*
|
||
|
* all tabs are selectable
|
||
|
*/
|
||
|
V_I4(pvarState) |= STATE_SYSTEM_SELECTABLE;
|
||
|
|
||
|
/*
|
||
|
* if this is the selected item, give it the selected state
|
||
|
*/
|
||
|
if ((idChild - 1 /*1-based*/) == GetSelectedItem())
|
||
|
{
|
||
|
V_I4(pvarState) |= STATE_SYSTEM_SELECTED;
|
||
|
|
||
|
/*
|
||
|
* if the tab control also has the focus, give the selected
|
||
|
* item the focused state as well
|
||
|
*/
|
||
|
if (m_fHaveFocus)
|
||
|
V_I4(pvarState) |= STATE_SYSTEM_FOCUSED;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (m_fHaveFocus)
|
||
|
V_I4(pvarState) |= STATE_SYSTEM_FOCUSED;
|
||
|
}
|
||
|
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::Scget_accState: child %d, returning 0x%08x"), idChild, V_I4(pvarState));
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scget_accHelp
|
||
|
*
|
||
|
* Retrieves an object's Help property string. Not all objects need to
|
||
|
* support this property.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scget_accHelp(VARIANT varChildID, BSTR* pbstrHelp)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scget_accHelp"));
|
||
|
|
||
|
sc = ScCheckPointers (pbstrHelp);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
/*
|
||
|
* no help
|
||
|
*/
|
||
|
*pbstrHelp = NULL;
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
return (sc = S_FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scget_accHelpTopic
|
||
|
*
|
||
|
* Retrieves the full path of the WinHelp file associated with the specified
|
||
|
* object and the identifier of the appropriate topic within that file. Not
|
||
|
* all objects need to support this property.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scget_accHelpTopic(BSTR* pbstrHelpFile, VARIANT varChildID, long* pidTopic)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scget_accHelpTopic"));
|
||
|
|
||
|
sc = ScCheckPointers (pbstrHelpFile, pidTopic);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
/*
|
||
|
* no help topic
|
||
|
*/
|
||
|
*pbstrHelpFile = NULL;
|
||
|
*pidTopic = 0;
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
return (sc = S_FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scget_accKeyboardShortcut
|
||
|
*
|
||
|
* Retrieves the specified object's shortcut key or access key (also known
|
||
|
* as the mnemonic). All objects that have a shortcut key or access key
|
||
|
* should support this property.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scget_accKeyboardShortcut(VARIANT varChildID, BSTR* pbstrKeyboardShortcut)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scget_accKeyboardShortcut"));
|
||
|
|
||
|
sc = ScCheckPointers (pbstrKeyboardShortcut);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
/*
|
||
|
* no shortcut keys
|
||
|
*/
|
||
|
*pbstrKeyboardShortcut = NULL;
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
return (sc = S_FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scget_accFocus
|
||
|
*
|
||
|
* Retrieves the object that has the keyboard focus.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scget_accFocus(VARIANT * pvarFocusChild)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scget_accFocus"));
|
||
|
|
||
|
sc = ScCheckPointers (pvarFocusChild);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
/*
|
||
|
* if we have the focus, return the (1-based) ID of the selected tab;
|
||
|
* otherwise, return VT_EMPTY
|
||
|
*/
|
||
|
if (m_fHaveFocus)
|
||
|
{
|
||
|
V_VT(pvarFocusChild) = VT_I4;
|
||
|
V_I4(pvarFocusChild) = GetSelectedItem() + 1;
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::Scget_accFocus: returning %d"), V_I4(pvarFocusChild));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
V_VT(pvarFocusChild) = VT_EMPTY;
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::Scget_accFocus: returning VT_EMPTY"));
|
||
|
}
|
||
|
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scget_accSelection
|
||
|
*
|
||
|
* Retrieves the selected children of this object.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scget_accSelection(VARIANT * pvarSelectedChildren)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scget_accSelection"));
|
||
|
|
||
|
sc = ScCheckPointers (pvarSelectedChildren);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
/*
|
||
|
* return the (1-based) ID of the selected tab, if there is one
|
||
|
*/
|
||
|
if (GetSelectedItem() != -1)
|
||
|
{
|
||
|
V_VT(pvarSelectedChildren) = VT_I4;
|
||
|
V_I4(pvarSelectedChildren) = GetSelectedItem() + 1;
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::Scget_accSelection: returning %d"), V_I4(pvarSelectedChildren));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
V_VT(pvarSelectedChildren) = VT_EMPTY;
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::Scget_accSelection: returning VT_EMPTY"));
|
||
|
}
|
||
|
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scget_accDefaultAction
|
||
|
*
|
||
|
* Retrieves a string that describes the object's default action. Not all
|
||
|
* objects have a default action.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scget_accDefaultAction(VARIANT varChildID, BSTR* pbstrDefaultAction)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scget_accDefaultAction"));
|
||
|
|
||
|
sc = ScCheckPointers (pbstrDefaultAction);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
/*
|
||
|
* default to "no default action"
|
||
|
*/
|
||
|
*pbstrDefaultAction = NULL;
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
/*
|
||
|
* individual tabs have a default action of "Switch", just like WC_TABCONTROL
|
||
|
*/
|
||
|
if (ValueOf(varChildID) != CHILDID_SELF)
|
||
|
{
|
||
|
CString strDefaultAction (MAKEINTRESOURCE (IDS_TabAccessiblity_DefaultAction));
|
||
|
CComBSTR bstrDefaultAction (strDefaultAction);
|
||
|
|
||
|
*pbstrDefaultAction = bstrDefaultAction.Detach();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sc = S_FALSE; // no default action
|
||
|
}
|
||
|
|
||
|
#ifdef DBG
|
||
|
USES_CONVERSION;
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::Scget_accDefaultAction: child %d, returning \"%s\""),
|
||
|
ValueOf(varChildID),
|
||
|
(*pbstrDefaultAction) ? W2T(*pbstrDefaultAction) : _T("<None>"));
|
||
|
#endif
|
||
|
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::ScaccSelect
|
||
|
*
|
||
|
* Modifies the selection or moves the keyboard focus of the specified
|
||
|
* object.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::ScaccSelect(long flagsSelect, VARIANT varChildID)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::ScaccSelect"));
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::ScaccSelect"));
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
LONG idChild = ValueOf(varChildID);
|
||
|
|
||
|
/*
|
||
|
* can't select the tab control itself, only child elements
|
||
|
*/
|
||
|
if (idChild == CHILDID_SELF)
|
||
|
return (sc = E_INVALIDARG);
|
||
|
|
||
|
/*
|
||
|
* the tab control doesn't support multiple selection, so reject
|
||
|
* requests dealing with multiple selection
|
||
|
*/
|
||
|
const long lInvalidFlags = SELFLAG_EXTENDSELECTION |
|
||
|
SELFLAG_ADDSELECTION |
|
||
|
SELFLAG_REMOVESELECTION;
|
||
|
|
||
|
if (flagsSelect & lInvalidFlags)
|
||
|
return (sc = E_INVALIDARG);
|
||
|
|
||
|
/*
|
||
|
* activate this view, if we're requested to take the focus
|
||
|
*/
|
||
|
if (flagsSelect & SELFLAG_TAKEFOCUS)
|
||
|
{
|
||
|
CFrameWnd* pFrame = GetParentFrame();
|
||
|
sc = ScCheckPointers (pFrame, E_FAIL);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
pFrame->SetActiveView (this);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* select the given tab, if requested
|
||
|
*/
|
||
|
if (flagsSelect & SELFLAG_TAKESELECTION)
|
||
|
{
|
||
|
if (SelectItem (idChild - 1 /*0-based*/, true /*bEnsureVisible*/) == -1)
|
||
|
return (sc = E_FAIL);
|
||
|
}
|
||
|
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::ScaccLocation
|
||
|
*
|
||
|
* Retrieves the specified object's current screen location.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::ScaccLocation (
|
||
|
long* pxLeft,
|
||
|
long* pyTop,
|
||
|
long* pcxWidth,
|
||
|
long* pcyHeight,
|
||
|
VARIANT varChildID)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::ScaccLocation"));
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::ScaccLocation"));
|
||
|
|
||
|
sc = ScCheckPointers (pxLeft, pyTop, pcxWidth, pcyHeight);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
// init out parameters
|
||
|
*pxLeft = *pyTop = *pcxWidth = *pcyHeight = 0;
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
LONG idChild = ValueOf(varChildID);
|
||
|
CRect rectLocation;
|
||
|
|
||
|
/*
|
||
|
* for the tab control itself, get the location of the entire window
|
||
|
*/
|
||
|
if (idChild == CHILDID_SELF)
|
||
|
GetWindowRect (rectLocation);
|
||
|
|
||
|
/*
|
||
|
* otherwise, get the rectangle of the tab and convert it to screen coords
|
||
|
*/
|
||
|
else
|
||
|
{
|
||
|
rectLocation = GetItem(idChild-1).GetRect();
|
||
|
MapWindowPoints (NULL, rectLocation);
|
||
|
}
|
||
|
|
||
|
*pxLeft = rectLocation.left;
|
||
|
*pyTop = rectLocation.top;
|
||
|
*pcxWidth = rectLocation.Width();
|
||
|
*pcyHeight = rectLocation.Height();
|
||
|
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::ScaccNavigate
|
||
|
*
|
||
|
* Traverses to another user interface element within a container and if
|
||
|
* possible, retrieves the object.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::ScaccNavigate (long lNavDir, VARIANT varStart, VARIANT * pvarEndUpAt)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::ScaccNavigate"));
|
||
|
|
||
|
sc = ScCheckPointers (pvarEndUpAt);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
// init out parameters
|
||
|
VariantInit (pvarEndUpAt);
|
||
|
|
||
|
sc = ScValidateChildID (varStart);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
LONG idFrom = ValueOf (varStart);
|
||
|
LONG idTo = -1;
|
||
|
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::ScaccNavigate: start=%d, direction=%d"), idFrom, lNavDir);
|
||
|
|
||
|
switch (lNavDir)
|
||
|
{
|
||
|
case NAVDIR_UP:
|
||
|
case NAVDIR_DOWN:
|
||
|
/*
|
||
|
* the tab control doesn't have the concept of up and down,
|
||
|
* so there's no screen element in that direction; just leave
|
||
|
* idTo == -1 and the code below the switch will take care
|
||
|
* of the rest
|
||
|
*/
|
||
|
break;
|
||
|
|
||
|
case NAVDIR_FIRSTCHILD:
|
||
|
case NAVDIR_LASTCHILD:
|
||
|
/*
|
||
|
* NAVDIR_FIRSTCHILD and NAVDIR_LASTCHILD must be relative
|
||
|
* to CHILDID_SELF
|
||
|
*/
|
||
|
if (idFrom != CHILDID_SELF)
|
||
|
return (sc = E_INVALIDARG);
|
||
|
|
||
|
idTo = (lNavDir == NAVDIR_FIRSTCHILD) ? 1 : GetItemCount();
|
||
|
break;
|
||
|
|
||
|
case NAVDIR_LEFT:
|
||
|
case NAVDIR_PREVIOUS:
|
||
|
/*
|
||
|
* if we're moving relative to a child element, bump idTo;
|
||
|
* if not, just leave idTo == -1 and the code below the switch
|
||
|
* will take of the rest
|
||
|
*/
|
||
|
if (idFrom != CHILDID_SELF)
|
||
|
idTo = idFrom - 1;
|
||
|
break;
|
||
|
|
||
|
case NAVDIR_RIGHT:
|
||
|
case NAVDIR_NEXT:
|
||
|
/*
|
||
|
* if we're moving relative to a child element, bump idTo;
|
||
|
* if not, just leave idTo == -1 and the code below the switch
|
||
|
* will take of the rest
|
||
|
*/
|
||
|
if (idFrom != CHILDID_SELF)
|
||
|
idTo = idFrom + 1;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return (sc = E_INVALIDARG);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* if we're trying to navigate to an invalid child ID, return "no element
|
||
|
* in that direction"
|
||
|
*/
|
||
|
if ((idTo < 1) || (idTo > GetItemCount()))
|
||
|
{
|
||
|
V_VT(pvarEndUpAt) = VT_EMPTY;
|
||
|
sc = S_FALSE;
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::ScaccNavigate: VT_EMPTY"));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* otherwise return the new child ID (don't change the selection here;
|
||
|
* the client will call IAccessible::accSelect to do that)
|
||
|
*/
|
||
|
else
|
||
|
{
|
||
|
V_VT(pvarEndUpAt) = VT_I4;
|
||
|
V_I4(pvarEndUpAt) = idTo;
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::ScaccNavigate: end=%d"), idTo);
|
||
|
}
|
||
|
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::ScaccHitTest
|
||
|
*
|
||
|
* Retrieves the child element or child object at a given point on the screen.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::ScaccHitTest (long x, long y, VARIANT* pvarChildAtPoint)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::ScaccHitTest"));
|
||
|
|
||
|
sc = ScCheckPointers (pvarChildAtPoint);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
// init out parameters
|
||
|
VariantInit (pvarChildAtPoint);
|
||
|
|
||
|
/*
|
||
|
* hit-test the given point, converted to client coordinates
|
||
|
*/
|
||
|
CPoint pt (x, y);
|
||
|
ScreenToClient (&pt);
|
||
|
int nHitTest = HitTest (pt);
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::ScaccHitTest: x=%d y=%d"), x, y);
|
||
|
|
||
|
/*
|
||
|
* not on a tab? see if it's within the client rect
|
||
|
*/
|
||
|
if (nHitTest == -1)
|
||
|
{
|
||
|
CRect rectClient;
|
||
|
GetClientRect (rectClient);
|
||
|
|
||
|
if (rectClient.PtInRect (pt))
|
||
|
{
|
||
|
V_VT(pvarChildAtPoint) = VT_I4;
|
||
|
V_I4(pvarChildAtPoint) = CHILDID_SELF;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
V_VT(pvarChildAtPoint) = VT_EMPTY;
|
||
|
sc = S_FALSE; // no element there
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* otherwise, it is on a tab; return the 1-based ID
|
||
|
*/
|
||
|
else
|
||
|
{
|
||
|
V_VT(pvarChildAtPoint) = VT_I4;
|
||
|
V_I4(pvarChildAtPoint) = nHitTest + 1;
|
||
|
}
|
||
|
|
||
|
#ifdef DBG
|
||
|
if (V_VT(pvarChildAtPoint) == VT_I4)
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::ScaccHitTest: returning %d"), ValueOf (*pvarChildAtPoint));
|
||
|
else
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::ScaccHitTest: returning VT_EMPTY"));
|
||
|
#endif
|
||
|
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::ScaccDoDefaultAction
|
||
|
*
|
||
|
* Performs the specified object's default action. Not all objects have a
|
||
|
* default action.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::ScaccDoDefaultAction (VARIANT varChildID)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::ScaccDoDefaultAction"));
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if(sc)
|
||
|
return (sc);
|
||
|
|
||
|
/*
|
||
|
* the tab control doesn't have a default action
|
||
|
*/
|
||
|
LONG idChild = ValueOf (varChildID);
|
||
|
Trace (tagTabAccessibility, TEXT("CFolderTabView::ScaccDoDefaultAction: child %d"), idChild);
|
||
|
if (idChild == CHILDID_SELF)
|
||
|
return (sc = E_INVALIDARG);
|
||
|
|
||
|
/*
|
||
|
* select the given tab item
|
||
|
*/
|
||
|
if (SelectItem (idChild - 1 /*0-based*/, true /*bEnsureVisible*/) == -1)
|
||
|
return (sc = E_FAIL);
|
||
|
|
||
|
return (sc);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scput_accName
|
||
|
*
|
||
|
* This is no longer supported. The SetWindowText or control-specific APIs
|
||
|
* should be used in place of this method.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scput_accName(VARIANT varChildID, BSTR bstrName)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scput_accName"));
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
return (sc = E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::Scput_accValue
|
||
|
*
|
||
|
* This is no longer supported. Control-specific APIs should be used in
|
||
|
* place of this method.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::Scput_accValue(VARIANT varChildID, BSTR bstrValue)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::Scput_accValue"));
|
||
|
|
||
|
sc = ScValidateChildID (varChildID);
|
||
|
if (sc)
|
||
|
return (sc);
|
||
|
|
||
|
return (sc = E_NOTIMPL);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::ScValidateChildID
|
||
|
*
|
||
|
* Determines if the supplied variant represents a valid child ID.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::ScValidateChildID (VARIANT &var)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::ScValidateChildID"));
|
||
|
|
||
|
/*
|
||
|
* child IDs must be VT_I4's
|
||
|
*/
|
||
|
if (V_VT(&var) != VT_I4)
|
||
|
return (sc = E_INVALIDARG);
|
||
|
|
||
|
return (ScValidateChildID (ValueOf(var)));
|
||
|
}
|
||
|
|
||
|
|
||
|
/*+-------------------------------------------------------------------------*
|
||
|
* CFolderTabView::ScValidateChildID
|
||
|
*
|
||
|
* Determines if the supplied ID is valid child ID.
|
||
|
*--------------------------------------------------------------------------*/
|
||
|
|
||
|
SC CFolderTabView::ScValidateChildID (LONG idChild)
|
||
|
{
|
||
|
DECLARE_SC (sc, TEXT("CFolderTabView::ScValidateChildID"));
|
||
|
|
||
|
/*
|
||
|
* child ID must be either CHILDID_SELF or a valid tab index
|
||
|
*/
|
||
|
if ((idChild < CHILDID_SELF) || (idChild > GetItemCount()))
|
||
|
return (sc = E_INVALIDARG);
|
||
|
|
||
|
return (sc);
|
||
|
}
|