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.

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