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.

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