11namespace tracktion {
inline namespace engine
14ProjectManager::ProjectManager (
Engine& e)
19ProjectManager::~ProjectManager()
23 jassert (openProjects.isEmpty());
27void ProjectManager::initialise()
31 auto& storage = engine.getPropertyStorage();
33 if (storage.getProperty (SettingID::findExamples,
false))
35 storage.setProperty (SettingID::findExamples,
juce::var());
37 auto examplesDir = storage.getAppPrefsFolder().getChildFile (
"examples");
42 for (
auto& f : exampleProjects)
43 addProjectToList (f, false, getActiveProjectsFolder(),
44 f.getFileName().containsIgnoreCase (
"Spiralling") ? 0 : -1);
53 if (folder[IDs::uid].toString().isEmpty())
57 ensureAllItemsHaveIDs (folder.
getChild(i));
60void ProjectManager::loadList()
66 auto xml = engine.getPropertyStorage().getXmlProperty (SettingID::projectList);
71 if (! folders.hasType (IDs::ROOT))
74 if (! getLibraryProjectsFolder().isValid()) folders.addChild (
juce::ValueTree (IDs::LIBRARY), -1,
nullptr);
75 if (! getActiveProjectsFolder().isValid()) folders.addChild (
juce::ValueTree (IDs::ACTIVE), 0,
nullptr);
77 jassert (getActiveProjectsFolder().isValid() && getLibraryProjectsFolder().isValid());
81 xml = engine.getPropertyStorage().getXmlProperty (SettingID::projects);
88 auto v = oldT4.getChildWithProperty (IDs::name,
"Library Projects");
90 for (
int i = v.getNumChildren(); --i >= 0;)
92 auto c = v.getChild(i);
93 v.removeChild (c,
nullptr);
94 getLibraryProjectsFolder().addChild (c, 0,
nullptr);
99 auto v = oldT4.getChildWithProperty (IDs::name,
"Active Projects");
101 for (
int i = v.getNumChildren(); --i >= 0;)
103 auto c = v.getChild(i);
104 v.removeChild (c,
nullptr);
105 getActiveProjectsFolder().addChild (c, 0,
nullptr);
111 ensureAllItemsHaveIDs (folders);
116 v.removeProperty (IDs::project,
nullptr);
118 for (
int i = 0; i < v.getNumChildren(); ++i)
119 stripProjectObjects (v.getChild(i));
122void ProjectManager::saveList()
126 auto foldersCopy = folders.createCopy();
127 stripProjectObjects (foldersCopy);
130 engine.getPropertyStorage().setXmlProperty (SettingID::projectList, *xml);
133static void findProjects (ProjectManager& pm,
const juce::ValueTree& folder,
136 if (
auto p = pm.getProjectFrom (folder))
140 findProjects (pm, folder.
getChild(i), list);
146 findProjects (*
this, folders, list);
153 findProjects (*
this, folder, list);
157juce::ValueTree ProjectManager::getActiveProjectsFolder() {
return folders.getChildWithName (IDs::ACTIVE); }
158juce::ValueTree ProjectManager::getLibraryProjectsFolder() {
return folders.getChildWithName (IDs::LIBRARY); }
161Project::Ptr ProjectManager::findProjectWithId (
const juce::ValueTree& folder,
int pid)
163 if (
auto p = getProjectFrom (folder))
164 if (p->getProjectID() == pid)
168 if (
auto p = findProjectWithId (folder.
getChild (i), pid))
174Project::Ptr ProjectManager::findProjectWithFile (
const juce::ValueTree& folder,
177 if (
auto p = getProjectFrom (folder))
178 if (p->getProjectFile() == f)
182 if (
auto p = findProjectWithFile (folder.
getChild (i), f))
189 bool createIfNotFound)
191 if (
auto p =
dynamic_cast<Project*
> (v.getProperty (IDs::project).getObject()))
194 if (createIfNotFound && v.hasType (IDs::PROJECT))
202 for (
auto* proj : openProjects)
203 if (proj->getProjectFile() == f)
207 p = createNewProject (f);
220Project::Ptr ProjectManager::getProject (
int pid)
224 for (
auto* p : openProjects)
225 if (p->getProjectID() == pid)
228 return findProjectWithId (folders, pid);
231Project::Ptr ProjectManager::getProject (
const juce::File& f)
235 for (
auto* p : openProjects)
236 if (p->getProjectFile() == f)
239 return findProjectWithFile (folders, f);
243Project::Ptr ProjectManager::addProjectToList (
const juce::File& f,
252 if (
auto existing = findProjectWithFile (folders, f))
255 auto p = createNewProject (f);
259 if (
auto existing = findProjectWithId (folders, p->getProjectID()))
262 auto v = createValueTree (IDs::PROJECT,
265 folderToAddTo.
addChild (v, index,
nullptr);
266 ensureAllItemsHaveIDs (folderToAddTo);
281static void removeProject (
const juce::ValueTree& folder,
const Project::Ptr& toRemove)
283 if (toRemove ==
nullptr)
286 if (
auto p = toRemove->projectManager.getProjectFrom (folder))
296 removeProject (folder.
getChild(i), toRemove);
299void ProjectManager::removeProjectFromList (
const juce::File& f)
303 if (
auto p = getProject (f))
305 if (! engine.getUIBehaviour().closeAllEditsBelongingToProject (*p))
309 removeProject (folders, p);
313 SelectionManager::deselectAllFromAllWindows();
315 engine.getUIBehaviour().updateAllProjectItemLists();
317 for (
auto edit : engine.getUIBehaviour().getAllOpenEdits())
319 edit->sendSourceFileUpdate();
321 addFileToRecentProjectsList (f);
325void ProjectManager::clearProjects()
329 folders.removeAllChildren (
nullptr);
330 openProjects.clear();
333static bool getValueTreeFor (
const juce::ValueTree& folder,
const Project* proj,
339 if (
auto p = proj->projectManager.getProjectFrom (folder, createIfNotFound))
349 if (getValueTreeFor (folder.
getChild(i), proj, result, createIfNotFound))
355juce::ValueTree ProjectManager::findFolderContaining (
const Project& p)
const
359 if (getValueTreeFor (folders, &p, result))
365juce::ValueTree ProjectManager::getFolderItemFor (
const Project& p)
const
369 if (getValueTreeFor (folders, &p, result))
375int ProjectManager::getFolderIndexFor (
const Project& p)
const
379 if (getValueTreeFor (folders, &p, result))
385void ProjectManager::updateProjectFile (Project& p,
const juce::File& f)
389 if (getValueTreeFor (folders, &p, result,
false))
395 if (
auto p = getProject (
id.getProjectID()))
396 return p->getProjectItemForID (
id);
413 if (
auto i = getProjectItem (
id))
414 return i->getSourceFile();
419void ProjectManager::saveAllProjects()
423 for (
auto p : getAllProjects (folders))
427Project::Ptr ProjectManager::createNewProject (
const juce::File& projectFile)
429 return new Project (engine, *
this, projectFile);
436 auto newProj = createNewProject (projectFile);
437 newProj->createNewProjectId();
444 newProj = addProjectToList (projectFile,
true, folderToAddTo);
446 if (newProj !=
nullptr)
448 if (newProj->getNumProjectItems() == 0)
450 if (
auto newEditProjectItem = newProj->createNewEdit())
452 newEditProjectItem->setDescription (
"(" +
TRANS(
"Created as the default edit for this project") +
")");
457 newProj->createDefaultFolders();
458 newProj->refreshFolderStructure();
460 engine.getUIBehaviour().selectProjectInFocusedWindow (newProj);
464 engine.getUIBehaviour().updateAllProjectItemLists();
470Project::Ptr ProjectManager::createNewProjectFromTemplate (
const juce::String& name,
const juce::File& lastPath,
475 if (! extractPath.createDirectory())
478 TracktionArchiveFile archive (engine, archiveFile);
481 bool aborted =
false;
483 archive.extractAllAsTask (extractPath,
false, filesCreated, aborted);
487 for (
auto& f : filesCreated)
489 if (isTracktionProjectFile (f))
493 auto p = createNewProject (f);
495 auto oldID = p->getProjectID();
496 p->createNewProjectId();
499 auto newID = p->getProjectID();
500 p->redirectIDsFromProject (oldID, newID);
505 auto newFileName = p->getProjectFile();
508 proj = addProjectToList (newFileName,
true, folder);
512 engine.getUIBehaviour().selectProjectInFocusedWindow (proj);
515 for (
int i = 0; i < proj->getNumProjectItems(); ++i)
517 auto mo = proj->getProjectItemAt (i);
521 ProjectItem::SetNameMode::forceRename);
524 proj->createDefaultFolders();
525 proj->refreshFolderStructure();
529 for (
int i = 0; i < proj->getNumProjectItems(); ++i)
530 jassert (proj->getProjectItemAt (i));
542Project::Ptr ProjectManager::createNewProjectInteractively (
const juce::String& name,
546 if (name.isNotEmpty())
548 auto& ui = engine.getUIBehaviour();
556 if (! ui.showOkCancelAlertBox (
TRANS(
"Create project"),
557 TRANS(
"This file already exists - do you want to open it?"),
565 if (! parentDir.exists())
571 auto r = ui.showYesNoCancelAlertBox (
572 TRANS(
"Create project"),
573 TRANS(
"The directory in which you're trying to create this project is not empty.")
575 +
TRANS(
"It's sensible to keep each project in its own directory, so "
576 "would you like to create a new subdirectory for it called \"XZZX\"?")
578 TRANS(
"Create a new subdirectory"),
579 TRANS(
"Use this directory anyway"),
593 ui.showWarningAlert (
TRANS(
"Create project"),
594 TRANS(
"The directory already existed and wasn't empty, so the project couldn't be created."));
599 if (! newDir.createDirectory())
601 ui.showWarningAlert (
TRANS(
"Create project"),
602 TRANS(
"Couldn't create the new directory")
603 +
":\n\n" + newDir.getFullPathName());
612 if (! projectFile.
create())
614 ui.showWarningAlert (
TRANS(
"Create project"),
615 TRANS(
"Couldn't write to the file")
621 return createNewProject (projectFile, folderToAddTo);
629 TracktionArchiveFile archive (engine, archiveFile);
631 if (! archive.isValidArchive())
633 engine.getUIBehaviour().showWarningMessage (
TRANS(
"This file wasn't a valid tracktion archive file"));
637 auto text =
TRANS(
"Choose a directory into which the archive \"XZZX\" should be unpacked")
638 .replace (
"XZZX", archiveFile.
getFileName()) +
"...";
640 auto lastPath = engine.getPropertyStorage().getDefaultLoadSaveDirectory (
"projectfile");
646 lastPath = archiveFile;
647 #if JUCE_MODAL_LOOPS_PERMITTED
650 if (chooser.browseForDirectory())
653 #if JUCE_MODAL_LOOPS_PERMITTED
654 auto destDir = chooser.getResult();
656 auto destDir = lastPath;
659 if (! destDir.createDirectory())
661 engine.getUIBehaviour().showWarningMessage (
TRANS(
"Couldn't create this target directory"));
667 if (! destDir.createDirectory())
669 engine.getUIBehaviour().showWarningMessage (
TRANS(
"Couldn't create this target directory"));
676 if (archive.extractAllAsTask (destDir,
false, newFiles, wasAborted))
680 for (
int i = newFiles.
size(); --i >= 0;)
681 if (! isTracktionProjectFile (newFiles.
getReference (i)))
686 engine.getUIBehaviour().showWarningMessage (
TRANS(
"This archive unpacked ok, but it didn't contain any project files!"));
690 for (
int i = newFiles.
size(); --i >= 0;)
691 if (
auto newProj = addProjectToList (newFiles.
getReference (i),
true, folder))
692 engine.getUIBehaviour().selectProjectInFocusedWindow (newProj);
695 for (
auto& f : newFiles)
697 if (
auto proj = getProject (f))
699 proj->createDefaultFolders();
700 proj->refreshFolderStructure();
709 engine.getUIBehaviour().showWarningMessage (
TRANS(
"Errors occurred whilst trying to unpack this archive"));
717 files.
addTokens (engine.getPropertyStorage().getProperty (SettingID::recentProjects).toString(),
";", {});
721 while (files.
size() > 8)
724 for (
int i = files.
size(); --i >= 0;)
734 if (
auto p = getProject (f))
735 if (findFolderContaining (*p).isValid())
741 for (
auto& f : files)
742 f =
juce::File (f).getFileNameWithoutExtension();
747void ProjectManager::addFileToRecentProjectsList (
const juce::File& f)
749 auto files = getRecentProjects (
false);
751 for (
auto& file : files)
752 if (
juce::File (file) == f)
755 if (
auto p = getProject (f))
756 if (findFolderContaining (*p).isValid())
761 engine.getPropertyStorage().setProperty (SettingID::recentProjects, files.
joinIntoString (
";"));
766 auto v = createValueTree (IDs::FOLDER,
770 ensureAllItemsHaveIDs (parent);
772 engine.getUIBehaviour().updateAllProjectItemLists();
bool isEmpty() const noexcept
int size() const noexcept
void remove(int indexToRemove)
ElementType & getReference(int index) noexcept
bool existsAsFile() const
const String & getFullPathName() const noexcept
String getFileName() const
File getChildFile(StringRef relativeOrAbsolutePath) const
String getFileNameWithoutExtension() const
File getNonexistentChildFile(const String &prefix, const String &suffix, bool putNumbersInBrackets=true) const
File getParentDirectory() const
static String createLegalFileName(const String &fileNameToFix)
Result createDirectory() const
bool isValid() const noexcept
ObjectClass * add(ObjectClass *newObject)
String joinIntoString(StringRef separatorString, int startIndex=0, int numberOfElements=-1) const
String & getReference(int index) noexcept
void removeEmptyStrings(bool removeWhitespaceStrings=true)
int size() const noexcept
void add(String stringToAdd)
int addTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
static String toHexString(IntegerType number)
String upToLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
static Time JUCE_CALLTYPE getCurrentTime() noexcept
void removeChild(const ValueTree &child, UndoManager *undoManager)
ValueTree getChild(int index) const
int getNumChildren() const noexcept
ValueTree & setProperty(const Identifier &name, const var &newValue, UndoManager *undoManager)
int indexOf(const ValueTree &child) const noexcept
void addChild(const ValueTree &child, int index, UndoManager *undoManager)
ValueTree getParent() const noexcept
static ValueTree fromXml(const XmlElement &xml)
The Tracktion Edit class!
ProjectItemID getProjectItemID() const noexcept
Returns the ProjectItemID of the Edit.
An ID representing one of the items in a Project.
int getProjectID() const
Returns the ID of the project this item belongs to.
#define TRANS(stringLiteral)
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.