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.

402 lines
13KB

  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::saveProject,
  170. CommandIDs::saveProjectAs,
  171. CommandIDs::closeProject,
  172. CommandIDs::openInIDE,
  173. CommandIDs::saveAndOpenInIDE,
  174. CommandIDs::showProjectSettings,
  175. StandardApplicationCommandIDs::del};
  176. commands.addArray (ids, numElementsInArray (ids));
  177. }
  178. void ProjectContentComponent::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
  179. {
  180. switch (commandID)
  181. {
  182. case CommandIDs::saveProject:
  183. result.setInfo ("Save Project",
  184. "Saves the current project",
  185. CommandCategories::general, 0);
  186. result.setActive (project != nullptr);
  187. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier, 0));
  188. break;
  189. case CommandIDs::saveProjectAs:
  190. result.setInfo ("Save Project As...",
  191. "Saves the current project to a different filename",
  192. CommandCategories::general, 0);
  193. result.setActive (project != nullptr);
  194. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  195. break;
  196. case CommandIDs::closeProject:
  197. result.setInfo ("Close Project",
  198. "Closes the current project",
  199. CommandCategories::general, 0);
  200. result.setActive (project != nullptr);
  201. break;
  202. case CommandIDs::openInIDE:
  203. #if JUCE_MAC
  204. result.setInfo ("Open in XCode...",
  205. #elif JUCE_WINDOWS
  206. result.setInfo ("Open in Visual Studio...",
  207. #else
  208. result.setInfo ("Open as a Makefile...",
  209. #endif
  210. "Launches the project in an external IDE",
  211. CommandCategories::general, 0);
  212. result.setActive (canProjectBeLaunched());
  213. break;
  214. case CommandIDs::saveAndOpenInIDE:
  215. #if JUCE_MAC
  216. result.setInfo ("Save Project and Open in XCode...",
  217. #elif JUCE_WINDOWS
  218. result.setInfo ("Save Project and Open in Visual Studio...",
  219. #else
  220. result.setInfo ("Save Project and Open as a Makefile...",
  221. #endif
  222. "Saves the project and launches it in an external IDE",
  223. CommandCategories::general, 0);
  224. result.setActive (canProjectBeLaunched());
  225. result.defaultKeypresses.add (KeyPress ('l', ModifierKeys::commandModifier, 0));
  226. break;
  227. case CommandIDs::showProjectSettings:
  228. result.setInfo ("Show Project Build Settings",
  229. "Shows the build options for the project",
  230. CommandCategories::general, 0);
  231. result.setActive (project != nullptr);
  232. result.defaultKeypresses.add (KeyPress ('i', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  233. break;
  234. case StandardApplicationCommandIDs::del:
  235. result.setInfo ("Delete", String::empty, CommandCategories::general, 0);
  236. result.defaultKeypresses.add (KeyPress (KeyPress::deleteKey, 0, 0));
  237. result.defaultKeypresses.add (KeyPress (KeyPress::backspaceKey, 0, 0));
  238. result.setActive (projectTree != nullptr);
  239. break;
  240. default:
  241. break;
  242. }
  243. }
  244. bool ProjectContentComponent::isCommandActive (const CommandID commandID)
  245. {
  246. return project != nullptr;
  247. }
  248. bool ProjectContentComponent::perform (const InvocationInfo& info)
  249. {
  250. switch (info.commandID)
  251. {
  252. case CommandIDs::saveProject:
  253. if (project != nullptr)
  254. {
  255. if (! reinvokeCommandAfterClosingPropertyEditors (info))
  256. project->save (true, true);
  257. }
  258. break;
  259. case CommandIDs::saveProjectAs:
  260. if (project != nullptr)
  261. {
  262. if (! reinvokeCommandAfterClosingPropertyEditors (info))
  263. project->saveAsInteractive (true);
  264. }
  265. break;
  266. case CommandIDs::closeProject:
  267. {
  268. MainWindow* mw = findParentComponentOfClass<MainWindow>();
  269. if (mw != nullptr)
  270. {
  271. if (! reinvokeCommandAfterClosingPropertyEditors (info))
  272. mw->closeCurrentProject();
  273. }
  274. }
  275. break;
  276. case CommandIDs::openInIDE:
  277. if (project != nullptr)
  278. {
  279. ScopedPointer <ProjectExporter> exporter (ProjectExporter::createPlatformDefaultExporter (*project));
  280. if (exporter != nullptr)
  281. exporter->launchProject();
  282. }
  283. break;
  284. case CommandIDs::saveAndOpenInIDE:
  285. if (project != nullptr)
  286. {
  287. if (! reinvokeCommandAfterClosingPropertyEditors (info))
  288. {
  289. if (project->save (true, true) == FileBasedDocument::savedOk)
  290. {
  291. ScopedPointer <ProjectExporter> exporter (ProjectExporter::createPlatformDefaultExporter (*project));
  292. if (exporter != nullptr)
  293. exporter->launchProject();
  294. }
  295. }
  296. }
  297. break;
  298. case CommandIDs::showProjectSettings:
  299. if (projectTree != nullptr)
  300. projectTree->getRootItem()->setSelected (true, true);
  301. break;
  302. case StandardApplicationCommandIDs::del:
  303. if (projectTree != nullptr)
  304. {
  305. ProjectTreeViewBase* p = dynamic_cast <ProjectTreeViewBase*> (projectTree->getRootItem());
  306. if (p != nullptr)
  307. p->deleteAllSelectedItems();
  308. }
  309. break;
  310. default:
  311. return false;
  312. }
  313. return true;
  314. }
  315. bool ProjectContentComponent::reinvokeCommandAfterClosingPropertyEditors (const InvocationInfo& info)
  316. {
  317. if (reinvokeCommandAfterCancellingModalComps (info))
  318. {
  319. grabKeyboardFocus(); // to force any open labels to close their text editors
  320. return true;
  321. }
  322. return false;
  323. }