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.

451 lines
14KB

  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_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. UnknownDocument (const UnknownDocument&);
  200. UnknownDocument& operator= (const UnknownDocument&);
  201. };
  202. //==============================================================================
  203. OpenDocumentManager::OpenDocumentManager()
  204. {
  205. }
  206. OpenDocumentManager::~OpenDocumentManager()
  207. {
  208. clearSingletonInstance();
  209. }
  210. juce_ImplementSingleton_SingleThreaded (OpenDocumentManager);
  211. //==============================================================================
  212. void OpenDocumentManager::registerEditor (DocumentEditorComponent* editor)
  213. {
  214. editors.add (editor);
  215. }
  216. void OpenDocumentManager::deregisterEditor (DocumentEditorComponent* editor)
  217. {
  218. editors.removeValue (editor);
  219. }
  220. //==============================================================================
  221. bool OpenDocumentManager::canOpenFile (const File& file)
  222. {
  223. return DrawableDocumentType::isDrawableFile (file)
  224. || SourceCodeEditor::isTextFile (file);
  225. }
  226. OpenDocumentManager::Document* OpenDocumentManager::getDocumentForFile (Project* project, const File& file)
  227. {
  228. for (int i = documents.size(); --i >= 0;)
  229. if (documents.getUnchecked(i)->isForFile (file))
  230. return documents.getUnchecked(i);
  231. Document* d = 0;
  232. if (ComponentDocumentType::isComponentFile (file))
  233. d = new ComponentDocumentType (project, file);
  234. else if (DrawableDocumentType::isDrawableFile (file))
  235. d = new DrawableDocumentType (project, file);
  236. else if (SourceCodeEditor::isTextFile (file))
  237. d = new SourceCodeDocument (file);
  238. else
  239. d = new UnknownDocument (project, file);
  240. documents.add (d);
  241. commandManager->commandStatusChanged();
  242. return d;
  243. }
  244. int OpenDocumentManager::getNumOpenDocuments() const
  245. {
  246. return documents.size();
  247. }
  248. OpenDocumentManager::Document* OpenDocumentManager::getOpenDocument (int index) const
  249. {
  250. return documents.getUnchecked (index);
  251. }
  252. void OpenDocumentManager::moveDocumentToTopOfStack (Document* doc)
  253. {
  254. for (int i = documents.size(); --i >= 0;)
  255. {
  256. if (doc == documents.getUnchecked(i))
  257. {
  258. documents.move (i, 0);
  259. commandManager->commandStatusChanged();
  260. break;
  261. }
  262. }
  263. }
  264. FileBasedDocument::SaveResult OpenDocumentManager::saveIfNeededAndUserAgrees (OpenDocumentManager::Document* doc)
  265. {
  266. if (! doc->needsSaving())
  267. return FileBasedDocument::savedOk;
  268. const int r = AlertWindow::showYesNoCancelBox (AlertWindow::QuestionIcon,
  269. TRANS("Closing document..."),
  270. TRANS("Do you want to save the changes to \"")
  271. + doc->getName() + "\"?",
  272. TRANS("save"),
  273. TRANS("discard changes"),
  274. TRANS("cancel"));
  275. if (r == 1)
  276. {
  277. // save changes
  278. return doc->save() ? FileBasedDocument::savedOk
  279. : FileBasedDocument::failedToWriteToFile;
  280. }
  281. else if (r == 2)
  282. {
  283. // discard changes
  284. return FileBasedDocument::savedOk;
  285. }
  286. return FileBasedDocument::userCancelledSave;
  287. }
  288. bool OpenDocumentManager::closeDocument (int index, bool saveIfNeeded)
  289. {
  290. Document* doc = documents [index];
  291. if (doc != 0)
  292. {
  293. if (saveIfNeeded)
  294. {
  295. if (saveIfNeededAndUserAgrees (doc) != FileBasedDocument::savedOk)
  296. return false;
  297. }
  298. for (int i = editors.size(); --i >= 0;)
  299. if (editors.getUnchecked(i)->getDocument() == doc)
  300. editors.getUnchecked(i)->deleteSelf();
  301. documents.remove (index);
  302. commandManager->commandStatusChanged();
  303. }
  304. return true;
  305. }
  306. bool OpenDocumentManager::closeDocument (Document* document, bool saveIfNeeded)
  307. {
  308. return closeDocument (documents.indexOf (document), saveIfNeeded);
  309. }
  310. void OpenDocumentManager::closeFile (const File& f, bool saveIfNeeded)
  311. {
  312. for (int i = documents.size(); --i >= 0;)
  313. {
  314. Document* d = documents.getUnchecked (i);
  315. if (d->isForFile (f))
  316. closeDocument (i, saveIfNeeded);
  317. }
  318. }
  319. bool OpenDocumentManager::closeAllDocumentsUsingProject (Project& project, bool saveIfNeeded)
  320. {
  321. for (int i = documents.size(); --i >= 0;)
  322. {
  323. Document* d = documents.getUnchecked (i);
  324. if (d->refersToProject (project))
  325. {
  326. if (! closeDocument (i, saveIfNeeded))
  327. return false;
  328. }
  329. }
  330. return true;
  331. }
  332. bool OpenDocumentManager::anyFilesNeedSaving() const
  333. {
  334. for (int i = documents.size(); --i >= 0;)
  335. {
  336. Document* d = documents.getUnchecked (i);
  337. if (d->needsSaving())
  338. return true;
  339. }
  340. return false;
  341. }
  342. bool OpenDocumentManager::saveAll()
  343. {
  344. for (int i = documents.size(); --i >= 0;)
  345. {
  346. Document* d = documents.getUnchecked (i);
  347. if (! d->save())
  348. return false;
  349. }
  350. return true;
  351. }
  352. void OpenDocumentManager::reloadModifiedFiles()
  353. {
  354. for (int i = documents.size(); --i >= 0;)
  355. {
  356. Document* d = documents.getUnchecked (i);
  357. if (d->hasFileBeenModifiedExternally())
  358. d->reloadFromFile();
  359. }
  360. }