The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

427 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "jucer_OpenDocumentManager.h"
  18. #include "jucer_FilePreviewComponent.h"
  19. #include "../Code Editor/jucer_SourceCodeEditor.h"
  20. #include "jucer_Application.h"
  21. //==============================================================================
  22. class UnknownDocument : public OpenDocumentManager::Document
  23. {
  24. public:
  25. UnknownDocument (Project* p, const File& f)
  26. : project (p), file (f)
  27. {
  28. reloadFromFile();
  29. }
  30. //==============================================================================
  31. struct Type : public OpenDocumentManager::DocumentType
  32. {
  33. bool canOpenFile (const File&) override { return true; }
  34. Document* openFile (Project* p, const File& f) override { return new UnknownDocument (p, f); }
  35. };
  36. //==============================================================================
  37. bool loadedOk() const override { return true; }
  38. bool isForFile (const File& f) const override { return file == f; }
  39. bool isForNode (const ValueTree&) const override { return false; }
  40. bool refersToProject (Project& p) const override { return project == &p; }
  41. Project* getProject() const override { return project; }
  42. bool needsSaving() const override { return false; }
  43. bool save() override { return true; }
  44. bool saveAs() override { return false; }
  45. bool hasFileBeenModifiedExternally() override { return fileModificationTime != file.getLastModificationTime(); }
  46. void reloadFromFile() override { fileModificationTime = file.getLastModificationTime(); }
  47. String getName() const override { return file.getFileName(); }
  48. File getFile() const override { return file; }
  49. Component* createEditor() override { return new ItemPreviewComponent (file); }
  50. Component* createViewer() override { return createEditor(); }
  51. void fileHasBeenRenamed (const File& newFile) override { file = newFile; }
  52. String getState() const override { return String::empty; }
  53. void restoreState (const String&) override {}
  54. String getType() const override
  55. {
  56. if (file.getFileExtension().isNotEmpty())
  57. return file.getFileExtension() + " file";
  58. jassertfalse;
  59. return "Unknown";
  60. }
  61. private:
  62. Project* const project;
  63. File file;
  64. Time fileModificationTime;
  65. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnknownDocument)
  66. };
  67. //==============================================================================
  68. OpenDocumentManager::DocumentType* createGUIDocumentType();
  69. OpenDocumentManager::OpenDocumentManager()
  70. {
  71. registerType (new UnknownDocument::Type());
  72. registerType (new SourceCodeDocument::Type());
  73. registerType (createGUIDocumentType());
  74. }
  75. OpenDocumentManager::~OpenDocumentManager()
  76. {
  77. }
  78. void OpenDocumentManager::clear()
  79. {
  80. documents.clear();
  81. types.clear();
  82. }
  83. //==============================================================================
  84. void OpenDocumentManager::registerType (DocumentType* type)
  85. {
  86. types.add (type);
  87. }
  88. //==============================================================================
  89. void OpenDocumentManager::addListener (DocumentCloseListener* listener)
  90. {
  91. listeners.addIfNotAlreadyThere (listener);
  92. }
  93. void OpenDocumentManager::removeListener (DocumentCloseListener* listener)
  94. {
  95. listeners.removeFirstMatchingValue (listener);
  96. }
  97. //==============================================================================
  98. bool OpenDocumentManager::canOpenFile (const File& file)
  99. {
  100. for (int i = types.size(); --i >= 0;)
  101. if (types.getUnchecked(i)->canOpenFile (file))
  102. return true;
  103. return false;
  104. }
  105. OpenDocumentManager::Document* OpenDocumentManager::openFile (Project* project, const File& file)
  106. {
  107. for (int i = documents.size(); --i >= 0;)
  108. if (documents.getUnchecked(i)->isForFile (file))
  109. return documents.getUnchecked(i);
  110. Document* d = nullptr;
  111. for (int i = types.size(); --i >= 0 && d == nullptr;)
  112. {
  113. if (types.getUnchecked(i)->canOpenFile (file))
  114. {
  115. d = types.getUnchecked(i)->openFile (project, file);
  116. jassert (d != nullptr);
  117. }
  118. }
  119. jassert (d != nullptr); // should always at least have been picked up by UnknownDocument
  120. documents.add (d);
  121. commandManager->commandStatusChanged();
  122. return d;
  123. }
  124. int OpenDocumentManager::getNumOpenDocuments() const
  125. {
  126. return documents.size();
  127. }
  128. OpenDocumentManager::Document* OpenDocumentManager::getOpenDocument (int index) const
  129. {
  130. return documents.getUnchecked (index);
  131. }
  132. FileBasedDocument::SaveResult OpenDocumentManager::saveIfNeededAndUserAgrees (OpenDocumentManager::Document* doc)
  133. {
  134. if (! doc->needsSaving())
  135. return FileBasedDocument::savedOk;
  136. const int r = AlertWindow::showYesNoCancelBox (AlertWindow::QuestionIcon,
  137. TRANS("Closing document..."),
  138. TRANS("Do you want to save the changes to \"")
  139. + doc->getName() + "\"?",
  140. TRANS("Save"),
  141. TRANS("Discard changes"),
  142. TRANS("Cancel"));
  143. if (r == 1) // save changes
  144. return doc->save() ? FileBasedDocument::savedOk
  145. : FileBasedDocument::failedToWriteToFile;
  146. if (r == 2) // discard changes
  147. return FileBasedDocument::savedOk;
  148. return FileBasedDocument::userCancelledSave;
  149. }
  150. bool OpenDocumentManager::closeDocument (int index, bool saveIfNeeded)
  151. {
  152. if (Document* doc = documents [index])
  153. {
  154. if (saveIfNeeded)
  155. if (saveIfNeededAndUserAgrees (doc) != FileBasedDocument::savedOk)
  156. return false;
  157. for (int i = listeners.size(); --i >= 0;)
  158. listeners.getUnchecked(i)->documentAboutToClose (doc);
  159. documents.remove (index);
  160. commandManager->commandStatusChanged();
  161. }
  162. return true;
  163. }
  164. bool OpenDocumentManager::closeDocument (Document* document, bool saveIfNeeded)
  165. {
  166. return closeDocument (documents.indexOf (document), saveIfNeeded);
  167. }
  168. void OpenDocumentManager::closeFile (const File& f, bool saveIfNeeded)
  169. {
  170. for (int i = documents.size(); --i >= 0;)
  171. {
  172. Document* d = documents.getUnchecked (i);
  173. if (d->isForFile (f))
  174. closeDocument (i, saveIfNeeded);
  175. }
  176. }
  177. bool OpenDocumentManager::closeAll (bool askUserToSave)
  178. {
  179. for (int i = getNumOpenDocuments(); --i >= 0;)
  180. if (! closeDocument (i, askUserToSave))
  181. return false;
  182. return true;
  183. }
  184. bool OpenDocumentManager::closeAllDocumentsUsingProject (Project& project, bool saveIfNeeded)
  185. {
  186. for (int i = documents.size(); --i >= 0;)
  187. {
  188. Document* d = documents.getUnchecked (i);
  189. if (d->refersToProject (project))
  190. {
  191. if (! closeDocument (i, saveIfNeeded))
  192. return false;
  193. }
  194. }
  195. return true;
  196. }
  197. bool OpenDocumentManager::anyFilesNeedSaving() const
  198. {
  199. for (int i = documents.size(); --i >= 0;)
  200. {
  201. Document* d = documents.getUnchecked (i);
  202. if (d->needsSaving())
  203. return true;
  204. }
  205. return false;
  206. }
  207. bool OpenDocumentManager::saveAll()
  208. {
  209. for (int i = documents.size(); --i >= 0;)
  210. {
  211. Document* d = documents.getUnchecked (i);
  212. if (! d->save())
  213. return false;
  214. }
  215. return true;
  216. }
  217. void OpenDocumentManager::reloadModifiedFiles()
  218. {
  219. for (int i = documents.size(); --i >= 0;)
  220. {
  221. Document* d = documents.getUnchecked (i);
  222. if (d->hasFileBeenModifiedExternally())
  223. d->reloadFromFile();
  224. }
  225. }
  226. void OpenDocumentManager::fileHasBeenRenamed (const File& oldFile, const File& newFile)
  227. {
  228. for (int i = documents.size(); --i >= 0;)
  229. {
  230. Document* d = documents.getUnchecked (i);
  231. if (d->isForFile (oldFile))
  232. d->fileHasBeenRenamed (newFile);
  233. }
  234. }
  235. //==============================================================================
  236. RecentDocumentList::RecentDocumentList()
  237. {
  238. IntrojucerApp::getApp().openDocumentManager.addListener (this);
  239. }
  240. RecentDocumentList::~RecentDocumentList()
  241. {
  242. IntrojucerApp::getApp().openDocumentManager.removeListener (this);
  243. }
  244. void RecentDocumentList::clear()
  245. {
  246. previousDocs.clear();
  247. nextDocs.clear();
  248. }
  249. void RecentDocumentList::newDocumentOpened (OpenDocumentManager::Document* document)
  250. {
  251. if (document != nullptr && document != getCurrentDocument())
  252. {
  253. nextDocs.clear();
  254. previousDocs.add (document);
  255. }
  256. }
  257. bool RecentDocumentList::canGoToPrevious() const
  258. {
  259. return previousDocs.size() > 1;
  260. }
  261. bool RecentDocumentList::canGoToNext() const
  262. {
  263. return nextDocs.size() > 0;
  264. }
  265. OpenDocumentManager::Document* RecentDocumentList::getPrevious()
  266. {
  267. if (! canGoToPrevious())
  268. return nullptr;
  269. nextDocs.insert (0, previousDocs.remove (previousDocs.size() - 1));
  270. return previousDocs.getLast();
  271. }
  272. OpenDocumentManager::Document* RecentDocumentList::getNext()
  273. {
  274. if (! canGoToNext())
  275. return nullptr;
  276. OpenDocumentManager::Document* d = nextDocs.remove (0);
  277. previousDocs.add (d);
  278. return d;
  279. }
  280. OpenDocumentManager::Document* RecentDocumentList::getClosestPreviousDocOtherThan (OpenDocumentManager::Document* oneToAvoid) const
  281. {
  282. for (int i = previousDocs.size(); --i >= 0;)
  283. if (previousDocs.getUnchecked(i) != oneToAvoid)
  284. return previousDocs.getUnchecked(i);
  285. return nullptr;
  286. }
  287. void RecentDocumentList::documentAboutToClose (OpenDocumentManager::Document* document)
  288. {
  289. previousDocs.removeAllInstancesOf (document);
  290. nextDocs.removeAllInstancesOf (document);
  291. jassert (! previousDocs.contains (document));
  292. jassert (! nextDocs.contains (document));
  293. }
  294. static void restoreDocList (Project& project, Array <OpenDocumentManager::Document*>& list, const XmlElement* xml)
  295. {
  296. if (xml != nullptr)
  297. {
  298. OpenDocumentManager& odm = IntrojucerApp::getApp().openDocumentManager;
  299. forEachXmlChildElementWithTagName (*xml, e, "DOC")
  300. {
  301. const File file (e->getStringAttribute ("file"));
  302. if (file.exists())
  303. {
  304. if (OpenDocumentManager::Document* doc = odm.openFile (&project, file))
  305. {
  306. doc->restoreState (e->getStringAttribute ("state"));
  307. list.add (doc);
  308. }
  309. }
  310. }
  311. }
  312. }
  313. void RecentDocumentList::restoreFromXML (Project& project, const XmlElement& xml)
  314. {
  315. clear();
  316. if (xml.hasTagName ("RECENT_DOCUMENTS"))
  317. {
  318. restoreDocList (project, previousDocs, xml.getChildByName ("PREVIOUS"));
  319. restoreDocList (project, nextDocs, xml.getChildByName ("NEXT"));
  320. }
  321. }
  322. static void saveDocList (const Array <OpenDocumentManager::Document*>& list, XmlElement& xml)
  323. {
  324. for (int i = 0; i < list.size(); ++i)
  325. {
  326. const OpenDocumentManager::Document& doc = *list.getUnchecked(i);
  327. XmlElement* e = xml.createNewChildElement ("DOC");
  328. e->setAttribute ("file", doc.getFile().getFullPathName());
  329. e->setAttribute ("state", doc.getState());
  330. }
  331. }
  332. XmlElement* RecentDocumentList::createXML() const
  333. {
  334. XmlElement* xml = new XmlElement ("RECENT_DOCUMENTS");
  335. saveDocList (previousDocs, *xml->createNewChildElement ("PREVIOUS"));
  336. saveDocList (nextDocs, *xml->createNewChildElement ("NEXT"));
  337. return xml;
  338. }