#pragma once #include "V8Tree/Instance.h" #include "V8Tree/Service.h" #include namespace RBX { class Selection; class SelectionChanged { friend class Selection; public: shared_ptr const addedItem; shared_ptr const removedItem; private: SelectionChanged(shared_ptr added, shared_ptr removed) :addedItem(added),removedItem(removed) {} }; class RBXInterface ISelectionBase { friend class Selection; private: virtual void onSelectionChanged(const SelectionChanged& event) = 0; }; extern const char *const sSelection; class Selection : public DescribedNonCreatable , public Service { private: copy_on_write_ptr selection; std::vector filteredSelections; typedef std::map Connections; Connections connections; public: rbx::signal selectionChanged; rbx::signal luaSelectionChanged; Selection(); ~Selection(); inline size_t size() const { return selection->size(); } shared_ptr front() const { return selection->size()>0 ? selection->front() : shared_ptr(); } shared_ptr back() const { return selection->size()>0 ? selection->back() : shared_ptr(); } Instances::const_iterator begin() const { return selection->begin(); } Instances::const_iterator end() const { return selection->end(); } const copy_on_write_ptr& getSelection() const { return selection; } void setSelection(Instance* instance); template void setSelection(_InIt _First, _InIt _Last) { // This can cause more events to fire than needed. However, it may be more efficient // than an O:n^2 algorithm. clearSelection(); for (; _First != _Last; ++_First) addToSelection(*_First); } void clearSelection(); // Used for reflection. Note that it might return NULL (or an empty container) shared_ptr getSelection2() { return selection.read(); } void setSelection(shared_ptr selection); void addToSelection(Instance* instance); void addToSelection(const shared_ptr& instance) { addToSelection(instance.get()); } template void addToSelection(_InIt _First, _InIt _Last) { for (; _First != _Last; ++_First) addToSelection(*_First); } void toggleSelection(Instance* instance); void toggleSelection(shared_ptr instances); void toggleSelection(const shared_ptr& instance) { toggleSelection(instance.get()); } template void toggleSelection(_InIt _First, _InIt _Last) { for (; _First != _Last; ++_First) toggleSelection(*_First); } void removeFromSelection(Instance* source); void removeFromSelection(const shared_ptr& instance) { removeFromSelection(instance.get()); } template void removeFromSelection(_InIt _First, _InIt _Last) { for (; _First != _Last; ++_First) removeFromSelection(*_First); } inline bool isSelected(const Instance* instance) const { return std::find( selection->begin(), selection->end(), shared_from(instance) )!=selection->end(); } // Utility class for adding Instances to the selection as a result of std algorithms //class AddIterator : public std::_Outit class AddIterator : public std::iterator { Selection* selection; public: AddIterator(Selection* selection):selection(selection) {} AddIterator& operator=(Instance* _Val) { selection->addToSelection(_Val); return (*this); } AddIterator& operator=(shared_ptr _Val) { selection->addToSelection(_Val); return (*this); } AddIterator& operator*() { // pretend to return designated value return (*this); } AddIterator& operator++() { // pretend to preincrement return (*this); } AddIterator operator++(int) { // pretend to postincrement return (*this); } }; // Utility class for removing Instances from the selection as a result of std algorithms class RemoveIterator : public std::iterator { Selection* selection; public: RemoveIterator(Selection* selection):selection(selection) {} RemoveIterator& operator=(Instance* _Val) { selection->removeFromSelection(_Val); return (*this); } RemoveIterator& operator=(shared_ptr _Val) { selection->removeFromSelection(_Val); return (*this); } RemoveIterator& operator*() { // pretend to return designated value return (*this); } RemoveIterator& operator++() { // pretend to preincrement return (*this); } RemoveIterator operator++(int) { // pretend to postincrement return (*this); } }; // Utility class for toggling selection as a result of std algorithms class ToggleIterator : public std::iterator { Selection* selection; public: ToggleIterator(Selection* selection):selection(selection) {} ToggleIterator& operator=(Instance* _Val) { selection->toggleSelection(_Val); return (*this); } ToggleIterator& operator=(shared_ptr _Val) { selection->toggleSelection(_Val); return (*this); } ToggleIterator& operator*() { // pretend to return designated value return (*this); } ToggleIterator& operator++() { // pretend to preincrement return (*this); } ToggleIterator operator++(int) { // pretend to postincrement return (*this); } }; virtual void onAncestryChanged(Instance* source); void addFilteredSelection(ISelectionBase* fs); void removeFilteredSelection(ISelectionBase* fs); private: void raiseAdded(shared_ptr instance); void raiseRemoved(shared_ptr source); void connect(Instance* instance); void disconnect(Instance* instance); void propagateChangeSignalToLua(const RBX::SelectionChanged& event); bool instanceCanLiveInSelection(Instance* instance); rbx::signals::connection selectionChangedConnection; }; extern const char* const sFilteredSelection; template class FilteredSelection : public NonFactoryProduct // TODO: Add a notifier? , public Service , public ISelectionBase { private: typedef NonFactoryProduct Super; public: typedef std::vector CollectionType; private: shared_ptr rootSelection; CollectionType filteredSelection; public: FilteredSelection() { setName("FilteredSelection"); } ~FilteredSelection() { if (rootSelection!=NULL) rootSelection->removeFilteredSelection(this); } // Returns the generic Selection service Selection* getSelection() { RBXASSERT(rootSelection!=NULL); return rootSelection.get(); } const CollectionType& items() const { return filteredSelection; } inline size_t size() const { return filteredSelection.size(); } inline C* front() { return size()>0 ? filteredSelection.front() : NULL; } inline const C* front() const { return size()>0 ? filteredSelection.front() : NULL; } inline C* back() { return size()>0 ? filteredSelection.back() : NULL; } inline const C* back() const { return size()>0 ? filteredSelection.back() : NULL; } typename CollectionType::const_iterator begin() const { return filteredSelection.begin(); } typename CollectionType::const_iterator end() const { return filteredSelection.end(); } template inline void for_each(_Fn1 _Func) { std::for_each(filteredSelection.begin(), filteredSelection.end(), _Func); } template inline C* find_if(_Pr _Pred) { typename CollectionType::iterator iter = std::find_if(filteredSelection.begin(), filteredSelection.end(), _Pred); if (iter!=end()) return *iter; else return NULL; } // These convenience functions pass through to the generic Selection. This means // that functions like "clearSelection" clears EVERYTHING, even selected // items that are not of type C void clearSelection() { getSelection()->clearSelection(); } template void addToSelection(_InIt _First, _InIt _Last) { getSelection()->addToSelection(_First, _Last); } template void removeFromSelection(_InIt _First, _InIt _Last) { getSelection()->removeFromSelection(_First, _Last); } void addToSelection(C* c) { getSelection()->addToSelection(c); } void setSelection(C* c) { getSelection()->setSelection(c); } template void setSelection(_InIt _First, _InIt _Last) { getSelection()->setSelection(_First, _Last); } protected: /*override*/ virtual void onAncestorChanged(const AncestorChanged& event); /*implement*/ virtual void onSelectionChanged(const SelectionChanged& event) { // If an item was added to the selection, add it to ours if it is of the right type C* c = dynamic_cast(event.addedItem.get()); if (c!=NULL) filteredSelection.push_back(c); // If an item was removed from the selection, remove it from ours if (event.removedItem!=NULL) { typename CollectionType::iterator it = std::find( filteredSelection.begin(), filteredSelection.end(), event.removedItem.get() ); if (it != filteredSelection.end()) filteredSelection.erase(it); } } }; template /*override*/ void FilteredSelection::onAncestorChanged(const AncestorChanged& event) { Super::onAncestorChanged(event); if (rootSelection==NULL) { rootSelection = shared_from(ServiceProvider::create(event.newParent)); if (rootSelection!=NULL) { // Filter the current items for (Instances::const_iterator iter = rootSelection->begin(); iter!=rootSelection->end(); ++iter) { C* c = dynamic_cast(iter->get()); if (c!=NULL) filteredSelection.push_back(c); } // Listen for changes rootSelection->addFilteredSelection(this); } } } } // namespace