mirror of
https://github.com/cookiengineer/audacity
synced 2025-11-23 17:30:17 +01:00
Move namespace Registry into new source files
This commit is contained in:
292
src/Registry.h
Normal file
292
src/Registry.h
Normal file
@@ -0,0 +1,292 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
Registry.h
|
||||
|
||||
Paul Licameli split from CommandManager.h
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef __AUDACITY_REGISTRY__
|
||||
#define __AUDACITY_REGISTRY__
|
||||
|
||||
#include "Prefs.h"
|
||||
|
||||
// Define classes and functions that associate parts of the user interface
|
||||
// with path names
|
||||
namespace Registry {
|
||||
// Items in the registry form an unordered tree, but each may also describe a
|
||||
// desired insertion point among its peers. The request might not be honored
|
||||
// (as when the other name is not found, or when more than one item requests
|
||||
// the same ordering), but this is not treated as an error.
|
||||
struct OrderingHint
|
||||
{
|
||||
// The default Unspecified hint is just like End, except that in case the
|
||||
// item is delegated to (by a SharedItem, ComputedItem, or nameless
|
||||
// transparent group), the delegating item's hint will be used instead
|
||||
enum Type : int {
|
||||
Before, After,
|
||||
Begin, End,
|
||||
Unspecified // keep this last
|
||||
} type{ Unspecified };
|
||||
|
||||
// name of some other BaseItem; significant only when type is Before or
|
||||
// After:
|
||||
Identifier name;
|
||||
|
||||
OrderingHint() {}
|
||||
OrderingHint( Type type_, const wxString &name_ = {} )
|
||||
: type(type_), name(name_) {}
|
||||
|
||||
bool operator == ( const OrderingHint &other ) const
|
||||
{ return name == other.name && type == other.type; }
|
||||
|
||||
bool operator < ( const OrderingHint &other ) const
|
||||
{
|
||||
// This sorts unspecified placements later
|
||||
return std::make_pair( type, name ) <
|
||||
std::make_pair( other.type, other.name );
|
||||
}
|
||||
};
|
||||
|
||||
// TODO C++17: maybe use std::variant (discriminated unions) to achieve
|
||||
// polymorphism by other means, not needing unique_ptr and dynamic_cast
|
||||
// and using less heap.
|
||||
// Most items in the table will be the large ones describing commands, so the
|
||||
// waste of space in unions for separators and sub-menus should not be
|
||||
// large.
|
||||
struct BaseItem {
|
||||
// declare at least one virtual function so dynamic_cast will work
|
||||
explicit
|
||||
BaseItem( const Identifier &internalName )
|
||||
: name{ internalName }
|
||||
{}
|
||||
virtual ~BaseItem();
|
||||
|
||||
const Identifier name;
|
||||
|
||||
OrderingHint orderingHint;
|
||||
};
|
||||
using BaseItemPtr = std::unique_ptr<BaseItem>;
|
||||
using BaseItemSharedPtr = std::shared_ptr<BaseItem>;
|
||||
using BaseItemPtrs = std::vector<BaseItemPtr>;
|
||||
|
||||
class Visitor;
|
||||
|
||||
|
||||
// An item that delegates to another held in a shared pointer; this allows
|
||||
// static tables of items to be computed once and reused
|
||||
// The name of the delegate is significant for path calculations, but the
|
||||
// SharedItem's ordering hint is used if the delegate has none
|
||||
struct SharedItem final : BaseItem {
|
||||
explicit SharedItem( const BaseItemSharedPtr &ptr_ )
|
||||
: BaseItem{ wxEmptyString }
|
||||
, ptr{ ptr_ }
|
||||
{}
|
||||
~SharedItem() override;
|
||||
|
||||
BaseItemSharedPtr ptr;
|
||||
};
|
||||
|
||||
// A convenience function
|
||||
inline std::unique_ptr<SharedItem> Shared( const BaseItemSharedPtr &ptr )
|
||||
{ return std::make_unique<SharedItem>( ptr ); }
|
||||
|
||||
// An item that computes some other item to substitute for it, each time
|
||||
// the ComputedItem is visited
|
||||
// The name of the substitute is significant for path calculations, but the
|
||||
// ComputedItem's ordering hint is used if the substitute has none
|
||||
struct ComputedItem final : BaseItem {
|
||||
// The type of functions that generate descriptions of items.
|
||||
// Return type is a shared_ptr to let the function decide whether to
|
||||
// recycle the object or rebuild it on demand each time.
|
||||
// Return value from the factory may be null
|
||||
template< typename VisitorType >
|
||||
using Factory = std::function< BaseItemSharedPtr( VisitorType & ) >;
|
||||
|
||||
using DefaultVisitor = Visitor;
|
||||
|
||||
explicit ComputedItem( const Factory< DefaultVisitor > &factory_ )
|
||||
: BaseItem( wxEmptyString )
|
||||
, factory{ factory_ }
|
||||
{}
|
||||
~ComputedItem() override;
|
||||
|
||||
Factory< DefaultVisitor > factory;
|
||||
};
|
||||
|
||||
// Common abstract base class for items that are not groups
|
||||
struct SingleItem : BaseItem {
|
||||
using BaseItem::BaseItem;
|
||||
~SingleItem() override = 0;
|
||||
};
|
||||
|
||||
// Common abstract base class for items that group other items
|
||||
struct GroupItem : BaseItem {
|
||||
using BaseItem::BaseItem;
|
||||
|
||||
// Construction from an internal name and a previously built-up
|
||||
// vector of pointers
|
||||
GroupItem( const Identifier &internalName, BaseItemPtrs &&items_ )
|
||||
: BaseItem{ internalName }, items{ std::move( items_ ) }
|
||||
{}
|
||||
~GroupItem() override = 0;
|
||||
|
||||
// Whether the item is non-significant for path naming
|
||||
// when it also has an empty name
|
||||
virtual bool Transparent() const = 0;
|
||||
|
||||
BaseItemPtrs items;
|
||||
};
|
||||
|
||||
// GroupItem adding variadic constructor conveniences
|
||||
template< typename VisitorType = ComputedItem::DefaultVisitor >
|
||||
struct InlineGroupItem : GroupItem {
|
||||
using GroupItem::GroupItem;
|
||||
// In-line, variadic constructor that doesn't require building a vector
|
||||
template< typename... Args >
|
||||
InlineGroupItem( const Identifier &internalName, Args&&... args )
|
||||
: GroupItem( internalName )
|
||||
{ Append( std::forward< Args >( args )... ); }
|
||||
|
||||
private:
|
||||
// nullary overload grounds the recursion
|
||||
void Append() {}
|
||||
// recursive overload
|
||||
template< typename Arg, typename... Args >
|
||||
void Append( Arg &&arg, Args&&... moreArgs )
|
||||
{
|
||||
// Dispatch one argument to the proper overload of AppendOne.
|
||||
// std::forward preserves rvalue/lvalue distinction of the actual
|
||||
// argument of the constructor call; that is, it inserts a
|
||||
// std::move() if and only if the original argument is rvalue
|
||||
AppendOne( std::forward<Arg>( arg ) );
|
||||
// recur with the rest of the arguments
|
||||
Append( std::forward<Args>(moreArgs)... );
|
||||
};
|
||||
|
||||
// Move one unique_ptr to an item into our array
|
||||
void AppendOne( BaseItemPtr&& ptr )
|
||||
{
|
||||
items.push_back( std::move( ptr ) );
|
||||
}
|
||||
// This overload allows a lambda or function pointer in the variadic
|
||||
// argument lists without any other syntactic wrapping, and also
|
||||
// allows implicit conversions to type Factory.
|
||||
// (Thus, a lambda can return a unique_ptr<BaseItem> rvalue even though
|
||||
// Factory's return type is shared_ptr, and the needed conversion is
|
||||
// applied implicitly.)
|
||||
void AppendOne( const ComputedItem::Factory<VisitorType> &factory )
|
||||
{
|
||||
auto adaptedFactory = [factory]( Registry::Visitor &visitor ){
|
||||
return factory( dynamic_cast< VisitorType& >( visitor ) );
|
||||
};
|
||||
AppendOne( std::make_unique<ComputedItem>( adaptedFactory ) );
|
||||
}
|
||||
// This overload lets you supply a shared pointer to an item, directly
|
||||
template<typename Subtype>
|
||||
void AppendOne( const std::shared_ptr<Subtype> &ptr )
|
||||
{ AppendOne( std::make_unique<SharedItem>(ptr) ); }
|
||||
};
|
||||
|
||||
// Inline group item also specifying transparency
|
||||
template< bool transparent,
|
||||
typename VisitorType = ComputedItem::DefaultVisitor >
|
||||
struct ConcreteGroupItem : InlineGroupItem< VisitorType >
|
||||
{
|
||||
using InlineGroupItem< VisitorType >::InlineGroupItem;
|
||||
~ConcreteGroupItem() {}
|
||||
bool Transparent() const override { return transparent; }
|
||||
};
|
||||
|
||||
// Concrete subclass of GroupItem that adds nothing else
|
||||
// TransparentGroupItem with an empty name is transparent to item path calculations
|
||||
// and propagates its ordering hint if subordinates don't specify hints
|
||||
// and it does specify one
|
||||
template< typename VisitorType = ComputedItem::DefaultVisitor >
|
||||
struct TransparentGroupItem final : ConcreteGroupItem< true, VisitorType >
|
||||
{
|
||||
using ConcreteGroupItem< true, VisitorType >::ConcreteGroupItem;
|
||||
~TransparentGroupItem() override {}
|
||||
};
|
||||
|
||||
// The /-separated path is relative to the GroupItem supplied to
|
||||
// RegisterItem.
|
||||
// For instance, wxT("Transport/Cursor") to locate an item under a sub-menu
|
||||
// of a main menu
|
||||
struct Placement {
|
||||
wxString path;
|
||||
OrderingHint hint;
|
||||
|
||||
Placement( const wxString &path_, const OrderingHint &hint_ = {} )
|
||||
: path( path_ ), hint( hint_ )
|
||||
{}
|
||||
};
|
||||
|
||||
// registry collects items, before consulting preferences and ordering
|
||||
// hints, and applying the merge procedure to them.
|
||||
// This function puts one more item into the registry.
|
||||
// The sequence of calls to RegisterItem has no significance for
|
||||
// determining the visitation ordering. When sequence is important, register
|
||||
// a GroupItem.
|
||||
void RegisterItem( GroupItem ®istry, const Placement &placement,
|
||||
BaseItemPtr pItem );
|
||||
|
||||
// Define actions to be done in Visit.
|
||||
// Default implementations do nothing
|
||||
// The supplied path does not include the name of the item
|
||||
class Visitor
|
||||
{
|
||||
public:
|
||||
virtual ~Visitor();
|
||||
using Path = std::vector< Identifier >;
|
||||
virtual void BeginGroup( GroupItem &item, const Path &path );
|
||||
virtual void EndGroup( GroupItem &item, const Path &path );
|
||||
virtual void Visit( SingleItem &item, const Path &path );
|
||||
};
|
||||
|
||||
// Top-down visitation of all items and groups in a tree rooted in
|
||||
// pTopItem, as merged with pRegistry.
|
||||
// The merger of the trees is recomputed in each call, not saved.
|
||||
// So neither given tree is modified.
|
||||
// But there may be a side effect on preferences to remember the ordering
|
||||
// imposed on each node of the unordered tree of registered items; each item
|
||||
// seen in the registry for the first time is placed somehere, and that
|
||||
// ordering should be kept the same thereafter in later runs (which may add
|
||||
// yet other previously unknown items).
|
||||
void Visit(
|
||||
Visitor &visitor,
|
||||
BaseItem *pTopItem,
|
||||
const GroupItem *pRegistry = nullptr );
|
||||
|
||||
// Typically a static object. Constructor initializes certain preferences
|
||||
// if they are not present. These preferences determine an extrinsic
|
||||
// visitation ordering for registered items. This is needed in some
|
||||
// places that have migrated from a system of exhaustive listings, to a
|
||||
// registry of plug-ins, and something must be done to preserve old
|
||||
// behavior. It can be done in the central place using string literal
|
||||
// identifiers only, not requiring static compilation or linkage dependency.
|
||||
struct OrderingPreferenceInitializer : PreferenceInitializer {
|
||||
using Literal = const wxChar *;
|
||||
using Pair = std::pair< Literal, Literal >;
|
||||
using Pairs = std::vector< Pair >;
|
||||
OrderingPreferenceInitializer(
|
||||
// Specifies the topmost preference section:
|
||||
Literal root,
|
||||
// Specifies /-separated Registry paths relative to root
|
||||
// (these should be blank or start with / and not end with /),
|
||||
// each with a ,-separated sequence of identifiers, which specify a
|
||||
// desired ordering at one node of the tree:
|
||||
Pairs pairs );
|
||||
|
||||
void operator () () override;
|
||||
|
||||
private:
|
||||
Pairs mPairs;
|
||||
Literal mRoot;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user