various: implemented multiple project support

This required various changes to various parts of the infrastructure (mostly
using paths to finding the "root project" of any given object throughout).
Now theoretically infinite projects can be opened and accounted for at any
given time.
This commit is contained in:
itsmattkc
2020-04-18 01:50:47 +10:00
parent 77c0db55a5
commit f73c41d3fa
48 changed files with 824 additions and 224 deletions
+162 -50
View File
@@ -67,8 +67,7 @@ Core::Core() :
main_window_(nullptr),
tool_(Tool::kPointer),
addable_object_(Tool::kAddableEmpty),
snapping_(true),
queue_autorecovery_(false)
snapping_(true)
{
}
@@ -157,7 +156,7 @@ void Core::Start()
if (startup_project_.isEmpty()) {
// If no load project is set, create a new one on open
AddOpenProject(std::make_shared<Project>());
CreateNewProject();
} else {
OpenProjectInternal(startup_project_);
}
@@ -252,6 +251,19 @@ void Core::ClearOpenRecentList()
recent_projects_.clear();
}
void Core::CreateNewProject()
{
// If we already have an empty/new project, switch to it
foreach (ProjectPtr already_open, open_projects_) {
if (already_open->is_new()) {
AddOpenProject(already_open);
return;
}
}
AddOpenProject(std::make_shared<Project>());
}
const bool &Core::snapping() const
{
return snapping_;
@@ -316,7 +328,7 @@ void Core::DialogPreferencesShow()
void Core::DialogProjectPropertiesShow()
{
ProjectPropertiesDialog ppd(main_window_);
ProjectPropertiesDialog ppd(GetActiveProject(), main_window_);
ppd.exec();
}
@@ -406,6 +418,21 @@ void Core::CreateNewSequence()
void Core::AddOpenProject(ProjectPtr p)
{
// Ensure project is not open at the moment
foreach (ProjectPtr already_open, open_projects_) {
if (already_open == p) {
// Signal UI to switch to this project
emit ProjectOpened(p.get());
return;
}
}
// If we currently have an empty project, close it first
if (!open_projects_.isEmpty() && open_projects_.last()->is_new()) {
CloseProject(open_projects_.last().get(), false);
}
connect(p.get(), &Project::ModifiedChanged, this, &Core::ProjectWasModified);
open_projects_.append(p);
PushRecentlyOpenedProject(p->filename());
@@ -433,6 +460,27 @@ bool Core::ConfirmImageSequence(const QString& filename)
return (mb.exec() == QMessageBox::Yes);
}
void Core::ProjectWasModified(bool e)
{
//Project* p = static_cast<Project*>(sender());
if (e) {
// If this project is modified, we know for sure the window should show a "modified" flag (the * in the titlebar)
main_window_->setWindowModified(true);
} else {
// If we just set this project to "not modified", see if all projects are not modified in which case we can hide
// the modified flag
foreach (ProjectPtr open, open_projects_) {
if (open->is_modified()) {
main_window_->setWindowModified(true);
return;
}
}
main_window_->setWindowModified(false);
}
}
void Core::DeclareTypesForQt()
{
qRegisterMetaType<NodeDependency>();
@@ -490,6 +538,7 @@ void Core::StartGUI(bool full_screen)
// When a new project is opened, update the mainwindow
connect(this, &Core::ProjectOpened, main_window_, &MainWindow::ProjectOpen);
connect(this, &Core::ProjectClosed, main_window_, &MainWindow::ProjectClose);
// Start autorecovery timer using the config value as its interval
SetAutorecoveryInterval(Config::Current()["AutorecoveryInterval"].toInt());
@@ -501,7 +550,7 @@ void Core::SaveProjectInternal(Project *project)
// Create save manager
ProjectSaveManager* psm = new ProjectSaveManager(project);
connect(psm, &Task::Succeeded, this, &Core::ProjectSaveSucceeded);
connect(psm, &ProjectSaveManager::ProjectSaveSucceeded, this, &Core::ProjectSaveSucceeded);
TaskDialog* task_dialog = new TaskDialog(psm, tr("Save Project"), main_window());
task_dialog->open();
@@ -509,21 +558,22 @@ void Core::SaveProjectInternal(Project *project)
void Core::SaveAutorecovery()
{
if (queue_autorecovery_) {
// FIXME: Save autorecovery of projects open
queue_autorecovery_ = false;
foreach (ProjectPtr p, open_projects_) {
if (!p->has_autorecovery_been_saved()) {
// FIXME: SAVE AN AUTORECOVERY PROJECT
p->set_autorecovery_saved(true);
}
}
}
void Core::ProjectSaveSucceeded()
void Core::ProjectSaveSucceeded(Project* p)
{
PushRecentlyOpenedProject(GetActiveProject()->filename());
PushRecentlyOpenedProject(p->filename());
SetProjectModified(false);
p->set_modified(false);
}
Project *Core::GetActiveProject()
Project *Core::GetActiveProject() const
{
ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
@@ -534,7 +584,7 @@ Project *Core::GetActiveProject()
}
}
ProjectViewModel *Core::GetActiveProjectModel()
ProjectViewModel *Core::GetActiveProjectModel() const
{
ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
@@ -545,7 +595,7 @@ ProjectViewModel *Core::GetActiveProjectModel()
}
}
Folder *Core::GetSelectedFolderInActiveProject()
Folder *Core::GetSelectedFolderInActiveProject() const
{
ProjectPanel* active_project_panel = PanelManager::instance()->MostRecentlyFocused<ProjectPanel>();
@@ -568,17 +618,6 @@ void Core::SetTimecodeDisplay(Timecode::Display d)
emit TimecodeDisplayChanged(d);
}
void Core::SetProjectModified(bool e)
{
main_window()->setWindowModified(e);
queue_autorecovery_ = e;
}
bool Core::IsProjectModified() const
{
return main_window_->isWindowModified();
}
void Core::SetAutorecoveryInterval(int minutes)
{
// Convert minutes to milliseconds
@@ -599,38 +638,19 @@ bool Core::SaveActiveProject()
{
Project* active_project = GetActiveProject();
if (!active_project) {
return false;
if (active_project) {
return SaveProject(active_project);
}
if (active_project->filename().isEmpty()) {
return SaveActiveProjectAs();
} else {
SaveProjectInternal(active_project);
return true;
}
return false;
}
bool Core::SaveActiveProjectAs()
{
Project* active_project = GetActiveProject();
if (!active_project) {
return false;
}
QString fn = QFileDialog::getSaveFileName(main_window_,
tr("Save Project As"),
QString(),
GetProjectFilter());
if (!fn.isEmpty()) {
active_project->set_filename(fn);
SaveProjectInternal(active_project);
return true;
if (active_project) {
return SaveProjectAs(active_project);
}
return false;
@@ -722,6 +742,35 @@ QString Core::GetRecentProjectsFilePath()
return QDir(GetConfigurationLocation()).filePath(QStringLiteral("recent"));
}
bool Core::SaveProject(Project *p)
{
if (p->filename().isEmpty()) {
return SaveProjectAs(p);
} else {
SaveProjectInternal(p);
return true;
}
}
bool Core::SaveProjectAs(Project *p)
{
QString fn = QFileDialog::getSaveFileName(main_window_,
tr("Save Project As"),
QString(),
GetProjectFilter());
if (!fn.isEmpty()) {
p->set_filename(fn);
SaveProjectInternal(p);
return true;
}
return false;
}
void Core::PushRecentlyOpenedProject(const QString& s)
{
if (s.isEmpty()) {
@@ -739,6 +788,15 @@ void Core::PushRecentlyOpenedProject(const QString& s)
void Core::OpenProjectInternal(const QString &filename)
{
// See if this project is open already
foreach (ProjectPtr p, open_projects_) {
if (p->filename() == filename) {
// This project is already open
AddOpenProject(p);
return;
}
}
ProjectLoadManager* plm = new ProjectLoadManager(filename);
// We use a blocking queued connection here because we want to ensure we have this project instance before the
@@ -818,6 +876,60 @@ void Core::OpenProjectFromRecentList(int index)
}
}
bool Core::CloseProject(Project *p, bool auto_open_new)
{
for (int i=0;i<open_projects_.size();i++) {
if (open_projects_.at(i).get() == p) {
if (p->is_modified()) {
QMessageBox mb(main_window_);
mb.setIcon(QMessageBox::Question);
mb.setWindowTitle(tr("Unsaved Changes"));
mb.setText(tr("The project '%1' has unsaved changes. Would you like to save them?")
.arg(p->name()));
QPushButton* yes_btn = mb.addButton(tr("Save"), QMessageBox::YesRole);
mb.addButton(tr("Don't Save"), QMessageBox::NoRole);
QPushButton* cancel_btn = mb.addButton(QMessageBox::Cancel);
mb.exec();
if (mb.clickedButton() == cancel_btn
|| (mb.clickedButton() == yes_btn && !SaveActiveProject())) {
// Don't close if the user clicked cancel on this messagebox OR they cancelled a save as operation
return false;
}
}
disconnect(p, &Project::ModifiedChanged, this, &Core::ProjectWasModified);
emit ProjectClosed(p);
open_projects_.removeAt(i);
break;
}
}
// Ensure a project is always active
if (auto_open_new && open_projects_.isEmpty()) {
CreateNewProject();
}
return true;
}
bool Core::CloseAllProjects(bool auto_open_new)
{
QList<ProjectPtr> copy = open_projects_;
foreach (ProjectPtr p, copy) {
if (!CloseProject(p.get(), auto_open_new)) {
return false;
}
}
return true;
}
void Core::OpenProject()
{
QString file = QFileDialog::getOpenFileName(main_window_,