windows-nt/Source/XPSP1/NT/shell/shell32/duisec.cpp
2020-09-26 16:20:57 +08:00

548 lines
14 KiB
C++

#include "shellprv.h"
#include "ids.h"
#include "duiview.h"
#include "duisec.h"
#include "duitask.h"
////////////////////////////////////////////////////////
// Expando class
////////////////////////////////////////////////////////
// Cached IDs
ATOM Expando::idTitle = NULL;
ATOM Expando::idIcon = NULL;
ATOM Expando::idTaskList = NULL;
ATOM Expando::idWatermark = NULL;
HRESULT Expando::Create(OUT Element** ppElement)
{
*ppElement = NULL;
Expando* pex = HNewAndZero<Expando>();
if (!pex)
return E_OUTOFMEMORY;
HRESULT hr = pex->Initialize();
if (FAILED(hr))
{
pex->Destroy();
return hr;
}
*ppElement = pex;
return S_OK;
}
Expando::Expando()
{
// Catch unexpected STACK allocations which would break us.
ASSERT(_puiHeader == NULL);
ASSERT(_pDUIView == NULL);
ASSERT(_pDefView == NULL);
// Initialize member variables.
_eDUISecID = DUISEC_UNKNOWN;
_bInfotip = FALSE;
}
Expando::~Expando()
{
DeleteAtom(idTitle);
DeleteAtom(idIcon);
DeleteAtom(idTaskList);
DeleteAtom(idWatermark);
if (_bInfotip)
_pDefView->DestroyInfotip(_hwndRoot, (UINT_PTR)this);
if (_puiHeader)
_puiHeader->Release();
if (_pDUIView)
_pDUIView->Release();
if (_pDefView)
_pDefView->Release();
}
HRESULT Expando::Initialize()
{
HRESULT hr;
// Initialize base
hr = Element::Initialize(0); // Normal display node creation
if (FAILED(hr))
return hr;
// Initialize
_fExpanding = false;
SetSelected(true);
// Cache atoms used for loading from resources
idTitle = AddAtomW(L"title");
idIcon = AddAtomW(L"icon");
idTaskList = AddAtomW(L"tasklist");
idWatermark = AddAtomW(L"watermark");
return S_OK;
}
void Expando::Initialize(DUISEC eDUISecID, IUIElement *puiHeader, CDUIView *pDUIView, CDefView *pDefView)
{
ASSERT(eDUISecID != DUISEC_UNKNOWN);
ASSERT(pDUIView);
ASSERT(pDefView);
_eDUISecID = eDUISecID;
_puiHeader = puiHeader;
if (_puiHeader)
_puiHeader->AddRef();
pDUIView->AddRef();
_pDUIView = pDUIView;
pDefView->AddRef();
_pDefView = pDefView;
_SetAccStateInfo(TRUE);
}
HRESULT Expando::ShowInfotipWindow(Element *peHeader, BOOL bShow)
{
HRESULT hr;
if (_puiHeader)
{
RECT rect = { 0 };
if (bShow)
{
_pDUIView->CalculateInfotipRect(peHeader, &rect);
if (_bInfotip)
{
// Reposition infotip at position.
hr = _pDefView->RepositionInfotip(_hwndRoot, (UINT_PTR)this, &rect);
}
else
{
// Create infotip at position (on the ui thread).
LPWSTR pwszInfotip;
hr = _puiHeader->get_Tooltip(NULL, &pwszInfotip);
if (SUCCEEDED(hr))
{
hr = GetElementRootHWND(this, &_hwndRoot);
if (SUCCEEDED(hr))
{
hr = _pDefView->CreateInfotip(_hwndRoot, (UINT_PTR)this, &rect, pwszInfotip, 0);
if (SUCCEEDED(hr))
{
_bInfotip = TRUE;
}
}
CoTaskMemFree(pwszInfotip);
}
}
}
else
{
if (_bInfotip)
{
// Reposition infotip at nowhere.
hr = _pDefView->RepositionInfotip(_hwndRoot, (UINT_PTR)this, &rect);
}
else
{
// No infotip == no show!
hr = S_OK;
}
}
}
else
{
hr = E_NOTIMPL;
}
return hr;
}
void Expando::OnEvent(Event* pev)
{
if (pev->uidType == Button::Click)
{
// Update exanded property based on clicks that originate
// only from the first child's subtree
Value* pv;
ElementList* peList = GetChildren(&pv);
if (peList && peList->GetSize() > 0)
{
if (peList->GetItem(0) == GetImmediateChild(pev->peTarget))
{
SetSelected(!GetSelected());
pev->fHandled = true;
}
}
pv->Release();
}
Element::OnEvent(pev);
}
////////////////////////////////////////////////////////
// System events
void Expando::OnPropertyChanged(PropertyInfo* ppi, int iIndex, Value* pvOld, Value* pvNew)
{
// Do default processing
Element::OnPropertyChanged(ppi, iIndex, pvOld, pvNew);
if (IsProp(Selected))
{
// Update height of second child based on expanded state
Value* pvChildren;
ElementList* peList = GetChildren(&pvChildren);
if (peList && peList->GetSize() > 1)
{
// The following will cause a relayout, mark object so that
// when the expando's Extent changes, it'll go through
// with the EnsureVisible. Otherwise, it's being resized
// as a result of something else. In which case, do nothing.
_fExpanding = true;
Element* pe = peList->GetItem(1);
// To achieve "pulldown" animation, we use a clipper control that will
// size it's child based on it's unconstrained desired size in its Y direction.
//
if (pvNew->GetBool())
{
pe->RemoveLocalValue(HeightProp);
_pDUIView->OnExpandSection(_eDUISecID, TRUE);
}
else
{
pe->SetHeight(0);
_pDUIView->OnExpandSection(_eDUISecID, FALSE);
}
}
pvChildren->Release();
_SetAccStateInfo(pvNew->GetBool());
}
else if (IsProp(Extent))
{
if (_fExpanding && GetSelected())
{
_fExpanding = false;
// On extent, we want to ensure that not just the client area but
// also the bottom margin of the expando is visible. Why? Simply
// because it looks better to scroll the expando plus its margin
// into view versus just the expando.
//
Value* pvSize;
Value* pvMargin;
const SIZE* psize = GetExtent(&pvSize);
const RECT* prect = GetMargin(&pvMargin);
EnsureVisible(0, 0, psize->cx, psize->cy + prect->bottom);
pvSize->Release();
pvMargin->Release();
}
}
else if (IsProp(MouseWithin))
{
// Extended processing for infotip...
Value* pvChildren;
ElementList* peList = GetChildren(&pvChildren);
if (peList && peList->GetSize() > 0 && pvNew->GetBool() && SHShowInfotips())
{
// ... only displays tip if mouse is within title of Expando.
Element *peHeader = peList->GetItem(0);
ShowInfotipWindow(peHeader, peHeader->GetMouseWithin());
}
else
{
ShowInfotipWindow(NULL, FALSE);
}
pvChildren->Release();
}
}
////////////////////////////////////////////////////////
// Property definitions
////////////////////////////////////////////////////////
// ClassInfo (must appear after property definitions)
// Define class info with type and base type, set static class pointer
IClassInfo* Expando::Class = NULL;
HRESULT Expando::Register()
{
return ClassInfo<Expando,Element>::Register(L"Expando", NULL, 0);
}
void Expando::UpdateTitleUI(IShellItemArray *psiItemArray)
{
if (_puiHeader)
{
LPWSTR pszTitle;
if (SUCCEEDED(_puiHeader->get_Name(psiItemArray, &pszTitle)))
{
Value* pv = Value::CreateString(pszTitle);
if (pv)
{
Element* pe = FindDescendent(StrToID(L"header"));
if (pe)
{
pe->SetAccessible(true);
pe->SetAccRole(ROLE_SYSTEM_OUTLINEBUTTON);
pe->SetValue (Element::AccNameProp, PI_Local, pv);
}
else
{
TraceMsg (TF_ERROR, "Expando::UpdateTitleUI: Button child for Expando not found.");
}
pe = FindDescendent (Expando::idTitle);
if (pe)
{
pe->SetValue (Element::ContentProp, PI_Local, pv);
}
else
{
TraceMsg (TF_ERROR, "Expando::UpdateTitleUI: FindDescendent for the title failed.");
}
pv->Release ();
}
else
{
TraceMsg (TF_ERROR, "Expando::UpdateTitleUI: CreateString for the title failed.");
}
CoTaskMemFree(pszTitle);
}
else
{
TraceMsg (TF_ERROR, "Expando::UpdateTitleUI: get_Name failed.");
}
}
}
void Expando::ShowExpando(BOOL fShow)
{
if (fShow && (_fShow != TRIBIT_TRUE))
{
SetHeight(-1);
RemoveLocalValue(MarginProp);
_fShow = TRIBIT_TRUE;
}
if (!fShow && (_fShow != TRIBIT_FALSE))
{
SetHeight(0);
SetMargin(0,0,0,0);
_fShow = TRIBIT_FALSE;
}
}
void Expando::_SetAccStateInfo (BOOL bExpanded)
{
// Update the accessibility state information
//
// Note: In the Expando::Initialize() method, we explicitly set the
// Selected state to true. This causes OnPropertyChanged to be called
// for the Selected property, which will call this method. However,
// the child elements will not exist yet (since we are in the creation process).
// Hence, the call to FindDescendent will return NULL and this method will exit.
// This method is explicitly called in the second version of Initialze to
// set the correct accessibility information.
Element * pe = FindDescendent(StrToID(L"header"));
if (pe)
{
TCHAR szDefaultAction[50] = {0};
if (bExpanded)
{
pe->SetAccState(STATE_SYSTEM_EXPANDED);
LoadString(HINST_THISDLL, IDS_EXPANDO_DEFAULT_ACTION_COLLAPSE, szDefaultAction, ARRAYSIZE(szDefaultAction));
}
else
{
pe->SetAccState(STATE_SYSTEM_COLLAPSED);
LoadString(HINST_THISDLL, IDS_EXPANDO_DEFAULT_ACTION_EXPAND, szDefaultAction, ARRAYSIZE(szDefaultAction));
}
pe->SetAccDefAction(szDefaultAction);
}
}
////////////////////////////////////////////////////////
// Clipper class
////////////////////////////////////////////////////////
HRESULT Clipper::Create(OUT Element** ppElement)
{
*ppElement = NULL;
Clipper* pc = HNewAndZero<Clipper>();
if (!pc)
return E_OUTOFMEMORY;
HRESULT hr = pc->Initialize();
if (FAILED(hr))
{
pc->Destroy();
return hr;
}
*ppElement = pc;
return S_OK;
}
HRESULT Clipper::Initialize()
{
// Initialize base
HRESULT hr = Element::Initialize(EC_SelfLayout); // Normal display node creation, self layout
if (FAILED(hr))
return hr;
// Children can exist outside of Element bounds
SetGadgetStyle(GetDisplayNode(), GS_CLIPINSIDE, GS_CLIPINSIDE);
return S_OK;
}
////////////////////////////////////////////////////////
// Self-layout methods
SIZE Clipper::_SelfLayoutUpdateDesiredSize(int cxConstraint, int cyConstraint, Surface* psrf)
{
UNREFERENCED_PARAMETER(cyConstraint);
Value* pvChildren;
SIZE size = { 0, 0 };
ElementList* peList = GetChildren(&pvChildren);
// Desired size of this is based solely on it's first child.
// Width is child's width, height is unconstrained height of child.
if (peList && peList->GetSize() > 0)
{
Element* pec = peList->GetItem(0);
size = pec->_UpdateDesiredSize(cxConstraint, INT_MAX, psrf);
if (size.cx > cxConstraint)
size.cx = cxConstraint;
if (size.cy > cyConstraint)
size.cy = cyConstraint;
}
pvChildren->Release();
return size;
}
void Clipper::_SelfLayoutDoLayout(int cx, int cy)
{
Value* pvChildren;
ElementList* peList = GetChildren(&pvChildren);
// Layout first child giving it's desired height and aligning
// it with the clipper's bottom edge
if (peList && peList->GetSize() > 0)
{
Element* pec = peList->GetItem(0);
const SIZE* pds = pec->GetDesiredSize();
pec->_UpdateLayoutPosition(0, cy - pds->cy);
pec->_UpdateLayoutSize(cx, pds->cy);
}
pvChildren->Release();
}
////////////////////////////////////////////////////////
// Property definitions
////////////////////////////////////////////////////////
// ClassInfo (must appear after property definitions)
// Define class info with type and base type, set static class pointer
IClassInfo* Clipper::Class = NULL;
HRESULT Clipper::Register()
{
return ClassInfo<Clipper,Element>::Register(L"Clipper", NULL, 0);
}
////////////////////////////////////////////////////////
// TaskList class
////////////////////////////////////////////////////////
HRESULT TaskList::Create(OUT Element** ppElement)
{
*ppElement = NULL;
TaskList* pc = HNewAndZero<TaskList>();
if (!pc)
return E_OUTOFMEMORY;
HRESULT hr = pc->Initialize();
if (FAILED(hr))
{
pc->Destroy();
return hr;
}
*ppElement = pc;
return S_OK;
}
HRESULT TaskList::Initialize()
{
// Initialize base
HRESULT hr = Element::Initialize(0); // Normal display node creation, self layout
if (FAILED(hr))
return hr;
return S_OK;
}
////////////////////////////////////////////////////////
// Hierarchy
Element* TaskList::GetAdjacent(Element* peFrom, int iNavDir, NavReference const* pnr, bool bKeyable)
{
if ((iNavDir & NAV_LOGICAL) && peFrom)
return NULL;
return Element::GetAdjacent(peFrom, iNavDir, pnr, bKeyable);
}
////////////////////////////////////////////////////////
// Property definitions
////////////////////////////////////////////////////////
// ClassInfo (must appear after property definitions)
// Define class info with type and base type, set static class pointer
IClassInfo* TaskList::Class = NULL;
HRESULT TaskList::Register()
{
return ClassInfo<TaskList,Element>::Register(L"TaskList", NULL, 0);
}