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.

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