|
|
#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); }
|