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.

1450 lines
48KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "../jucer_Headers.h"
  18. #include "jucer_ProjectContentComponent.h"
  19. #include "jucer_Module.h"
  20. #include "../Application/jucer_MainWindow.h"
  21. #include "../Application/jucer_Application.h"
  22. #include "../Application/jucer_DownloadCompileEngineThread.h"
  23. #include "../Code Editor/jucer_SourceCodeEditor.h"
  24. #include "../Utility/jucer_FilePathPropertyComponent.h"
  25. #include "jucer_HeaderComponent.h"
  26. #include "jucer_ProjectTab.h"
  27. #include "jucer_LiveBuildTab.h"
  28. //==============================================================================
  29. struct ContentViewport : public Component
  30. {
  31. ContentViewport (Component* content)
  32. {
  33. addAndMakeVisible (viewport);
  34. viewport.setViewedComponent (content, true);
  35. }
  36. void resized() override
  37. {
  38. viewport.setBounds (getLocalBounds());
  39. }
  40. Viewport viewport;
  41. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentViewport)
  42. };
  43. //==========================================================================
  44. struct ProjectSettingsComp : public Component,
  45. private ChangeListener
  46. {
  47. ProjectSettingsComp (Project& p)
  48. : project (p),
  49. group (project.getProjectFilenameRoot(), Icon (getIcons().settings, Colours::transparentBlack))
  50. {
  51. addAndMakeVisible (group);
  52. updatePropertyList();
  53. project.addChangeListener (this);
  54. }
  55. ~ProjectSettingsComp()
  56. {
  57. project.removeChangeListener (this);
  58. }
  59. void resized() override
  60. {
  61. group.updateSize (12, 0, getWidth() - 24);
  62. group.setBounds (getLocalBounds().reduced (12, 0));
  63. }
  64. void updatePropertyList()
  65. {
  66. PropertyListBuilder props;
  67. project.createPropertyEditors (props);
  68. group.setProperties (props);
  69. group.setName ("Project Settings");
  70. lastProjectType = project.getProjectTypeValue().getValue();
  71. parentSizeChanged();
  72. }
  73. void changeListenerCallback (ChangeBroadcaster*) override
  74. {
  75. if (lastProjectType != project.getProjectTypeValue().getValue())
  76. updatePropertyList();
  77. }
  78. void parentSizeChanged() override
  79. {
  80. const auto width = jmax (550, getParentWidth());
  81. auto y = group.updateSize (12, 0, width - 12);
  82. y = jmax (getParentHeight(), y);
  83. setSize (width, y);
  84. }
  85. Project& project;
  86. var lastProjectType;
  87. ConfigTreeItemTypes::PropertyGroupComponent group;
  88. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectSettingsComp)
  89. };
  90. //==============================================================================
  91. struct LiveBuildSettingsComp : public Component
  92. {
  93. LiveBuildSettingsComp (Project& p)
  94. : group ("Live Build Settings", Icon (getIcons().settings, Colours::transparentBlack))
  95. {
  96. addAndMakeVisible (&group);
  97. PropertyListBuilder props;
  98. LiveBuildProjectSettings::getLiveSettings (p, props);
  99. group.setProperties (props);
  100. group.setName ("Live Build Settings");
  101. }
  102. void resized() override
  103. {
  104. group.updateSize (12, 0, getWidth() - 24);
  105. group.setBounds (getLocalBounds().reduced (12, 0));
  106. }
  107. void parentSizeChanged() override
  108. {
  109. const auto width = jmax (550, getParentWidth());
  110. auto y = group.updateSize (12, 0, width - 12);
  111. y = jmax (getParentHeight(), y);
  112. setSize (width, y);
  113. }
  114. ConfigTreeItemTypes::PropertyGroupComponent group;
  115. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LiveBuildSettingsComp)
  116. };
  117. //==============================================================================
  118. struct LogoComponent : public Component
  119. {
  120. LogoComponent()
  121. {
  122. ScopedPointer<XmlElement> svg (XmlDocument::parse (BinaryData::background_logo_svg));
  123. logo = Drawable::createFromSVG (*svg);
  124. }
  125. void paint (Graphics& g) override
  126. {
  127. g.setColour (findColour (defaultTextColourId));
  128. Rectangle<int> r (getLocalBounds());
  129. g.setFont (15.0f);
  130. g.drawFittedText (getVersionInfo(), r.removeFromBottom (50), Justification::centredBottom, 3);
  131. logo->drawWithin (g, r.withTrimmedBottom (r.getHeight() / 4).toFloat(),
  132. RectanglePlacement (RectanglePlacement::centred), 1.0f);
  133. }
  134. static String getVersionInfo()
  135. {
  136. return SystemStats::getJUCEVersion()
  137. + newLine
  138. + ProjucerApplication::getApp().getVersionDescription();
  139. }
  140. ScopedPointer<Drawable> logo;
  141. };
  142. //==============================================================================
  143. ProjectContentComponent::ProjectContentComponent()
  144. : project (nullptr),
  145. currentDocument (nullptr),
  146. sidebarTabs (TabbedButtonBar::TabsAtTop)
  147. {
  148. setOpaque (true);
  149. setWantsKeyboardFocus (true);
  150. addAndMakeVisible (logo = new LogoComponent());
  151. addAndMakeVisible (header = new HeaderComponent());
  152. addAndMakeVisible (fileNameLabel = new Label());
  153. fileNameLabel->setJustificationType (Justification::centred);
  154. sidebarSizeConstrainer.setMinimumWidth (200);
  155. sidebarSizeConstrainer.setMaximumWidth (500);
  156. sidebarTabs.setOutline (0);
  157. sidebarTabs.getTabbedButtonBar().setMinimumTabScaleFactor (0.5);
  158. ProjucerApplication::getApp().openDocumentManager.addListener (this);
  159. Desktop::getInstance().addFocusChangeListener (this);
  160. startTimer (1600);
  161. }
  162. ProjectContentComponent::~ProjectContentComponent()
  163. {
  164. Desktop::getInstance().removeFocusChangeListener (this);
  165. killChildProcess();
  166. ProjucerApplication::getApp().openDocumentManager.removeListener (this);
  167. logo = nullptr;
  168. header = nullptr;
  169. setProject (nullptr);
  170. contentView = nullptr;
  171. fileNameLabel = nullptr;
  172. removeChildComponent (&bubbleMessage);
  173. jassert (getNumChildComponents() <= 1);
  174. }
  175. void ProjectContentComponent::paint (Graphics& g)
  176. {
  177. g.fillAll (findColour (backgroundColourId));
  178. }
  179. void ProjectContentComponent::resized()
  180. {
  181. auto r = getLocalBounds();
  182. r.removeFromRight (10);
  183. r.removeFromLeft (15);
  184. r.removeFromBottom (40);
  185. r.removeFromTop (5);
  186. if (header != nullptr)
  187. header->setBounds (r.removeFromTop (40));
  188. r.removeFromTop (10);
  189. auto sidebarArea = r.removeFromLeft (sidebarTabs.getWidth() != 0 ? sidebarTabs.getWidth()
  190. : r.getWidth() / 4);
  191. if (sidebarTabs.isVisible())
  192. sidebarTabs.setBounds (sidebarArea);
  193. if (resizerBar != nullptr)
  194. resizerBar->setBounds (r.withWidth (4));
  195. if (auto* h = dynamic_cast<HeaderComponent*> (header.get()))
  196. {
  197. h->sidebarTabsWidthChanged (sidebarTabs.getWidth());
  198. r.removeFromRight (h->getUserButtonWidth());
  199. }
  200. if (contentView != nullptr)
  201. {
  202. if (fileNameLabel != nullptr && fileNameLabel->isVisible())
  203. fileNameLabel->setBounds (r.removeFromTop (15));
  204. contentView->setBounds (r);
  205. }
  206. if (logo != nullptr)
  207. logo->setBounds (r.reduced (r.getWidth() / 6, r.getHeight() / 6));
  208. }
  209. void ProjectContentComponent::lookAndFeelChanged()
  210. {
  211. repaint();
  212. if (translationTool != nullptr)
  213. translationTool->repaint();
  214. }
  215. void ProjectContentComponent::childBoundsChanged (Component* child)
  216. {
  217. if (child == &sidebarTabs)
  218. resized();
  219. }
  220. void ProjectContentComponent::setProject (Project* newProject)
  221. {
  222. if (project != newProject)
  223. {
  224. lastCrashMessage = String();
  225. killChildProcess();
  226. if (project != nullptr)
  227. project->removeChangeListener (this);
  228. contentView = nullptr;
  229. resizerBar = nullptr;
  230. deleteProjectTabs();
  231. project = newProject;
  232. rebuildProjectTabs();
  233. }
  234. }
  235. //==============================================================================
  236. LiveBuildTab* findBuildTab (const TabbedComponent& tabs)
  237. {
  238. return dynamic_cast<LiveBuildTab*> (tabs.getTabContentComponent (1));
  239. }
  240. bool ProjectContentComponent::isBuildTabEnabled() const
  241. {
  242. auto bt = findBuildTab (sidebarTabs);
  243. return bt != nullptr && bt->isEnabled;
  244. }
  245. void ProjectContentComponent::createProjectTabs()
  246. {
  247. jassert (project != nullptr);
  248. const auto tabColour = Colours::transparentBlack;
  249. auto* pTab = new ProjectTab (project);
  250. sidebarTabs.addTab ("Project", tabColour, pTab, true);
  251. const CompileEngineChildProcess::Ptr childProc (getChildProcess());
  252. sidebarTabs.addTab ("Build", tabColour, new LiveBuildTab (childProc, lastCrashMessage), true);
  253. if (childProc != nullptr)
  254. {
  255. childProc->crashHandler = [this] (const String& m) { this->handleCrash (m); };
  256. sidebarTabs.getTabbedButtonBar().getTabButton (1)->setExtraComponent (new BuildStatusTabComp (childProc->errorList,
  257. childProc->activityList),
  258. TabBarButton::afterText);
  259. }
  260. }
  261. void ProjectContentComponent::deleteProjectTabs()
  262. {
  263. if (project != nullptr && sidebarTabs.getNumTabs() > 0)
  264. {
  265. PropertiesFile& settings = project->getStoredProperties();
  266. if (sidebarTabs.getWidth() > 0)
  267. settings.setValue ("projectPanelWidth", sidebarTabs.getWidth());
  268. settings.setValue ("lastViewedTabIndex", sidebarTabs.getCurrentTabIndex());
  269. for (int i = 0; i < 3; ++i)
  270. settings.setValue ("projectTabPanelHeight" + String (i),
  271. getProjectTab()->getPanelHeightProportion (i));
  272. }
  273. sidebarTabs.clearTabs();
  274. }
  275. void ProjectContentComponent::rebuildProjectTabs()
  276. {
  277. deleteProjectTabs();
  278. if (project != nullptr)
  279. {
  280. addAndMakeVisible (sidebarTabs);
  281. createProjectTabs();
  282. //======================================================================
  283. auto& settings = project->getStoredProperties();
  284. auto lastTreeWidth = settings.getValue ("projectPanelWidth").getIntValue();
  285. if (lastTreeWidth < 150)
  286. lastTreeWidth = 240;
  287. sidebarTabs.setBounds (0, 0, lastTreeWidth, getHeight());
  288. sidebarTabs.setCurrentTabIndex (settings.getValue ("lastViewedTabIndex", "0").getIntValue());
  289. auto* projectTab = getProjectTab();
  290. for (int i = 2; i >= 0; --i)
  291. projectTab->setPanelHeightProportion (i, settings.getValue ("projectTabPanelHeight" + String (i), String ("1"))
  292. .getFloatValue());
  293. //======================================================================
  294. addAndMakeVisible (resizerBar = new ResizableEdgeComponent (&sidebarTabs, &sidebarSizeConstrainer,
  295. ResizableEdgeComponent::rightEdge));
  296. resizerBar->setAlwaysOnTop (true);
  297. project->addChangeListener (this);
  298. updateMissingFileStatuses();
  299. if (auto* h = dynamic_cast<HeaderComponent*> (header.get()))
  300. {
  301. h->setVisible (true);
  302. h->setCurrentProject (project);
  303. }
  304. }
  305. else
  306. {
  307. sidebarTabs.setVisible (false);
  308. header->setVisible (false);
  309. }
  310. resized();
  311. }
  312. void ProjectContentComponent::saveTreeViewState()
  313. {
  314. }
  315. void ProjectContentComponent::saveOpenDocumentList()
  316. {
  317. if (project != nullptr)
  318. {
  319. ScopedPointer<XmlElement> xml (recentDocumentList.createXML());
  320. if (xml != nullptr)
  321. project->getStoredProperties().setValue ("lastDocs", xml);
  322. }
  323. }
  324. void ProjectContentComponent::reloadLastOpenDocuments()
  325. {
  326. if (project != nullptr)
  327. {
  328. ScopedPointer<XmlElement> xml (project->getStoredProperties().getXmlValue ("lastDocs"));
  329. if (xml != nullptr)
  330. {
  331. recentDocumentList.restoreFromXML (*project, *xml);
  332. showDocument (recentDocumentList.getCurrentDocument(), true);
  333. }
  334. }
  335. }
  336. bool ProjectContentComponent::documentAboutToClose (OpenDocumentManager::Document* document)
  337. {
  338. hideDocument (document);
  339. return true;
  340. }
  341. void ProjectContentComponent::changeListenerCallback (ChangeBroadcaster*)
  342. {
  343. updateMissingFileStatuses();
  344. }
  345. void ProjectContentComponent::updateMissingFileStatuses()
  346. {
  347. if (auto* pTab = getProjectTab())
  348. if (auto* tree = pTab->getFileTreePanel())
  349. tree->updateMissingFileStatuses();
  350. }
  351. bool ProjectContentComponent::showEditorForFile (const File& f, bool grabFocus)
  352. {
  353. if (getCurrentFile() == f
  354. || showDocument (ProjucerApplication::getApp().openDocumentManager.openFile (project, f), grabFocus))
  355. {
  356. fileNameLabel->setText (f.getFileName(), dontSendNotification);
  357. return true;
  358. }
  359. return false;
  360. }
  361. bool ProjectContentComponent::hasFileInRecentList (const File& f) const
  362. {
  363. return recentDocumentList.contains (f);
  364. }
  365. File ProjectContentComponent::getCurrentFile() const
  366. {
  367. return currentDocument != nullptr ? currentDocument->getFile()
  368. : File();
  369. }
  370. bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc, bool grabFocus)
  371. {
  372. if (doc == nullptr)
  373. return false;
  374. if (doc->hasFileBeenModifiedExternally())
  375. doc->reloadFromFile();
  376. if (doc == getCurrentDocument() && contentView != nullptr)
  377. {
  378. if (grabFocus)
  379. contentView->grabKeyboardFocus();
  380. return true;
  381. }
  382. recentDocumentList.newDocumentOpened (doc);
  383. bool opened = setEditorComponent (doc->createEditor(), doc);
  384. if (opened && grabFocus && isShowing())
  385. contentView->grabKeyboardFocus();
  386. return opened;
  387. }
  388. void ProjectContentComponent::hideEditor()
  389. {
  390. currentDocument = nullptr;
  391. contentView = nullptr;
  392. ProjucerApplication::getCommandManager().commandStatusChanged();
  393. resized();
  394. }
  395. void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc)
  396. {
  397. if (doc == currentDocument)
  398. {
  399. if (OpenDocumentManager::Document* replacement = recentDocumentList.getClosestPreviousDocOtherThan (doc))
  400. showDocument (replacement, true);
  401. else
  402. hideEditor();
  403. }
  404. }
  405. bool ProjectContentComponent::setEditorComponent (Component* editor,
  406. OpenDocumentManager::Document* doc)
  407. {
  408. if (editor != nullptr)
  409. {
  410. contentView = nullptr;
  411. if (doc == nullptr)
  412. {
  413. auto* viewport = new ContentViewport (editor);
  414. contentView = viewport;
  415. currentDocument = nullptr;
  416. fileNameLabel->setVisible (false);
  417. addAndMakeVisible (viewport);
  418. }
  419. else
  420. {
  421. contentView = editor;
  422. currentDocument = doc;
  423. fileNameLabel->setVisible (true);
  424. addAndMakeVisible (editor);
  425. }
  426. resized();
  427. ProjucerApplication::getCommandManager().commandStatusChanged();
  428. return true;
  429. }
  430. return false;
  431. }
  432. void ProjectContentComponent::closeDocument()
  433. {
  434. if (currentDocument != nullptr)
  435. ProjucerApplication::getApp().openDocumentManager.closeDocument (currentDocument, true);
  436. else if (contentView != nullptr)
  437. if (! goToPreviousFile())
  438. hideEditor();
  439. }
  440. static void showSaveWarning (OpenDocumentManager::Document* currentDocument)
  441. {
  442. AlertWindow::showMessageBox (AlertWindow::WarningIcon,
  443. TRANS("Save failed!"),
  444. TRANS("Couldn't save the file:")
  445. + "\n" + currentDocument->getFile().getFullPathName());
  446. }
  447. void ProjectContentComponent::saveDocument()
  448. {
  449. if (currentDocument != nullptr)
  450. {
  451. if (! currentDocument->save())
  452. showSaveWarning (currentDocument);
  453. }
  454. else
  455. saveProject();
  456. }
  457. void ProjectContentComponent::saveAs()
  458. {
  459. if (currentDocument != nullptr && ! currentDocument->saveAs())
  460. showSaveWarning (currentDocument);
  461. }
  462. bool ProjectContentComponent::goToPreviousFile()
  463. {
  464. OpenDocumentManager::Document* doc = recentDocumentList.getCurrentDocument();
  465. if (doc == nullptr || doc == getCurrentDocument())
  466. doc = recentDocumentList.getPrevious();
  467. return showDocument (doc, true);
  468. }
  469. bool ProjectContentComponent::goToNextFile()
  470. {
  471. return showDocument (recentDocumentList.getNext(), true);
  472. }
  473. bool ProjectContentComponent::canGoToCounterpart() const
  474. {
  475. return currentDocument != nullptr
  476. && currentDocument->getCounterpartFile().exists();
  477. }
  478. bool ProjectContentComponent::goToCounterpart()
  479. {
  480. if (currentDocument != nullptr)
  481. {
  482. const File file (currentDocument->getCounterpartFile());
  483. if (file.exists())
  484. return showEditorForFile (file, true);
  485. }
  486. return false;
  487. }
  488. bool ProjectContentComponent::saveProject()
  489. {
  490. return project != nullptr
  491. && project->save (true, true) == FileBasedDocument::savedOk;
  492. }
  493. void ProjectContentComponent::closeProject()
  494. {
  495. if (auto* const mw = findParentComponentOfClass<MainWindow>())
  496. mw->closeCurrentProject();
  497. }
  498. void ProjectContentComponent::showProjectSettings()
  499. {
  500. setEditorComponent (new ProjectSettingsComp (*project), nullptr);
  501. }
  502. void ProjectContentComponent::showCurrentExporterSettings()
  503. {
  504. if (auto* h = dynamic_cast<HeaderComponent*> (header.get()))
  505. showExporterSettings (h->getSelectedExporterName());
  506. }
  507. void ProjectContentComponent::showExporterSettings (const String& exporterName)
  508. {
  509. showExportersPanel();
  510. if (auto* exportersPanel = getProjectTab()->getExportersTreePanel())
  511. {
  512. if (auto* exporters = dynamic_cast<ExportersTreeRoot*>(exportersPanel->rootItem.get()))
  513. {
  514. for (int i = exporters->getNumSubItems(); i >= 0; --i)
  515. {
  516. if (auto* e = dynamic_cast<ConfigTreeItemTypes::ExporterItem*> (exporters->getSubItem (i)))
  517. {
  518. if (e->getDisplayName() == exporterName)
  519. {
  520. if (e->isSelected())
  521. e->setSelected (false, true);
  522. e->setSelected (true, true);
  523. }
  524. }
  525. }
  526. }
  527. }
  528. }
  529. void ProjectContentComponent::showModule (const String& moduleID)
  530. {
  531. showModulesPanel();
  532. if (auto* modsPanel = getProjectTab()->getModuleTreePanel())
  533. {
  534. if (auto* mods = dynamic_cast<ConfigTreeItemTypes::EnabledModulesItem*> (modsPanel->rootItem.get()))
  535. {
  536. for (int i = mods->getNumSubItems(); --i >= 0;)
  537. {
  538. if (auto* m = dynamic_cast<ConfigTreeItemTypes::ModuleItem*> (mods->getSubItem (i)))
  539. {
  540. if (m->moduleID == moduleID)
  541. {
  542. if (m->isSelected())
  543. m->setSelected (false, true);
  544. m->setSelected (true, true);
  545. }
  546. }
  547. }
  548. }
  549. }
  550. }
  551. void ProjectContentComponent::showLiveBuildSettings()
  552. {
  553. setEditorComponent (new LiveBuildSettingsComp (*project), nullptr);
  554. }
  555. void ProjectContentComponent::showUserSettings()
  556. {
  557. if (auto* headerComp = dynamic_cast<HeaderComponent*> (header.get()))
  558. headerComp->showUserSettings();
  559. }
  560. StringArray ProjectContentComponent::getExportersWhichCanLaunch() const
  561. {
  562. StringArray s;
  563. if (project != nullptr)
  564. for (Project::ExporterIterator exporter (*project); exporter.next();)
  565. if (exporter->canLaunchProject())
  566. s.add (exporter->getName());
  567. return s;
  568. }
  569. void ProjectContentComponent::openInSelectedIDE (bool saveFirst)
  570. {
  571. if (project != nullptr)
  572. {
  573. if (auto* headerComp = dynamic_cast<HeaderComponent*> (header.get()))
  574. {
  575. auto selectedIDE = headerComp->getSelectedExporterName();
  576. for (Project::ExporterIterator exporter (*project); exporter.next();)
  577. {
  578. if (exporter->canLaunchProject() && exporter->getName() == selectedIDE)
  579. {
  580. if (saveFirst)
  581. saveProject();
  582. exporter->launchProject();
  583. break;
  584. }
  585. }
  586. }
  587. }
  588. }
  589. static void newExporterMenuCallback (int result, ProjectContentComponent* comp)
  590. {
  591. if (comp != nullptr && result > 0)
  592. {
  593. if (Project* p = comp->getProject())
  594. {
  595. String exporterName (ProjectExporter::getExporterNames() [result - 1]);
  596. if (exporterName.isNotEmpty())
  597. p->addNewExporter (exporterName);
  598. }
  599. }
  600. }
  601. void ProjectContentComponent::showNewExporterMenu()
  602. {
  603. if (project != nullptr)
  604. {
  605. PopupMenu menu;
  606. menu.addSectionHeader ("Create a new export target:");
  607. Array<ProjectExporter::ExporterTypeInfo> exporters (ProjectExporter::getExporterTypes());
  608. for (int i = 0; i < exporters.size(); ++i)
  609. {
  610. const ProjectExporter::ExporterTypeInfo& type = exporters.getReference(i);
  611. menu.addItem (i + 1, type.name, true, false, type.getIcon());
  612. }
  613. menu.showMenuAsync (PopupMenu::Options(),
  614. ModalCallbackFunction::forComponent (newExporterMenuCallback, this));
  615. }
  616. }
  617. void ProjectContentComponent::deleteSelectedTreeItems()
  618. {
  619. if (auto* const tree = getProjectTab()->getTreeWithSelectedItems())
  620. tree->deleteSelectedItems();
  621. }
  622. void ProjectContentComponent::showBubbleMessage (Rectangle<int> pos, const String& text)
  623. {
  624. addChildComponent (bubbleMessage);
  625. bubbleMessage.setColour (BubbleComponent::backgroundColourId, Colours::white.withAlpha (0.7f));
  626. bubbleMessage.setColour (BubbleComponent::outlineColourId, Colours::black.withAlpha (0.8f));
  627. bubbleMessage.setAlwaysOnTop (true);
  628. bubbleMessage.showAt (pos, AttributedString (text), 3000, true, false);
  629. }
  630. //==============================================================================
  631. void ProjectContentComponent::showTranslationTool()
  632. {
  633. if (translationTool != nullptr)
  634. {
  635. translationTool->toFront (true);
  636. }
  637. else if (project != nullptr)
  638. {
  639. new FloatingToolWindow ("Translation File Builder",
  640. "transToolWindowPos",
  641. new TranslationToolComponent(),
  642. translationTool, true,
  643. 600, 700,
  644. 600, 400, 10000, 10000);
  645. }
  646. }
  647. //==============================================================================
  648. struct AsyncCommandRetrier : public Timer
  649. {
  650. AsyncCommandRetrier (const ApplicationCommandTarget::InvocationInfo& i) : info (i)
  651. {
  652. info.originatingComponent = nullptr;
  653. startTimer (500);
  654. }
  655. void timerCallback() override
  656. {
  657. stopTimer();
  658. ProjucerApplication::getCommandManager().invoke (info, true);
  659. delete this;
  660. }
  661. ApplicationCommandTarget::InvocationInfo info;
  662. JUCE_DECLARE_NON_COPYABLE (AsyncCommandRetrier)
  663. };
  664. bool reinvokeCommandAfterCancellingModalComps (const ApplicationCommandTarget::InvocationInfo& info)
  665. {
  666. if (ModalComponentManager::getInstance()->cancelAllModalComponents())
  667. {
  668. new AsyncCommandRetrier (info);
  669. return true;
  670. }
  671. return false;
  672. }
  673. //==============================================================================
  674. ApplicationCommandTarget* ProjectContentComponent::getNextCommandTarget()
  675. {
  676. return findFirstTargetParentComponent();
  677. }
  678. void ProjectContentComponent::getAllCommands (Array <CommandID>& commands)
  679. {
  680. const CommandID ids[] = { CommandIDs::saveProject,
  681. CommandIDs::closeProject,
  682. CommandIDs::saveDocument,
  683. CommandIDs::saveDocumentAs,
  684. CommandIDs::closeDocument,
  685. CommandIDs::goToPreviousDoc,
  686. CommandIDs::goToNextDoc,
  687. CommandIDs::goToCounterpart,
  688. CommandIDs::showProjectSettings,
  689. CommandIDs::showProjectTab,
  690. CommandIDs::showBuildTab,
  691. CommandIDs::showFileExplorerPanel,
  692. CommandIDs::showModulesPanel,
  693. CommandIDs::showExportersPanel,
  694. CommandIDs::showExporterSettings,
  695. CommandIDs::openInIDE,
  696. CommandIDs::saveAndOpenInIDE,
  697. CommandIDs::createNewExporter,
  698. CommandIDs::deleteSelectedItem,
  699. CommandIDs::showTranslationTool,
  700. CommandIDs::cleanAll,
  701. CommandIDs::toggleBuildEnabled,
  702. CommandIDs::buildNow,
  703. CommandIDs::toggleContinuousBuild,
  704. CommandIDs::launchApp,
  705. CommandIDs::killApp,
  706. CommandIDs::reinstantiateComp,
  707. CommandIDs::showWarnings,
  708. CommandIDs::nextError,
  709. CommandIDs::prevError };
  710. commands.addArray (ids, numElementsInArray (ids));
  711. }
  712. void ProjectContentComponent::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
  713. {
  714. String documentName;
  715. if (currentDocument != nullptr)
  716. documentName = " '" + currentDocument->getName().substring (0, 32) + "'";
  717. #if JUCE_MAC
  718. const ModifierKeys cmdCtrl (ModifierKeys::ctrlModifier | ModifierKeys::commandModifier);
  719. #else
  720. const ModifierKeys cmdCtrl (ModifierKeys::ctrlModifier | ModifierKeys::altModifier);
  721. #endif
  722. switch (commandID)
  723. {
  724. case CommandIDs::saveProject:
  725. result.setInfo ("Save Project",
  726. "Saves the current project",
  727. CommandCategories::general, 0);
  728. result.setActive (project != nullptr);
  729. break;
  730. case CommandIDs::closeProject:
  731. result.setInfo ("Close Project",
  732. "Closes the current project",
  733. CommandCategories::general, 0);
  734. result.setActive (project != nullptr);
  735. break;
  736. case CommandIDs::saveDocument:
  737. result.setInfo ("Save" + documentName,
  738. "Saves the current document",
  739. CommandCategories::general, 0);
  740. result.setActive (currentDocument != nullptr || project != nullptr);
  741. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier, 0));
  742. break;
  743. case CommandIDs::saveDocumentAs:
  744. result.setInfo ("Save As...",
  745. "Saves the current document to a new location",
  746. CommandCategories::general, 0);
  747. result.setActive (currentDocument != nullptr);
  748. result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  749. break;
  750. case CommandIDs::closeDocument:
  751. result.setInfo ("Close" + documentName,
  752. "Closes the current document",
  753. CommandCategories::general, 0);
  754. result.setActive (contentView != nullptr);
  755. result.defaultKeypresses.add (KeyPress ('w', cmdCtrl, 0));
  756. break;
  757. case CommandIDs::goToPreviousDoc:
  758. result.setInfo ("Previous Document",
  759. "Go to previous document",
  760. CommandCategories::general, 0);
  761. result.setActive (recentDocumentList.canGoToPrevious());
  762. result.defaultKeypresses.add (KeyPress (KeyPress::leftKey, cmdCtrl, 0));
  763. break;
  764. case CommandIDs::goToNextDoc:
  765. result.setInfo ("Next Document",
  766. "Go to next document",
  767. CommandCategories::general, 0);
  768. result.setActive (recentDocumentList.canGoToNext());
  769. result.defaultKeypresses.add (KeyPress (KeyPress::rightKey, cmdCtrl, 0));
  770. break;
  771. case CommandIDs::goToCounterpart:
  772. result.setInfo ("Open Counterpart File",
  773. "Open corresponding header or cpp file",
  774. CommandCategories::general, 0);
  775. result.setActive (canGoToCounterpart());
  776. result.defaultKeypresses.add (KeyPress (KeyPress::upKey, cmdCtrl, 0));
  777. break;
  778. case CommandIDs::showProjectSettings:
  779. result.setInfo ("Show Project Settings",
  780. "Shows the main project options page",
  781. CommandCategories::general, 0);
  782. result.setActive (project != nullptr);
  783. result.defaultKeypresses.add (KeyPress ('p', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  784. break;
  785. case CommandIDs::showProjectTab:
  786. result.setInfo ("Show Project Tab",
  787. "Shows the tab containing the project information",
  788. CommandCategories::general, 0);
  789. result.setActive (project != nullptr);
  790. result.defaultKeypresses.add (KeyPress ('p', ModifierKeys::commandModifier, 0));
  791. break;
  792. case CommandIDs::showBuildTab:
  793. result.setInfo ("Show Build Tab",
  794. "Shows the tab containing the build panel",
  795. CommandCategories::general, 0);
  796. result.setActive (project != nullptr);
  797. result.defaultKeypresses.add (KeyPress ('b', ModifierKeys::commandModifier, 0));
  798. break;
  799. case CommandIDs::showFileExplorerPanel:
  800. result.setInfo ("Show File Explorer Panel",
  801. "Shows the panel containing the tree of files for this project",
  802. CommandCategories::general, 0);
  803. result.setActive (project != nullptr);
  804. result.defaultKeypresses.add (KeyPress ('f', ModifierKeys::commandModifier, 0));
  805. break;
  806. case CommandIDs::showModulesPanel:
  807. result.setInfo ("Show Modules Panel",
  808. "Shows the panel containing the project's list of modules",
  809. CommandCategories::general, 0);
  810. result.setActive (project != nullptr);
  811. result.defaultKeypresses.add (KeyPress ('m', ModifierKeys::commandModifier, 0));
  812. break;
  813. case CommandIDs::showExportersPanel:
  814. result.setInfo ("Show Exporters Panel",
  815. "Shows the panel containing the project's list of exporters",
  816. CommandCategories::general, 0);
  817. result.setActive (project != nullptr);
  818. result.defaultKeypresses.add (KeyPress ('e', ModifierKeys::commandModifier, 0));
  819. break;
  820. case CommandIDs::showExporterSettings:
  821. result.setInfo ("Show Exporter Settings",
  822. "Shows the settings page for the currently selected exporter",
  823. CommandCategories::general, 0);
  824. result.setActive (project != nullptr);
  825. result.defaultKeypresses.add (KeyPress ('e', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  826. break;
  827. case CommandIDs::openInIDE:
  828. result.setInfo ("Open in IDE...",
  829. "Launches the project in an external IDE",
  830. CommandCategories::general, 0);
  831. result.setActive (ProjectExporter::canProjectBeLaunched (project));
  832. break;
  833. case CommandIDs::saveAndOpenInIDE:
  834. result.setInfo ("Save Project and Open in IDE...",
  835. "Saves the project and launches it in an external IDE",
  836. CommandCategories::general, 0);
  837. result.setActive (ProjectExporter::canProjectBeLaunched (project));
  838. result.defaultKeypresses.add (KeyPress ('l', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  839. break;
  840. case CommandIDs::createNewExporter:
  841. result.setInfo ("Create New Exporter...",
  842. "Creates a new exporter for a compiler type",
  843. CommandCategories::general, 0);
  844. result.setActive (project != nullptr);
  845. break;
  846. case CommandIDs::deleteSelectedItem:
  847. result.setInfo ("Delete Selected File",
  848. String(),
  849. CommandCategories::general, 0);
  850. result.defaultKeypresses.add (KeyPress (KeyPress::deleteKey, 0, 0));
  851. result.defaultKeypresses.add (KeyPress (KeyPress::backspaceKey, 0, 0));
  852. result.setActive (sidebarTabs.getCurrentTabIndex() == 0);
  853. break;
  854. case CommandIDs::showTranslationTool:
  855. result.setInfo ("Translation File Builder",
  856. "Shows the translation file helper tool",
  857. CommandCategories::general, 0);
  858. break;
  859. case CommandIDs::cleanAll:
  860. result.setInfo ("Clean All",
  861. "Cleans all intermediate files",
  862. CommandCategories::general, 0);
  863. result.defaultKeypresses.add (KeyPress ('k', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  864. result.setActive (project != nullptr);
  865. break;
  866. case CommandIDs::toggleBuildEnabled:
  867. result.setInfo ("Enable Compilation",
  868. "Enables/disables the compiler",
  869. CommandCategories::general, 0);
  870. result.defaultKeypresses.add (KeyPress ('b', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  871. result.setActive (project != nullptr);
  872. result.setTicked (childProcess != nullptr);
  873. break;
  874. case CommandIDs::buildNow:
  875. result.setInfo ("Build Now",
  876. "Recompiles any out-of-date files and updates the JIT engine",
  877. CommandCategories::general, 0);
  878. result.defaultKeypresses.add (KeyPress ('b', ModifierKeys::commandModifier, 0));
  879. result.setActive (childProcess != nullptr);
  880. break;
  881. case CommandIDs::toggleContinuousBuild:
  882. result.setInfo ("Enable Continuous Recompiling",
  883. "Continuously recompiles any changes made in code editors",
  884. CommandCategories::general, 0);
  885. result.setActive (childProcess != nullptr);
  886. result.setTicked (isContinuousRebuildEnabled());
  887. break;
  888. case CommandIDs::launchApp:
  889. result.setInfo ("Launch Application",
  890. "Invokes the app's main() function",
  891. CommandCategories::general, 0);
  892. result.defaultKeypresses.add (KeyPress ('r', ModifierKeys::commandModifier, 0));
  893. result.setActive (childProcess != nullptr && childProcess->canLaunchApp());
  894. break;
  895. case CommandIDs::killApp:
  896. result.setInfo ("Stop Application",
  897. "Kills the app if it's running",
  898. CommandCategories::general, 0);
  899. result.defaultKeypresses.add (KeyPress ('.', ModifierKeys::commandModifier, 0));
  900. result.setActive (childProcess != nullptr && childProcess->canKillApp());
  901. break;
  902. case CommandIDs::reinstantiateComp:
  903. result.setInfo ("Re-instantiate Components",
  904. "Re-loads any component editors that are open",
  905. CommandCategories::general, 0);
  906. result.defaultKeypresses.add (KeyPress ('r', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
  907. result.setActive (childProcess != nullptr);
  908. break;
  909. case CommandIDs::showWarnings:
  910. result.setInfo ("Show Warnings",
  911. "Shows or hides compilation warnings",
  912. CommandCategories::general, 0);
  913. result.setActive (project != nullptr);
  914. result.setTicked (areWarningsEnabled());
  915. break;
  916. case CommandIDs::nextError:
  917. result.setInfo ("Highlight next error",
  918. "Jumps to the next error or warning",
  919. CommandCategories::general, 0);
  920. result.defaultKeypresses.add (KeyPress ('\'', ModifierKeys::commandModifier, 0));
  921. result.setActive (childProcess != nullptr && ! childProcess->errorList.isEmpty());
  922. break;
  923. case CommandIDs::prevError:
  924. result.setInfo ("Highlight previous error",
  925. "Jumps to the last error or warning",
  926. CommandCategories::general, 0);
  927. result.defaultKeypresses.add (KeyPress ('\"', ModifierKeys::commandModifier, 0));
  928. result.setActive (childProcess != nullptr && ! childProcess->errorList.isEmpty());
  929. break;
  930. default:
  931. break;
  932. }
  933. }
  934. bool ProjectContentComponent::perform (const InvocationInfo& info)
  935. {
  936. switch (info.commandID)
  937. {
  938. case CommandIDs::saveProject:
  939. case CommandIDs::closeProject:
  940. case CommandIDs::saveDocument:
  941. case CommandIDs::saveDocumentAs:
  942. case CommandIDs::closeDocument:
  943. case CommandIDs::goToPreviousDoc:
  944. case CommandIDs::goToNextDoc:
  945. case CommandIDs::goToCounterpart:
  946. case CommandIDs::saveAndOpenInIDE:
  947. if (reinvokeCommandAfterCancellingModalComps (info))
  948. {
  949. grabKeyboardFocus(); // to force any open labels to close their text editors
  950. return true;
  951. }
  952. break;
  953. default:
  954. break;
  955. }
  956. if (isCurrentlyBlockedByAnotherModalComponent())
  957. return false;
  958. switch (info.commandID)
  959. {
  960. case CommandIDs::saveProject: saveProject(); break;
  961. case CommandIDs::closeProject: closeProject(); break;
  962. case CommandIDs::saveDocument: saveDocument(); break;
  963. case CommandIDs::saveDocumentAs: saveAs(); break;
  964. case CommandIDs::closeDocument: closeDocument(); break;
  965. case CommandIDs::goToPreviousDoc: goToPreviousFile(); break;
  966. case CommandIDs::goToNextDoc: goToNextFile(); break;
  967. case CommandIDs::goToCounterpart: goToCounterpart(); break;
  968. case CommandIDs::showProjectSettings: showProjectSettings(); break;
  969. case CommandIDs::showProjectTab: showProjectTab(); break;
  970. case CommandIDs::showBuildTab: showBuildTab(); break;
  971. case CommandIDs::showFileExplorerPanel: showFilesPanel(); break;
  972. case CommandIDs::showModulesPanel: showModulesPanel(); break;
  973. case CommandIDs::showExportersPanel: showExportersPanel(); break;
  974. case CommandIDs::showExporterSettings: showCurrentExporterSettings(); break;
  975. case CommandIDs::openInIDE: openInSelectedIDE (false); break;
  976. case CommandIDs::saveAndOpenInIDE: openInSelectedIDE (true); break;
  977. case CommandIDs::createNewExporter: showNewExporterMenu(); break;
  978. case CommandIDs::deleteSelectedItem: deleteSelectedTreeItems(); break;
  979. case CommandIDs::showTranslationTool: showTranslationTool(); break;
  980. case CommandIDs::cleanAll: cleanAll(); break;
  981. case CommandIDs::toggleBuildEnabled: setBuildEnabled (! isBuildEnabled()); break;
  982. case CommandIDs::buildNow: rebuildNow(); break;
  983. case CommandIDs::toggleContinuousBuild: setContinuousRebuildEnabled (! isContinuousRebuildEnabled()); break;
  984. case CommandIDs::launchApp: launchApp(); break;
  985. case CommandIDs::killApp: killApp(); break;
  986. case CommandIDs::reinstantiateComp: reinstantiateLivePreviewWindows(); break;
  987. case CommandIDs::showWarnings: toggleWarnings(); break;
  988. case CommandIDs::nextError: showNextError(); break;
  989. case CommandIDs::prevError: showPreviousError(); break;
  990. default:
  991. return false;
  992. }
  993. return true;
  994. }
  995. void ProjectContentComponent::getSelectedProjectItemsBeingDragged (const DragAndDropTarget::SourceDetails& dragSourceDetails,
  996. OwnedArray<Project::Item>& selectedNodes)
  997. {
  998. FileTreeItemTypes::ProjectTreeItemBase::getSelectedProjectItemsBeingDragged (dragSourceDetails, selectedNodes);
  999. }
  1000. //==============================================================================
  1001. void ProjectContentComponent::killChildProcess()
  1002. {
  1003. if (childProcess != nullptr)
  1004. {
  1005. deleteProjectTabs();
  1006. childProcess = nullptr;
  1007. ProjucerApplication::getApp().childProcessCache->removeOrphans();
  1008. }
  1009. }
  1010. void ProjectContentComponent::setBuildEnabled (bool b)
  1011. {
  1012. if (project != nullptr && b != isBuildEnabled())
  1013. {
  1014. LiveBuildProjectSettings::setBuildDisabled (*project, ! b);
  1015. killChildProcess();
  1016. refreshTabsIfBuildStatusChanged();
  1017. }
  1018. }
  1019. void ProjectContentComponent::cleanAll()
  1020. {
  1021. lastCrashMessage = String();
  1022. if (childProcess != nullptr)
  1023. childProcess->cleanAll();
  1024. else if (Project* p = getProject())
  1025. CompileEngineChildProcess::cleanAllCachedFilesForProject (*p);
  1026. }
  1027. void ProjectContentComponent::handleCrash (const String& message)
  1028. {
  1029. lastCrashMessage = message.isEmpty() ? TRANS("JIT process stopped responding!")
  1030. : (TRANS("JIT process crashed!") + ":\n\n" + message);
  1031. if (project != nullptr)
  1032. {
  1033. setBuildEnabled (false);
  1034. showBuildTab();
  1035. }
  1036. }
  1037. bool ProjectContentComponent::isBuildEnabled() const
  1038. {
  1039. return project != nullptr && ! LiveBuildProjectSettings::isBuildDisabled (*project)
  1040. && CompileEngineDLL::getInstance()->isLoaded();
  1041. }
  1042. void ProjectContentComponent::refreshTabsIfBuildStatusChanged()
  1043. {
  1044. if (project != nullptr
  1045. && (sidebarTabs.getNumTabs() < 2
  1046. || isBuildEnabled() != isBuildTabEnabled()))
  1047. rebuildProjectTabs();
  1048. }
  1049. bool ProjectContentComponent::areWarningsEnabled() const
  1050. {
  1051. return project != nullptr && ! LiveBuildProjectSettings::areWarningsDisabled (*project);
  1052. }
  1053. void ProjectContentComponent::updateWarningState()
  1054. {
  1055. if (childProcess != nullptr)
  1056. childProcess->errorList.setWarningsEnabled (areWarningsEnabled());
  1057. }
  1058. void ProjectContentComponent::toggleWarnings()
  1059. {
  1060. if (project != nullptr)
  1061. {
  1062. LiveBuildProjectSettings::setWarningsDisabled (*project, areWarningsEnabled());
  1063. updateWarningState();
  1064. }
  1065. }
  1066. static ProjucerAppClasses::ErrorListComp* findErrorListComp (const TabbedComponent& tabs)
  1067. {
  1068. if (LiveBuildTab* bt = findBuildTab (tabs))
  1069. return bt->errorListComp;
  1070. return nullptr;
  1071. }
  1072. void ProjectContentComponent::showNextError()
  1073. {
  1074. if (ProjucerAppClasses::ErrorListComp* el = findErrorListComp (sidebarTabs))
  1075. {
  1076. showBuildTab();
  1077. el->showNext();
  1078. }
  1079. }
  1080. void ProjectContentComponent::showPreviousError()
  1081. {
  1082. if (ProjucerAppClasses::ErrorListComp* el = findErrorListComp (sidebarTabs))
  1083. {
  1084. showBuildTab();
  1085. el->showPrevious();
  1086. }
  1087. }
  1088. void ProjectContentComponent::reinstantiateLivePreviewWindows()
  1089. {
  1090. if (childProcess != nullptr)
  1091. childProcess->reinstantiatePreviews();
  1092. }
  1093. void ProjectContentComponent::launchApp()
  1094. {
  1095. if (childProcess != nullptr)
  1096. childProcess->launchApp();
  1097. }
  1098. void ProjectContentComponent::killApp()
  1099. {
  1100. if (childProcess != nullptr)
  1101. childProcess->killApp();
  1102. }
  1103. void ProjectContentComponent::rebuildNow()
  1104. {
  1105. if (childProcess != nullptr)
  1106. childProcess->flushEditorChanges();
  1107. }
  1108. void ProjectContentComponent::globalFocusChanged (Component* focusedComponent)
  1109. {
  1110. const bool nowForeground = (Process::isForegroundProcess()
  1111. && (focusedComponent == this || isParentOf (focusedComponent)));
  1112. if (nowForeground != isForeground)
  1113. {
  1114. isForeground = nowForeground;
  1115. if (childProcess != nullptr)
  1116. childProcess->processActivationChanged (isForeground);
  1117. }
  1118. }
  1119. void ProjectContentComponent::timerCallback()
  1120. {
  1121. if (! isBuildEnabled())
  1122. killChildProcess();
  1123. refreshTabsIfBuildStatusChanged();
  1124. }
  1125. bool ProjectContentComponent::isContinuousRebuildEnabled()
  1126. {
  1127. return getAppSettings().getGlobalProperties().getBoolValue ("continuousRebuild", true);
  1128. }
  1129. void ProjectContentComponent::setContinuousRebuildEnabled (bool b)
  1130. {
  1131. getAppSettings().getGlobalProperties().setValue ("continuousRebuild", b);
  1132. if (childProcess != nullptr)
  1133. childProcess->setContinuousRebuild (b);
  1134. }
  1135. ReferenceCountedObjectPtr<CompileEngineChildProcess> ProjectContentComponent::getChildProcess()
  1136. {
  1137. if (childProcess == nullptr && isBuildEnabled())
  1138. {
  1139. childProcess = ProjucerApplication::getApp().childProcessCache->getOrCreate (*project);
  1140. if (childProcess != nullptr)
  1141. childProcess->setContinuousRebuild (isContinuousRebuildEnabled());
  1142. }
  1143. return childProcess;
  1144. }
  1145. void ProjectContentComponent::handleMissingSystemHeaders()
  1146. {
  1147. #if JUCE_MAC
  1148. const String tabMessage = "Compiler not available due to missing system headers\nPlease install a recent version of Xcode";
  1149. const String alertWindowMessage = "Missing system headers\nPlease install a recent version of Xcode";
  1150. #elif JUCE_WINDOWS
  1151. const String tabMessage = "Compiler not available due to missing system headers\nPlease install a recent version of Visual Studio and the Windows Desktop SDK";
  1152. const String alertWindowMessage = "Missing system headers\nPlease install a recent version of Visual Studio and the Windows Desktop SDK";
  1153. #elif JUCE_LINUX
  1154. const String tabMessage = "Compiler not available due to missing system headers\nPlease do a sudo apt-get install ...";
  1155. const String alertWindowMessage = "Missing system headers\nPlease do sudo apt-get install ...";
  1156. #endif
  1157. setBuildEnabled (false);
  1158. deleteProjectTabs();
  1159. createProjectTabs();
  1160. if (auto* bt = getLiveBuildTab())
  1161. {
  1162. bt->isEnabled = false;
  1163. bt->errorMessage = tabMessage;
  1164. }
  1165. showBuildTab();
  1166. AlertWindow::showMessageBox (AlertWindow::AlertIconType::WarningIcon,
  1167. "Missing system headers", alertWindowMessage);
  1168. }
  1169. //==============================================================================
  1170. void ProjectContentComponent::showProjectPanel (const int index)
  1171. {
  1172. showProjectTab();
  1173. if (auto* pTab = getProjectTab())
  1174. pTab->showPanel (index);
  1175. }
  1176. ProjectTab* ProjectContentComponent::getProjectTab()
  1177. {
  1178. return dynamic_cast<ProjectTab*> (sidebarTabs.getTabContentComponent (0));
  1179. }
  1180. LiveBuildTab* ProjectContentComponent::getLiveBuildTab()
  1181. {
  1182. return dynamic_cast<LiveBuildTab*> (sidebarTabs.getTabContentComponent (1));
  1183. }