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.

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