Files
foobar2000-sdk/libPPUI/CListAccessible.h
2021-12-14 00:28:25 -07:00

293 lines
10 KiB
C++

#pragma once
#include <memory>
#pragma comment(lib, "oleacc.lib")
#include "CListControl-Cell.h"
#include "CListControlWithSelection.h"
//! Internal class interfacing with Windows accessibility APIs. \n
//! This class is not tied to any specific control and requires most of its methods to be overridden. \n
//! With CListControl you want to use CListControlAccImpl<> template instead of using CListAccessible directly.
class CListAccessible {
public:
void AccInitialize(CWindow wnd);
void AccCleanup();
LRESULT AccGetObject(WPARAM wp,LPARAM lp);
CWindow AccGetWnd() const {return m_wnd;}
virtual size_t AccGetItemCount() const {return 0;}
virtual LONG AccGetItemRole( size_t index ) const {return ROLE_SYSTEM_LISTITEM;}
virtual void AccGetItemName(size_t index, pfc::string_base & out) const {out = "";}
virtual void AccGetName(pfc::string_base & out) const;
virtual size_t AccGetFocusItem() const {return ~0;}
virtual bool AccIsItemSelected(size_t index) const {return false;}
virtual bool AccIsItemChecked( size_t index ) const { return false; }
virtual bool AccIsItemVisible(size_t index) const {return false;}
virtual bool AccGetItemDefaultAction(pfc::string_base & out) const {return false;}
virtual bool AccExecuteItemDefaultAction(size_t index) {return false;}
virtual void AccSetSelection(pfc::bit_array const & affected, pfc::bit_array const & state) {}
virtual void AccSetFocusItem(size_t index) {}
virtual bool AccGetItemRect(size_t index, CRect & out) const {return false;}
virtual bool AccGetItemDescription(size_t index, pfc::string_base & out) const {return false;}
virtual size_t AccItemHitTest(CPoint const & pt) const {return ~0;}
virtual DWORD AccGetOtherRole(size_t index) {return 0;}
virtual size_t AccGetOtherCount() const {return 0;}
virtual void AccGetOtherName(size_t index, pfc::string_base & out) const {out = "";}
virtual size_t AccGetFocusOther() const {return ~0;}
virtual void AccSetFocusOther(size_t index) {}
virtual bool AccIsOtherVisible(size_t index) const {return false;}
virtual bool AccGetOtherDescription(size_t index, pfc::string_base & out) const {return false;}
virtual size_t AccOtherHitTest(CPoint const & pt) const {return ~0;}
virtual bool AccIsOtherFocusable(size_t index) const {return false;}
virtual bool AccGetOtherDefaultAction(size_t index, pfc::string_base & out) const {return false;}
virtual bool AccExecuteOtherDefaultAction(size_t index) {return false;}
virtual bool AccGetOtherRect(size_t index, CRect & out) const {return false;}
virtual CWindow AccGetOtherChildWnd(size_t index) const {return NULL;}
void AccItemNamesChanged( pfc::bit_array const & mask);
void AccReloadItems(pfc::bit_array const & mask );
void AccRefreshItems(pfc::bit_array const & mask, UINT what);
void AccStateChange(pfc::bit_array const & mask);
void AccStateChange(size_t index);
void AccItemLayoutChanged();
void AccFocusItemChanged(size_t index);
void AccFocusOtherChanged(size_t index);
void AccSelectionChanged(const pfc::bit_array & affected, const pfc::bit_array & status);
void AccLocationChange();
protected:
~CListAccessible() { * m_killSwitch = true; }
private:
CWindow m_wnd;
std::shared_ptr<bool> m_killSwitch = std::make_shared<bool>();
pfc::com_ptr_t<IAccessible> m_interface;
};
//! Basic wrapper implementing CListAccessible methods on top of CListControl. Leaves many methods to be overridden by calle
template<typename TBaseClass> class CListControlAccImpl : public TBaseClass, protected CListAccessible {
public:
template<typename ... arg_t> CListControlAccImpl( arg_t && ... arg ) : TBaseClass(std::forward<arg_t>(arg) ... ) {}
BEGIN_MSG_MAP_EX(CListControlAccImpl)
MESSAGE_HANDLER(WM_GETOBJECT,OnGetObject)
MESSAGE_HANDLER(WM_DESTROY,OnDestroyPassThru)
MESSAGE_HANDLER(WM_CREATE,OnCreatePassThru)
CHAIN_MSG_MAP(TBaseClass)
END_MSG_MAP()
public:
void ReloadData() {
TBaseClass::ReloadData();
AccItemLayoutChanged();
}
void ReloadItems(pfc::bit_array const & mask) {
TBaseClass::ReloadItems(mask);
AccReloadItems(mask);
}
protected:
size_t AccGetItemCount() const {return this->GetItemCount();}
size_t AccGetFocusItem() const {return this->GetFocusItem();}
bool AccIsItemSelected(size_t index) const {return this->IsItemSelected(index);}
bool AccIsItemVisible(size_t index) const {
if (index >= AccGetItemCount()) return false;
return this->IsRectVisible(this->GetItemRect(index));
}
bool AccExecuteItemDefaultAction(size_t index) {if (index < AccGetItemCount()) {this->ExecuteDefaultAction(index);return true;} else return false;}
bool AccGetItemRect(size_t index, CRect & out) const {
if (index >= AccGetItemCount()) return false;
out = this->GetItemRect(index);
return true;
}
size_t AccItemHitTest(CPoint const & pt) const {
size_t item;
if (!this->ItemFromPoint(pt,item)) return ~0;
return item;
}
void OnViewOriginChange(CPoint p_delta) override {
TBaseClass::OnViewOriginChange(p_delta);
AccLocationChange();
}
/* overrideme, optional
void AccGetName(pfc::string_base & out) const;
bool AccGetItemDefaultAction(pfc::string_base & out) const;
*/
LONG AccRoleAt(size_t idx, size_t sub) const {
auto cell = this->GetCellType(idx, sub);
if (cell == nullptr) return 0;
return cell->AccRole();
}
bool isCellText(size_t idx, size_t sub) const {
switch (AccRoleAt(idx, sub)) {
case ROLE_SYSTEM_TEXT:
case ROLE_SYSTEM_STATICTEXT:
case ROLE_SYSTEM_LISTITEM:
return true;
default:
return false;
}
}
bool useCellForDescription(size_t idx, size_t sub) const {
return sub > 0 && isCellText(idx, sub);
}
bool AccGetItemDescription(size_t index, pfc::string_base& out) const {
pfc::string_formatter ret, temp, temp2;
const size_t total = this->GetColumnCount();
for (size_t walk = 0; walk < total; ) {
if (useCellForDescription(index, walk) && this->GetSubItemText(index, walk, temp) && temp.length() > 0) {
if (ret.length() > 0) ret << "; ";
this->GetColumnText(walk, temp2);
if (temp2.length() > 0) ret << temp2 << ": ";
ret << temp;
}
size_t d = this->GetSubItemSpan(index, walk);
if (d < 1) d = 1;
walk += d;
}
bool rv = (ret.length() > 0);
if (rv) out = ret;
return ret;
}
void AccGetItemNameAlt(size_t index, pfc::string_base & out) const {
pfc::string_formatter ret, temp;
const size_t total = this->GetColumnCount();
for (size_t walk = 0; walk < total; ) {
if (this->isCellText(index, walk) && this->GetSubItemText(index, walk, temp) && temp.length() > 0) {
if (ret.length() > 0) ret << "; ";
ret << temp;
}
size_t d = this->GetSubItemSpan(index, walk);
if (d < 1) d = 1;
walk += d;
}
out = ret;
}
// Item name by default taken from column 0, override this if you supply another
void AccGetItemName(size_t index, pfc::string_base & out) const {
this->GetSubItemText(index, 0, out);
}
void AccSetSelection(pfc::bit_array const & affected, pfc::bit_array const & state) override {
this->SetSelection(affected, state);
}
void AccSetFocusItem(size_t index) override {
this->SetFocusItem(index);
}
void OnFocusChangedGroup(int iGroup) override {
TBaseClass::OnFocusChangedGroup(iGroup);
AccFocusOtherChanged((size_t)iGroup);
}
void OnFocusChanged(size_t f) override {
TBaseClass::OnFocusChanged(f);
AccFocusItemChanged(f);
}
void OnSelectionChanged(pfc::bit_array const & affected, pfc::bit_array const & status) override {
TBaseClass::OnSelectionChanged(affected, status);
AccSelectionChanged(affected, status);
}
CWindow AccGetOtherChildWnd(size_t index) const {return index == 0 ? CWindow(this->GetHeaderCtrl()) : CWindow(NULL) ;}
virtual DWORD AccGetOtherRole(size_t index) {return index > 0 ? ROLE_SYSTEM_GROUPING : ROLE_SYSTEM_WINDOW;}//FIXME?
virtual bool AccGetOtherDescription(size_t index, pfc::string_base & out) const {return false;}//FIXME??
size_t AccGetOtherCount() const {return 1 + this->GetGroupCount();}
void AccGetOtherName(size_t index, pfc::string_base & out) const override {
if (index == 0) out = "Columns Header";
else if (!this->GetGroupHeaderText((int)index, out)) out = "";
}
size_t AccGetFocusOther() const override {
int focus = this->GetGroupFocus();
if (focus > 0) return (size_t) focus;
else return SIZE_MAX;
}
void AccSetFocusOther(size_t index) override {
if (index > 0) this->SetGroupFocus((int)index);
}
bool AccIsOtherVisible(size_t index) const override {
if (index == 0) return true;
CRect rc;
if (!this->GetGroupHeaderRect((int)index,rc)) return false;
return IsRectVisible(rc);
}
size_t AccOtherHitTest(CPoint const & pt) const {
{
CPoint s(pt);
if (this->ClientToScreen(&s)) {
CRect rc;
auto hdr = this->GetHeaderCtrl();
if (hdr != NULL && hdr.GetWindowRect(rc)) {
if (rc.PtInRect(s)) {
return 0;
}
}
}
}
int group;
if (this->GroupHeaderFromPoint(pt,group)) return (size_t) group;
return ~0;
}
bool AccIsOtherFocusable(size_t index) const {return index > 0;}
bool AccGetOtherDefaultAction(size_t index, pfc::string_base & out) const {return false;}
bool AccExecuteOtherDefaultAction(size_t index) {return false;}
bool AccGetOtherRect(size_t index, CRect & out) const {
if (index == 0) {
CRect rc, client;
auto hdr = this->GetHeaderCtrl();
if ( hdr == NULL ) return false;
if (!hdr.GetWindowRect(rc)) return false;
if (!this->ScreenToClient(rc)) return false;
if (!this->GetClientRect(client)) return false;
return !! out.IntersectRect(rc, client);
} else {
return this->GetGroupHeaderRect((int)index, out);
}
}
LONG AccGetItemRole( size_t index ) const override {
auto type = this->GetCellType( index, 0 );
if ( type != nullptr ) {
return type->AccRole();
}
return ROLE_SYSTEM_LISTITEM;
}
void SetCellCheckState(size_t item, size_t subItem, bool value) override {
__super::SetCellCheckState(item, subItem, value);
this->AccStateChange(item);
}
bool AccIsItemChecked( size_t index ) const override {
auto type = this->GetCellType( index, 0 );
if ( type != nullptr && type->IsToggle() ) {
return this->GetCellCheckState( index, 0 );
}
return false;
}
private:
bool IsRectVisible(CRect const & rc) const {
return !!CRect().IntersectRect(this->GetClientRectHook(),rc);
}
LRESULT OnCreatePassThru(UINT,WPARAM,LPARAM,BOOL& bHandled) {
bHandled = FALSE;
try { AccInitialize(*this); } catch(...) {AccCleanup();}
return 0;
}
LRESULT OnDestroyPassThru(UINT,WPARAM,LPARAM,BOOL& bHandled) {
bHandled = FALSE;
AccCleanup();
return 0;
}
LRESULT OnGetObject(UINT,WPARAM wp, LPARAM lp, BOOL & ) {
return this->AccGetObject(wp,lp);
}
};
class CListControlWithSelectionImpl;
typedef CListControlAccImpl< CListControlWithSelectionImpl > CListControlWithSelectionAccImpl;