11namespace tracktion {
inline namespace engine
18 : onDelete (onDelete_) {}
34 selectables.removeValue (s);
40 return selectables.contains (
const_cast<Selectable*
> (s));
43 void handleAsyncUpdate()
override
51 for (
auto s : selectables)
53 needingUpdate.push_back (s);
56 for (
auto s : needingUpdate)
58 s->sendChangeCallbackToListenersIfNeeded();
73void Selectable::initialise()
75 if (updateTimerInstance ==
nullptr)
80Selectable::Selectable()
82 if (updateTimerInstance)
83 updateTimerInstance->add (
this);
86Selectable::~Selectable()
88 masterReference.clear();
90 if (! hasNotifiedListenersOfDeletion)
95 notifyListenersOfDeletion();
105 if (selectableListeners.size() > 0)
107 needsAnUpdate =
true;
114 needsAnUpdate =
false;
119 return s !=
nullptr && updateTimerInstance->isValid (s);
124 addSelectableListener (l);
129 removeSelectableListener (l);
132void Selectable::addSelectableListener (SelectableListener* l)
134 TRACKTION_ASSERT_MESSAGE_THREAD
136 jassert (! isCallingListeners);
137 jassert (! hasNotifiedListenersOfDeletion);
138 selectableListeners.add (l);
141void Selectable::removeSelectableListener (SelectableListener* l)
143 TRACKTION_ASSERT_MESSAGE_THREAD
144 jassert (! isCallingListeners);
145 selectableListeners.remove (l);
148void Selectable::sendChangeCallbackToListenersIfNeeded()
150 TRACKTION_ASSERT_MESSAGE_THREAD
151 needsAnUpdate =
false;
155 selectableListeners.call ([self =
makeSafeRef (*
this)] (SelectableListener& l)
157 if (
auto s = self.get())
158 l.selectableObjectChanged (s);
164void Selectable::propertiesChanged()
166 for (SelectionManager::Iterator sm; sm.next();)
168 if (sm->isSelected (
this))
170 auto list = sm->getSelectedObjects();
179void Selectable::notifyListenersOfDeletion()
181 if (! hasNotifiedListenersOfDeletion)
183 hasNotifiedListenersOfDeletion =
true;
185 updateTimerInstance->remove (
this);
187 if (! selectableListeners.isEmpty())
194 for (
auto l : selectableListeners.getListeners())
199 if (
auto s = self.get())
201 if (! s->selectableListeners.contains (&l))
204 l.selectableObjectAboutToBeDeleted (s);
218void Selectable::deselect()
220 for (SelectionManager::Iterator sm; sm.next();)
225Selectable::Listener::Listener (Selectable& s)
229 selectable->addSelectableListener (
this);
232Selectable::Listener::~Listener()
235 selectable->removeSelectableListener (
this);
239SelectableClass::SelectableClass() {}
240SelectableClass::~SelectableClass() {}
254SelectableClass::ClassInstanceBase::ClassInstanceBase() { getAllSelectableClasses().add (
this); }
255SelectableClass::ClassInstanceBase::~ClassInstanceBase() { getAllSelectableClasses().removeAllInstancesOf (
this); }
257SelectableClass* SelectableClass::findClassFor (
const Selectable& s)
260 auto& cache = getSelectableClassCache();
263 if (
auto found = cache.find (typeIndex); found != cache.end())
264 return found->second;
266 for (
auto cls : getAllSelectableClasses())
268 if (
auto c = cls->getClassForObject (&s))
270 cache[typeIndex] = c;
275 SelectableClass* result =
nullptr;
277 for (
auto cls : getAllSelectableClasses())
279 if (
auto c = cls->getClassForObject (&s))
281 if (result ==
nullptr)
288 if (result !=
nullptr)
295SelectableClass* SelectableClass::findClassFor (
const Selectable* s)
297 return s !=
nullptr ? findClassFor (*s) : nullptr;
303 if (selectedObjects.size() == 1)
304 if (
auto s = selectedObjects.getFirst())
305 return s->getSelectableDescription();
309 for (
auto o : selectedObjects)
313 if (names.
size() == 1)
316 .replace (
"XYYZ", names[0]);
333 return otherClass ==
this;
354bool SelectableClass::areAllObjectsOfUniformType (
const SelectableList& list)
356 if (list.size() <= 1)
359 auto s1 = list.getUnchecked (0);
361 for (
int i = list.size(); --i > 0;)
363 auto s2 = list.getUnchecked (i);
365 if (
typeid (*s1) !=
typeid (*s2))
375SelectionManager::SelectionManager (
Engine& e) : ChangeBroadcaster(), engine (e)
377 allManagers.add (
this);
380SelectionManager::~SelectionManager()
383 allManagers.removeAllInstancesOf (
this);
386void SelectionManager::selectionChanged()
388 ++selectionChangeCount;
392SelectableClass* SelectionManager::getFirstSelectableClass()
const
394 return SelectableClass::findClassFor (selected.getFirst());
397void SelectionManager::clearList()
401 s->removeSelectableListener (this);
406SelectionManager::Iterator::Iterator() {}
408bool SelectionManager::Iterator::next()
413SelectionManager* SelectionManager::Iterator::get()
const
415 return allManagers[index];
418int SelectionManager::getNumObjectsSelected()
const
420 return selected.size();
423Selectable* SelectionManager::getSelectedObject (
int index)
const
426 return selected[index];
429const SelectableList& SelectionManager::getSelectedObjects()
const
434bool SelectionManager::isSelected (
const Selectable*
object)
const
436 return object !=
nullptr && isSelected (*
object);
439bool SelectionManager::isSelected (
const Selectable&
object)
const
441 return selected.contains (
const_cast<Selectable*
> (&
object));
444void SelectionManager::deselectAll()
448 if (selected.size() > 0)
452 s->selectionStatusChanged (false);
459static bool canBeSelected (Selectable& newItem)
461 if (
auto newItemClass = SelectableClass::findClassFor (newItem))
462 return newItemClass->canBeSelected (newItem);
466static bool canSelectAtTheSameTime (
const SelectableList& selected, Selectable& newItem)
468 auto newItemClass = SelectableClass::findClassFor (newItem);
470 for (
int i = 0; i < selected.size(); ++i)
472 auto s = selected.getUnchecked (i);
474 if (
auto currentClass = selected.getSelectableClass (i))
475 if (! (currentClass->canClassesBeSelectedAtTheSameTime (newItemClass)
476 && currentClass->canObjectsBeSelectedAtTheSameTime (*s, newItem)))
483void SelectionManager::selectOnly (Selectable* s) { select (s,
false); }
484void SelectionManager::selectOnly (Selectable& s) { select (s,
false); }
485void SelectionManager::addToSelection (Selectable* s) { select (s,
true); }
486void SelectionManager::addToSelection (Selectable& s) { select (s,
true); }
488void SelectionManager::select (Selectable* s,
bool addToCurrentSelection)
491 select (*s, addToCurrentSelection);
494void SelectionManager::select (Selectable& s,
bool addToCurrentSelection)
500 selected.removeAllInstancesOf (&s);
504 if (! canBeSelected (s))
507 if (! selected.contains (&s))
509 addToCurrentSelection = addToCurrentSelection
510 && canSelectAtTheSameTime (selected, s);
512 if (! addToCurrentSelection)
516 s.selectionStatusChanged (
true);
518 s.addSelectableListener (
this);
520 else if (! addToCurrentSelection)
522 for (
int i = selected.size(); --i >= 0;)
523 if (selected.getUnchecked (i) != &s)
524 deselect (selected.getUnchecked (i));
528void SelectionManager::select (
const SelectableList& listSrc)
531 for (
auto s : listSrc)
532 if (Selectable::isSelectableValid (s) && canBeSelected (*s))
535 if (list != selected)
544void SelectionManager::deselect (Selectable* s)
547 auto index = selected.indexOf (s);
551 s->removeSelectableListener (
this);
552 selected.remove (index);
553 s->selectionStatusChanged (
false);
558void SelectionManager::selectableObjectChanged (Selectable*)
562void SelectionManager::selectableObjectAboutToBeDeleted (Selectable* s)
565 auto index = selected.indexOf (s);
569 selected.remove (index);
574void SelectionManager::refreshPropertyPanel()
579void SelectionManager::refreshAllPropertyPanels()
581 for (SelectionManager::Iterator sm; sm.next();)
582 sm->refreshPropertyPanel();
585void SelectionManager::refreshAllPropertyPanelsShowing (Selectable& s)
587 for (SelectionManager::Iterator sm; sm.next();)
588 if (sm->isSelected (s))
589 sm->refreshPropertyPanel();
592void SelectionManager::deselectAllFromAllWindows()
594 for (SelectionManager::Iterator sm; sm.next();)
598SelectionManager* SelectionManager::findSelectionManagerContaining (
const Selectable* s)
600 for (SelectionManager::Iterator sm; sm.next();)
601 if (sm->isSelected (s))
607SelectionManager* SelectionManager::findSelectionManagerContaining (
const Selectable& s)
609 return findSelectionManagerContaining (&s);
613bool SelectionManager::copySelected()
618 if (
auto cls = getFirstSelectableClass())
620 auto& clipboard = *Clipboard::getInstance();
622 SelectableClass::AddClipboardEntryParams clipboardParams (clipboard);
623 clipboardParams.items = selected;
625 if (editViewID != -1)
627 clipboardParams.edit = getEdit();
628 clipboardParams.editViewID = editViewID;
632 cls->addClipboardEntriesFor (clipboardParams);
634 return ! clipboard.isEmpty();
640void SelectionManager::deleteSelected()
644 if (
auto cls = getFirstSelectableClass())
646 cls->deleteSelected ({
this, selected,
false});
649bool SelectionManager::cutSelected()
653 if (
auto cls = getFirstSelectableClass())
655 if (cls->canCutSelected (selected) && copySelected())
658 cls->deleteSelected ({
this, selected,
true});
670 if (
auto cls = getFirstSelectableClass())
672 return cls->pasteClipboard (
SelectableList (selected), editViewID);
681 if (
auto cls = getFirstSelectableClass())
688 keepOldItemsSelected,
692 cls->selectOtherObjects (params);
700 if (
auto cls = getFirstSelectableClass())
701 cls->keepSelectedObjectOnScreen (selected);
704Edit* SelectionManager::getEdit()
const
711 if (
auto smc = c->findParentComponentOfClass<ComponentWithSelectionManager>())
712 return smc->getSelectionManager();
717SelectionManager* SelectionManager::findSelectionManager (
const juce::Component& c)
719 return findSelectionManager (&c);
722SelectionManager::ScopedSelectionState::ScopedSelectionState (SelectionManager& m)
723 : manager (m), selected (manager.selected)
727SelectionManager::ScopedSelectionState::~ScopedSelectionState()
729 manager.select (selected);
733bool SelectionManager::ChangedSelectionDetector::isFirstChangeSinceSelection (SelectionManager* sm)
737 int newCount = sm->selectionChangeCount;
739 if (lastSelectionChangeCount != newCount)
741 lastSelectionChangeCount = newCount;
749void SelectionManager::ChangedSelectionDetector::reset()
751 lastSelectionChangeCount = 0;
757 if (index >= items.size())
760 if (index < classes.size())
761 if (
auto sc = classes.getUnchecked (index))
765 classes.resize (items.size());
766 auto selectableClass = SelectableClass::findClassFor (items[index]);
767 classes.setUnchecked (index, selectableClass);
769 return selectableClass;
void triggerAsyncUpdate()
int size() const noexcept
bool addIfNotAlreadyThere(const String &stringToAdd, bool ignoreCase=false)
String replace(StringRef stringToReplace, StringRef stringToInsertInstead, bool ignoreCase=false) const
The Tracktion Edit class!
Represents a type of object that can be selected.
virtual bool pasteClipboard(const SelectableList ¤tlySelectedItems, int editViewID)
This gives the selected items a first chance to paste the clipboard contents when the user presses ct...
virtual bool canObjectsBeSelectedAtTheSameTime(Selectable &object1, Selectable &object2)
This is only called if canClassesBeSelectedAtTheSameTime() has already returned true for the other ob...
virtual void addClipboardEntriesFor(AddClipboardEntryParams &)
A class should use this to create XML clipboard entries for the given set of items.
virtual void keepSelectedObjectOnScreen(const SelectableList ¤tlySelectedObjects)
if implemented, this should do whatever is appropriate to make these objects visible - e....
virtual void selectOtherObjects(const SelectOtherObjectsParams &)
Must try to find and select objects that are related to these ones in the specified way.
virtual bool canBeSelected(const Selectable &object)
If it's possible for an object to be selected.
virtual bool canClassesBeSelectedAtTheSameTime(SelectableClass *otherClass)
if it's possible for an object of this class to be selected at the same time as an object of the clas...
virtual juce::String getDescriptionOfSelectedGroup(const SelectableList &)
Must return a description of this particular group of objects.
virtual void deleteSelected(const DeleteSelectedParams ¶ms)
Deletes this set of objects.
Base class for things that can be selected, and whose properties can appear in the properties panel.
virtual void changed()
This should be called to send a change notification to any SelectableListeners that are registered wi...
virtual void selectionStatusChanged(bool isNowSelected)
Can be overridden to tell this object that it has just been selected or deselected.
static bool isSelectableValid(const Selectable *) noexcept
checks whether this object has been deleted.
virtual void selectableAboutToBeDeleted()
Called just before the selectable is about to be deleted so any subclasses should still be valid at t...
void cancelAnyPendingUpdates()
If changed() has been called, this will cancel any pending async change notificaions.
Manages a list of items that are currently selected.
SafeSelectable< Edit > edit
If this SelectionManager is being used to represent items inside a particular view of an edit,...
void selectOtherObjects(SelectableClass::Relationship, bool keepOldItemsSelected)
Selects related objects, e.g.
bool pasteSelected()
Offers the selected things the chance to paste the contents of the clipboard onto themselves,...
void keepSelectedObjectsOnScreen()
Scrolls whatever is necessary to keep the selected stuff visible.
#define TRANS(stringLiteral)
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
auto makeSafeVector(const Iterable &selectables) -> std::vector< SafeSelectable< typename std::remove_reference< decltype(*selectables[0])>::type > >
Creates a std::vector<SafeSelectable<Something>> for a given juce::Array of selectable objects.
SafeSelectable< SelectableType > makeSafeRef(SelectableType &selectable)
Creates a SafeSelectable for a given selectable object.
A list of Selectables, similar to a juce::Array but contains a cached list of the SelectableClasses f...
std::pair< Selectable *, SelectableClass * > getSelectableAndClass(int index) const
Returns the selectable and it's associated class.
SelectableClass * getSelectableClass(int index) const
Returns the selectable class for a given Selectable in the list.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.