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.

393 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #include "jucer_HeaderComponent.h"
  19. #include "../../Application/jucer_Application.h"
  20. #include "../../ProjectSaving/jucer_ProjectExporter.h"
  21. #include "../../Project/UI/jucer_ProjectContentComponent.h"
  22. #include "../../LiveBuildEngine/jucer_MessageIDs.h"
  23. #include "../../LiveBuildEngine/jucer_SourceCodeRange.h"
  24. #include "../../LiveBuildEngine/jucer_ClassDatabase.h"
  25. #include "../../LiveBuildEngine/jucer_DiagnosticMessage.h"
  26. #include "../../LiveBuildEngine/jucer_CompileEngineClient.h"
  27. //==============================================================================
  28. HeaderComponent::HeaderComponent (ProjectContentComponent* pcc)
  29. : projectContentComponent (pcc)
  30. {
  31. addAndMakeVisible (configLabel);
  32. addAndMakeVisible (exporterBox);
  33. exporterBox.onChange = [this] { updateExporterButton(); };
  34. juceIcon.setImage (ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize), RectanglePlacement::centred);
  35. addAndMakeVisible (juceIcon);
  36. addAndMakeVisible (userAvatar);
  37. userAvatar.addChangeListener (this);
  38. projectNameLabel.setText ({}, dontSendNotification);
  39. addAndMakeVisible (projectNameLabel);
  40. initialiseButtons();
  41. }
  42. HeaderComponent::~HeaderComponent()
  43. {
  44. if (childProcess != nullptr)
  45. {
  46. childProcess->activityList.removeChangeListener (this);
  47. childProcess->errorList.removeChangeListener (this);
  48. }
  49. }
  50. //==============================================================================
  51. void HeaderComponent::resized()
  52. {
  53. auto bounds = getLocalBounds();
  54. configLabel.setFont ({ (float) bounds.getHeight() / 3.0f });
  55. {
  56. auto headerBounds = bounds.removeFromLeft (tabsWidth);
  57. const int buttonSize = 25;
  58. auto buttonBounds = headerBounds.removeFromRight (buttonSize);
  59. projectSettingsButton.setBounds (buttonBounds.removeFromBottom (buttonSize).reduced (2));
  60. juceIcon.setBounds (headerBounds.removeFromLeft (headerBounds.getHeight()).reduced (2));
  61. headerBounds.removeFromRight (5);
  62. projectNameLabel.setBounds (headerBounds);
  63. }
  64. {
  65. auto exporterWidth = jmin (400, bounds.getWidth() / 2);
  66. Rectangle<int> exporterBounds (0, 0, exporterWidth, bounds.getHeight());
  67. exporterBounds.setCentre (bounds.getCentre());
  68. runAppButton.setBounds (exporterBounds.removeFromRight (exporterBounds.getHeight()).reduced (2));
  69. saveAndOpenInIDEButton.setBounds (exporterBounds.removeFromRight (exporterBounds.getHeight()).reduced (2));
  70. exporterBounds.removeFromRight (5);
  71. exporterBox.setBounds (exporterBounds.removeFromBottom (roundToInt ((float) exporterBounds.getHeight() / 1.8f)));
  72. configLabel.setBounds (exporterBounds);
  73. }
  74. userAvatar.setBounds (bounds.removeFromRight (userAvatar.isDisplaingGPLLogo() ? roundToInt ((float) bounds.getHeight() * 1.9f)
  75. : bounds.getHeight()).reduced (2));
  76. }
  77. void HeaderComponent::paint (Graphics& g)
  78. {
  79. g.fillAll (findColour (backgroundColourId));
  80. if (isBuilding)
  81. getLookAndFeel().drawSpinningWaitAnimation (g, findColour (treeIconColourId),
  82. runAppButton.getX(), runAppButton.getY(),
  83. runAppButton.getWidth(), runAppButton.getHeight());
  84. }
  85. //==============================================================================
  86. void HeaderComponent::setCurrentProject (Project* newProject)
  87. {
  88. isBuilding = false;
  89. stopTimer();
  90. repaint();
  91. projectNameLabel.setText ({}, dontSendNotification);
  92. project = newProject;
  93. if (project != nullptr)
  94. {
  95. exportersTree = project->getExporters();
  96. exportersTree.addListener (this);
  97. updateExporters();
  98. projectNameValue.referTo (project->getProjectValue (Ids::name));
  99. projectNameValue.addListener (this);
  100. updateName();
  101. childProcess = ProjucerApplication::getApp().childProcessCache->getExisting (*project);
  102. if (childProcess != nullptr)
  103. {
  104. childProcess->activityList.addChangeListener (this);
  105. childProcess->errorList.addChangeListener (this);
  106. runAppButton.setTooltip ({});
  107. runAppButton.setEnabled (true);
  108. }
  109. else
  110. {
  111. runAppButton.setTooltip ("Enable live-build engine to launch application");
  112. runAppButton.setEnabled (false);
  113. }
  114. }
  115. }
  116. //==============================================================================
  117. void HeaderComponent::updateExporters()
  118. {
  119. auto selectedExporter = getSelectedExporter();
  120. exporterBox.clear();
  121. auto preferredExporterIndex = -1;
  122. int i = 0;
  123. for (Project::ExporterIterator exporter (*project); exporter.next(); ++i)
  124. {
  125. auto exporterName = exporter->getUniqueName();
  126. exporterBox.addItem (exporterName, i + 1);
  127. if (selectedExporter != nullptr && exporterName == selectedExporter->getUniqueName())
  128. exporterBox.setSelectedId (i + 1);
  129. if (exporterName.contains (ProjectExporter::getCurrentPlatformExporterTypeInfo().displayName) && preferredExporterIndex == -1)
  130. preferredExporterIndex = i;
  131. }
  132. if (exporterBox.getSelectedItemIndex() == -1)
  133. {
  134. if (preferredExporterIndex == -1)
  135. {
  136. i = 0;
  137. for (Project::ExporterIterator exporter (*project); exporter.next(); ++i)
  138. {
  139. if (exporter->canLaunchProject())
  140. {
  141. preferredExporterIndex = i;
  142. break;
  143. }
  144. }
  145. }
  146. exporterBox.setSelectedItemIndex (preferredExporterIndex != -1 ? preferredExporterIndex : 0);
  147. }
  148. updateExporterButton();
  149. }
  150. std::unique_ptr<ProjectExporter> HeaderComponent::getSelectedExporter() const
  151. {
  152. if (project != nullptr)
  153. {
  154. int i = 0;
  155. auto selectedIndex = exporterBox.getSelectedItemIndex();
  156. for (Project::ExporterIterator exporter (*project); exporter.next();)
  157. if (i++ == selectedIndex)
  158. return std::move (exporter.exporter);
  159. }
  160. return nullptr;
  161. }
  162. bool HeaderComponent::canCurrentExporterLaunchProject() const
  163. {
  164. if (project != nullptr)
  165. {
  166. if (auto selectedExporter = getSelectedExporter())
  167. {
  168. for (Project::ExporterIterator exporter (*project); exporter.next();)
  169. if (exporter->canLaunchProject() && exporter->getUniqueName() == selectedExporter->getUniqueName())
  170. return true;
  171. }
  172. }
  173. return false;
  174. }
  175. //==============================================================================
  176. void HeaderComponent::sidebarTabsWidthChanged (int newWidth)
  177. {
  178. tabsWidth = newWidth;
  179. resized();
  180. }
  181. void HeaderComponent::liveBuildEnablementChanged (bool isEnabled)
  182. {
  183. runAppButton.setVisible (isEnabled);
  184. }
  185. //==============================================================================
  186. void HeaderComponent::changeListenerCallback (ChangeBroadcaster* source)
  187. {
  188. if (source == &userAvatar)
  189. {
  190. resized();
  191. }
  192. else if (childProcess != nullptr && source == &childProcess->activityList)
  193. {
  194. if (childProcess->activityList.getNumActivities() > 0)
  195. buildPing();
  196. else
  197. buildFinished (childProcess->errorList.getNumErrors() == 0);
  198. }
  199. }
  200. void HeaderComponent::valueChanged (Value&)
  201. {
  202. updateName();
  203. }
  204. void HeaderComponent::timerCallback()
  205. {
  206. repaint();
  207. }
  208. //==============================================================================
  209. void HeaderComponent::initialiseButtons()
  210. {
  211. addAndMakeVisible (projectSettingsButton);
  212. projectSettingsButton.onClick = [this] { projectContentComponent->showProjectSettings(); };
  213. addAndMakeVisible (saveAndOpenInIDEButton);
  214. saveAndOpenInIDEButton.setBackgroundColour (Colours::white);
  215. saveAndOpenInIDEButton.setIconInset (7);
  216. saveAndOpenInIDEButton.onClick = [this]
  217. {
  218. if (project != nullptr)
  219. {
  220. if (project->isSaveAndExportDisabled())
  221. {
  222. auto setWarningVisible = [this] (const Identifier& identifier)
  223. {
  224. auto child = project->getProjectMessages().getChildWithName (ProjectMessages::Ids::warning)
  225. .getChildWithName (identifier);
  226. if (child.isValid())
  227. child.setProperty (ProjectMessages::Ids::isVisible, true, nullptr);
  228. };
  229. if (project->hasIncompatibleLicenseTypeAndSplashScreenSetting())
  230. setWarningVisible (ProjectMessages::Ids::incompatibleLicense);
  231. if (project->isFileModificationCheckPending())
  232. setWarningVisible (ProjectMessages::Ids::jucerFileModified);
  233. }
  234. else
  235. {
  236. if (auto exporter = getSelectedExporter())
  237. project->openProjectInIDE (*exporter, true);
  238. }
  239. }
  240. };
  241. addAndMakeVisible (runAppButton);
  242. runAppButton.setIconInset (7);
  243. runAppButton.onClick = [this]
  244. {
  245. if (childProcess != nullptr)
  246. childProcess->launchApp();
  247. };
  248. updateExporterButton();
  249. }
  250. void HeaderComponent::updateName()
  251. {
  252. if (project != nullptr)
  253. projectNameLabel.setText (project->getDocumentTitle(), dontSendNotification);
  254. }
  255. void HeaderComponent::updateExporterButton()
  256. {
  257. if (auto selectedExporter = getSelectedExporter())
  258. {
  259. auto selectedName = selectedExporter->getUniqueName();
  260. for (auto info : ProjectExporter::getExporterTypeInfos())
  261. {
  262. if (selectedName.contains (info.displayName))
  263. {
  264. saveAndOpenInIDEButton.setImage (info.icon);
  265. saveAndOpenInIDEButton.repaint();
  266. saveAndOpenInIDEButton.setEnabled (canCurrentExporterLaunchProject());
  267. }
  268. }
  269. }
  270. }
  271. //==============================================================================
  272. void HeaderComponent::buildPing()
  273. {
  274. if (! isTimerRunning())
  275. {
  276. isBuilding = true;
  277. runAppButton.setEnabled (false);
  278. runAppButton.setTooltip ("Building...");
  279. startTimer (50);
  280. }
  281. }
  282. void HeaderComponent::buildFinished (bool success)
  283. {
  284. stopTimer();
  285. isBuilding = false;
  286. repaint();
  287. setRunAppButtonState (success);
  288. }
  289. void HeaderComponent::setRunAppButtonState (bool buildWasSuccessful)
  290. {
  291. bool shouldEnableButton = false;
  292. if (buildWasSuccessful)
  293. {
  294. if (childProcess != nullptr)
  295. {
  296. if (childProcess->isAppRunning() || (! childProcess->isAppRunning() && childProcess->canLaunchApp()))
  297. {
  298. runAppButton.setTooltip ("Launch application");
  299. shouldEnableButton = true;
  300. }
  301. else
  302. {
  303. runAppButton.setTooltip ("Application can't be launched");
  304. }
  305. }
  306. else
  307. {
  308. runAppButton.setTooltip ("Enable live-build engine to launch application");
  309. }
  310. }
  311. else
  312. {
  313. runAppButton.setTooltip ("Error building application");
  314. }
  315. runAppButton.setEnabled (shouldEnableButton);
  316. }