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.

1472 lines
49KB

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