logic: Ask user before reparsing the project

While the message box with the question is visible the ui is blocked. A message appears when:
* when project was edited
* when preferences were edited
* when project file has more recent changes than db
* when db has a different version than Coati
This commit is contained in:
Eberhard Graether
2016-04-24 22:47:39 +02:00
parent a6e57a6564
commit 6be207a68a
16 changed files with 342 additions and 70 deletions
+82 -9
View File
@@ -92,7 +92,7 @@ bool Application::hasGUI()
return m_hasGUI;
}
void Application::loadProject(const FilePath& projectSettingsFilePath)
void Application::createAndLoadProject(const FilePath& projectSettingsFilePath)
{
MessageStatus("Loading Project: " + projectSettingsFilePath.str(), false, true).dispatch();
@@ -102,20 +102,63 @@ void Application::loadProject(const FilePath& projectSettingsFilePath)
m_storageCache->clear();
m_project = Project::create(m_storageCache.get());
m_project->load(projectSettingsFilePath);
loadProject(projectSettingsFilePath);
if (m_hasGUI)
{
m_mainView->setTitle(
"Coati - " +
projectSettingsFilePath.fileName());
m_mainView->setTitle("Coati - " + projectSettingsFilePath.fileName());
m_mainView->updateRecentProjectMenu();
m_mainView->hideStartScreen();
m_componentManager->refreshViews();
}
}
void Application::loadProject(const FilePath& projectSettingsFilePath)
{
bool reparse = false;
Project::ProjectState state = m_project->load(projectSettingsFilePath);
if (state == Project::PROJECT_OUTDATED)
{
if (m_hasGUI)
{
std::vector<std::string> options;
options.push_back("Yes");
options.push_back("No");
int result = m_mainView->confirm(
"The project file was changed after the last analysis. The project needs to get fully reanalysed to "
"reflect the current project state. Do you want to reanalyze the project?", options);
reparse = (result == 0);
}
}
else if (state == Project::PROJECT_OUTVERSIONED)
{
MessageStatus("Can't load project").dispatch();
reparse = true;
if (m_hasGUI)
{
std::vector<std::string> options;
options.push_back("Yes");
options.push_back("No");
int result = m_mainView->confirm(
"This project was analyzed with a different version of Coati. It needs to be fully reanalyzed to be used "
"with this version of Coati. Do you want to reanalyze the project?", options);
reparse = (result == 0);
}
}
if (reparse)
{
m_project->clearStorage();
m_project->load(projectSettingsFilePath);
}
}
void Application::refreshProject()
{
MessageStatus("Refreshing Project").dispatch();
@@ -126,7 +169,12 @@ void Application::refreshProject()
m_componentManager->refreshViews();
}
m_project->reload();
Project::ProjectState state = m_project->reload();
if (state != Project::PROJECT_LOADED)
{
MessageStatus("Can't refresh project").dispatch();
loadProject(m_project->getProjectSettingsFilePath());
}
}
void Application::saveProject(const FilePath& projectSettingsFilePath)
@@ -156,18 +204,43 @@ void Application::handleMessage(MessageFinishedParsing* message)
void Application::handleMessage(MessageLoadProject* message)
{
FilePath projectSettingsFilePath(message->projectSettingsFilePath);
if (projectSettingsFilePath.empty())
{
projectSettingsFilePath = m_project->getProjectSettingsFilePath();
if (projectSettingsFilePath.empty())
{
return;
}
}
if (message->forceRefresh)
{
if (m_hasGUI)
{
std::vector<std::string> options;
options.push_back("Yes");
options.push_back("No");
int result = m_mainView->confirm(
"Some settings were changed, the project needs to be fully reanalyzed. "
"Do you want to reanalyze the project?", options);
if (result == 1)
{
return;
}
}
m_project->clearStorage();
}
else if (FilePath(message->projectSettingsFilePath) == m_project->getProjectSettingsFilePath())
else if (projectSettingsFilePath == m_project->getProjectSettingsFilePath())
{
return;
}
try
{
loadProject(message->projectSettingsFilePath);
createAndLoadProject(projectSettingsFilePath);
}
catch (...)
{
+1
View File
@@ -33,6 +33,7 @@ public:
~Application();
void createAndLoadProject(const FilePath& projectSettingsFilePath);
void loadProject(const FilePath& projectSettingsFilePath);
void refreshProject();
void saveProject(const FilePath& projectSettingsFilePath);
+80 -32
View File
@@ -30,29 +30,62 @@ const FilePath& Project::getProjectSettingsFilePath() const
return m_projectSettingsFilepath;
}
bool Project::load(const FilePath& projectSettingsFile)
Project::ProjectState Project::load(const FilePath& projectSettingsFile)
{
bool success = ProjectSettings::getInstance()->load(projectSettingsFile);
m_state = PROJECT_NONE;
bool success = true;
if (!projectSettingsFile.empty() && projectSettingsFile != m_projectSettingsFilepath)
{
success = ProjectSettings::getInstance()->load(projectSettingsFile);
}
if (success)
{
setProjectSettingsFilePath(projectSettingsFile);
updateFileManager();
switch (m_state)
{
case PROJECT_NONE:
break;
case PROJECT_EMPTY:
parseCode();
break;
case PROJECT_LOADED:
case PROJECT_OUTDATED:
m_storage->finishParsing();
MessageFinishedParsing(0, 0, 0, true).dispatch();
break;
case PROJECT_OUTVERSIONED:
m_storage.reset();
break;
}
}
if (m_storageWasLoaded)
return m_state;
}
Project::ProjectState Project::reload()
{
if (m_state == PROJECT_LOADED &&
FileSystem::getFileInfoForPath(m_projectSettingsFilepath).lastWriteTime >
FileSystem::getFileInfoForPath(m_storage->getDbFilePath()).lastWriteTime)
{
m_storage->startParsing();
m_storage->finishParsing();
MessageFinishedParsing(0, 0, 0, true).dispatch();
m_state = PROJECT_OUTDATED;
}
else
else if (!m_projectSettingsFilepath.empty() && (m_state == PROJECT_EMPTY || m_state == PROJECT_LOADED))
{
ProjectSettings::getInstance()->load(m_projectSettingsFilepath);
updateFileManager();
parseCode();
}
m_storageWasLoaded = true;
return success;
return m_state;
}
bool Project::save(const FilePath& projectSettingsFile)
@@ -74,25 +107,17 @@ bool Project::save(const FilePath& projectSettingsFile)
return true;
}
void Project::reload()
{
if (!m_projectSettingsFilepath.empty())
{
ProjectSettings::getInstance()->load(m_projectSettingsFilepath);
updateFileManager();
setProjectSettingsFilePath(m_projectSettingsFilepath);
}
parseCode();
}
void Project::clearStorage()
{
if (m_state == PROJECT_OUTVERSIONED)
{
loadStorage(m_projectSettingsFilepath);
}
if (m_storage)
{
m_storage->clear();
m_storageWasLoaded = false;
m_state = PROJECT_EMPTY;
}
}
@@ -133,6 +158,8 @@ void Project::parseCode()
));
Task::dispatch(taskGroup);
m_state = PROJECT_LOADED;
}
void Project::logStats() const
@@ -142,21 +169,33 @@ void Project::logStats() const
void Project::setProjectSettingsFilePath(const FilePath& path)
{
m_storageWasLoaded = false;
if (path.empty())
{
m_storage.reset();
m_state = PROJECT_NONE;
}
else
{
FilePath dbPath = FilePath(path).replaceExtension("coatidb");
m_storageWasLoaded = dbPath.exists();
loadStorage(path);
if (!m_storage || !dbPath.exists())
Version version = m_storage->getVersion();
if (version.isEmpty())
{
m_storage = std::make_shared<Storage>(dbPath);
m_storageWasLoaded = m_storage->init();
m_state = PROJECT_EMPTY;
m_storage->init();
}
else if (version.isDifferentStorageVersionThan(Version::getApplicationVersion()))
{
m_state = PROJECT_OUTVERSIONED;
m_storage.reset();
}
else if (FileSystem::getFileInfoForPath(path).lastWriteTime > FileSystem::getFileInfoForPath(m_storage->getDbFilePath()).lastWriteTime)
{
m_state = PROJECT_OUTDATED;
}
else
{
m_state = PROJECT_LOADED;
}
}
@@ -164,6 +203,15 @@ void Project::setProjectSettingsFilePath(const FilePath& path)
m_projectSettingsFilepath = path;
}
void Project::loadStorage(const FilePath& path)
{
FilePath dbPath = FilePath(path).replaceExtension("coatidb");
if (!m_storage || path != m_projectSettingsFilepath || !dbPath.exists())
{
m_storage = std::make_shared<Storage>(dbPath);
}
}
void Project::updateFileManager()
{
std::shared_ptr<ProjectSettings> projSettings = ProjectSettings::getInstance();
@@ -226,6 +274,6 @@ Parser::Arguments Project::getParserArguments() const
Project::Project(StorageAccessProxy* storageAccessProxy)
: m_storageAccessProxy(storageAccessProxy)
, m_storageWasLoaded(false)
, m_state(PROJECT_NONE)
{
}
+15 -3
View File
@@ -13,15 +13,25 @@ class StorageAccessProxy;
class Project
{
public:
enum ProjectState
{
PROJECT_NONE,
PROJECT_EMPTY,
PROJECT_LOADED,
PROJECT_OUTDATED,
PROJECT_OUTVERSIONED
};
static std::shared_ptr<Project> create(StorageAccessProxy* storageAccessProxy);
~Project();
const FilePath& getProjectSettingsFilePath() const;
bool load(const FilePath& projectSettingsFile);
ProjectState load(const FilePath& projectSettingsFile);
ProjectState reload();
bool save(const FilePath& projectSettingsFile);
void reload();
void clearStorage();
@@ -35,17 +45,19 @@ private:
void parseCode();
void setProjectSettingsFilePath(const FilePath& path);
void loadStorage(const FilePath& path);
void updateFileManager();
Parser::Arguments getParserArguments() const;
StorageAccessProxy* const m_storageAccessProxy;
ProjectState m_state;
FilePath m_projectSettingsFilepath;
FileManager m_fileManager;
std::shared_ptr<Storage> m_storage;
bool m_storageWasLoaded;
};
#endif // PROJECT_H
+5
View File
@@ -7,3 +7,8 @@ MainView::MainView()
MainView::~MainView()
{
}
int MainView::confirm(const std::string& message)
{
return confirm(message, std::vector<std::string>());
}
+4
View File
@@ -2,6 +2,7 @@
#define MAIN_VIEW_H
#include <string>
#include <vector>
#include "component/view/ViewLayout.h"
@@ -16,6 +17,9 @@ public:
virtual void setTitle(const std::string& title) = 0;
virtual void activateWindow() = 0;
virtual void updateRecentProjectMenu() = 0;
virtual int confirm(const std::string& message);
virtual int confirm(const std::string& message, const std::vector<std::string>& options) = 0;
};
#endif // MAIN_VIEW_H
+9 -9
View File
@@ -10,9 +10,10 @@
#include "utility/utilityString.h"
#include "utility/Version.h"
SqliteStorage::SqliteStorage(const std::string& dbFilePath)
SqliteStorage::SqliteStorage(const FilePath& dbFilePath)
: m_dbFilePath(dbFilePath)
{
m_database.open(dbFilePath.c_str());
m_database.open(m_dbFilePath.str().c_str());
m_database.execDML("PRAGMA foreign_keys=ON;");
}
@@ -22,23 +23,17 @@ SqliteStorage::~SqliteStorage()
m_database.close();
}
bool SqliteStorage::init()
void SqliteStorage::init()
{
Version version = getVersion();
if (version.isEmpty())
{
setup();
return false;
}
else if (version.isDifferentStorageVersionThan(Version::getApplicationVersion()))
{
clear();
return false;
}
else
{
return true;
}
}
@@ -71,6 +66,11 @@ void SqliteStorage::rollbackTransaction()
m_database.execDML("ROLLBACK TRANSACTION;");
}
FilePath SqliteStorage::getDbFilePath() const
{
return m_dbFilePath;
}
Version SqliteStorage::getVersion() const
{
std::string versionStr = getMetaValue("version");
+5 -2
View File
@@ -20,10 +20,10 @@ class Version;
class SqliteStorage
{
public:
SqliteStorage(const std::string& dbFilePath);
SqliteStorage(const FilePath& dbFilePath);
~SqliteStorage();
bool init();
void init();
void setup();
void clear();
@@ -31,6 +31,8 @@ public:
void commitTransaction();
void rollbackTransaction();
FilePath getDbFilePath() const;
Version getVersion() const;
void setVersion(const Version& version);
@@ -129,6 +131,7 @@ private:
ResultType getFirstResult(const std::string& query) const;
mutable CppSQLite3DB m_database;
FilePath m_dbFilePath;
};
template <typename ResultType>
+8 -3
View File
@@ -25,7 +25,7 @@
#include "settings/ApplicationSettings.h"
Storage::Storage(const FilePath& dbPath)
: m_sqliteStorage(dbPath.str())
: m_sqliteStorage(dbPath)
{
}
@@ -33,18 +33,23 @@ Storage::~Storage()
{
}
FilePath Storage::getDbFilePath() const
{
return m_sqliteStorage.getDbFilePath();
}
Version Storage::getVersion() const
{
return m_sqliteStorage.getVersion();
}
bool Storage::init()
void Storage::init()
{
m_commandIndex.addNode(0, NameHierarchy(SearchMatch::getCommandName(SearchMatch::COMMAND_ALL)));
m_commandIndex.addNode(0, NameHierarchy(SearchMatch::getCommandName(SearchMatch::COMMAND_ERROR)));
m_commandIndex.finishSetup();
return m_sqliteStorage.init();
m_sqliteStorage.init();
}
void Storage::clear()
+2 -1
View File
@@ -22,9 +22,10 @@ public:
Storage(const FilePath& dbPath);
virtual ~Storage();
FilePath getDbFilePath() const;
Version getVersion() const;
bool init();
void init();
void clear();
void clearCaches();
+12 -4
View File
@@ -1,6 +1,7 @@
#include "settings/ApplicationSettings.h"
#include "utility/ResourcePaths.h"
#include "utility/utility.h"
std::shared_ptr<ApplicationSettings> ApplicationSettings::s_instance;
@@ -14,10 +15,21 @@ std::shared_ptr<ApplicationSettings> ApplicationSettings::getInstance()
return s_instance;
}
ApplicationSettings::ApplicationSettings()
{
}
ApplicationSettings::~ApplicationSettings()
{
}
bool ApplicationSettings::operator==(const ApplicationSettings& other) const
{
return
utility::isPermutation<FilePath>(getHeaderSearchPaths(), other.getHeaderSearchPaths()) &&
utility::isPermutation<FilePath>(getFrameworkSearchPaths(), other.getFrameworkSearchPaths());
}
int ApplicationSettings::getMaxRecentProjectsCount() const
{
return 7;
@@ -161,10 +173,6 @@ void ApplicationSettings::setCodeSnippetExpandRange(int range)
setValue<int>("code/snippet/expand_range", range);
}
ApplicationSettings::ApplicationSettings()
{
}
std::vector<FilePath> ApplicationSettings::getRecentProjects() const
{
std::vector<FilePath> recentProjects;
+3 -1
View File
@@ -10,8 +10,11 @@ class ApplicationSettings
{
public:
static std::shared_ptr<ApplicationSettings> getInstance();
ApplicationSettings();
~ApplicationSettings();
bool operator==(const ApplicationSettings& other) const;
int getMaxRecentProjectsCount() const;
// source
@@ -77,7 +80,6 @@ public:
void setLicenseCheck(const std::string& hash);
private:
ApplicationSettings();
ApplicationSettings(const ApplicationSettings&);
void operator=(const ApplicationSettings&);
+57
View File
@@ -1,5 +1,10 @@
#include "qt/view/QtMainView.h"
#include <chrono>
#include <thread>
#include <QMessageBox>
#include "utility/logging/logging.h"
#include "qt/window/QtMainWindow.h"
@@ -12,6 +17,7 @@ QtMainView::QtMainView()
, m_activateWindowFunctor(std::bind(&QtMainView::doActivateWindow, this))
, m_updateRecentProjectMenuFunctor(std::bind(&QtMainView::doUpdateRecentProjectMenu, this))
, m_forceLicenseScreenFunctor(std::bind(&QtMainView::doForceLicenseScreen, this, std::placeholders::_1))
, m_confirmFunctor(std::bind(&QtMainView::doConfirm, this, std::placeholders::_1, std::placeholders::_2))
{
m_window = std::make_shared<QtMainWindow>();
m_window->show();
@@ -89,6 +95,30 @@ void QtMainView::updateRecentProjectMenu()
m_updateRecentProjectMenuFunctor();
}
int QtMainView::confirm(const std::string& message, const std::vector<std::string>& options)
{
m_confirmDone = false;
m_confirmFunctor(message, options);
while (true)
{
{
std::lock_guard<std::mutex> lock(m_confirmMutex);
if (m_confirmDone)
{
break;
}
}
const int SLEEP_TIME_MS = 25;
std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME_MS));
}
return m_confirmResult;
}
void QtMainView::handleMessage(MessageForceEnterLicense* message)
{
m_forceLicenseScreenFunctor(message->licenseExpired);
@@ -144,3 +174,30 @@ void QtMainView::doForceLicenseScreen(bool expired)
{
m_window->forceEnterLicense(expired);
}
void QtMainView::doConfirm(const std::string& message, const std::vector<std::string>& options)
{
QMessageBox msgBox;
msgBox.setText(message.c_str());
for (const std::string& option : options)
{
msgBox.addButton(option.c_str(), QMessageBox::AcceptRole);
}
msgBox.exec();
m_confirmResult = -1;
for (int i = 0; i < msgBox.buttons().size(); i++)
{
if (msgBox.clickedButton() == msgBox.buttons().at(i))
{
m_confirmResult = i;
break;
}
}
std::lock_guard<std::mutex> lock(m_confirmMutex);
m_confirmDone = true;
}
+9
View File
@@ -46,6 +46,8 @@ public:
virtual void activateWindow();
virtual void updateRecentProjectMenu();
virtual int confirm(const std::string& message, const std::vector<std::string>& options);
private:
void handleMessage(MessageForceEnterLicense* message);
void handleMessage(MessageProjectNew* message);
@@ -59,6 +61,8 @@ private:
void doUpdateRecentProjectMenu();
void doForceLicenseScreen(bool expired);
void doConfirm(const std::string& message, const std::vector<std::string>& options);
std::shared_ptr<QtMainWindow> m_window;
std::vector<View*> m_views;
@@ -69,6 +73,11 @@ private:
QtThreadedFunctor<> m_activateWindowFunctor;
QtThreadedFunctor<> m_updateRecentProjectMenuFunctor;
QtThreadedFunctor<bool> m_forceLicenseScreenFunctor;
QtThreadedFunctor<const std::string&, const std::vector<std::string>&> m_confirmFunctor;
std::mutex m_confirmMutex;
bool m_confirmDone;
int m_confirmResult;
};
#endif // QT_MAIN_VIEW_H
@@ -30,10 +30,15 @@
QtProjectWizzard::QtProjectWizzard(QWidget* parent)
: QtWindowStackElement(parent)
, m_windowStack(this)
, m_editing(false)
{
connect(&m_windowStack, SIGNAL(push()), this, SLOT(windowStackChanged()));
connect(&m_windowStack, SIGNAL(pop()), this, SLOT(windowStackChanged()));
ApplicationSettings* appSettings = ApplicationSettings::getInstance().get();
m_appSettings.setHeaderSearchPaths(appSettings->getHeaderSearchPaths());
m_appSettings.setFrameworkSearchPaths(appSettings->getFrameworkSearchPaths());
m_parserManager = std::make_shared<SolutionParserManager>();
// wip
@@ -166,6 +171,7 @@ void QtProjectWizzard::refreshProjectFromCompilationDatabase(const std::string&
void QtProjectWizzard::editProject(const ProjectSettings& settings)
{
m_settings = settings;
m_editing = true;
showSummary();
@@ -204,7 +210,7 @@ void QtProjectWizzard::showPreferences()
}
);
connect(window, SIGNAL(next()), this, SLOT(cancelWizzard()));
connect(window, SIGNAL(next()), this, SLOT(savePreferences()));
}
template<typename T>
@@ -304,6 +310,12 @@ void QtProjectWizzard::cancelWizzard()
emit canceled();
}
void QtProjectWizzard::finishWizzard()
{
m_windowStack.clearWindows();
emit finished();
}
void QtProjectWizzard::windowStackChanged()
{
QWidget* window = m_windowStack.getTopWindow();
@@ -602,12 +614,35 @@ void QtProjectWizzard::createProject()
m_settings.save(path);
bool forceRefresh = !(m_settings == *ProjectSettings::getInstance().get());
bool edited = false;
if (m_editing)
{
bool settingsChanged = !(m_settings == *ProjectSettings::getInstance().get());
bool appSettingsChanged = !(m_appSettings == *ApplicationSettings::getInstance().get());
if (settingsChanged || appSettingsChanged)
{
edited = true;
}
}
MessageDispatchWhenLicenseValid(
std::make_shared<MessageLoadProject>(path, forceRefresh)
std::make_shared<MessageLoadProject>(path, edited)
).dispatch();
m_windowStack.clearWindows();
emit finished();
finishWizzard();
}
void QtProjectWizzard::savePreferences()
{
bool appSettingsChanged = !(m_appSettings == *ApplicationSettings::getInstance().get());
if (appSettingsChanged)
{
MessageDispatchWhenLicenseValid(
std::make_shared<MessageLoadProject>("", true)
).dispatch();
}
cancelWizzard();
}
@@ -6,9 +6,11 @@
#include "qt/window/project_wizzard/QtProjectWizzardContentSelect.h"
#include "qt/window/QtWindowStack.h"
#include "settings/ApplicationSettings.h"
#include "settings/ProjectSettings.h"
#include "utility/solution/SolutionParserManager.h"
class ProjectSettings;
class QtProjectWizzardContentSummary;
class QtProjectWizzardWindow;
@@ -55,12 +57,18 @@ private:
QtWindowStack m_windowStack;
std::shared_ptr<QtProjectWizzardWindow> m_popup;
ProjectSettings m_settings;
ApplicationSettings m_appSettings;
bool m_editing;
std::shared_ptr<SolutionParserManager> m_parserManager;
private slots:
void cancelWizzard();
void finishWizzard();
void windowStackChanged();
void popupClosed();
@@ -87,6 +95,7 @@ private slots:
void showSummary();
void createProject();
void savePreferences();
};
template<>