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.

388 lines
12KB

  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. //==============================================================================
  22. class SourceCodeDocument : public OpenDocumentManager::Document
  23. {
  24. public:
  25. //==============================================================================
  26. SourceCodeDocument (const File& file_)
  27. : modDetector (file_)
  28. {
  29. codeDoc = new CodeDocument();
  30. reloadFromFile();
  31. }
  32. ~SourceCodeDocument()
  33. {
  34. }
  35. //==============================================================================
  36. class Type : public OpenDocumentManager::DocumentType
  37. {
  38. public:
  39. Type() {}
  40. ~Type() {}
  41. bool canOpenFile (const File& file) { return SourceCodeEditor::isTextFile (file); }
  42. Document* openFile (Project*, const File& file) { return new SourceCodeDocument (file); }
  43. };
  44. //==============================================================================
  45. bool loadedOk() const { return true; }
  46. bool isForFile (const File& file) const { return modDetector.getFile() == file; }
  47. bool isForNode (const ValueTree& node) const { return false; }
  48. bool refersToProject (Project& project) const { return false; }
  49. String getName() const { return modDetector.getFile().getFileName(); }
  50. String getType() const { return modDetector.getFile().getFileExtension() + " file"; }
  51. bool needsSaving() const { return codeDoc != nullptr && codeDoc->hasChangedSinceSavePoint(); }
  52. bool hasFileBeenModifiedExternally() { return modDetector.hasBeenModified(); }
  53. void fileHasBeenRenamed (const File& newFile) { modDetector.fileHasBeenRenamed (newFile); }
  54. void reloadFromFile()
  55. {
  56. modDetector.updateHash();
  57. ScopedPointer <InputStream> in (modDetector.getFile().createInputStream());
  58. if (in != nullptr)
  59. codeDoc->loadFromStream (*in);
  60. }
  61. bool save()
  62. {
  63. TemporaryFile temp (modDetector.getFile());
  64. ScopedPointer <FileOutputStream> out (temp.getFile().createOutputStream());
  65. if (out == nullptr || ! codeDoc->writeToStream (*out))
  66. return false;
  67. out = nullptr;
  68. if (! temp.overwriteTargetFileWithTemporary())
  69. return false;
  70. modDetector.updateHash();
  71. return true;
  72. }
  73. Component* createEditor()
  74. {
  75. CodeTokeniser* tokeniser = nullptr;
  76. if (SourceCodeEditor::isCppFile (modDetector.getFile()))
  77. tokeniser = &cppTokeniser;
  78. return new SourceCodeEditor (this, *codeDoc, tokeniser);
  79. }
  80. Component* createViewer() { return createEditor(); }
  81. private:
  82. FileModificationDetector modDetector;
  83. ScopedPointer <CodeDocument> codeDoc;
  84. CPlusPlusCodeTokeniser cppTokeniser;
  85. };
  86. //==============================================================================
  87. class UnknownDocument : public OpenDocumentManager::Document
  88. {
  89. public:
  90. UnknownDocument (Project* project_, const File& file_)
  91. : project (project_), file (file_)
  92. {
  93. reloadFromFile();
  94. }
  95. ~UnknownDocument() {}
  96. //==============================================================================
  97. class Type : public OpenDocumentManager::DocumentType
  98. {
  99. public:
  100. Type() {}
  101. ~Type() {}
  102. bool canOpenFile (const File& file) { return true; }
  103. Document* openFile (Project* project, const File& file) { return new UnknownDocument (project, file); }
  104. };
  105. //==============================================================================
  106. bool loadedOk() const { return true; }
  107. bool isForFile (const File& file_) const { return file == file_; }
  108. bool isForNode (const ValueTree& node_) const { return false; }
  109. bool refersToProject (Project& p) const { return project == &p; }
  110. bool needsSaving() const { return false; }
  111. bool save() { return true; }
  112. bool hasFileBeenModifiedExternally() { return fileModificationTime != file.getLastModificationTime(); }
  113. void reloadFromFile() { fileModificationTime = file.getLastModificationTime(); }
  114. String getName() const { return file.getFileName(); }
  115. Component* createEditor() { return new ItemPreviewComponent (file); }
  116. Component* createViewer() { return createEditor(); }
  117. void fileHasBeenRenamed (const File& newFile) { file = newFile; }
  118. String getType() const
  119. {
  120. if (file.getFileExtension().isNotEmpty())
  121. return file.getFileExtension() + " file";
  122. jassertfalse
  123. return "Unknown";
  124. }
  125. private:
  126. Project* const project;
  127. File file;
  128. Time fileModificationTime;
  129. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnknownDocument);
  130. };
  131. //==============================================================================
  132. OpenDocumentManager::OpenDocumentManager()
  133. {
  134. registerType (new UnknownDocument::Type());
  135. registerType (new SourceCodeDocument::Type());
  136. }
  137. OpenDocumentManager::~OpenDocumentManager()
  138. {
  139. clearSingletonInstance();
  140. }
  141. juce_ImplementSingleton_SingleThreaded (OpenDocumentManager);
  142. //==============================================================================
  143. void OpenDocumentManager::registerType (DocumentType* type)
  144. {
  145. types.add (type);
  146. }
  147. //==============================================================================
  148. void OpenDocumentManager::addListener (DocumentCloseListener* listener)
  149. {
  150. listeners.addIfNotAlreadyThere (listener);
  151. }
  152. void OpenDocumentManager::removeListener (DocumentCloseListener* listener)
  153. {
  154. listeners.removeValue (listener);
  155. }
  156. //==============================================================================
  157. bool OpenDocumentManager::canOpenFile (const File& file)
  158. {
  159. for (int i = types.size(); --i >= 0;)
  160. if (types.getUnchecked(i)->canOpenFile (file))
  161. return true;
  162. return false;
  163. }
  164. OpenDocumentManager::Document* OpenDocumentManager::getDocumentForFile (Project* project, const File& file)
  165. {
  166. for (int i = documents.size(); --i >= 0;)
  167. if (documents.getUnchecked(i)->isForFile (file))
  168. return documents.getUnchecked(i);
  169. Document* d = nullptr;
  170. for (int i = types.size(); --i >= 0 && d == nullptr;)
  171. {
  172. if (types.getUnchecked(i)->canOpenFile (file))
  173. {
  174. d = types.getUnchecked(i)->openFile (project, file);
  175. jassert (d != nullptr);
  176. }
  177. }
  178. jassert (d != nullptr);
  179. if (d != nullptr)
  180. documents.add (d);
  181. commandManager->commandStatusChanged();
  182. return d;
  183. }
  184. int OpenDocumentManager::getNumOpenDocuments() const
  185. {
  186. return documents.size();
  187. }
  188. OpenDocumentManager::Document* OpenDocumentManager::getOpenDocument (int index) const
  189. {
  190. return documents.getUnchecked (index);
  191. }
  192. void OpenDocumentManager::moveDocumentToTopOfStack (Document* doc)
  193. {
  194. for (int i = documents.size(); --i >= 0;)
  195. {
  196. if (doc == documents.getUnchecked(i))
  197. {
  198. documents.move (i, 0);
  199. commandManager->commandStatusChanged();
  200. break;
  201. }
  202. }
  203. }
  204. FileBasedDocument::SaveResult OpenDocumentManager::saveIfNeededAndUserAgrees (OpenDocumentManager::Document* doc)
  205. {
  206. if (! doc->needsSaving())
  207. return FileBasedDocument::savedOk;
  208. const int r = AlertWindow::showYesNoCancelBox (AlertWindow::QuestionIcon,
  209. TRANS("Closing document..."),
  210. TRANS("Do you want to save the changes to \"")
  211. + doc->getName() + "\"?",
  212. TRANS("save"),
  213. TRANS("discard changes"),
  214. TRANS("cancel"));
  215. if (r == 1)
  216. {
  217. // save changes
  218. return doc->save() ? FileBasedDocument::savedOk
  219. : FileBasedDocument::failedToWriteToFile;
  220. }
  221. else if (r == 2)
  222. {
  223. // discard changes
  224. return FileBasedDocument::savedOk;
  225. }
  226. return FileBasedDocument::userCancelledSave;
  227. }
  228. bool OpenDocumentManager::closeDocument (int index, bool saveIfNeeded)
  229. {
  230. Document* doc = documents [index];
  231. if (doc != nullptr)
  232. {
  233. if (saveIfNeeded)
  234. {
  235. if (saveIfNeededAndUserAgrees (doc) != FileBasedDocument::savedOk)
  236. return false;
  237. }
  238. for (int i = listeners.size(); --i >= 0;)
  239. listeners.getUnchecked(i)->documentAboutToClose (doc);
  240. documents.remove (index);
  241. commandManager->commandStatusChanged();
  242. }
  243. return true;
  244. }
  245. bool OpenDocumentManager::closeDocument (Document* document, bool saveIfNeeded)
  246. {
  247. return closeDocument (documents.indexOf (document), saveIfNeeded);
  248. }
  249. void OpenDocumentManager::closeFile (const File& f, bool saveIfNeeded)
  250. {
  251. for (int i = documents.size(); --i >= 0;)
  252. {
  253. Document* d = documents.getUnchecked (i);
  254. if (d->isForFile (f))
  255. closeDocument (i, saveIfNeeded);
  256. }
  257. }
  258. bool OpenDocumentManager::closeAllDocumentsUsingProject (Project& project, bool saveIfNeeded)
  259. {
  260. for (int i = documents.size(); --i >= 0;)
  261. {
  262. Document* d = documents.getUnchecked (i);
  263. if (d->refersToProject (project))
  264. {
  265. if (! closeDocument (i, saveIfNeeded))
  266. return false;
  267. }
  268. }
  269. return true;
  270. }
  271. bool OpenDocumentManager::anyFilesNeedSaving() const
  272. {
  273. for (int i = documents.size(); --i >= 0;)
  274. {
  275. Document* d = documents.getUnchecked (i);
  276. if (d->needsSaving())
  277. return true;
  278. }
  279. return false;
  280. }
  281. bool OpenDocumentManager::saveAll()
  282. {
  283. for (int i = documents.size(); --i >= 0;)
  284. {
  285. Document* d = documents.getUnchecked (i);
  286. if (! d->save())
  287. return false;
  288. }
  289. return true;
  290. }
  291. void OpenDocumentManager::reloadModifiedFiles()
  292. {
  293. for (int i = documents.size(); --i >= 0;)
  294. {
  295. Document* d = documents.getUnchecked (i);
  296. if (d->hasFileBeenModifiedExternally())
  297. d->reloadFromFile();
  298. }
  299. }
  300. void OpenDocumentManager::fileHasBeenRenamed (const File& oldFile, const File& newFile)
  301. {
  302. for (int i = documents.size(); --i >= 0;)
  303. {
  304. Document* d = documents.getUnchecked (i);
  305. if (d->isForFile (oldFile))
  306. d->fileHasBeenRenamed (newFile);
  307. }
  308. }