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.

435 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* project_, const File& file_)
  27. : project (project_), file (file_)
  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& file_) const { return file == file_; }
  40. bool isForNode (const ValueTree& node_) 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. Document* doc = documents [index];
  157. if (doc != nullptr)
  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. OpenDocumentManager::Document* doc = odm.openFile (&project, file);
  312. if (doc != nullptr)
  313. {
  314. doc->restoreState (e->getStringAttribute ("state"));
  315. list.add (doc);
  316. }
  317. }
  318. }
  319. }
  320. }
  321. void RecentDocumentList::restoreFromXML (Project& project, const XmlElement& xml)
  322. {
  323. clear();
  324. if (xml.hasTagName ("RECENT_DOCUMENTS"))
  325. {
  326. restoreDocList (project, previousDocs, xml.getChildByName ("PREVIOUS"));
  327. restoreDocList (project, nextDocs, xml.getChildByName ("NEXT"));
  328. }
  329. }
  330. static void saveDocList (const Array <OpenDocumentManager::Document*>& list, XmlElement& xml)
  331. {
  332. for (int i = 0; i < list.size(); ++i)
  333. {
  334. const OpenDocumentManager::Document& doc = *list.getUnchecked(i);
  335. XmlElement* e = xml.createNewChildElement ("DOC");
  336. e->setAttribute ("file", doc.getFile().getFullPathName());
  337. e->setAttribute ("state", doc.getState());
  338. }
  339. }
  340. XmlElement* RecentDocumentList::createXML() const
  341. {
  342. XmlElement* xml = new XmlElement ("RECENT_DOCUMENTS");
  343. saveDocList (previousDocs, *xml->createNewChildElement ("PREVIOUS"));
  344. saveDocList (nextDocs, *xml->createNewChildElement ("NEXT"));
  345. return xml;
  346. }