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.

1606 lines
56KB

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