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.

508 lines
16KB

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