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.

432 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* p, const File& f) { return new UnknownDocument (p, f); }
  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 saveAs() { return false; }
  46. bool hasFileBeenModifiedExternally() { return fileModificationTime != file.getLastModificationTime(); }
  47. void reloadFromFile() { fileModificationTime = file.getLastModificationTime(); }
  48. String getName() const { return file.getFileName(); }
  49. File getFile() const { return file; }
  50. Component* createEditor() { return new ItemPreviewComponent (file); }
  51. Component* createViewer() { return createEditor(); }
  52. void fileHasBeenRenamed (const File& newFile) { file = newFile; }
  53. String getState() const { return String::empty; }
  54. void restoreState (const String&) {}
  55. String getType() const
  56. {
  57. if (file.getFileExtension().isNotEmpty())
  58. return file.getFileExtension() + " file";
  59. jassertfalse
  60. return "Unknown";
  61. }
  62. private:
  63. Project* const project;
  64. File file;
  65. Time fileModificationTime;
  66. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnknownDocument)
  67. };
  68. //==============================================================================
  69. OpenDocumentManager::OpenDocumentManager()
  70. {
  71. registerType (new UnknownDocument::Type());
  72. registerType (new SourceCodeDocument::Type());
  73. }
  74. OpenDocumentManager::~OpenDocumentManager()
  75. {
  76. }
  77. void OpenDocumentManager::clear()
  78. {
  79. documents.clear();
  80. types.clear();
  81. }
  82. //==============================================================================
  83. void OpenDocumentManager::registerType (DocumentType* type)
  84. {
  85. types.add (type);
  86. }
  87. //==============================================================================
  88. void OpenDocumentManager::addListener (DocumentCloseListener* listener)
  89. {
  90. listeners.addIfNotAlreadyThere (listener);
  91. }
  92. void OpenDocumentManager::removeListener (DocumentCloseListener* listener)
  93. {
  94. listeners.removeFirstMatchingValue (listener);
  95. }
  96. //==============================================================================
  97. bool OpenDocumentManager::canOpenFile (const File& file)
  98. {
  99. for (int i = types.size(); --i >= 0;)
  100. if (types.getUnchecked(i)->canOpenFile (file))
  101. return true;
  102. return false;
  103. }
  104. OpenDocumentManager::Document* OpenDocumentManager::openFile (Project* project, const File& file)
  105. {
  106. for (int i = documents.size(); --i >= 0;)
  107. if (documents.getUnchecked(i)->isForFile (file))
  108. return documents.getUnchecked(i);
  109. Document* d = nullptr;
  110. for (int i = types.size(); --i >= 0 && d == nullptr;)
  111. {
  112. if (types.getUnchecked(i)->canOpenFile (file))
  113. {
  114. d = types.getUnchecked(i)->openFile (project, file);
  115. jassert (d != nullptr);
  116. }
  117. }
  118. jassert (d != nullptr); // should always at least have been picked up by UnknownDocument
  119. documents.add (d);
  120. commandManager->commandStatusChanged();
  121. return d;
  122. }
  123. int OpenDocumentManager::getNumOpenDocuments() const
  124. {
  125. return documents.size();
  126. }
  127. OpenDocumentManager::Document* OpenDocumentManager::getOpenDocument (int index) const
  128. {
  129. return documents.getUnchecked (index);
  130. }
  131. FileBasedDocument::SaveResult OpenDocumentManager::saveIfNeededAndUserAgrees (OpenDocumentManager::Document* doc)
  132. {
  133. if (! doc->needsSaving())
  134. return FileBasedDocument::savedOk;
  135. const int r = AlertWindow::showYesNoCancelBox (AlertWindow::QuestionIcon,
  136. TRANS("Closing document..."),
  137. TRANS("Do you want to save the changes to \"")
  138. + doc->getName() + "\"?",
  139. TRANS("save"),
  140. TRANS("discard changes"),
  141. TRANS("cancel"));
  142. if (r == 1)
  143. {
  144. // save changes
  145. return doc->save() ? FileBasedDocument::savedOk
  146. : FileBasedDocument::failedToWriteToFile;
  147. }
  148. else if (r == 2)
  149. {
  150. // discard changes
  151. return FileBasedDocument::savedOk;
  152. }
  153. return FileBasedDocument::userCancelledSave;
  154. }
  155. bool OpenDocumentManager::closeDocument (int index, bool saveIfNeeded)
  156. {
  157. if (Document* doc = documents [index])
  158. {
  159. if (saveIfNeeded)
  160. {
  161. if (saveIfNeededAndUserAgrees (doc) != FileBasedDocument::savedOk)
  162. return false;
  163. }
  164. for (int i = listeners.size(); --i >= 0;)
  165. listeners.getUnchecked(i)->documentAboutToClose (doc);
  166. documents.remove (index);
  167. commandManager->commandStatusChanged();
  168. }
  169. return true;
  170. }
  171. bool OpenDocumentManager::closeDocument (Document* document, bool saveIfNeeded)
  172. {
  173. return closeDocument (documents.indexOf (document), saveIfNeeded);
  174. }
  175. void OpenDocumentManager::closeFile (const File& f, bool saveIfNeeded)
  176. {
  177. for (int i = documents.size(); --i >= 0;)
  178. {
  179. Document* d = documents.getUnchecked (i);
  180. if (d->isForFile (f))
  181. closeDocument (i, saveIfNeeded);
  182. }
  183. }
  184. bool OpenDocumentManager::closeAll (bool askUserToSave)
  185. {
  186. for (int i = getNumOpenDocuments(); --i >= 0;)
  187. if (! closeDocument (i, askUserToSave))
  188. return false;
  189. return true;
  190. }
  191. bool OpenDocumentManager::closeAllDocumentsUsingProject (Project& project, bool saveIfNeeded)
  192. {
  193. for (int i = documents.size(); --i >= 0;)
  194. {
  195. Document* d = documents.getUnchecked (i);
  196. if (d->refersToProject (project))
  197. {
  198. if (! closeDocument (i, saveIfNeeded))
  199. return false;
  200. }
  201. }
  202. return true;
  203. }
  204. bool OpenDocumentManager::anyFilesNeedSaving() const
  205. {
  206. for (int i = documents.size(); --i >= 0;)
  207. {
  208. Document* d = documents.getUnchecked (i);
  209. if (d->needsSaving())
  210. return true;
  211. }
  212. return false;
  213. }
  214. bool OpenDocumentManager::saveAll()
  215. {
  216. for (int i = documents.size(); --i >= 0;)
  217. {
  218. Document* d = documents.getUnchecked (i);
  219. if (! d->save())
  220. return false;
  221. }
  222. return true;
  223. }
  224. void OpenDocumentManager::reloadModifiedFiles()
  225. {
  226. for (int i = documents.size(); --i >= 0;)
  227. {
  228. Document* d = documents.getUnchecked (i);
  229. if (d->hasFileBeenModifiedExternally())
  230. d->reloadFromFile();
  231. }
  232. }
  233. void OpenDocumentManager::fileHasBeenRenamed (const File& oldFile, const File& newFile)
  234. {
  235. for (int i = documents.size(); --i >= 0;)
  236. {
  237. Document* d = documents.getUnchecked (i);
  238. if (d->isForFile (oldFile))
  239. d->fileHasBeenRenamed (newFile);
  240. }
  241. }
  242. //==============================================================================
  243. RecentDocumentList::RecentDocumentList()
  244. {
  245. IntrojucerApp::getApp().openDocumentManager.addListener (this);
  246. }
  247. RecentDocumentList::~RecentDocumentList()
  248. {
  249. IntrojucerApp::getApp().openDocumentManager.removeListener (this);
  250. }
  251. void RecentDocumentList::clear()
  252. {
  253. previousDocs.clear();
  254. nextDocs.clear();
  255. }
  256. void RecentDocumentList::newDocumentOpened (OpenDocumentManager::Document* document)
  257. {
  258. if (document != nullptr && document != getCurrentDocument())
  259. {
  260. nextDocs.clear();
  261. previousDocs.add (document);
  262. }
  263. }
  264. bool RecentDocumentList::canGoToPrevious() const
  265. {
  266. return previousDocs.size() > 1;
  267. }
  268. bool RecentDocumentList::canGoToNext() const
  269. {
  270. return nextDocs.size() > 0;
  271. }
  272. OpenDocumentManager::Document* RecentDocumentList::getPrevious()
  273. {
  274. if (! canGoToPrevious())
  275. return nullptr;
  276. nextDocs.insert (0, previousDocs.remove (previousDocs.size() - 1));
  277. return previousDocs.getLast();
  278. }
  279. OpenDocumentManager::Document* RecentDocumentList::getNext()
  280. {
  281. if (! canGoToNext())
  282. return nullptr;
  283. OpenDocumentManager::Document* d = nextDocs.remove (0);
  284. previousDocs.add (d);
  285. return d;
  286. }
  287. OpenDocumentManager::Document* RecentDocumentList::getClosestPreviousDocOtherThan (OpenDocumentManager::Document* oneToAvoid) const
  288. {
  289. for (int i = previousDocs.size(); --i >= 0;)
  290. if (previousDocs.getUnchecked(i) != oneToAvoid)
  291. return previousDocs.getUnchecked(i);
  292. return nullptr;
  293. }
  294. void RecentDocumentList::documentAboutToClose (OpenDocumentManager::Document* document)
  295. {
  296. previousDocs.removeAllInstancesOf (document);
  297. nextDocs.removeAllInstancesOf (document);
  298. jassert (! previousDocs.contains (document));
  299. jassert (! nextDocs.contains (document));
  300. }
  301. static void restoreDocList (Project& project, Array <OpenDocumentManager::Document*>& list, const XmlElement* xml)
  302. {
  303. if (xml != nullptr)
  304. {
  305. OpenDocumentManager& odm = IntrojucerApp::getApp().openDocumentManager;
  306. forEachXmlChildElementWithTagName (*xml, e, "DOC")
  307. {
  308. const File file (e->getStringAttribute ("file"));
  309. if (file.exists())
  310. {
  311. if (OpenDocumentManager::Document* doc = odm.openFile (&project, file))
  312. {
  313. doc->restoreState (e->getStringAttribute ("state"));
  314. list.add (doc);
  315. }
  316. }
  317. }
  318. }
  319. }
  320. void RecentDocumentList::restoreFromXML (Project& project, const XmlElement& xml)
  321. {
  322. clear();
  323. if (xml.hasTagName ("RECENT_DOCUMENTS"))
  324. {
  325. restoreDocList (project, previousDocs, xml.getChildByName ("PREVIOUS"));
  326. restoreDocList (project, nextDocs, xml.getChildByName ("NEXT"));
  327. }
  328. }
  329. static void saveDocList (const Array <OpenDocumentManager::Document*>& list, XmlElement& xml)
  330. {
  331. for (int i = 0; i < list.size(); ++i)
  332. {
  333. const OpenDocumentManager::Document& doc = *list.getUnchecked(i);
  334. XmlElement* e = xml.createNewChildElement ("DOC");
  335. e->setAttribute ("file", doc.getFile().getFullPathName());
  336. e->setAttribute ("state", doc.getState());
  337. }
  338. }
  339. XmlElement* RecentDocumentList::createXML() const
  340. {
  341. XmlElement* xml = new XmlElement ("RECENT_DOCUMENTS");
  342. saveDocList (previousDocs, *xml->createNewChildElement ("PREVIOUS"));
  343. saveDocList (nextDocs, *xml->createNewChildElement ("NEXT"));
  344. return xml;
  345. }