Audacity 3.2.0
Registry.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5Registry.cpp
6
7Paul Licameli split from Menus.cpp
8
9**********************************************************************/
10
11#include "Registry.h"
12
13#include <unordered_set>
14
15#include <wx/log.h>
16
17#include "BasicUI.h"
18
19namespace {
20
21struct ItemOrdering;
22
23using namespace Registry;
25{
26 struct Item{
27 // Predefined, or merged from registry already:
29 // Corresponding item from the registry, its sub-items to be merged:
31 // Ordering hint for the merged item:
33 };
34 std::vector< Item > items;
35 std::vector< BaseItemSharedPtr > &computedItems;
36
37 // A linear search. Smarter search may not be worth the effort.
38 using Iterator = decltype( items )::iterator;
39 auto Find( const Identifier &name ) -> Iterator
40 {
41 auto end = items.end();
42 return name.empty()
43 ? end
44 : std::find_if( items.begin(), end,
45 [&]( const Item& item ){
46 return name == item.visitNow->name; } );
47 }
48
49 auto InsertNewItemUsingPreferences(
50 ItemOrdering &itemOrdering, BaseItem *pItem ) -> bool;
51
52 auto InsertNewItemUsingHint(
53 BaseItem *pItem, const OrderingHint &hint, size_t endItemsCount,
54 bool force )
55 -> bool;
56
57 auto MergeLater( Item &found, const Identifier &name ) -> GroupItem *;
58
59 auto SubordinateSingleItem( Item &found, BaseItem *pItem ) -> void;
60
61 auto SubordinateMultipleItems( Item &found, GroupItem *pItems ) -> void;
62
63 auto MergeWithExistingItem(
64 Visitor &visitor, ItemOrdering &itemOrdering, BaseItem *pItem ) -> bool;
65
66 using NewItem = std::pair< BaseItem*, OrderingHint >;
67 using NewItems = std::vector< NewItem >;
68
69 auto MergeLikeNamedItems(
70 Visitor &visitor, ItemOrdering &itemOrdering,
71 NewItems::const_iterator left, NewItems::const_iterator right,
72 int iPass, size_t endItemsCount, bool force )
73 -> bool;
74
75 auto MergeItemsAscendingNamesPass(
76 Visitor &visitor, ItemOrdering &itemOrdering,
77 NewItems &newItems, int iPass, size_t endItemsCount, bool force )
78 -> void;
79
80 auto MergeItemsDescendingNamesPass(
81 Visitor &visitor, ItemOrdering &itemOrdering,
82 NewItems &newItems, int iPass, size_t endItemsCount, bool force )
83 -> void;
84
85 auto MergeItems(
86 Visitor &visitor, ItemOrdering &itemOrdering,
87 const BaseItemPtrs &toMerge, const OrderingHint &hint ) -> void;
88};
89
90// When a computed or shared item, or nameless grouping, specifies a hint and
91// the subordinate does not, propagate the hint.
92const OrderingHint &ChooseHint(BaseItem *delegate, const OrderingHint &hint)
93{
94 return !delegate || delegate->orderingHint.type == OrderingHint::Unspecified
95 ? hint
96 : delegate->orderingHint;
97}
98
99// "Collection" of items is the first pass of visitation, and resolves
100// delegation and delayed computation and splices transparent group nodes.
101// This first pass is done at each group, starting with a top-level group.
102// This pass does not descend to the leaves. Rather, the visitation passes
103// alternate as the entire tree is recursively visited.
104
105// forward declaration for mutually recursive functions
106void CollectItem( Registry::Visitor &visitor,
107 CollectedItems &collection, BaseItem *Item, const OrderingHint &hint );
109 CollectedItems &collection, const BaseItemPtrs &items,
110 const OrderingHint &hint )
111{
112 for ( auto &item : items )
113 CollectItem( visitor, collection, item.get(),
114 ChooseHint( item.get(), hint ) );
115}
117 CollectedItems &collection, BaseItem *pItem, const OrderingHint &hint )
118{
119 if (!pItem)
120 return;
121
122 using namespace Registry;
123 if (const auto pShared =
124 dynamic_cast<SharedItem*>( pItem )) {
125 auto delegate = pShared->ptr.get();
126 if ( delegate )
127 // recursion
128 CollectItem( visitor, collection, delegate,
129 ChooseHint( delegate, pShared->orderingHint ) );
130 }
131 else
132 if (const auto pComputed =
133 dynamic_cast<ComputedItem*>( pItem )) {
134 auto result = pComputed->factory( visitor );
135 if (result) {
136 // Guarantee long enough lifetime of the result
137 collection.computedItems.push_back( result );
138 // recursion
139 CollectItem( visitor, collection, result.get(),
140 ChooseHint( result.get(), pComputed->orderingHint ) );
141 }
142 }
143 else
144 if (auto pGroup = dynamic_cast<GroupItem*>(pItem)) {
145 if (pGroup->Transparent() && pItem->name.empty())
146 // nameless grouping item is transparent to path calculations
147 // collect group members now
148 // recursion
150 visitor, collection, pGroup->items, ChooseHint( pGroup, hint ) );
151 else
152 // all other group items
153 // defer collection of members until collecting at next lower level
154 collection.items.push_back( {pItem, nullptr, hint} );
155 }
156 else {
157 wxASSERT( dynamic_cast<SingleItem*>(pItem) );
158 // common to all single items
159 collection.items.push_back( {pItem, nullptr, hint} );
160 }
161}
162
163using Path = std::vector< Identifier >;
164
165 std::unordered_set< wxString > sBadPaths;
167 const TranslatableString &format, const wxString &key, const Identifier &name )
168 {
169 // Warn, but not more than once in a session for each bad path
170 auto badPath = key + '/' + name.GET();
171 if ( sBadPaths.insert( badPath ).second ) {
172 auto msg = TranslatableString{ format }.Format( badPath );
173 // debug message
174 wxLogDebug( msg.Translation() );
175#ifdef IS_ALPHA
176 // user-visible message
178#endif
179 }
180 }
181
182 void ReportGroupGroupCollision( const wxString &key, const Identifier &name )
183 {
184 BadPath(
185XO("Plug-in group at %s was merged with a previously defined group"),
186 key, name);
187 }
188
189 void ReportItemItemCollision( const wxString &key, const Identifier &name )
190 {
191 BadPath(
192XO("Plug-in item at %s conflicts with a previously defined item and was discarded"),
193 key, name);
194 }
195
196 void ReportConflictingPlacements( const wxString &key, const Identifier &name )
197 {
198 BadPath(
199XO("Plug-in items at %s specify conflicting placements"),
200 key, name);
201 }
202
204 wxString key;
205
206 ItemOrdering( const Path &path )
207 {
208 // The set of path names determines only an unordered tree.
209 // We want an ordering of the tree that is stable across runs.
210 // The last used ordering for this node can be found in preferences at this
211 // key:
212 wxArrayString strings;
213 for (const auto &id : path)
214 strings.push_back( id.GET() );
215 key = '/' + ::wxJoin( strings, '/', '\0' );
216 }
217
218 // Retrieve the old ordering on demand, if needed to merge something.
219 bool gotOrdering = false;
220 wxString strValue;
221 wxArrayString ordering;
222
223 auto Get() -> wxArrayString & {
224 if ( !gotOrdering ) {
225 gPrefs->Read(key, &strValue);
226 ordering = ::wxSplit( strValue, ',' );
227 gotOrdering = true;
228 }
229 return ordering;
230 };
231};
232
233// For each group node, this is called only in the first pass of merging of
234// items. It might fail to place an item in the first visitation of a
235// registry, but then succeed in later visitations in the same or later
236// runs of the program, because of persistent side-effects on the
237// preferences done at the very end of the visitation.
238auto CollectedItems::InsertNewItemUsingPreferences(
239 ItemOrdering &itemOrdering, BaseItem *pItem )
240 -> bool
241{
242 // Note that if more than one plug-in registers items under the same
243 // node, then it is not specified which plug-in is handled first,
244 // the first time registration happens. It might happen that you
245 // add a plug-in, run the program, then add another, then run again;
246 // registration order determined by those actions might not
247 // correspond to the order of re-loading of modules in later
248 // sessions. But whatever ordering is chosen the first time some
249 // plug-in is seen -- that ordering gets remembered in preferences.
250
251 if ( !pItem->name.empty() ) {
252 // Check saved ordering first, and rebuild that as well as is possible
253 auto &ordering = itemOrdering.Get();
254 auto begin2 = ordering.begin(), end2 = ordering.end(),
255 found2 = std::find( begin2, end2, pItem->name );
256 if ( found2 != end2 ) {
257 auto insertPoint = items.end();
258 // Find the next name in the saved ordering that is known already
259 // in the collection.
260 while ( ++found2 != end2 ) {
261 auto known = Find( *found2 );
262 if ( known != insertPoint ) {
263 insertPoint = known;
264 break;
265 }
266 }
267 items.insert( insertPoint, {pItem, nullptr,
268 // Hints no longer matter:
269 {}} );
270 return true;
271 }
272 }
273
274 return false;
275}
276
277// For each group node, this may be called in the second and later passes
278// of merging of items
279auto CollectedItems::InsertNewItemUsingHint(
280 BaseItem *pItem, const OrderingHint &hint, size_t endItemsCount,
281 bool force ) -> bool
282{
283 auto begin = items.begin(), end = items.end(),
284 insertPoint = end - endItemsCount;
285
286 // pItem should have a name; if not, ignore the hint, and put it at the
287 // default place, but only if in the final pass.
288 if ( pItem->name.empty() ) {
289 if ( !force )
290 return false;
291 }
292 else {
293 switch ( hint.type ) {
294 case OrderingHint::Before:
295 case OrderingHint::After: {
296 // Default to the end if the name is not found.
297 auto found = Find( hint.name );
298 if ( found == end ) {
299 if ( !force )
300 return false;
301 else
302 insertPoint = found;
303 }
304 else {
305 insertPoint = found;
306 if ( hint.type == OrderingHint::After )
307 ++insertPoint;
308 }
309 break;
310 }
312 insertPoint = begin;
313 break;
314 case OrderingHint::End:
315 insertPoint = end;
316 break;
317 case OrderingHint::Unspecified:
318 default:
319 if ( !force )
320 return false;
321 break;
322 }
323 }
324
325 // Insert the item; the hint has been used and no longer matters
326 items.insert( insertPoint, {pItem, nullptr,
327 // Hints no longer matter:
328 {}} );
329 return true;
330}
331
332auto CollectedItems::MergeLater( Item &found, const Identifier &name )
333 -> GroupItem *
334{
335 auto subGroup = found.mergeLater;
336 if ( !subGroup ) {
337 auto newGroup = std::make_shared<TransparentGroupItem<>>( name );
338 computedItems.push_back( newGroup );
339 subGroup = found.mergeLater = newGroup.get();
340 }
341 return subGroup;
342}
343
344auto CollectedItems::SubordinateSingleItem( Item &found, BaseItem *pItem )
345 -> void
346{
347 MergeLater( found, pItem->name )->items.push_back(
348 std::make_unique<SharedItem>(
349 // shared pointer with vacuous deleter
350 std::shared_ptr<BaseItem>( pItem, [](void*){} ) ) );
351}
352
353auto CollectedItems::SubordinateMultipleItems( Item &found, GroupItem *pItems )
354 -> void
355{
356 auto subGroup = MergeLater( found, pItems->name );
357 for ( const auto &pItem : pItems->items )
358 subGroup->items.push_back( std::make_unique<SharedItem>(
359 // shared pointer with vacuous deleter
360 std::shared_ptr<BaseItem>( pItem.get(), [](void*){} ) ) );
361}
362
363auto CollectedItems::MergeWithExistingItem(
364 Visitor &visitor, ItemOrdering &itemOrdering, BaseItem *pItem ) -> bool
365{
366 // Assume no null pointers remain after CollectItems:
367 const auto &name = pItem->name;
368 const auto found = Find( name );
369 if (found != items.end()) {
370 // Collision of names between collection and registry!
371 // There are 2 * 2 = 4 cases, as each of the two are group items or
372 // not.
373 auto pCollectionGroup = dynamic_cast< GroupItem * >( found->visitNow );
374 auto pRegistryGroup = dynamic_cast< GroupItem * >( pItem );
375 if (pCollectionGroup) {
376 if (pRegistryGroup) {
377 // This is the expected case of collision.
378 // Subordinate items from one of the groups will be merged in
379 // another call to MergeItems at a lower level of path.
380 // Note, however, that at most one of the two should be other
381 // than a plain grouping item; if not, we must lose the extra
382 // information carried by one of them.
383 bool pCollectionGrouping = pCollectionGroup->Transparent();
384 auto pRegistryGrouping = pRegistryGroup->Transparent();
385 if ( !(pCollectionGrouping || pRegistryGrouping) )
386 ReportGroupGroupCollision( itemOrdering.key, name );
387
388 if ( pCollectionGrouping && !pRegistryGrouping ) {
389 // Swap their roles
390 found->visitNow = pRegistryGroup;
391 SubordinateMultipleItems( *found, pCollectionGroup );
392 }
393 else
394 SubordinateMultipleItems( *found, pRegistryGroup );
395 }
396 else {
397 // Registered non-group item collides with a previously defined
398 // group.
399 // Resolve this by subordinating the non-group item below
400 // that group.
401 SubordinateSingleItem( *found, pItem );
402 }
403 }
404 else {
405 if (pRegistryGroup) {
406 // Subordinate the previously merged single item below the
407 // newly merged group.
408 // In case the name occurred in two different static registries,
409 // the final merge is the same, no matter which is treated first.
410 auto demoted = found->visitNow;
411 found->visitNow = pRegistryGroup;
412 SubordinateSingleItem( *found, demoted );
413 }
414 else
415 // Collision of non-group items is the worst case!
416 // The later-registered item is lost.
417 // Which one you lose might be unpredictable when both originate
418 // from static registries.
419 ReportItemItemCollision( itemOrdering.key, name );
420 }
421 return true;
422 }
423 else
424 // A name is registered that is not known in the collection.
425 return false;
426}
427
428auto CollectedItems::MergeLikeNamedItems(
429 Visitor &visitor, ItemOrdering &itemOrdering,
430 NewItems::const_iterator left, NewItems::const_iterator right,
431 const int iPass, size_t endItemsCount, bool force )
432 -> bool
433{
434 // Try to place the first item of the range.
435 // If such an item is a group, then we always retain the kind of
436 // grouping that was registered. (Which doesn't always happen when
437 // there is name collision in MergeWithExistingItem.)
438 auto iter = left;
439 auto &item = *iter;
440 auto pItem = item.first;
441 const auto &hint = item.second;
442 bool success = false;
443 if ( iPass == -1 )
444 // A first pass consults preferences.
445 success = InsertNewItemUsingPreferences( itemOrdering, pItem );
446 else if ( iPass == hint.type ) {
447 // Later passes for choosing placements.
448 // Maybe it fails in this pass, because a placement refers to some
449 // other name that has not yet been placed.
450 success =
451 InsertNewItemUsingHint( pItem, hint, endItemsCount, force );
452 wxASSERT( !force || success );
453 }
454
455 if ( success ) {
456 // Resolve collisions among remaining like-named items.
457 ++iter;
458 if ( iter != right && iPass != 0 &&
459 iter->second.type != OrderingHint::Unspecified &&
460 !( iter->second == hint ) ) {
461 // A diagnostic message sometimes
462 ReportConflictingPlacements( itemOrdering.key, pItem->name );
463 }
464 while ( iter != right )
465 // Re-invoke MergeWithExistingItem for this item, which is known
466 // to have a name collision, so ignore the return value.
467 MergeWithExistingItem( visitor, itemOrdering, iter++ -> first );
468 }
469
470 return success;
471}
472
473inline bool MajorComp(
475 // Descending sort!
476 return a.first->name > b.first->name;
477};
478inline bool MinorComp(
480 // Sort by hint type.
481 // This sorts items with unspecified hints last.
482 return a.second < b.second;
483};
484inline bool Comp(
486 if ( MajorComp( a, b ) )
487 return true;
488 if ( MajorComp( b, a ) )
489 return false;
490 return MinorComp( a, b );
491};
492
493auto CollectedItems::MergeItemsAscendingNamesPass(
494 Visitor &visitor, ItemOrdering &itemOrdering, NewItems &newItems,
495 const int iPass, size_t endItemsCount, bool force ) -> void
496{
497 // Inner loop over ranges of like-named items.
498 auto rright = newItems.rbegin();
499 auto rend = newItems.rend();
500 while ( rright != rend ) {
501 // Find the range
502 using namespace std::placeholders;
503 auto rleft = std::find_if(
504 rright + 1, rend, std::bind( MajorComp, _1, *rright ) );
505
506 bool success = MergeLikeNamedItems(
507 visitor, itemOrdering, rleft.base(), rright.base(), iPass,
508 endItemsCount, force );
509
510 if ( success ) {
511 auto diff = rend - rleft;
512 newItems.erase( rleft.base(), rright.base() );
513 rend = newItems.rend();
514 rleft = rend - diff;
515 }
516 rright = rleft;
517 }
518}
519
520auto CollectedItems::MergeItemsDescendingNamesPass(
521 Visitor &visitor, ItemOrdering &itemOrdering, NewItems &newItems,
522 const int iPass, size_t endItemsCount, bool force ) -> void
523{
524 // Inner loop over ranges of like-named items.
525 auto left = newItems.begin();
526 while ( left != newItems.end() ) {
527 // Find the range
528 using namespace std::placeholders;
529 auto right = std::find_if(
530 left + 1, newItems.end(), std::bind( MajorComp, *left, _1 ) );
531
532 bool success = MergeLikeNamedItems(
533 visitor, itemOrdering, left, right, iPass,
534 endItemsCount, force );
535
536 if ( success )
537 left = newItems.erase( left, right );
538 else
539 left = right;
540 }
541};
542
543auto CollectedItems::MergeItems(
544 Visitor &visitor, ItemOrdering &itemOrdering,
545 const BaseItemPtrs &toMerge, const OrderingHint &hint ) -> void
546{
547 // First do expansion of nameless groupings, and caching of computed
548 // items, just as for the previously collected items.
549 CollectedItems newCollection{ {}, computedItems };
550 CollectItems( visitor, newCollection, toMerge, hint );
551
552 // Try to merge each, resolving name collisions with items already in the
553 // tree, and collecting those with names that don't collide.
554 NewItems newItems;
555 for ( const auto &item : newCollection.items )
556 if ( !MergeWithExistingItem( visitor, itemOrdering, item.visitNow ) )
557 newItems.push_back( { item.visitNow, item.hint } );
558
559 // Choose placements for items with NEW names.
560
561 // First sort so that like named items are together, and for the same name,
562 // items with more specific ordering hints come earlier.
563 std::sort( newItems.begin(), newItems.end(), Comp );
564
565 // Outer loop over trial passes.
566 int iPass = -1;
567 bool force = false;
568 size_t oldSize = 0;
569 size_t endItemsCount = 0;
570 auto prevSize = newItems.size();
571 while( !newItems.empty() )
572 {
573 // If several items have the same hint, we try to preserve the sort by
574 // name (an internal identifier, not necessarily user visible), just to
575 // have some determinacy. That requires passing one or the other way
576 // over newItems.
577 bool descending =
578 ( iPass == OrderingHint::After || iPass == OrderingHint::Begin );
579
580 if ( descending )
581 MergeItemsDescendingNamesPass(
582 visitor, itemOrdering, newItems, iPass, endItemsCount, force );
583 else
584 MergeItemsAscendingNamesPass(
585 visitor, itemOrdering, newItems, iPass, endItemsCount, force );
586
587 auto newSize = newItems.size();
588 ++iPass;
589
590 if ( iPass == 0 )
591 // Just tried insertion by preferences. Don't try it again.
592 oldSize = newSize;
593 else if ( iPass == OrderingHint::Unspecified ) {
594 if ( !force ) {
595 iPass = 0, oldSize = newSize;
596 // Are we really ready for the final pass?
597 bool progress = ( oldSize > newSize );
598 if ( progress )
599 // No. While some progress is made, don't force final placements.
600 // Retry Before and After hints.
601 ;
602 else
603 force = true;
604 }
605 }
606 else if ( iPass == OrderingHint::End && endItemsCount == 0 )
607 // Remember the size before we put the ending items in place
608 endItemsCount = newSize - prevSize;
609
610 prevSize = newSize;
611 }
612}
613
614// forward declaration for mutually recursive functions
615void VisitItem(
616 Registry::Visitor &visitor, CollectedItems &collection,
617 Path &path, BaseItem *pItem,
618 const GroupItem *pToMerge, const OrderingHint &hint,
619 bool &doFlush );
621 Registry::Visitor &visitor, CollectedItems &collection,
622 Path &path, GroupItem *pGroup,
623 const GroupItem *pToMerge, const OrderingHint &hint,
624 bool &doFlush )
625{
626 // Make a NEW collection for this subtree, sharing the memo cache
627 CollectedItems newCollection{ {}, collection.computedItems };
628
629 // Gather items at this level
630 // (The ordering hint is irrelevant when not merging items in)
631 CollectItems( visitor, newCollection, pGroup->items, {} );
632
633 path.push_back( pGroup->name.GET() );
634
635 // Merge with the registry
636 if ( pToMerge )
637 {
638 ItemOrdering itemOrdering{ path };
639 newCollection.MergeItems( visitor, itemOrdering, pToMerge->items, hint );
640
641 // Remember the NEW ordering, if there was any need to use the old.
642 // This makes a side effect in preferences.
643 if ( itemOrdering.gotOrdering ) {
644 wxString newValue;
645 for ( const auto &item : newCollection.items ) {
646 const auto &name = item.visitNow->name;
647 if ( !name.empty() )
648 newValue += newValue.empty()
649 ? name.GET()
650 : ',' + name.GET();
651 }
652 if (newValue != itemOrdering.strValue) {
653 gPrefs->Write( itemOrdering.key, newValue );
654 doFlush = true;
655 }
656 }
657 }
658
659 // Now visit them
660 for ( const auto &item : newCollection.items )
661 VisitItem( visitor, collection, path,
662 item.visitNow, item.mergeLater, item.hint,
663 doFlush );
664
665 path.pop_back();
666}
668 Registry::Visitor &visitor, CollectedItems &collection,
669 Path &path, BaseItem *pItem,
670 const GroupItem *pToMerge, const OrderingHint &hint,
671 bool &doFlush )
672{
673 if (!pItem)
674 return;
675
676 if (const auto pSingle =
677 dynamic_cast<SingleItem*>( pItem )) {
678 wxASSERT( !pToMerge );
679 visitor.Visit( *pSingle, path );
680 }
681 else
682 if (const auto pGroup =
683 dynamic_cast<GroupItem*>( pItem )) {
684 visitor.BeginGroup( *pGroup, path );
685 // recursion
687 visitor, collection, path, pGroup, pToMerge, hint, doFlush );
688 visitor.EndGroup( *pGroup, path );
689 }
690 else
691 wxASSERT( false );
692}
693
694}
695
696namespace Registry {
697
699
701
703
705
707
711void Visitor::Visit(SingleItem &, const Path &) {}
712
713void Visit( Visitor &visitor, BaseItem *pTopItem, const GroupItem *pRegistry )
714{
715 std::vector< BaseItemSharedPtr > computedItems;
716 bool doFlush = false;
717 CollectedItems collection{ {}, computedItems };
718 Path emptyPath;
719 VisitItem(
720 visitor, collection, emptyPath, pTopItem,
721 pRegistry, pRegistry->orderingHint, doFlush );
722 // Flush any writes done by MergeItems()
723 if (doFlush)
724 gPrefs->Flush();
725}
726
728 Literal root, Pairs pairs )
729 : mPairs{ std::move( pairs ) }
730 , mRoot{ root }
731{
732 (*this)();
733}
734
736{
737 bool doFlush = false;
738 for (const auto &pair : mPairs) {
739 const auto key = wxString{'/'} + mRoot + pair.first;
740 if ( gPrefs->Read(key).empty() ) {
741 gPrefs->Write( key, pair.second );
742 doFlush = true;
743 }
744 }
745
746 if (doFlush)
747 gPrefs->Flush();
748}
749
750void RegisterItem( GroupItem &registry, const Placement &placement,
751 BaseItemPtr pItem )
752{
753 // Since registration determines only an unordered tree of menu items,
754 // we can sort children of each node lexicographically for our convenience.
755 BaseItemPtrs *pItems;
756 struct Comparator {
757 bool operator()
758 ( const Identifier &component, const BaseItemPtr& pItem ) const {
759 return component < pItem->name; }
760 bool operator()
761 ( const BaseItemPtr& pItem, const Identifier &component ) const {
762 return pItem->name < component; }
763 };
764 auto find = [&pItems]( const Identifier &component ){ return std::equal_range(
765 pItems->begin(), pItems->end(), component, Comparator() ); };
766
767 auto pNode = &registry;
768 pItems = &pNode->items;
769
770 const auto pathComponents = ::wxSplit( placement.path, '/' );
771 auto pComponent = pathComponents.begin(), end = pathComponents.end();
772
773 // Descend the registry hierarchy, while groups matching the path components
774 // can be found
775 auto debugPath = wxString{'/'} + registry.name.GET();
776 while ( pComponent != end ) {
777 const auto &pathComponent = *pComponent;
778
779 // Try to find an item already present that is a group item with the
780 // same name; we don't care which if there is more than one.
781 const auto range = find( pathComponent );
782 const auto iter2 = std::find_if( range.first, range.second,
783 [](const BaseItemPtr &pItem){
784 return dynamic_cast< GroupItem* >( pItem.get() ); } );
785
786 if ( iter2 != range.second ) {
787 // A matching group in the registry, so descend
788 pNode = static_cast< GroupItem* >( iter2->get() );
789 pItems = &pNode->items;
790 debugPath += '/' + pathComponent;
791 ++pComponent;
792 }
793 else
794 // Insert at this level;
795 // If there are no more path components, and a name collision of
796 // the added item with something already in the registry, don't resolve
797 // it yet in this function, but see MergeItems().
798 break;
799 }
800
801 // Create path group items for remaining components
802 while ( pComponent != end ) {
803 auto newNode = std::make_unique<TransparentGroupItem<>>( *pComponent );
804 pNode = newNode.get();
805 pItems->insert( find( pNode->name ).second, std::move( newNode ) );
806 pItems = &pNode->items;
807 ++pComponent;
808 }
809
810 // Remember the hint, to be used later in merging.
811 pItem->orderingHint = placement.hint;
812
813 // Now insert the item.
814 pItems->insert( find( pItem->name ).second, std::move( pItem ) );
815}
816
817}
Toolkit-neutral facade for basic user interface services.
static const AudacityProject::AttachedObjects::RegisteredFactory key
const TranslatableString name
Definition: Distortion.cpp:82
int format
Definition: ExportPCM.cpp:56
#define XO(s)
Definition: Internat.h:31
FileConfig * gPrefs
Definition: Prefs.cpp:71
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
An explicitly nonlocalized string, not meant for the user to see.
Definition: Identifier.h:22
bool empty() const
Definition: Identifier.h:61
const wxString & GET() const
Explicit conversion to wxString, meant to be ugly-looking and demanding of a comment why it's correct...
Definition: Identifier.h:66
virtual void BeginGroup(GroupItem &item, const Path &path)
Definition: Registry.cpp:709
std::vector< Identifier > Path
Definition: Registry.h:245
virtual ~Visitor()
Definition: Registry.cpp:708
virtual void EndGroup(GroupItem &item, const Path &path)
Definition: Registry.cpp:710
virtual void Visit(SingleItem &item, const Path &path)
Definition: Registry.cpp:711
Holds a msgid for the translation catalog; may also bind format arguments.
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.
Definition: BasicUI.h:269
bool Begin(const FilePath &dataDir)
Definition: Journal.cpp:226
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
Definition: Menus.h:36
void Visit(Visitor &visitor, BaseItem *pTopItem, const GroupItem *pRegistry)
Definition: Registry.cpp:713
std::unique_ptr< BaseItem > BaseItemPtr
Definition: Registry.h:71
std::vector< BaseItemPtr > BaseItemPtrs
Definition: Registry.h:73
void RegisterItem(GroupItem &registry, const Placement &placement, BaseItemPtr pItem)
Definition: Registry.cpp:750
void CollectItem(Registry::Visitor &visitor, CollectedItems &collection, BaseItem *Item, const OrderingHint &hint)
Definition: Registry.cpp:116
bool MinorComp(const CollectedItems::NewItem &a, const CollectedItems::NewItem &b)
Definition: Registry.cpp:478
void ReportConflictingPlacements(const wxString &key, const Identifier &name)
Definition: Registry.cpp:196
std::vector< Identifier > Path
Definition: Registry.cpp:163
void VisitItem(Registry::Visitor &visitor, CollectedItems &collection, Path &path, BaseItem *pItem, const GroupItem *pToMerge, const OrderingHint &hint, bool &doFlush)
Definition: Registry.cpp:667
void ReportItemItemCollision(const wxString &key, const Identifier &name)
Definition: Registry.cpp:189
std::unordered_set< wxString > sBadPaths
Definition: Registry.cpp:165
bool Comp(const CollectedItems::NewItem &a, const CollectedItems::NewItem &b)
Definition: Registry.cpp:484
void CollectItems(Registry::Visitor &visitor, CollectedItems &collection, const BaseItemPtrs &items, const OrderingHint &hint)
Definition: Registry.cpp:108
const OrderingHint & ChooseHint(BaseItem *delegate, const OrderingHint &hint)
Definition: Registry.cpp:92
void VisitItems(Registry::Visitor &visitor, CollectedItems &collection, Path &path, GroupItem *pGroup, const GroupItem *pToMerge, const OrderingHint &hint, bool &doFlush)
Definition: Registry.cpp:620
void BadPath(const TranslatableString &format, const wxString &key, const Identifier &name)
Definition: Registry.cpp:166
bool MajorComp(const CollectedItems::NewItem &a, const CollectedItems::NewItem &b)
Definition: Registry.cpp:473
void ReportGroupGroupCollision(const wxString &key, const Identifier &name)
Definition: Registry.cpp:182
STL namespace.
virtual ~BaseItem()
Definition: Registry.cpp:698
const Identifier name
Definition: Registry.h:67
OrderingHint orderingHint
Definition: Registry.h:69
~ComputedItem() override
Definition: Registry.cpp:702
virtual bool Transparent() const =0
~GroupItem() override=0
Definition: Registry.cpp:706
BaseItemPtrs items
Definition: Registry.h:141
OrderingPreferenceInitializer(Literal root, Pairs pairs)
Definition: Registry.cpp:727
OrderingHint hint
Definition: Registry.h:221
~SharedItem() override
Definition: Registry.cpp:700
~SingleItem() override=0
Definition: Registry.cpp:704
std::vector< BaseItemSharedPtr > & computedItems
Definition: Registry.cpp:35
auto Find(const Identifier &name) -> Iterator
Definition: Registry.cpp:39
std::pair< BaseItem *, OrderingHint > NewItem
Definition: Registry.cpp:66