|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2022 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- By using JUCE, you agree to the terms of both the JUCE 7 End-User License
- Agreement and JUCE Privacy Policy.
-
- End User License Agreement: www.juce.com/juce-7-licence
- Privacy Policy: www.juce.com/juce-privacy-policy
-
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- #include "../Application/jucer_Headers.h"
- #include "jucer_OpenDocumentManager.h"
- #include "../CodeEditor/jucer_ItemPreviewComponent.h"
- #include "../Application/jucer_Application.h"
-
-
- //==============================================================================
- class UnknownDocument final : public OpenDocumentManager::Document
- {
- public:
- UnknownDocument (Project* p, const File& f)
- : project (p), file (f)
- {
- handleReloadFromFile();
- }
-
- //==============================================================================
- struct Type final : public OpenDocumentManager::DocumentType
- {
- bool canOpenFile (const File&) override { return true; }
- Document* openFile (Project* p, const File& f) override { return new UnknownDocument (p, f); }
- };
-
- //==============================================================================
- bool loadedOk() const override { return true; }
- bool isForFile (const File& f) const override { return file == f; }
- bool isForNode (const ValueTree&) const override { return false; }
- bool refersToProject (Project& p) const override { return project == &p; }
- Project* getProject() const override { return project; }
- bool needsSaving() const override { return false; }
- bool saveSyncWithoutAsking() override { return true; }
- void saveAsync (std::function<void (bool)>) override {}
- void saveAsAsync (std::function<void (bool)>) override {}
- bool hasFileBeenModifiedExternally() override { return fileModificationTime != file.getLastModificationTime(); }
- void reloadFromFile() override { handleReloadFromFile(); }
- String getName() const override { return file.getFileName(); }
- File getFile() const override { return file; }
- std::unique_ptr<Component> createEditor() override { return std::make_unique<ItemPreviewComponent> (file); }
- std::unique_ptr<Component> createViewer() override { return createEditor(); }
- void fileHasBeenRenamed (const File& newFile) override { file = newFile; }
- String getState() const override { return {}; }
- void restoreState (const String&) override {}
-
- String getType() const override
- {
- if (file.getFileExtension().isNotEmpty())
- return file.getFileExtension() + " file";
-
- jassertfalse;
- return "Unknown";
- }
-
- private:
- void handleReloadFromFile() { fileModificationTime = file.getLastModificationTime(); }
-
- Project* const project;
- File file;
- Time fileModificationTime;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnknownDocument)
- };
-
-
- //==============================================================================
- OpenDocumentManager::DocumentType* createGUIDocumentType();
-
- OpenDocumentManager::OpenDocumentManager()
- {
- registerType (new UnknownDocument::Type());
- registerType (new SourceCodeDocument::Type());
- registerType (createGUIDocumentType());
- }
-
- OpenDocumentManager::~OpenDocumentManager()
- {
- }
-
- void OpenDocumentManager::clear()
- {
- documents.clear();
- types.clear();
- }
-
- //==============================================================================
- void OpenDocumentManager::registerType (DocumentType* type, int index)
- {
- types.insert (index, type);
- }
-
- //==============================================================================
- void OpenDocumentManager::addListener (DocumentCloseListener* listener)
- {
- listeners.addIfNotAlreadyThere (listener);
- }
-
- void OpenDocumentManager::removeListener (DocumentCloseListener* listener)
- {
- listeners.removeFirstMatchingValue (listener);
- }
-
- //==============================================================================
- bool OpenDocumentManager::canOpenFile (const File& file)
- {
- for (int i = types.size(); --i >= 0;)
- if (types.getUnchecked (i)->canOpenFile (file))
- return true;
-
- return false;
- }
-
- OpenDocumentManager::Document* OpenDocumentManager::openFile (Project* project, const File& file)
- {
- for (int i = documents.size(); --i >= 0;)
- if (documents.getUnchecked (i)->isForFile (file))
- return documents.getUnchecked (i);
-
- Document* d = nullptr;
-
- for (int i = types.size(); --i >= 0 && d == nullptr;)
- {
- if (types.getUnchecked (i)->canOpenFile (file))
- {
- d = types.getUnchecked (i)->openFile (project, file);
- jassert (d != nullptr);
- }
- }
-
- jassert (d != nullptr); // should always at least have been picked up by UnknownDocument
-
- documents.add (d);
- ProjucerApplication::getCommandManager().commandStatusChanged();
- return d;
- }
-
- int OpenDocumentManager::getNumOpenDocuments() const
- {
- return documents.size();
- }
-
- OpenDocumentManager::Document* OpenDocumentManager::getOpenDocument (int index) const
- {
- return documents.getUnchecked (index);
- }
-
- void OpenDocumentManager::saveIfNeededAndUserAgrees (OpenDocumentManager::Document* doc,
- std::function<void (FileBasedDocument::SaveResult)> callback)
- {
- if (! doc->needsSaving())
- {
- NullCheckedInvocation::invoke (callback, FileBasedDocument::savedOk);
- return;
- }
-
- auto options = MessageBoxOptions::makeOptionsYesNoCancel (MessageBoxIconType::QuestionIcon,
- TRANS ("Closing document..."),
- TRANS ("Do you want to save the changes to \"")
- + doc->getName() + "\"?",
- TRANS ("Save"),
- TRANS ("Discard changes"),
- TRANS ("Cancel"));
- messageBox = AlertWindow::showScopedAsync (options, [parent = WeakReference<OpenDocumentManager> { this }, doc, callback] (int r)
- {
- if (parent == nullptr)
- return;
-
- if (r == 1)
- {
- doc->saveAsync ([parent, callback] (bool hasSaved)
- {
- if (parent == nullptr)
- return;
-
- NullCheckedInvocation::invoke (callback, hasSaved ? FileBasedDocument::savedOk : FileBasedDocument::failedToWriteToFile);
- });
- return;
- }
-
- NullCheckedInvocation::invoke (callback, r == 2 ? FileBasedDocument::savedOk : FileBasedDocument::userCancelledSave);
- });
- }
-
- bool OpenDocumentManager::closeDocumentWithoutSaving (Document* doc)
- {
- if (documents.contains (doc))
- {
- bool canClose = true;
-
- for (int i = listeners.size(); --i >= 0;)
- if (auto* l = listeners[i])
- if (! l->documentAboutToClose (doc))
- canClose = false;
-
- if (! canClose)
- return false;
-
- documents.removeObject (doc);
- ProjucerApplication::getCommandManager().commandStatusChanged();
- }
-
- return true;
- }
-
- void OpenDocumentManager::closeDocumentAsync (Document* doc, SaveIfNeeded saveIfNeeded, std::function<void (bool)> callback)
- {
- if (! documents.contains (doc))
- {
- NullCheckedInvocation::invoke (callback, true);
- return;
- }
-
- if (saveIfNeeded == SaveIfNeeded::yes)
- {
- saveIfNeededAndUserAgrees (doc,
- [parent = WeakReference<OpenDocumentManager> { this }, doc, callback] (FileBasedDocument::SaveResult result)
- {
- if (parent == nullptr)
- return;
-
- if (result != FileBasedDocument::savedOk)
- {
- NullCheckedInvocation::invoke (callback, false);
- return;
- }
-
- auto closed = parent->closeDocumentWithoutSaving (doc);
-
- NullCheckedInvocation::invoke (callback, closed);
- });
-
- return;
- }
-
- auto closed = closeDocumentWithoutSaving (doc);
-
- NullCheckedInvocation::invoke (callback, closed);
- }
-
- void OpenDocumentManager::closeFileWithoutSaving (const File& f)
- {
- for (int i = documents.size(); --i >= 0;)
- if (auto* d = documents[i])
- if (d->isForFile (f))
- closeDocumentWithoutSaving (d);
- }
-
- static void closeLastAsyncRecusrsive (WeakReference<OpenDocumentManager> parent,
- OpenDocumentManager::SaveIfNeeded askUserToSave,
- std::function<void (bool)> callback)
- {
- auto lastIndex = parent->getNumOpenDocuments() - 1;
-
- if (lastIndex < 0)
- {
- NullCheckedInvocation::invoke (callback, true);
- return;
- }
-
- parent->closeDocumentAsync (parent->getOpenDocument (lastIndex),
- askUserToSave,
- [parent, askUserToSave, callback] (bool closedSuccessfully)
- {
- if (parent == nullptr)
- return;
-
- if (! closedSuccessfully)
- {
- NullCheckedInvocation::invoke (callback, false);
- return;
- }
-
- closeLastAsyncRecusrsive (parent, askUserToSave, std::move (callback));
- });
- }
-
- void OpenDocumentManager::closeAllAsync (SaveIfNeeded askUserToSave, std::function<void (bool)> callback)
- {
- closeLastAsyncRecusrsive (this, askUserToSave, std::move (callback));
- }
-
- void OpenDocumentManager::closeLastDocumentUsingProjectRecursive (WeakReference<OpenDocumentManager> parent,
- Project* project,
- SaveIfNeeded askUserToSave,
- std::function<void (bool)> callback)
- {
- for (int i = documents.size(); --i >= 0;)
- {
- if (auto* d = documents[i])
- {
- if (d->getProject() == project)
- {
- closeDocumentAsync (d, askUserToSave, [parent, project, askUserToSave, callback] (bool closedSuccessfully)
- {
- if (parent == nullptr)
- return;
-
- if (! closedSuccessfully)
- {
- NullCheckedInvocation::invoke (callback, false);
- return;
- }
-
- parent->closeLastDocumentUsingProjectRecursive (parent, project, askUserToSave, std::move (callback));
- });
-
- return;
- }
- }
- }
-
- NullCheckedInvocation::invoke (callback, true);
- }
-
- void OpenDocumentManager::closeAllDocumentsUsingProjectAsync (Project& project, SaveIfNeeded askUserToSave, std::function<void (bool)> callback)
- {
- WeakReference<OpenDocumentManager> parent { this };
- closeLastDocumentUsingProjectRecursive (parent, &project, askUserToSave, std::move (callback));
- }
-
- void OpenDocumentManager::closeAllDocumentsUsingProjectWithoutSaving (Project& project)
- {
- for (int i = documents.size(); --i >= 0;)
- if (Document* d = documents[i])
- if (d->refersToProject (project))
- closeDocumentWithoutSaving (d);
- }
-
- bool OpenDocumentManager::anyFilesNeedSaving() const
- {
- for (int i = documents.size(); --i >= 0;)
- if (documents.getUnchecked (i)->needsSaving())
- return true;
-
- return false;
- }
-
- void OpenDocumentManager::saveAllSyncWithoutAsking()
- {
- for (int i = documents.size(); --i >= 0;)
- {
- if (documents.getUnchecked (i)->saveSyncWithoutAsking())
- ProjucerApplication::getCommandManager().commandStatusChanged();
- }
- }
-
- void OpenDocumentManager::reloadModifiedFiles()
- {
- for (int i = documents.size(); --i >= 0;)
- {
- Document* d = documents.getUnchecked (i);
-
- if (d->hasFileBeenModifiedExternally())
- d->reloadFromFile();
- }
- }
-
- void OpenDocumentManager::fileHasBeenRenamed (const File& oldFile, const File& newFile)
- {
- for (int i = documents.size(); --i >= 0;)
- {
- Document* d = documents.getUnchecked (i);
-
- if (d->isForFile (oldFile))
- d->fileHasBeenRenamed (newFile);
- }
- }
-
-
- //==============================================================================
- RecentDocumentList::RecentDocumentList()
- {
- ProjucerApplication::getApp().openDocumentManager.addListener (this);
- }
-
- RecentDocumentList::~RecentDocumentList()
- {
- ProjucerApplication::getApp().openDocumentManager.removeListener (this);
- }
-
- void RecentDocumentList::clear()
- {
- previousDocs.clear();
- nextDocs.clear();
- }
-
- void RecentDocumentList::newDocumentOpened (OpenDocumentManager::Document* document)
- {
- if (document != nullptr && document != getCurrentDocument())
- {
- nextDocs.clear();
- previousDocs.add (document);
- }
- }
-
- bool RecentDocumentList::canGoToPrevious() const
- {
- return previousDocs.size() > 1;
- }
-
- bool RecentDocumentList::canGoToNext() const
- {
- return nextDocs.size() > 0;
- }
-
- OpenDocumentManager::Document* RecentDocumentList::getPrevious()
- {
- if (! canGoToPrevious())
- return nullptr;
-
- nextDocs.insert (0, previousDocs.removeAndReturn (previousDocs.size() - 1));
- return previousDocs.getLast();
- }
-
- OpenDocumentManager::Document* RecentDocumentList::getNext()
- {
- if (! canGoToNext())
- return nullptr;
-
- OpenDocumentManager::Document* d = nextDocs.removeAndReturn (0);
- previousDocs.add (d);
- return d;
- }
-
- bool RecentDocumentList::contains (const File& f) const
- {
- for (int i = previousDocs.size(); --i >= 0;)
- if (previousDocs.getUnchecked (i)->getFile() == f)
- return true;
-
- return false;
- }
-
- OpenDocumentManager::Document* RecentDocumentList::getClosestPreviousDocOtherThan (OpenDocumentManager::Document* oneToAvoid) const
- {
- for (int i = previousDocs.size(); --i >= 0;)
- if (previousDocs.getUnchecked (i) != oneToAvoid)
- return previousDocs.getUnchecked (i);
-
- return nullptr;
- }
-
- bool RecentDocumentList::documentAboutToClose (OpenDocumentManager::Document* document)
- {
- previousDocs.removeAllInstancesOf (document);
- nextDocs.removeAllInstancesOf (document);
-
- jassert (! previousDocs.contains (document));
- jassert (! nextDocs.contains (document));
-
- return true;
- }
-
- static void restoreDocList (Project& project, Array <OpenDocumentManager::Document*>& list, const XmlElement* xml)
- {
- if (xml != nullptr)
- {
- OpenDocumentManager& odm = ProjucerApplication::getApp().openDocumentManager;
-
- for (auto* e : xml->getChildWithTagNameIterator ("DOC"))
- {
- const File file (e->getStringAttribute ("file"));
-
- if (file.exists())
- {
- if (OpenDocumentManager::Document* doc = odm.openFile (&project, file))
- {
- doc->restoreState (e->getStringAttribute ("state"));
-
- list.add (doc);
- }
- }
- }
- }
- }
-
- void RecentDocumentList::restoreFromXML (Project& project, const XmlElement& xml)
- {
- clear();
-
- if (xml.hasTagName ("RECENT_DOCUMENTS"))
- {
- restoreDocList (project, previousDocs, xml.getChildByName ("PREVIOUS"));
- restoreDocList (project, nextDocs, xml.getChildByName ("NEXT"));
- }
- }
-
- static void saveDocList (const Array <OpenDocumentManager::Document*>& list, XmlElement& xml)
- {
- for (int i = 0; i < list.size(); ++i)
- {
- const OpenDocumentManager::Document& doc = *list.getUnchecked (i);
-
- XmlElement* e = xml.createNewChildElement ("DOC");
-
- e->setAttribute ("file", doc.getFile().getFullPathName());
- e->setAttribute ("state", doc.getState());
- }
- }
-
- std::unique_ptr<XmlElement> RecentDocumentList::createXML() const
- {
- auto xml = std::make_unique<XmlElement> ("RECENT_DOCUMENTS");
-
- saveDocList (previousDocs, *xml->createNewChildElement ("PREVIOUS"));
- saveDocList (nextDocs, *xml->createNewChildElement ("NEXT"));
-
- return xml;
- }
|