13#include <unordered_set>
28 : GroupItemBase{ identifier }
29 , ordering{ ordering == Strong ? Weak : ordering }
56 auto end = items.end();
59 : std::find_if( items.begin(),
end,
60 [&](
const Item& item ){
61 return name == item.visitNow->name; } );
64 auto InsertNewItemUsingPreferences(
67 auto InsertNewItemUsingHint(
68 BaseItem *pItem,
const OrderingHint &hint,
size_t endItemsCount,
73 GroupItemBase::Ordering ordering) -> GroupItemBase *;
75 void SubordinateSingleItem(Item &found, BaseItem *pItem);
77 void SubordinateMultipleItems(Item &found, GroupItemBase &items);
79 bool MergeWithExistingItem(
ItemOrdering &itemOrdering, BaseItem *pItem);
81 using NewItem = std::pair< BaseItem*, OrderingHint >;
85 NewItems::const_iterator left, NewItems::const_iterator right,
86 int iPass,
size_t endItemsCount,
bool force);
88 void MergeItemsAscendingNamesPass(
ItemOrdering &itemOrdering,
89 NewItems &newItems,
int iPass,
size_t endItemsCount,
bool force);
91 void MergeItemsDescendingNamesPass(
ItemOrdering &itemOrdering,
92 NewItems &newItems,
int iPass,
size_t endItemsCount,
bool force);
96 void *pComputedItemContext);
103 return !delegate || delegate->orderingHint.
type == OrderingHint::Unspecified
105 : delegate->orderingHint;
115void CollectItem(CollectedItems &collection, BaseItem *Item,
120 for (
auto &item : items )
122 ChooseHint(item.get(), hint), pComputedItemContext);
125 BaseItem *pItem,
const OrderingHint &hint,
void *pComputedItemContext)
131 if (
const auto pIndirect =
132 dynamic_cast<IndirectItemBase*
>(pItem)) {
133 auto delegate = pIndirect->ptr.get();
137 ChooseHint(delegate, pIndirect->orderingHint), pComputedItemContext);
140 if (
const auto pComputed =
141 dynamic_cast<ComputedItemBase*
>(pItem)) {
142 auto result = pComputed->factory(pComputedItemContext);
148 ChooseHint(result.get(), pComputed->orderingHint),
149 pComputedItemContext);
153 if (
auto pGroup =
dynamic_cast<GroupItemBase*
>(pItem)) {
154 if (pGroup->GetOrdering() == GroupItemBase::Anonymous)
159 ChooseHint(pGroup, hint), pComputedItemContext);
163 collection.
items.push_back( {pItem,
nullptr, hint} );
168 collection.
items.push_back( {pItem,
nullptr, hint} );
172using Path = std::vector< Identifier >;
179 auto badPath =
key +
'/' +
name.GET();
180 if (
sBadPaths.insert( badPath ).second ) {
183 wxLogDebug( msg.Translation() );
194XO(
"Plug-in group at %s was merged with a previously defined group"),
201XO(
"Plug-in item at %s conflicts with a previously defined item and was discarded"),
208XO(
"Plug-in items at %s specify conflicting placements"),
221 wxArrayString strings;
222 for (
const auto &
id : path)
223 strings.push_back(
id.GET() );
224 key =
'/' + ::wxJoin( strings,
'/',
'\0' );
228 bool gotOrdering =
false;
232 auto Get() -> wxArrayString & {
233 if ( !gotOrdering ) {
235 ordering = ::wxSplit( strValue,
',' );
247auto CollectedItems::InsertNewItemUsingPreferences(
260 if ( !pItem->name.empty() ) {
262 auto &ordering = itemOrdering.Get();
263 auto begin2 = ordering.begin(), end2 = ordering.end(),
264 found2 = std::find( begin2, end2, pItem->name );
265 if ( found2 != end2 ) {
266 auto insertPoint = items.end();
269 while ( ++found2 != end2 ) {
270 auto known =
Find( *found2 );
271 if ( known != insertPoint ) {
276 items.insert( insertPoint, {pItem,
nullptr,
288auto CollectedItems::InsertNewItemUsingHint(
289 BaseItem *pItem,
const OrderingHint &hint,
size_t endItemsCount,
292 auto begin = items.begin(),
end = items.end(),
293 insertPoint =
end - endItemsCount;
297 if ( pItem->name.empty() ) {
302 switch ( hint.type ) {
303 case OrderingHint::Before:
304 case OrderingHint::After: {
306 auto found =
Find( hint.name );
307 if ( found ==
end ) {
315 if ( hint.type == OrderingHint::After )
323 case OrderingHint::End:
326 case OrderingHint::Unspecified:
335 items.insert( insertPoint, {pItem,
nullptr,
342 GroupItemBase::Ordering ordering) -> GroupItemBase *
344 auto subGroup = found.mergeLater;
346 auto newGroup = std::make_shared<PlaceHolder>(
name, ordering);
347 computedItems.push_back(newGroup);
348 subGroup = found.mergeLater = newGroup.get();
353void CollectedItems::SubordinateSingleItem(
Item &found, BaseItem *pItem)
355 MergeLater(found, pItem->name, GroupItemBase::Weak)->push_back(
356 std::make_unique<IndirectItemBase>(
358 std::shared_ptr<BaseItem>(pItem, [](
void*){})));
361void CollectedItems::SubordinateMultipleItems(
Item &found, GroupItemBase &items)
363 auto subGroup = MergeLater(found, items.name, items.GetOrdering());
364 for (
const auto &pItem : items)
365 subGroup->push_back(std::make_unique<IndirectItemBase>(
367 std::shared_ptr<BaseItem>(pItem.get(), [](
void*){})));
370bool CollectedItems::MergeWithExistingItem(
374 const auto &
name = pItem->name;
376 if (found != items.end()) {
380 auto pCollectionGroup =
dynamic_cast< GroupItemBase *
>( found->visitNow );
381 auto pRegistryGroup =
dynamic_cast< GroupItemBase *
>( pItem );
382 if (pCollectionGroup) {
383 if (pRegistryGroup) {
390 bool pCollectionGrouping =
391 (pCollectionGroup->GetOrdering() != GroupItemBase::Strong);
392 auto pRegistryGrouping =
393 (pRegistryGroup->GetOrdering() != GroupItemBase::Strong);
394 if ( !(pCollectionGrouping || pRegistryGrouping) )
397 if ( pCollectionGrouping && !pRegistryGrouping ) {
399 found->visitNow = pRegistryGroup;
400 SubordinateMultipleItems(*found, *pCollectionGroup);
403 SubordinateMultipleItems(*found, *pRegistryGroup);
410 SubordinateSingleItem(*found, pItem);
414 if (pRegistryGroup) {
419 auto demoted = found->visitNow;
420 found->visitNow = pRegistryGroup;
421 SubordinateSingleItem(*found, demoted);
438 NewItems::const_iterator left, NewItems::const_iterator right,
439 const int iPass,
size_t endItemsCount,
bool force)
447 auto pItem = item.first;
448 const auto &hint = item.second;
449 bool success =
false;
452 success = InsertNewItemUsingPreferences( itemOrdering, pItem );
453 else if ( iPass == hint.type ) {
458 InsertNewItemUsingHint( pItem, hint, endItemsCount, force );
459 wxASSERT( !force || success );
465 if ( iter != right && iPass != 0 &&
466 iter->second.type != OrderingHint::Unspecified &&
467 !( iter->second == hint ) ) {
471 while ( iter != right )
474 MergeWithExistingItem(itemOrdering, iter++ -> first);
483 return a.first->name > b.first->name;
489 return a.second < b.second;
500void CollectedItems::MergeItemsAscendingNamesPass(
ItemOrdering &itemOrdering,
501 NewItems &newItems,
const int iPass,
size_t endItemsCount,
bool force)
504 auto rright = newItems.rbegin();
505 auto rend = newItems.rend();
506 while ( rright != rend ) {
508 using namespace std::placeholders;
509 auto rleft = std::find_if(
510 rright + 1, rend, std::bind(
MajorComp, _1, *rright ) );
512 bool success = MergeLikeNamedItems(itemOrdering,
513 rleft.base(), rright.base(), iPass, endItemsCount, force);
516 auto diff = rend - rleft;
517 newItems.erase( rleft.base(), rright.base() );
518 rend = newItems.rend();
525void CollectedItems::MergeItemsDescendingNamesPass(
ItemOrdering &itemOrdering,
526 NewItems &newItems,
const int iPass,
size_t endItemsCount,
bool force)
529 auto left = newItems.begin();
530 while ( left != newItems.end() ) {
532 using namespace std::placeholders;
533 auto right = std::find_if(
534 left + 1, newItems.end(), std::bind(
MajorComp, *left, _1 ) );
536 bool success = MergeLikeNamedItems(itemOrdering, left, right, iPass,
537 endItemsCount, force );
540 left = newItems.erase( left, right );
548 void *pComputedItemContext)
556 CollectItems(newCollection, toMerge, hint, pComputedItemContext);
560 for (
const auto &item : newCollection.items)
561 if (!MergeWithExistingItem(itemOrdering, item.visitNow))
562 newItems.push_back({ item.visitNow, item.hint });
569 std::sort( newItems.begin(), newItems.end(),
Comp );
575 size_t endItemsCount = 0;
576 auto prevSize = newItems.size();
577 while( !newItems.empty() )
587 MergeItemsDescendingNamesPass(itemOrdering,
588 newItems, iPass, endItemsCount, force);
590 MergeItemsAscendingNamesPass(itemOrdering,
591 newItems, iPass, endItemsCount, force);
593 auto newSize = newItems.size();
599 else if ( iPass == OrderingHint::Unspecified ) {
601 iPass = 0, oldSize = newSize;
603 bool progress = ( oldSize > newSize );
612 else if (iPass == OrderingHint::End && endItemsCount == 0)
614 assert(newSize >= prevSize || newSize == 0);
616 endItemsCount = newSize - prevSize;
626 Path &path,
const BaseItem *pItem,
627 const GroupItemBase *pToMerge,
const OrderingHint &hint,
628 bool &doFlush,
void *pComputedItemContext);
631 Path &path,
const GroupItemBase &group,
632 const GroupItemBase *pToMerge,
const OrderingHint &hint,
633 bool &doFlush,
void *pComputedItemContext)
641 pComputedItemContext);
643 path.push_back(group.name.GET());
649 newCollection.MergeItems(itemOrdering, *pToMerge, hint,
650 pComputedItemContext);
654 if ( itemOrdering.gotOrdering ) {
656 for (
const auto &item : newCollection.items ) {
657 const auto &
name = item.visitNow->name;
659 newValue += newValue.empty()
663 if (newValue != itemOrdering.strValue) {
671 for (
const auto &item : newCollection.items )
673 item.visitNow, item.mergeLater, item.hint,
674 doFlush, pComputedItemContext);
680 Path &path,
const BaseItem *pItem,
681 const GroupItemBase *pToMerge,
const OrderingHint &hint,
682 bool &doFlush,
void *pComputedItemContext)
687 if (
const auto pSingle =
689 wxASSERT( !pToMerge );
690 visitor.Visit( *pSingle, path );
693 if (
const auto pGroup =
694 dynamic_cast<const GroupItemBase*
>(pItem)) {
695 visitor.BeginGroup(*pGroup, path);
698 visitor, collection, path, *pGroup, pToMerge, hint, doFlush,
699 pComputedItemContext);
700 visitor.EndGroup(*pGroup, path);
712BaseItem::~BaseItem() {}
714IndirectItemBase::~IndirectItemBase() {}
716ComputedItemBase::~ComputedItemBase() {}
720GroupItemBase::~GroupItemBase() {}
721auto GroupItemBase::GetOrdering() const ->
Ordering {
return Strong; }
723VisitorBase::~VisitorBase() =
default;
729 assert(pComputedItemContext);
730 std::vector< BaseItemSharedPtr > computedItems;
731 bool doFlush =
false;
732 CollectedItems collection{ {}, computedItems };
735 visitor, collection, emptyPath, pTopItem,
736 pRegistry, pRegistry->orderingHint, doFlush, pComputedItemContext);
744 : mPairs{
std::move( pairs ) }
752 bool doFlush =
false;
753 for (
const auto &pair :
mPairs) {
754 const auto key = wxString{
'/'} +
mRoot + pair.first;
770 std::vector<BaseItemPtr> *pItems{};
774 return component < pItem->name; }
777 return pItem->name < component; }
779 auto find = [&pItems](
const Identifier &component ){
return std::equal_range(
780 pItems->begin(), pItems->end(), component, Comparator() ); };
782 auto pNode = ®istry;
783 pItems = &pNode->
items;
785 const auto pathComponents = ::wxSplit( placement.
path,
'/' );
786 auto pComponent = pathComponents.begin(),
end = pathComponents.end();
790 auto debugPath = wxString{
'/'} + registry.name.GET();
791 while ( pComponent !=
end ) {
792 const auto &pathComponent = *pComponent;
796 const auto range = find( pathComponent );
797 const auto iter2 = std::find_if( range.first, range.second,
799 return dynamic_cast< GroupItemBase* >( pItem.get() ); } );
801 if ( iter2 != range.second ) {
804 pItems = &pNode->
items;
805 debugPath +=
'/' + pathComponent;
817 while ( pComponent !=
end ) {
819 std::make_unique<PlaceHolder>(*pComponent, GroupItemBase::Weak);
820 pNode = newNode.get();
821 pItems->insert( find( pNode->name ).second, std::move( newNode ) );
822 pItems = &pNode->items;
827 pItem->orderingHint = placement.
hint;
830 pItems->insert( find( pItem->name ).second, std::move( pItem ) );
Toolkit-neutral facade for basic user interface services.
audacity::BasicSettings * gPrefs
An explicitly nonlocalized string, not meant for the user to see.
Holds a msgid for the translation catalog; may also bind format arguments.
virtual bool Flush() noexcept=0
virtual bool Write(const wxString &key, bool value)=0
virtual bool Read(const wxString &key, bool *value) const =0
PROJECT_FILE_IO_API wxString Find(const FilePath &path)
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
bool Begin(const FilePath &dataDir)
REGISTRIES_API void Visit(VisitorBase &visitor, const GroupItemBase *pTopItem, const GroupItemBase *pRegistry, void *pComputedItemContext)
REGISTRIES_API void RegisterItem(GroupItemBase ®istry, const Placement &placement, BaseItemPtr pItem)
Type-erased implementation details, don't call directly.
std::unique_ptr< BaseItem > BaseItemPtr
std::vector< Identifier > Path
bool MinorComp(const CollectedItems::NewItem &a, const CollectedItems::NewItem &b)
void ReportConflictingPlacements(const wxString &key, const Identifier &name)
std::vector< Identifier > Path
void VisitItem(VisitorBase &visitor, CollectedItems &collection, Path &path, const BaseItem *pItem, const GroupItemBase *pToMerge, const OrderingHint &hint, bool &doFlush, void *pComputedItemContext)
void CollectItem(CollectedItems &collection, BaseItem *Item, const OrderingHint &hint, void *pComputedItemContext)
void ReportItemItemCollision(const wxString &key, const Identifier &name)
std::unordered_set< wxString > sBadPaths
bool Comp(const CollectedItems::NewItem &a, const CollectedItems::NewItem &b)
void CollectItems(CollectedItems &collection, const GroupItemBase &items, const OrderingHint &hint, void *pComputedItemContext)
void VisitItems(VisitorBase &visitor, CollectedItems &collection, Path &path, const GroupItemBase &group, const GroupItemBase *pToMerge, const OrderingHint &hint, bool &doFlush, void *pComputedItemContext)
const OrderingHint & ChooseHint(BaseItem *delegate, const OrderingHint &hint)
void BadPath(const TranslatableString &format, const wxString &key, const Identifier &name)
bool MajorComp(const CollectedItems::NewItem &a, const CollectedItems::NewItem &b)
void ReportGroupGroupCollision(const wxString &key, const Identifier &name)
const char * end(const char *str) noexcept
const char * begin(const char *str) noexcept
static EmptyContext Instance
Has variadic and range constructors that check types.
enum Registry::OrderingHint::Type type
void operator()() override
std::vector< Pair > Pairs
OrderingPreferenceInitializer(Literal root, Pairs pairs)
Common abstract base class for items that are not groups.
Common abstract base class for items that group other items.
Ordering
Choose treatment of the children of the group when merging trees.
GroupItemBase * mergeLater
std::vector< BaseItemSharedPtr > & computedItems
decltype(items)::iterator Iterator
std::vector< NewItem > NewItems
auto Find(const Identifier &name) -> Iterator
std::pair< BaseItem *, OrderingHint > NewItem
std::vector< Item > items
ItemOrdering(const Path &path)
auto Get() -> wxArrayString &
PlaceHolder(const Identifier &identifier, Ordering ordering)
Ordering GetOrdering() const override