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.

419 lines
14KB

  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_ProjectContentComponent.h"
  19. #include "../Application/jucer_MainWindow.h"
  20. #include "../Code Editor/jucer_SourceCodeEditor.h"
  21. #include "jucer_ProjectInformationComponent.h"
  22. #include "jucer_TreeViewTypes.h"
  23. #include "../Project Saving/jucer_ProjectExporter.h"
  24. //==============================================================================
  25. ProjectContentComponent::ProjectContentComponent()
  26. : project (nullptr),
  27. currentDocument (nullptr)
  28. {
  29. setOpaque (true);
  30. setWantsKeyboardFocus (true);
  31. treeSizeConstrainer.setMinimumWidth (100);
  32. treeSizeConstrainer.setMaximumWidth (500);
  33. }
  34. ProjectContentComponent::~ProjectContentComponent()
  35. {
  36. setProject (nullptr);
  37. contentView = nullptr;
  38. jassert (getNumChildComponents() == 0);
  39. }
  40. void ProjectContentComponent::paint (Graphics& g)
  41. {
  42. g.fillAll (Colour::greyLevel (0.8f));
  43. }
  44. void ProjectContentComponent::setProject (Project* newProject)
  45. {
  46. if (project != newProject)
  47. {
  48. PropertiesFile& settings = StoredSettings::getInstance()->getProps();
  49. if (project != nullptr)
  50. project->removeChangeListener (this);
  51. contentView = nullptr;
  52. resizerBar = nullptr;
  53. if (projectTree != nullptr)
  54. {
  55. settings.setValue ("projectTreeviewWidth", projectTree->getWidth());
  56. projectTree->deleteRootItem();
  57. projectTree = nullptr;
  58. }
  59. project = newProject;
  60. if (project != nullptr)
  61. {
  62. addChildAndSetID (projectTree = new TreeView(), "tree");
  63. projectTree->setRootItemVisible (true);
  64. projectTree->setMultiSelectEnabled (true);
  65. projectTree->setDefaultOpenness (true);
  66. projectTree->setColour (TreeView::backgroundColourId, Colour::greyLevel (0.93f));
  67. projectTree->setIndentSize (14);
  68. projectTree->setRootItem (new GroupTreeViewItem (project->getMainGroup()));
  69. projectTree->getRootItem()->setOpen (true);
  70. String lastTreeWidth (settings.getValue ("projectTreeviewWidth"));
  71. if (lastTreeWidth.getIntValue() < 150)
  72. lastTreeWidth = "250";
  73. projectTree->setBounds ("0, 0, left + " + lastTreeWidth + ", parent.height");
  74. addChildAndSetID (resizerBar = new ResizableEdgeComponent (projectTree, &treeSizeConstrainer, ResizableEdgeComponent::rightEdge),
  75. "resizer");
  76. resizerBar->setBounds ("tree.right, 0, tree.right + 4, parent.height");
  77. project->addChangeListener (this);
  78. if (currentDocument == nullptr)
  79. invokeDirectly (CommandIDs::showProjectSettings, true);
  80. updateMissingFileStatuses();
  81. const ScopedPointer<XmlElement> treeOpenness (settings.getXmlValue ("treeViewState_" + project->getProjectUID()));
  82. if (treeOpenness != nullptr)
  83. projectTree->restoreOpennessState (*treeOpenness, true);
  84. }
  85. }
  86. }
  87. void ProjectContentComponent::saveTreeViewState()
  88. {
  89. if (projectTree != nullptr)
  90. {
  91. const ScopedPointer<XmlElement> opennessState (projectTree->getOpennessState (true));
  92. if (opennessState != nullptr)
  93. StoredSettings::getInstance()->getProps()
  94. .setValue ("treeViewState_" + project->getProjectUID(), opennessState);
  95. }
  96. }
  97. void ProjectContentComponent::changeListenerCallback (ChangeBroadcaster*)
  98. {
  99. updateMissingFileStatuses();
  100. }
  101. void ProjectContentComponent::updateMissingFileStatuses()
  102. {
  103. if (projectTree != nullptr)
  104. {
  105. ProjectTreeViewBase* p = dynamic_cast <ProjectTreeViewBase*> (projectTree->getRootItem());
  106. if (p != nullptr)
  107. p->checkFileStatus();
  108. }
  109. }
  110. bool ProjectContentComponent::showEditorForFile (const File& f)
  111. {
  112. return showDocument (OpenDocumentManager::getInstance()->openFile (project, f));
  113. }
  114. bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc)
  115. {
  116. if (doc == nullptr)
  117. return false;
  118. OpenDocumentManager::getInstance()->moveDocumentToTopOfStack (doc);
  119. if (doc->hasFileBeenModifiedExternally())
  120. doc->reloadFromFile();
  121. return setEditorComponent (doc->createEditor(), doc);
  122. }
  123. void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc)
  124. {
  125. if (doc == currentDocument)
  126. {
  127. currentDocument = nullptr;
  128. contentView = nullptr;
  129. updateMainWindowTitle();
  130. commandManager->commandStatusChanged();
  131. }
  132. }
  133. bool ProjectContentComponent::setEditorComponent (Component* editor, OpenDocumentManager::Document* doc)
  134. {
  135. if (editor != nullptr)
  136. {
  137. contentView = editor;
  138. currentDocument = doc;
  139. addAndMakeVisible (editor);
  140. editor->setBounds ("resizer.right, 0, parent.right, parent.height");
  141. updateMainWindowTitle();
  142. commandManager->commandStatusChanged();
  143. return true;
  144. }
  145. updateMainWindowTitle();
  146. return false;
  147. }
  148. void ProjectContentComponent::updateMainWindowTitle()
  149. {
  150. MainWindow* mw = findParentComponentOfClass<MainWindow>();
  151. if (mw != nullptr)
  152. mw->updateTitle (currentDocument != nullptr ? currentDocument->getName() : String::empty);
  153. }
  154. bool ProjectContentComponent::canProjectBeLaunched() const
  155. {
  156. if (project != nullptr)
  157. {
  158. ScopedPointer <ProjectExporter> launcher (ProjectExporter::createPlatformDefaultExporter (*project));
  159. return launcher != nullptr;
  160. }
  161. return false;
  162. }
  163. ApplicationCommandTarget* ProjectContentComponent::getNextCommandTarget()
  164. {
  165. return findFirstTargetParentComponent();
  166. }
  167. void ProjectContentComponent::getAllCommands (Array <CommandID>& commands)
  168. {
  169. const CommandID ids[] = { CommandIDs::saveDocument,
  170. CommandIDs::closeDocument,
  171. CommandIDs::saveProject,
  172. CommandIDs::closeProject,
  173. CommandIDs::openInIDE,
  174. CommandIDs::saveAndOpenInIDE,
  175. CommandIDs::showProjectSettings,
  176. StandardApplicationCommandIDs::del };
  177. commands.addArray (ids, numElementsInArray (ids));
  178. }
  179. void ProjectContentComponent::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
  180. {
  181. String documentName;
  182. if (currentDocument != nullptr)
  183. documentName = " '" + currentDocument->getName().substring (0, 32) + "'";
  184. switch (commandID)
  185. {
  186. case CommandIDs::saveProject:
  187. result.setInfo ("Save Project",
  188. "Saves the current project",
  189. CommandCategories::general, 0);
  190. result.setActive (project != nullptr);
  191. break;
  192. case CommandIDs::closeProject:
  193. result.setInfo ("Close Project",
  194. "Closes the current project",
  195. CommandCategories::general, 0);
  196. result.setActive (project != nullptr);
  197. break;
  198. case CommandIDs::saveDocument:
  199. result.setInfo ("Save" + documentName,
  200. "Saves the current document",
  201. CommandCategories::general, 0);
  202. result.setActive (currentDocument != nullptr || project != nullptr);
  203. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier, 0));
  204. break;
  205. case CommandIDs::closeDocument:
  206. result.setInfo ("Close" + documentName,
  207. "Closes the current document",
  208. CommandCategories::general, 0);
  209. result.setActive (currentDocument != nullptr);
  210. #if JUCE_MAC
  211. result.defaultKeypresses.add (KeyPress ('w', ModifierKeys::commandModifier | ModifierKeys::ctrlModifier, 0));
  212. #else
  213. result.defaultKeypresses.add (KeyPress ('w', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  214. #endif
  215. break;
  216. case CommandIDs::openInIDE:
  217. #if JUCE_MAC
  218. result.setInfo ("Open in XCode...",
  219. #elif JUCE_WINDOWS
  220. result.setInfo ("Open in Visual Studio...",
  221. #else
  222. result.setInfo ("Open as a Makefile...",
  223. #endif
  224. "Launches the project in an external IDE",
  225. CommandCategories::general, 0);
  226. result.setActive (canProjectBeLaunched());
  227. break;
  228. case CommandIDs::saveAndOpenInIDE:
  229. #if JUCE_MAC
  230. result.setInfo ("Save Project and Open in XCode...",
  231. #elif JUCE_WINDOWS
  232. result.setInfo ("Save Project and Open in Visual Studio...",
  233. #else
  234. result.setInfo ("Save Project and Open as a Makefile...",
  235. #endif
  236. "Saves the project and launches it in an external IDE",
  237. CommandCategories::general, 0);
  238. result.setActive (canProjectBeLaunched());
  239. result.defaultKeypresses.add (KeyPress ('l', ModifierKeys::commandModifier, 0));
  240. break;
  241. case CommandIDs::showProjectSettings:
  242. result.setInfo ("Show Project Build Settings",
  243. "Shows the build options for the project",
  244. CommandCategories::general, 0);
  245. result.setActive (project != nullptr);
  246. result.defaultKeypresses.add (KeyPress ('i', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  247. break;
  248. case StandardApplicationCommandIDs::del:
  249. result.setInfo ("Delete", String::empty, CommandCategories::general, 0);
  250. result.defaultKeypresses.add (KeyPress (KeyPress::deleteKey, 0, 0));
  251. result.defaultKeypresses.add (KeyPress (KeyPress::backspaceKey, 0, 0));
  252. result.setActive (projectTree != nullptr);
  253. break;
  254. default:
  255. break;
  256. }
  257. }
  258. bool ProjectContentComponent::isCommandActive (const CommandID commandID)
  259. {
  260. return project != nullptr;
  261. }
  262. bool ProjectContentComponent::perform (const InvocationInfo& info)
  263. {
  264. switch (info.commandID)
  265. {
  266. case CommandIDs::saveProject:
  267. if (project != nullptr && ! reinvokeCommandAfterClosingPropertyEditors (info))
  268. project->save (true, true);
  269. break;
  270. case CommandIDs::closeProject:
  271. {
  272. MainWindow* const mw = findParentComponentOfClass<MainWindow>();
  273. if (mw != nullptr && ! reinvokeCommandAfterClosingPropertyEditors (info))
  274. mw->closeCurrentProject();
  275. }
  276. break;
  277. case CommandIDs::saveDocument:
  278. if (! reinvokeCommandAfterClosingPropertyEditors (info))
  279. {
  280. if (currentDocument != nullptr)
  281. currentDocument->save();
  282. else if (project != nullptr)
  283. project->save (true, true);
  284. }
  285. break;
  286. case CommandIDs::closeDocument:
  287. if (currentDocument != nullptr)
  288. OpenDocumentManager::getInstance()->closeDocument (currentDocument, true);
  289. break;
  290. case CommandIDs::openInIDE:
  291. if (project != nullptr)
  292. {
  293. ScopedPointer <ProjectExporter> exporter (ProjectExporter::createPlatformDefaultExporter (*project));
  294. if (exporter != nullptr)
  295. exporter->launchProject();
  296. }
  297. break;
  298. case CommandIDs::saveAndOpenInIDE:
  299. if (project != nullptr)
  300. {
  301. if (! reinvokeCommandAfterClosingPropertyEditors (info))
  302. {
  303. if (project->save (true, true) == FileBasedDocument::savedOk)
  304. {
  305. ScopedPointer <ProjectExporter> exporter (ProjectExporter::createPlatformDefaultExporter (*project));
  306. if (exporter != nullptr)
  307. exporter->launchProject();
  308. }
  309. }
  310. }
  311. break;
  312. case CommandIDs::showProjectSettings:
  313. if (projectTree != nullptr)
  314. projectTree->getRootItem()->setSelected (true, true);
  315. break;
  316. case StandardApplicationCommandIDs::del:
  317. if (projectTree != nullptr)
  318. {
  319. ProjectTreeViewBase* p = dynamic_cast <ProjectTreeViewBase*> (projectTree->getRootItem());
  320. if (p != nullptr)
  321. p->deleteAllSelectedItems();
  322. }
  323. break;
  324. default:
  325. return false;
  326. }
  327. return true;
  328. }
  329. bool ProjectContentComponent::reinvokeCommandAfterClosingPropertyEditors (const InvocationInfo& info)
  330. {
  331. if (reinvokeCommandAfterCancellingModalComps (info))
  332. {
  333. grabKeyboardFocus(); // to force any open labels to close their text editors
  334. return true;
  335. }
  336. return false;
  337. }