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.

1538 lines
53KB

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