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.

384 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->hasIncompatibleLicenseTypeAndSplashScreenSetting())
  221. {
  222. auto child = project->getProjectMessages().getChildWithName (ProjectMessages::Ids::warning)
  223. .getChildWithName (ProjectMessages::Ids::incompatibleLicense);
  224. if (child.isValid())
  225. child.setProperty (ProjectMessages::Ids::isVisible, true, nullptr);
  226. }
  227. else
  228. {
  229. if (auto exporter = getSelectedExporter())
  230. project->openProjectInIDE (*exporter, true);
  231. }
  232. }
  233. };
  234. addAndMakeVisible (runAppButton);
  235. runAppButton.setIconInset (7);
  236. runAppButton.onClick = [this]
  237. {
  238. if (childProcess != nullptr)
  239. childProcess->launchApp();
  240. };
  241. updateExporterButton();
  242. }
  243. void HeaderComponent::updateName()
  244. {
  245. if (project != nullptr)
  246. projectNameLabel.setText (project->getDocumentTitle(), dontSendNotification);
  247. }
  248. void HeaderComponent::updateExporterButton()
  249. {
  250. if (auto selectedExporter = getSelectedExporter())
  251. {
  252. auto selectedName = selectedExporter->getUniqueName();
  253. for (auto info : ProjectExporter::getExporterTypeInfos())
  254. {
  255. if (selectedName.contains (info.displayName))
  256. {
  257. saveAndOpenInIDEButton.setImage (info.icon);
  258. saveAndOpenInIDEButton.repaint();
  259. saveAndOpenInIDEButton.setEnabled (canCurrentExporterLaunchProject());
  260. }
  261. }
  262. }
  263. }
  264. //==============================================================================
  265. void HeaderComponent::buildPing()
  266. {
  267. if (! isTimerRunning())
  268. {
  269. isBuilding = true;
  270. runAppButton.setEnabled (false);
  271. runAppButton.setTooltip ("Building...");
  272. startTimer (50);
  273. }
  274. }
  275. void HeaderComponent::buildFinished (bool success)
  276. {
  277. stopTimer();
  278. isBuilding = false;
  279. repaint();
  280. setRunAppButtonState (success);
  281. }
  282. void HeaderComponent::setRunAppButtonState (bool buildWasSuccessful)
  283. {
  284. bool shouldEnableButton = false;
  285. if (buildWasSuccessful)
  286. {
  287. if (childProcess != nullptr)
  288. {
  289. if (childProcess->isAppRunning() || (! childProcess->isAppRunning() && childProcess->canLaunchApp()))
  290. {
  291. runAppButton.setTooltip ("Launch application");
  292. shouldEnableButton = true;
  293. }
  294. else
  295. {
  296. runAppButton.setTooltip ("Application can't be launched");
  297. }
  298. }
  299. else
  300. {
  301. runAppButton.setTooltip ("Enable live-build engine to launch application");
  302. }
  303. }
  304. else
  305. {
  306. runAppButton.setTooltip ("Error building application");
  307. }
  308. runAppButton.setEnabled (shouldEnableButton);
  309. }