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.

431 lines
13KB

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