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.

448 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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_SourceCodeEditor.h"
  20. #include "Drawable Editor/jucer_DrawableEditor.h"
  21. #include "jucer_ItemPreviewComponent.h"
  22. #include "Component Editor/jucer_ComponentEditor.h"
  23. //==============================================================================
  24. class SourceCodeDocument : public OpenDocumentManager::Document
  25. {
  26. public:
  27. SourceCodeDocument (const File& file_)
  28. : modDetector (file_)
  29. {
  30. codeDoc = new CodeDocument();
  31. reloadFromFile();
  32. }
  33. ~SourceCodeDocument()
  34. {
  35. }
  36. bool loadedOk() const { return true; }
  37. bool isForFile (const File& file) const { return modDetector.getFile() == file; }
  38. bool isForNode (const ValueTree& node) const { return false; }
  39. bool refersToProject (Project& project) const { return false; }
  40. const String getName() const { return modDetector.getFile().getFileName(); }
  41. const String getType() const { return modDetector.getFile().getFileExtension() + " file"; }
  42. bool needsSaving() const { return codeDoc != 0 && codeDoc->hasChangedSinceSavePoint(); }
  43. bool hasFileBeenModifiedExternally() { return modDetector.hasBeenModified(); }
  44. void reloadFromFile()
  45. {
  46. modDetector.updateHash();
  47. ScopedPointer <InputStream> in (modDetector.getFile().createInputStream());
  48. if (in != 0)
  49. codeDoc->loadFromStream (*in);
  50. }
  51. bool save()
  52. {
  53. TemporaryFile temp (modDetector.getFile());
  54. ScopedPointer <FileOutputStream> out (temp.getFile().createOutputStream());
  55. if (out == 0 || ! codeDoc->writeToStream (*out))
  56. return false;
  57. out = 0;
  58. return temp.overwriteTargetFileWithTemporary();
  59. }
  60. Component* createEditor()
  61. {
  62. CodeTokeniser* tokeniser = 0;
  63. if (SourceCodeEditor::isCppFile (modDetector.getFile()))
  64. tokeniser = &cppTokeniser;
  65. return new SourceCodeEditor (this, *codeDoc, tokeniser);
  66. }
  67. private:
  68. FileModificationDetector modDetector;
  69. ScopedPointer <CodeDocument> codeDoc;
  70. CPlusPlusCodeTokeniser cppTokeniser;
  71. };
  72. //==============================================================================
  73. class ComponentDocumentType : public OpenDocumentManager::Document
  74. {
  75. public:
  76. ComponentDocumentType (Project* project_, const File& file_)
  77. : project (project_),
  78. modDetector (file_)
  79. {
  80. reloadFromFile();
  81. }
  82. ~ComponentDocumentType()
  83. {
  84. componentDoc = 0;
  85. }
  86. static bool isComponentFile (const File& file) { return ComponentDocument::isComponentFile (file); }
  87. bool loadedOk() const { return componentDoc != 0; }
  88. bool isForFile (const File& file) const { return modDetector.getFile() == file; }
  89. bool isForNode (const ValueTree& node) const { return false; }
  90. bool refersToProject (Project& p) const { return project == &p; }
  91. const String getType() const { return "Jucer Component"; }
  92. const String getName() const { return modDetector.getFile().getFileName(); }
  93. bool needsSaving() const { return componentDoc != 0 && componentDoc->hasChangedSinceLastSave(); }
  94. bool hasFileBeenModifiedExternally() { return modDetector.hasBeenModified(); }
  95. void reloadFromFile()
  96. {
  97. modDetector.updateHash();
  98. if (componentDoc == 0)
  99. componentDoc = new ComponentDocument (project, modDetector.getFile());
  100. if (! componentDoc->reload())
  101. componentDoc = 0;
  102. }
  103. bool save()
  104. {
  105. return componentDoc->save();
  106. }
  107. Component* createEditor()
  108. {
  109. if (componentDoc == 0)
  110. {
  111. jassertfalse;
  112. return 0;
  113. }
  114. return new ComponentEditor (this, project, componentDoc);
  115. }
  116. private:
  117. Project* project;
  118. FileModificationDetector modDetector;
  119. ScopedPointer <ComponentDocument> componentDoc;
  120. };
  121. //==============================================================================
  122. class DrawableDocumentType : public OpenDocumentManager::Document
  123. {
  124. public:
  125. DrawableDocumentType (Project* project_, const File& file_)
  126. : project (project_),
  127. modDetector (file_)
  128. {
  129. reloadFromFile();
  130. }
  131. ~DrawableDocumentType()
  132. {
  133. drawableDoc = 0;
  134. }
  135. static bool isDrawableFile (const File& file) { return file.hasFileExtension (".drawable"); }
  136. bool loadedOk() const { return drawableDoc != 0; }
  137. bool isForFile (const File& file) const { return modDetector.getFile() == file; }
  138. bool isForNode (const ValueTree& node) const { return false; }
  139. bool refersToProject (Project& p) const { return project == &p; }
  140. const String getType() const { return "Drawable"; }
  141. const String getName() const { return modDetector.getFile().getFileName(); }
  142. bool needsSaving() const { return drawableDoc != 0 && drawableDoc->hasChangedSinceLastSave(); }
  143. bool hasFileBeenModifiedExternally() { return modDetector.hasBeenModified(); }
  144. void reloadFromFile()
  145. {
  146. modDetector.updateHash();
  147. if (drawableDoc == 0)
  148. drawableDoc = new DrawableDocument (project, modDetector.getFile());
  149. if (! drawableDoc->reload())
  150. drawableDoc = 0;
  151. }
  152. bool save()
  153. {
  154. return drawableDoc->save();
  155. }
  156. Component* createEditor()
  157. {
  158. jassert (drawableDoc != 0);
  159. if (drawableDoc == 0)
  160. return 0;
  161. return new DrawableEditor (this, project, drawableDoc);
  162. }
  163. private:
  164. Project* project;
  165. FileModificationDetector modDetector;
  166. ScopedPointer <DrawableDocument> drawableDoc;
  167. };
  168. //==============================================================================
  169. class UnknownDocument : public OpenDocumentManager::Document
  170. {
  171. public:
  172. UnknownDocument (Project* project_, const File& file_)
  173. : project (project_), file (file_)
  174. {
  175. reloadFromFile();
  176. }
  177. ~UnknownDocument() {}
  178. bool loadedOk() const { return true; }
  179. bool isForFile (const File& file_) const { return file == file_; }
  180. bool isForNode (const ValueTree& node_) const { return false; }
  181. bool refersToProject (Project& p) const { return project == &p; }
  182. bool needsSaving() const { return false; }
  183. bool save() { return true; }
  184. bool hasFileBeenModifiedExternally() { return fileModificationTime != file.getLastModificationTime(); }
  185. void reloadFromFile() { fileModificationTime = file.getLastModificationTime(); }
  186. const String getName() const { return file.getFileName(); }
  187. Component* createEditor() { return new ItemPreviewComponent (file); }
  188. const String getType() const
  189. {
  190. if (file.getFileExtension().isNotEmpty())
  191. return file.getFileExtension() + " file";
  192. jassertfalse
  193. return "Unknown";
  194. }
  195. private:
  196. Project* const project;
  197. const File file;
  198. Time fileModificationTime;
  199. };
  200. //==============================================================================
  201. OpenDocumentManager::OpenDocumentManager()
  202. {
  203. }
  204. OpenDocumentManager::~OpenDocumentManager()
  205. {
  206. clearSingletonInstance();
  207. }
  208. juce_ImplementSingleton_SingleThreaded (OpenDocumentManager);
  209. //==============================================================================
  210. void OpenDocumentManager::registerEditor (DocumentEditorComponent* editor)
  211. {
  212. editors.add (editor);
  213. }
  214. void OpenDocumentManager::deregisterEditor (DocumentEditorComponent* editor)
  215. {
  216. editors.removeValue (editor);
  217. }
  218. //==============================================================================
  219. bool OpenDocumentManager::canOpenFile (const File& file)
  220. {
  221. return DrawableDocumentType::isDrawableFile (file)
  222. || SourceCodeEditor::isTextFile (file);
  223. }
  224. OpenDocumentManager::Document* OpenDocumentManager::getDocumentForFile (Project* project, const File& file)
  225. {
  226. for (int i = documents.size(); --i >= 0;)
  227. if (documents.getUnchecked(i)->isForFile (file))
  228. return documents.getUnchecked(i);
  229. Document* d = 0;
  230. if (ComponentDocumentType::isComponentFile (file))
  231. d = new ComponentDocumentType (project, file);
  232. else if (DrawableDocumentType::isDrawableFile (file))
  233. d = new DrawableDocumentType (project, file);
  234. else if (SourceCodeEditor::isTextFile (file))
  235. d = new SourceCodeDocument (file);
  236. else
  237. d = new UnknownDocument (project, file);
  238. documents.add (d);
  239. commandManager->commandStatusChanged();
  240. return d;
  241. }
  242. int OpenDocumentManager::getNumOpenDocuments() const
  243. {
  244. return documents.size();
  245. }
  246. OpenDocumentManager::Document* OpenDocumentManager::getOpenDocument (int index) const
  247. {
  248. return documents.getUnchecked (index);
  249. }
  250. void OpenDocumentManager::moveDocumentToTopOfStack (Document* doc)
  251. {
  252. for (int i = documents.size(); --i >= 0;)
  253. {
  254. if (doc == documents.getUnchecked(i))
  255. {
  256. documents.move (i, 0);
  257. commandManager->commandStatusChanged();
  258. break;
  259. }
  260. }
  261. }
  262. FileBasedDocument::SaveResult OpenDocumentManager::saveIfNeededAndUserAgrees (OpenDocumentManager::Document* doc)
  263. {
  264. if (! doc->needsSaving())
  265. return FileBasedDocument::savedOk;
  266. const int r = AlertWindow::showYesNoCancelBox (AlertWindow::QuestionIcon,
  267. TRANS("Closing document..."),
  268. TRANS("Do you want to save the changes to \"")
  269. + doc->getName() + "\"?",
  270. TRANS("save"),
  271. TRANS("discard changes"),
  272. TRANS("cancel"));
  273. if (r == 1)
  274. {
  275. // save changes
  276. return doc->save() ? FileBasedDocument::savedOk
  277. : FileBasedDocument::failedToWriteToFile;
  278. }
  279. else if (r == 2)
  280. {
  281. // discard changes
  282. return FileBasedDocument::savedOk;
  283. }
  284. return FileBasedDocument::userCancelledSave;
  285. }
  286. bool OpenDocumentManager::closeDocument (int index, bool saveIfNeeded)
  287. {
  288. Document* doc = documents [index];
  289. if (doc != 0)
  290. {
  291. if (saveIfNeeded)
  292. {
  293. if (saveIfNeededAndUserAgrees (doc) != FileBasedDocument::savedOk)
  294. return false;
  295. }
  296. for (int i = editors.size(); --i >= 0;)
  297. if (editors.getUnchecked(i)->getDocument() == doc)
  298. editors.getUnchecked(i)->deleteSelf();
  299. documents.remove (index);
  300. commandManager->commandStatusChanged();
  301. }
  302. return true;
  303. }
  304. bool OpenDocumentManager::closeDocument (Document* document, bool saveIfNeeded)
  305. {
  306. return closeDocument (documents.indexOf (document), saveIfNeeded);
  307. }
  308. void OpenDocumentManager::closeFile (const File& f, bool saveIfNeeded)
  309. {
  310. for (int i = documents.size(); --i >= 0;)
  311. {
  312. Document* d = documents.getUnchecked (i);
  313. if (d->isForFile (f))
  314. closeDocument (i, saveIfNeeded);
  315. }
  316. }
  317. bool OpenDocumentManager::closeAllDocumentsUsingProject (Project& project, bool saveIfNeeded)
  318. {
  319. for (int i = documents.size(); --i >= 0;)
  320. {
  321. Document* d = documents.getUnchecked (i);
  322. if (d->refersToProject (project))
  323. {
  324. if (! closeDocument (i, saveIfNeeded))
  325. return false;
  326. }
  327. }
  328. return true;
  329. }
  330. bool OpenDocumentManager::anyFilesNeedSaving() const
  331. {
  332. for (int i = documents.size(); --i >= 0;)
  333. {
  334. Document* d = documents.getUnchecked (i);
  335. if (d->needsSaving())
  336. return true;
  337. }
  338. return false;
  339. }
  340. bool OpenDocumentManager::saveAll()
  341. {
  342. for (int i = documents.size(); --i >= 0;)
  343. {
  344. Document* d = documents.getUnchecked (i);
  345. if (! d->save())
  346. return false;
  347. }
  348. return true;
  349. }
  350. void OpenDocumentManager::reloadModifiedFiles()
  351. {
  352. for (int i = documents.size(); --i >= 0;)
  353. {
  354. Document* d = documents.getUnchecked (i);
  355. if (d->hasFileBeenModifiedExternally())
  356. d->reloadFromFile();
  357. }
  358. }