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

645 lines
17 KiB
C++

// Copyright (c) 1996-1999 Microsoft Corporation
// --------------------------------------------------------------------------
//
// TITLEBAR.CPP
//
// Title bar class.
//
// NOTE:
// Every time we make a drawing, hittesting change to USER for titlebars,
// update this file also! I.E. when you
// (1) Add a titlebar element like a new button
// (2) Change the spacing like margins
// (3) Add a new type of titlebar beyond normal/small
// (4) Shuffle the layout
//
// ISSUES:
// (1) Need "button down" info from USER and hence a shared <oleuser.h>
// (2) Need "hovered" info from USER
// (3) For FE, we need a SC_IME system command. The TrackIMEButton()
// code does the command in line, unlike all other titlebar buttons.
// This makes it not programmable.
//
// --------------------------------------------------------------------------
#include "oleacc_p.h"
#include "default.h"
#include "window.h"
#include "client.h"
#include "titlebar.h"
// --------------------------------------------------------------------------
//
// CreateTitleBarObject()
//
// --------------------------------------------------------------------------
HRESULT CreateTitleBarObject(HWND hwnd, long idObject, REFIID riid, void** ppvTitle)
{
UNUSED(idObject);
return(CreateTitleBarThing(hwnd, 0, riid, ppvTitle));
}
// --------------------------------------------------------------------------
//
// CreateTitleBarThing()
//
// --------------------------------------------------------------------------
HRESULT CreateTitleBarThing(HWND hwnd, long iChildCur, REFIID riid, void** ppvTitle)
{
CTitleBar * ptitlebar;
HRESULT hr;
InitPv(ppvTitle);
ptitlebar = new CTitleBar();
if (ptitlebar)
{
if (! ptitlebar->FInitialize(hwnd, iChildCur))
{
delete ptitlebar;
return(E_FAIL);
}
}
else
return(E_OUTOFMEMORY);
hr = ptitlebar->QueryInterface(riid, ppvTitle);
if (!SUCCEEDED(hr))
delete ptitlebar;
return(hr);
}
// --------------------------------------------------------------------------
//
// GetRealChild()
//
// --------------------------------------------------------------------------
long GetRealChild(DWORD dwStyle, LONG lChild)
{
switch (lChild)
{
case INDEX_TITLEBAR_MINBUTTON:
if (dwStyle & WS_MINIMIZE)
lChild = INDEX_TITLEBAR_RESTOREBUTTON;
break;
case INDEX_TITLEBAR_MAXBUTTON:
if (dwStyle & WS_MAXIMIZE)
lChild = INDEX_TITLEBAR_RESTOREBUTTON;
break;
}
return(lChild);
}
// --------------------------------------------------------------------------
//
// CTitleBar::FInitialize
//
// --------------------------------------------------------------------------
BOOL CTitleBar::FInitialize(HWND hwndTitleBar, LONG iChildCur)
{
if (! IsWindow(hwndTitleBar))
return(FALSE);
m_hwnd = hwndTitleBar;
m_cChildren = CCHILDREN_TITLEBAR;
m_idChildCur = iChildCur;
return(TRUE);
}
// --------------------------------------------------------------------------
//
// CTitleBar::Clone()
//
// --------------------------------------------------------------------------
STDMETHODIMP CTitleBar::Clone(IEnumVARIANT ** ppenum)
{
return(CreateTitleBarThing(m_hwnd, m_idChildCur, IID_IEnumVARIANT, (void**)ppenum));
}
// --------------------------------------------------------------------------
//
// CTitleBar::get_accName()
//
// Returns the proper noun name of the object.
//
// --------------------------------------------------------------------------
STDMETHODIMP CTitleBar::get_accName(VARIANT varChild, BSTR * pszName)
{
long index;
LONG dwStyle;
InitPv(pszName);
if (! ValidateChild(&varChild))
return(E_INVALIDARG);
// The titlebar doesn't have a name itself
if (!varChild.lVal)
return(S_FALSE);
//
// Figure out what string to _really_ load (depends on window state)
//
dwStyle = GetWindowLong(m_hwnd, GWL_STYLE);
index = GetRealChild(dwStyle, varChild.lVal);
return(HrCreateString(STR_TITLEBAR_NAME+index, pszName));
}
// --------------------------------------------------------------------------
//
// CTitleBar::get_accValue()
//
// The value of the titlebar itself is the text inside.
//
// --------------------------------------------------------------------------
STDMETHODIMP CTitleBar::get_accValue(VARIANT varChild, BSTR* pszValue)
{
InitPv(pszValue);
if (!ValidateChild(&varChild))
return(E_INVALIDARG);
//
// Only the titlebar has a value, the child buttons don't.
//
if (varChild.lVal)
//CWO, 1/16/97, Changed to S_FALSE from E_NOT_APPLICABLE
return(S_FALSE);
return(HrGetWindowName(m_hwnd, FALSE, pszValue));
}
// --------------------------------------------------------------------------
//
// CTitleBar::get_accDescription()
//
// Returns a full sentence describing the object.
//
// --------------------------------------------------------------------------
STDMETHODIMP CTitleBar::get_accDescription(VARIANT varChild, BSTR * pszDesc)
{
long index;
LONG dwStyle;
InitPv(pszDesc);
if (! ValidateChild(&varChild))
return(E_INVALIDARG);
//
// Figure out what string to _really_ load, depends on state.
//
dwStyle = GetWindowLong(m_hwnd, GWL_STYLE);
index = GetRealChild(dwStyle, varChild.lVal);
return(HrCreateString(STR_TITLEBAR_DESCRIPTION+index, pszDesc));
}
// --------------------------------------------------------------------------
//
// CTitleBar::get_accRole()
//
// --------------------------------------------------------------------------
STDMETHODIMP CTitleBar::get_accRole(VARIANT varChild, VARIANT * pvarRole)
{
InitPvar(pvarRole);
if (! ValidateChild(&varChild))
return(E_INVALIDARG);
pvarRole->vt = VT_I4;
if (varChild.lVal == INDEX_TITLEBAR_SELF)
pvarRole->lVal = ROLE_SYSTEM_TITLEBAR;
else
pvarRole->lVal = ROLE_SYSTEM_PUSHBUTTON;
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CTitleBar::get_accState()
//
// --------------------------------------------------------------------------
STDMETHODIMP CTitleBar::get_accState(VARIANT varChild, VARIANT* pvarState)
{
TITLEBARINFO ti;
InitPvar(pvarState);
if (! ValidateChild(&varChild))
return(E_INVALIDARG);
pvarState->vt = VT_I4;
pvarState->lVal = 0;
if (! MyGetTitleBarInfo(m_hwnd, &ti) ||
(ti.rgstate[INDEX_TITLEBAR_SELF] & STATE_SYSTEM_INVISIBLE))
{
pvarState->lVal |= STATE_SYSTEM_INVISIBLE;
return(S_OK);
}
pvarState->lVal |= ti.rgstate[INDEX_TITLEBAR_SELF];
pvarState->lVal |= ti.rgstate[varChild.lVal];
// only the title bar itself is focusable.
if (varChild.lVal != INDEX_TITLEBAR_SELF)
pvarState->lVal &= ~STATE_SYSTEM_FOCUSABLE;
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CTitleBar::get_accDefaultAction()
//
// NOTE: only the buttons have default actions. The default action of
// the system menu is ambiguous, since it is unknown until the window
// enters menu mode.
//
// --------------------------------------------------------------------------
STDMETHODIMP CTitleBar::get_accDefaultAction(VARIANT varChild, BSTR*
pszDefAction)
{
long index;
LONG dwStyle;
InitPv(pszDefAction);
if (! ValidateChild(&varChild))
return(E_INVALIDARG);
if (!varChild.lVal)
return(E_NOT_APPLICABLE);
//
// Get the string
//
dwStyle = GetWindowLong(m_hwnd, GWL_STYLE);
index = GetRealChild(dwStyle, varChild.lVal);
//
// BOGUS! The IME button doesn't have a def action either since
// we can't change the keyboard layout indirectly via WM_SYSCOMMAND.
// The IME code does the work in line. We need to make an SC_.
//
if (index <= INDEX_TITLEBAR_IMEBUTTON)
return(E_NOT_APPLICABLE);
return(HrCreateString(STR_BUTTON_PUSH, pszDefAction));
}
// --------------------------------------------------------------------------
//
// CTitleBar::accSelect()
//
// --------------------------------------------------------------------------
STDMETHODIMP CTitleBar::accSelect(long flagsSel, VARIANT varChild)
{
if (! ValidateChild(&varChild))
return(E_INVALIDARG);
if (! ValidateSelFlags(flagsSel))
return(E_INVALIDARG);
if (flagsSel & SELFLAG_TAKEFOCUS)
{
if (varChild.lVal == CHILDID_SELF)
{
MySetFocus(m_hwnd);
return (S_OK);
}
}
return(E_NOT_APPLICABLE);
}
// --------------------------------------------------------------------------
//
// CTitleBar::accNavigate()
//
// --------------------------------------------------------------------------
STDMETHODIMP CTitleBar::accNavigate(long dwNavDir, VARIANT varStart,
VARIANT * pvarEnd)
{
TITLEBARINFO ti;
long lEndUp;
InitPvar(pvarEnd);
if (! ValidateChild(&varStart) ||
! ValidateNavDir(dwNavDir, varStart.lVal))
return(E_INVALIDARG);
if (! MyGetTitleBarInfo(m_hwnd, &ti))
return(S_FALSE);
if (dwNavDir == NAVDIR_FIRSTCHILD)
dwNavDir = NAVDIR_NEXT;
else if (dwNavDir == NAVDIR_LASTCHILD)
{
dwNavDir = NAVDIR_PREVIOUS;
varStart.lVal = m_cChildren + 1;
}
else if (varStart.lVal == INDEX_TITLEBAR_SELF)
return(GetParentToNavigate(OBJID_TITLEBAR, m_hwnd,
OBJID_WINDOW, dwNavDir, pvarEnd));
//
// NOTE: It is up to the caller to make sure the item navigation
// is starting from is visible.
//
switch (dwNavDir)
{
case NAVDIR_LEFT:
case NAVDIR_PREVIOUS:
//
// Is there anything to the left of us?
//
lEndUp = varStart.lVal;
while (--lEndUp >= INDEX_TITLEBAR_MIC)
{
if (!(ti.rgstate[lEndUp] & STATE_SYSTEM_INVISIBLE))
break;
}
if (lEndUp < INDEX_TITLEBAR_MIC)
lEndUp = 0;
break;
case NAVDIR_RIGHT:
case NAVDIR_NEXT:
//
// Is there anything to the right of us?
//
lEndUp = varStart.lVal;
while (++lEndUp <= INDEX_TITLEBAR_MAC)
{
if (!(ti.rgstate[lEndUp] & STATE_SYSTEM_INVISIBLE))
break;
}
if (lEndUp > INDEX_TITLEBAR_MAC)
lEndUp = 0;
break;
default:
lEndUp = 0;
break;
}
if (lEndUp)
{
pvarEnd->vt = VT_I4;
pvarEnd->lVal = lEndUp;
return(S_OK);
}
else
return(S_FALSE);
}
// --------------------------------------------------------------------------
//
// CTitleBar::accLocation()
//
// Gets the screen rect of a particular element. If the item isn't
// actually present, this will fail.
//
// NOTE: It is up to the caller to make sure that the container (titlebar)
// is visible before calling accLocation on a child.
//
// --------------------------------------------------------------------------
STDMETHODIMP CTitleBar::accLocation(long* pxLeft, long* pyTop,
long* pcxWidth, long* pcyHeight, VARIANT varChild)
{
int cyBorder;
int cxyButton;
TITLEBARINFO ti;
int index;
InitAccLocation(pxLeft, pyTop, pcxWidth, pcyHeight);
if (! ValidateChild(&varChild))
return(E_INVALIDARG);
if (! MyGetTitleBarInfo(m_hwnd, &ti))
return(S_FALSE);
//
// If this object isn't around, fail.
//
if ((ti.rgstate[INDEX_TITLEBAR_SELF] & (STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_OFFSCREEN)) ||
(ti.rgstate[varChild.lVal] & STATE_SYSTEM_INVISIBLE))
{
return(S_FALSE);
}
cyBorder = GetSystemMetrics(SM_CYBORDER);
cxyButton = ti.rcTitleBar.bottom - ti.rcTitleBar.top - cyBorder;
if (varChild.lVal == INDEX_TITLEBAR_SELF)
{
*pxLeft = ti.rcTitleBar.left;
*pyTop = ti.rcTitleBar.top;
*pcxWidth = ti.rcTitleBar.right - ti.rcTitleBar.left;
*pcyHeight = ti.rcTitleBar.bottom - ti.rcTitleBar.top;
}
else
{
*pyTop = ti.rcTitleBar.top;
*pcxWidth = cxyButton;
*pcyHeight = cxyButton;
// Where is the left side of the button? Our INDEX_s are
// conveniently defined in left-to-right order. Start at the
// end and work backwards to the system menu. Subtract cxyButton
// when a child is present.
*pxLeft = ti.rcTitleBar.right - cxyButton;
for (index = INDEX_TITLEBAR_MAC; index > INDEX_TITLEBAR_SELF; index--)
{
if (index == varChild.lVal)
break;
if (!(ti.rgstate[index] & STATE_SYSTEM_INVISIBLE))
*pxLeft -= cxyButton;
}
}
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CTitleBar::accHitTest()
//
// --------------------------------------------------------------------------
STDMETHODIMP CTitleBar::accHitTest(long xLeft, long yTop, VARIANT* pvarChild)
{
POINT pt;
int cxyButton;
int index;
TITLEBARINFO ti;
InitPvar(pvarChild);
if (! MyGetTitleBarInfo(m_hwnd, &ti) ||
(ti.rgstate[INDEX_TITLEBAR_SELF] & (STATE_SYSTEM_INVISIBLE || STATE_SYSTEM_OFFSCREEN)))
return(S_FALSE);
pt.x = xLeft;
pt.y = yTop;
//
// We return VT_EMPTY when the point isn't in the titlebar at all.
//
if (! PtInRect(&ti.rcTitleBar, pt))
return(S_FALSE);
pvarChild->vt = VT_I4;
pvarChild->lVal = INDEX_TITLEBAR_SELF;
cxyButton = ti.rcTitleBar.bottom - ti.rcTitleBar.top - GetSystemMetrics(SM_CYBORDER);
// If yTop is greater than this, the point is on the border drawn below
// the caption
if (yTop < ti.rcTitleBar.top + cxyButton)
{
//
// Start at the right side and work backwards.
//
pt.x = ti.rcTitleBar.right - cxyButton;
for (index = INDEX_TITLEBAR_MAC; index > INDEX_TITLEBAR_SELF; index--)
{
//
// This child is here.
//
if (!(ti.rgstate[index] & STATE_SYSTEM_INVISIBLE))
{
//
// Is this point where this child is?
//
if (xLeft >= pt.x)
{
pvarChild->lVal = index;
break;
}
pt.x -= cxyButton;
}
}
}
return(S_OK);
}
// --------------------------------------------------------------------------
//
// CTitleBar::accDoDefaultAction()
//
// --------------------------------------------------------------------------
STDMETHODIMP CTitleBar::accDoDefaultAction(VARIANT varChild)
{
WPARAM scCommand = 0;
int index;
TITLEBARINFO ti;
if (! ValidateChild(&varChild))
return(E_INVALIDARG);
//
// We return nothing for the titlebar & system menu objects. Hence it
// is a real error to attempt to do the default action on them.
//
if (varChild.lVal <= INDEX_TITLEBAR_IMEBUTTON)
return(E_NOT_APPLICABLE);
if (! MyGetTitleBarInfo(m_hwnd, &ti) ||
(ti.rgstate[INDEX_TITLEBAR_SELF] & STATE_SYSTEM_INVISIBLE))
return(S_FALSE);
//
// We do NOT do the default action of an object that is invisible.
//
if (ti.rgstate[varChild.lVal] & STATE_SYSTEM_INVISIBLE)
return(S_FALSE);
index = GetRealChild(GetWindowLong(m_hwnd, GWL_STYLE), varChild.lVal);
switch (index)
{
case INDEX_TITLEBAR_MINBUTTON:
scCommand = SC_MINIMIZE;
break;
case INDEX_TITLEBAR_HELPBUTTON:
scCommand = SC_CONTEXTHELP;
break;
case INDEX_TITLEBAR_MAXBUTTON:
scCommand = SC_MAXIMIZE;
break;
case INDEX_TITLEBAR_RESTOREBUTTON:
scCommand = SC_RESTORE;
break;
case INDEX_TITLEBAR_CLOSEBUTTON:
scCommand = SC_CLOSE;
break;
default:
AssertStr( TEXT("Invalid ChildID for child of titlebar") );
}
//
// Context help puts into a modal loop, which will block the calling
// thread until the loop ends. Hence we post this instead of sending it.
//
// Note that we will no doubt do something similar in menus.
//
PostMessage(m_hwnd, WM_SYSCOMMAND, scCommand, 0L);
return(S_OK);
}