diff --git a/include/eepp/system/lock.hpp b/include/eepp/system/lock.hpp index 021fa394a..df9bc9c97 100644 --- a/include/eepp/system/lock.hpp +++ b/include/eepp/system/lock.hpp @@ -24,6 +24,23 @@ class EE_API Lock : NonCopyable { Mutex& mMutex; ///< Mutex to lock / unlock }; +class EE_API ConditionalLock : NonCopyable { + public: + /** @brief Construct the lock with a target mutex + * The mutex passed to Lock is automatically locked. + * @param condition Only if condition is true the mutex will be locked/unlocked. + * @param mutex Mutex to lock */ + explicit ConditionalLock( bool condition, Mutex* mutex ); + + /** @brief Destructor + * The destructor of Lock automatically unlocks its mutex. */ + ~ConditionalLock(); + + private: + Mutex* mMutex{ nullptr }; ///< Mutex to lock / unlock + bool mCondition{ false }; +}; + }} // namespace EE::System #endif diff --git a/include/eepp/ui/models/model.hpp b/include/eepp/ui/models/model.hpp index 972d0f204..560868154 100644 --- a/include/eepp/ui/models/model.hpp +++ b/include/eepp/ui/models/model.hpp @@ -136,11 +136,11 @@ class EE_API Model { void endDeleteRows(); void endDeleteColumns(); - Mutex& resourceLock(); + Mutex& resourceMutex(); - void acquireResourceLock() { mResourceLock.lock(); } + void acquireResourceMutex() { mResourceLock.lock(); } - void releaseResourceLock() { mResourceLock.unlock(); } + void releaseResourceMutex() { mResourceLock.unlock(); } protected: Model(){}; diff --git a/src/eepp/scene/actionmanager.cpp b/src/eepp/scene/actionmanager.cpp index 1f11c3049..99e4700e3 100644 --- a/src/eepp/scene/actionmanager.cpp +++ b/src/eepp/scene/actionmanager.cpp @@ -101,7 +101,10 @@ void ActionManager::update( const Time& time ) { Lock l( mMutex ); - for ( auto it = mActions.begin(); it != mActions.end(); ++it ) { + // Actions can be added during action updates, we need to only iterate the current actions + auto actions = mActions; + + for ( auto it = actions.begin(); it != actions.end(); ++it ) { Action* action = *it; action->update( time ); diff --git a/src/eepp/system/lock.cpp b/src/eepp/system/lock.cpp index 9064687d6..b10e1e42d 100644 --- a/src/eepp/system/lock.cpp +++ b/src/eepp/system/lock.cpp @@ -11,4 +11,15 @@ Lock::~Lock() { mMutex.unlock(); } +ConditionalLock::ConditionalLock( bool condition, Mutex* mutex ) : + mMutex( mutex ), mCondition( condition ) { + if ( mMutex && mCondition ) + mMutex->lock(); +} + +ConditionalLock::~ConditionalLock() { + if ( mMutex && mCondition ) + mMutex->unlock(); +} + }} // namespace EE::System diff --git a/src/eepp/ui/models/filesystemmodel.cpp b/src/eepp/ui/models/filesystemmodel.cpp index f9488b27d..4241dfa06 100644 --- a/src/eepp/ui/models/filesystemmodel.cpp +++ b/src/eepp/ui/models/filesystemmodel.cpp @@ -702,7 +702,7 @@ bool FileSystemModel::handleFileEvent( const FileEvent& event ) { bool ret; { - Lock l( resourceLock() ); + Lock l( resourceMutex() ); ret = handleFileEventLocked( event ); } diff --git a/src/eepp/ui/models/model.cpp b/src/eepp/ui/models/model.cpp index 8b70eda1c..6e3aed32f 100644 --- a/src/eepp/ui/models/model.cpp +++ b/src/eepp/ui/models/model.cpp @@ -303,7 +303,7 @@ void Model::handleDelete( Operation const& operation ) { } } -Mutex& Model::resourceLock() { +Mutex& Model::resourceMutex() { return mResourceLock; } diff --git a/src/eepp/ui/uitableview.cpp b/src/eepp/ui/uitableview.cpp index b9e96ba07..b5ffdeb6d 100644 --- a/src/eepp/ui/uitableview.cpp +++ b/src/eepp/ui/uitableview.cpp @@ -26,7 +26,7 @@ bool UITableView::isType( const Uint32& type ) const { void UITableView::drawChilds() { int realIndex = 0; - Lock l( const_cast( getModel() )->resourceLock() ); + ConditionalLock l( getModel() != nullptr, getModel() ? &getModel()->resourceMutex() : nullptr ); size_t start = mScrollOffset.y / getRowHeight(); size_t end = eemin( (size_t)eeceil( ( mScrollOffset.y + mSize.getHeight() ) / getRowHeight() ), @@ -62,7 +62,8 @@ Node* UITableView::overFind( const Vector2f& point ) { Node* pOver = NULL; if ( mEnabled && mVisible ) { - Lock l( const_cast( getModel() )->resourceLock() ); + ConditionalLock l( getModel() != nullptr, + getModel() ? &getModel()->resourceMutex() : nullptr ); updateWorldPolygon(); if ( mWorldBounds.contains( point ) && mPoly.pointInside( point ) ) { @@ -110,7 +111,7 @@ Node* UITableView::overFind( const Vector2f& point ) { Float UITableView::getMaxColumnContentWidth( const size_t& colIndex, bool bestGuess ) { Float lWidth = 0; - Lock l( const_cast( getModel() )->resourceLock() ); + ConditionalLock l( getModel() != nullptr, getModel() ? &getModel()->resourceMutex() : nullptr ); if ( getModel()->rowCount() == 0 ) return lWidth; getUISceneNode()->setIsLoading( true ); @@ -175,7 +176,7 @@ Uint32 UITableView::onKeyDown( const KeyEvent& event ) { return UIAbstractTableView::onKeyDown( event ); auto curIndex = getSelection().first(); int pageSize = eefloor( getVisibleArea().getHeight() / getRowHeight() ) - 1; - Lock l( const_cast( getModel() )->resourceLock() ); + ConditionalLock l( getModel() != nullptr, getModel() ? &getModel()->resourceMutex() : nullptr ); switch ( event.getKeyCode() ) { case KEY_PAGEUP: { if ( curIndex.row() - pageSize < 0 ) { @@ -278,7 +279,8 @@ Uint32 UITableView::onKeyDown( const KeyEvent& event ) { ModelIndex UITableView::findRowWithText( const std::string& text, const bool& caseSensitive, const bool& exactMatch ) const { const Model* model = getModel(); - Lock l( const_cast( getModel() )->resourceLock() ); + ConditionalLock l( getModel() != nullptr, + getModel() ? &const_cast( getModel() )->resourceMutex() : nullptr ); if ( !model || model->rowCount() == 0 ) return {}; size_t rc = model->rowCount(); diff --git a/src/eepp/ui/uitreeview.cpp b/src/eepp/ui/uitreeview.cpp index 76c124277..ca29d9e2f 100644 --- a/src/eepp/ui/uitreeview.cpp +++ b/src/eepp/ui/uitreeview.cpp @@ -44,7 +44,7 @@ void UITreeView::traverseTree( TreeViewCallback callback ) const { if ( !getModel() ) return; auto& model = *getModel(); - Lock l( const_cast( getModel() )->resourceLock() ); + Lock l( const_cast( getModel() )->resourceMutex() ); int indentLevel = 0; Float yOffset = getHeaderHeight(); int rowIndex = -1; @@ -118,7 +118,8 @@ void UITreeView::bindNavigationClick( UIWidget* widget ) { auto mouseEvent = static_cast( event ); auto idx = mouseEvent->getNode()->getParent()->asType()->getCurIndex(); if ( mouseEvent->getFlags() & EE_BUTTON_LMASK ) { - Lock l( const_cast( getModel() )->resourceLock() ); + ConditionalLock l( getModel() != nullptr, + getModel() ? &getModel()->resourceMutex() : nullptr ); if ( getModel()->rowCount( idx ) ) { auto& data = getIndexMetadata( idx ); data.open = !data.open; @@ -157,7 +158,8 @@ UIWidget* UITreeView::setupCell( UITableCell* widget, UIWidget* rowWidget, if ( icon ) { Vector2f pos( icon->convertToNodeSpace( mouseEvent->getPosition().asFloat() ) ); if ( pos >= Vector2f::Zero && pos <= icon->getPixelsSize() ) { - Lock l( const_cast( getModel() )->resourceLock() ); + ConditionalLock l( getModel() != nullptr, + getModel() ? &getModel()->resourceMutex() : nullptr ); auto idx = mouseEvent->getNode()->getParent()->asType()->getCurIndex(); if ( getModel()->rowCount( idx ) ) { @@ -646,7 +648,8 @@ void UITreeView::onSortColumn( const size_t& ) { ModelIndex UITreeView::findRowWithText( const std::string& text, const bool& caseSensitive, const bool& exactMatch ) const { const Model* model = getModel(); - Lock l( const_cast( getModel() )->resourceLock() ); + ConditionalLock l( getModel() != nullptr, + getModel() ? &const_cast( getModel() )->resourceMutex() : nullptr ); if ( !model || model->rowCount() == 0 ) return {}; ModelIndex foundIndex = {}; @@ -692,7 +695,8 @@ ModelIndex UITreeView::selectRowWithPath( std::string path ) { size_t rowCount = 0; { - Lock l( const_cast( getModel() )->resourceLock() ); + ConditionalLock l( getModel() != nullptr, + getModel() ? &getModel()->resourceMutex() : nullptr ); rowCount = getModel()->rowCount( foundIndex ); } if ( rowCount ) { diff --git a/src/tools/codeeditor/autocompletemodule.cpp b/src/tools/codeeditor/autocompletemodule.cpp index ff76c5ca8..177517366 100644 --- a/src/tools/codeeditor/autocompletemodule.cpp +++ b/src/tools/codeeditor/autocompletemodule.cpp @@ -445,10 +445,12 @@ static std::vector fuzzyMatchSymbols( const AutoCompleteModule::Sym void AutoCompleteModule::runUpdateSuggestions( const std::string& symbol, const SymbolsList& symbols, UICodeEditor* editor ) { - Lock l( mLangSymbolsMutex ); - Lock l2( mSuggestionsMutex ); - mSuggestions = fuzzyMatchSymbols( symbols, symbol, mSuggestionsMaxVisible ); - mSuggestionsEditor = editor; + { + Lock l( mLangSymbolsMutex ); + Lock l2( mSuggestionsMutex ); + mSuggestions = fuzzyMatchSymbols( symbols, symbol, mSuggestionsMaxVisible ); + mSuggestionsEditor = editor; + } editor->runOnMainThread( [editor] { editor->invalidateDraw(); } ); } diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp index cf637ea87..998f05dc5 100644 --- a/src/tools/codeeditor/codeeditor.cpp +++ b/src/tools/codeeditor/codeeditor.cpp @@ -1920,22 +1920,28 @@ void App::initProjectTreeView( const std::string& path ) { return 1; } ); - if ( !path.empty() && FileSystem::fileExists( path ) ) { + if ( !path.empty() ) { if ( FileSystem::isDirectory( path ) ) { loadFolder( path ); } else { std::string rpath( FileSystem::getRealPath( path ) ); + std::string folderPath( FileSystem::fileRemoveFileName( rpath ) ); - mFileSystemModel = FileSystemModel::New( FileSystem::fileRemoveFileName( rpath ), - FileSystemModel::Mode::FilesAndDirectories, - { true, true, true } ); + if ( FileSystem::isDirectory( folderPath ) ) { + mFileSystemModel = FileSystemModel::New( + folderPath, FileSystemModel::Mode::FilesAndDirectories, { true, true, true } ); - mProjectTreeView->setModel( mFileSystemModel ); + mProjectTreeView->setModel( mFileSystemModel ); - if ( mFileSystemListener ) - mFileSystemListener->setFileSystemModel( mFileSystemModel ); + if ( mFileSystemListener ) + mFileSystemListener->setFileSystemModel( mFileSystemModel ); - loadFileFromPath( rpath, false ); + if ( FileSystem::fileExists( rpath ) ) { + loadFileFromPath( rpath, false ); + } else if ( FileSystem::fileCanWrite( folderPath ) ) { + loadFileFromPath( path, false ); + } + } } } else { loadFolder( "." );