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.

389 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 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. const String getName() const { return modDetector.getFile().getFileName(); }
  50. const String getType() const { return modDetector.getFile().getFileExtension() + " file"; }
  51. bool needsSaving() const { return codeDoc != 0 && 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 != 0)
  59. codeDoc->loadFromStream (*in);
  60. }
  61. bool save()
  62. {
  63. TemporaryFile temp (modDetector.getFile());
  64. ScopedPointer <FileOutputStream> out (temp.getFile().createOutputStream());
  65. if (out == 0 || ! codeDoc->writeToStream (*out))
  66. return false;
  67. out = 0;
  68. if (! temp.overwriteTargetFileWithTemporary())
  69. return false;
  70. modDetector.updateHash();
  71. return true;
  72. }
  73. Component* createEditor()
  74. {
  75. CodeTokeniser* tokeniser = 0;
  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. const 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. const 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. UnknownDocument (const UnknownDocument&);
  130. UnknownDocument& operator= (const UnknownDocument&);
  131. };
  132. //==============================================================================
  133. OpenDocumentManager::OpenDocumentManager()
  134. {
  135. registerType (new UnknownDocument::Type());
  136. registerType (new SourceCodeDocument::Type());
  137. }
  138. OpenDocumentManager::~OpenDocumentManager()
  139. {
  140. clearSingletonInstance();
  141. }
  142. juce_ImplementSingleton_SingleThreaded (OpenDocumentManager);
  143. //==============================================================================
  144. void OpenDocumentManager::registerType (DocumentType* type)
  145. {
  146. types.add (type);
  147. }
  148. //==============================================================================
  149. void OpenDocumentManager::addListener (DocumentCloseListener* listener)
  150. {
  151. listeners.addIfNotAlreadyThere (listener);
  152. }
  153. void OpenDocumentManager::removeListener (DocumentCloseListener* listener)
  154. {
  155. listeners.removeValue (listener);
  156. }
  157. //==============================================================================
  158. bool OpenDocumentManager::canOpenFile (const File& file)
  159. {
  160. for (int i = types.size(); --i >= 0;)
  161. if (types.getUnchecked(i)->canOpenFile (file))
  162. return true;
  163. return false;
  164. }
  165. OpenDocumentManager::Document* OpenDocumentManager::getDocumentForFile (Project* project, const File& file)
  166. {
  167. for (int i = documents.size(); --i >= 0;)
  168. if (documents.getUnchecked(i)->isForFile (file))
  169. return documents.getUnchecked(i);
  170. Document* d = 0;
  171. for (int i = types.size(); --i >= 0 && d == 0;)
  172. {
  173. if (types.getUnchecked(i)->canOpenFile (file))
  174. {
  175. d = types.getUnchecked(i)->openFile (project, file);
  176. jassert (d != 0);
  177. }
  178. }
  179. jassert (d != 0);
  180. if (d != 0)
  181. documents.add (d);
  182. commandManager->commandStatusChanged();
  183. return d;
  184. }
  185. int OpenDocumentManager::getNumOpenDocuments() const
  186. {
  187. return documents.size();
  188. }
  189. OpenDocumentManager::Document* OpenDocumentManager::getOpenDocument (int index) const
  190. {
  191. return documents.getUnchecked (index);
  192. }
  193. void OpenDocumentManager::moveDocumentToTopOfStack (Document* doc)
  194. {
  195. for (int i = documents.size(); --i >= 0;)
  196. {
  197. if (doc == documents.getUnchecked(i))
  198. {
  199. documents.move (i, 0);
  200. commandManager->commandStatusChanged();
  201. break;
  202. }
  203. }
  204. }
  205. FileBasedDocument::SaveResult OpenDocumentManager::saveIfNeededAndUserAgrees (OpenDocumentManager::Document* doc)
  206. {
  207. if (! doc->needsSaving())
  208. return FileBasedDocument::savedOk;
  209. const int r = AlertWindow::showYesNoCancelBox (AlertWindow::QuestionIcon,
  210. TRANS("Closing document..."),
  211. TRANS("Do you want to save the changes to \"")
  212. + doc->getName() + "\"?",
  213. TRANS("save"),
  214. TRANS("discard changes"),
  215. TRANS("cancel"));
  216. if (r == 1)
  217. {
  218. // save changes
  219. return doc->save() ? FileBasedDocument::savedOk
  220. : FileBasedDocument::failedToWriteToFile;
  221. }
  222. else if (r == 2)
  223. {
  224. // discard changes
  225. return FileBasedDocument::savedOk;
  226. }
  227. return FileBasedDocument::userCancelledSave;
  228. }
  229. bool OpenDocumentManager::closeDocument (int index, bool saveIfNeeded)
  230. {
  231. Document* doc = documents [index];
  232. if (doc != 0)
  233. {
  234. if (saveIfNeeded)
  235. {
  236. if (saveIfNeededAndUserAgrees (doc) != FileBasedDocument::savedOk)
  237. return false;
  238. }
  239. for (int i = listeners.size(); --i >= 0;)
  240. listeners.getUnchecked(i)->documentAboutToClose (doc);
  241. documents.remove (index);
  242. commandManager->commandStatusChanged();
  243. }
  244. return true;
  245. }
  246. bool OpenDocumentManager::closeDocument (Document* document, bool saveIfNeeded)
  247. {
  248. return closeDocument (documents.indexOf (document), saveIfNeeded);
  249. }
  250. void OpenDocumentManager::closeFile (const File& f, bool saveIfNeeded)
  251. {
  252. for (int i = documents.size(); --i >= 0;)
  253. {
  254. Document* d = documents.getUnchecked (i);
  255. if (d->isForFile (f))
  256. closeDocument (i, saveIfNeeded);
  257. }
  258. }
  259. bool OpenDocumentManager::closeAllDocumentsUsingProject (Project& project, bool saveIfNeeded)
  260. {
  261. for (int i = documents.size(); --i >= 0;)
  262. {
  263. Document* d = documents.getUnchecked (i);
  264. if (d->refersToProject (project))
  265. {
  266. if (! closeDocument (i, saveIfNeeded))
  267. return false;
  268. }
  269. }
  270. return true;
  271. }
  272. bool OpenDocumentManager::anyFilesNeedSaving() const
  273. {
  274. for (int i = documents.size(); --i >= 0;)
  275. {
  276. Document* d = documents.getUnchecked (i);
  277. if (d->needsSaving())
  278. return true;
  279. }
  280. return false;
  281. }
  282. bool OpenDocumentManager::saveAll()
  283. {
  284. for (int i = documents.size(); --i >= 0;)
  285. {
  286. Document* d = documents.getUnchecked (i);
  287. if (! d->save())
  288. return false;
  289. }
  290. return true;
  291. }
  292. void OpenDocumentManager::reloadModifiedFiles()
  293. {
  294. for (int i = documents.size(); --i >= 0;)
  295. {
  296. Document* d = documents.getUnchecked (i);
  297. if (d->hasFileBeenModifiedExternally())
  298. d->reloadFromFile();
  299. }
  300. }
  301. void OpenDocumentManager::fileHasBeenRenamed (const File& oldFile, const File& newFile)
  302. {
  303. for (int i = documents.size(); --i >= 0;)
  304. {
  305. Document* d = documents.getUnchecked (i);
  306. if (d->isForFile (oldFile))
  307. d->fileHasBeenRenamed (newFile);
  308. }
  309. }