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.

396 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. setTitle ("Header");
  32. setFocusContainerType (FocusContainerType::focusContainer);
  33. addAndMakeVisible (configLabel);
  34. addAndMakeVisible (exporterBox);
  35. exporterBox.onChange = [this] { updateExporterButton(); };
  36. juceIcon.setImage (ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize), RectanglePlacement::centred);
  37. addAndMakeVisible (juceIcon);
  38. addAndMakeVisible (userAvatar);
  39. userAvatar.addChangeListener (this);
  40. projectNameLabel.setText ({}, dontSendNotification);
  41. addAndMakeVisible (projectNameLabel);
  42. initialiseButtons();
  43. }
  44. HeaderComponent::~HeaderComponent()
  45. {
  46. if (childProcess != nullptr)
  47. {
  48. childProcess->activityList.removeChangeListener (this);
  49. childProcess->errorList.removeChangeListener (this);
  50. }
  51. }
  52. //==============================================================================
  53. void HeaderComponent::resized()
  54. {
  55. auto bounds = getLocalBounds();
  56. configLabel.setFont ({ (float) bounds.getHeight() / 3.0f });
  57. {
  58. auto headerBounds = bounds.removeFromLeft (tabsWidth);
  59. const int buttonSize = 25;
  60. auto buttonBounds = headerBounds.removeFromRight (buttonSize);
  61. projectSettingsButton.setBounds (buttonBounds.removeFromBottom (buttonSize).reduced (2));
  62. juceIcon.setBounds (headerBounds.removeFromLeft (headerBounds.getHeight()).reduced (2));
  63. headerBounds.removeFromRight (5);
  64. projectNameLabel.setBounds (headerBounds);
  65. }
  66. {
  67. auto exporterWidth = jmin (400, bounds.getWidth() / 2);
  68. Rectangle<int> exporterBounds (0, 0, exporterWidth, bounds.getHeight());
  69. exporterBounds.setCentre (bounds.getCentre());
  70. runAppButton.setBounds (exporterBounds.removeFromRight (exporterBounds.getHeight()).reduced (2));
  71. saveAndOpenInIDEButton.setBounds (exporterBounds.removeFromRight (exporterBounds.getHeight()).reduced (2));
  72. exporterBounds.removeFromRight (5);
  73. exporterBox.setBounds (exporterBounds.removeFromBottom (roundToInt ((float) exporterBounds.getHeight() / 1.8f)));
  74. configLabel.setBounds (exporterBounds);
  75. }
  76. userAvatar.setBounds (bounds.removeFromRight (userAvatar.isDisplaingGPLLogo() ? roundToInt ((float) bounds.getHeight() * 1.9f)
  77. : bounds.getHeight()).reduced (2));
  78. }
  79. void HeaderComponent::paint (Graphics& g)
  80. {
  81. g.fillAll (findColour (backgroundColourId));
  82. if (isBuilding)
  83. getLookAndFeel().drawSpinningWaitAnimation (g, findColour (treeIconColourId),
  84. runAppButton.getX(), runAppButton.getY(),
  85. runAppButton.getWidth(), runAppButton.getHeight());
  86. }
  87. //==============================================================================
  88. void HeaderComponent::setCurrentProject (Project* newProject)
  89. {
  90. isBuilding = false;
  91. stopTimer();
  92. repaint();
  93. projectNameLabel.setText ({}, dontSendNotification);
  94. project = newProject;
  95. if (project != nullptr)
  96. {
  97. exportersTree = project->getExporters();
  98. exportersTree.addListener (this);
  99. updateExporters();
  100. projectNameValue.referTo (project->getProjectValue (Ids::name));
  101. projectNameValue.addListener (this);
  102. updateName();
  103. childProcess = ProjucerApplication::getApp().childProcessCache->getExisting (*project);
  104. if (childProcess != nullptr)
  105. {
  106. childProcess->activityList.addChangeListener (this);
  107. childProcess->errorList.addChangeListener (this);
  108. runAppButton.setTooltip ({});
  109. runAppButton.setEnabled (true);
  110. }
  111. else
  112. {
  113. runAppButton.setTooltip ("Enable live-build engine to launch application");
  114. runAppButton.setEnabled (false);
  115. }
  116. }
  117. }
  118. //==============================================================================
  119. void HeaderComponent::updateExporters()
  120. {
  121. auto selectedExporter = getSelectedExporter();
  122. exporterBox.clear();
  123. auto preferredExporterIndex = -1;
  124. int i = 0;
  125. for (Project::ExporterIterator exporter (*project); exporter.next(); ++i)
  126. {
  127. auto exporterName = exporter->getUniqueName();
  128. exporterBox.addItem (exporterName, i + 1);
  129. if (selectedExporter != nullptr && exporterName == selectedExporter->getUniqueName())
  130. exporterBox.setSelectedId (i + 1);
  131. if (exporterName.contains (ProjectExporter::getCurrentPlatformExporterTypeInfo().displayName) && preferredExporterIndex == -1)
  132. preferredExporterIndex = i;
  133. }
  134. if (exporterBox.getSelectedItemIndex() == -1)
  135. {
  136. if (preferredExporterIndex == -1)
  137. {
  138. i = 0;
  139. for (Project::ExporterIterator exporter (*project); exporter.next(); ++i)
  140. {
  141. if (exporter->canLaunchProject())
  142. {
  143. preferredExporterIndex = i;
  144. break;
  145. }
  146. }
  147. }
  148. exporterBox.setSelectedItemIndex (preferredExporterIndex != -1 ? preferredExporterIndex : 0);
  149. }
  150. updateExporterButton();
  151. }
  152. std::unique_ptr<ProjectExporter> HeaderComponent::getSelectedExporter() const
  153. {
  154. if (project != nullptr)
  155. {
  156. int i = 0;
  157. auto selectedIndex = exporterBox.getSelectedItemIndex();
  158. for (Project::ExporterIterator exporter (*project); exporter.next();)
  159. if (i++ == selectedIndex)
  160. return std::move (exporter.exporter);
  161. }
  162. return nullptr;
  163. }
  164. bool HeaderComponent::canCurrentExporterLaunchProject() const
  165. {
  166. if (project != nullptr)
  167. {
  168. if (auto selectedExporter = getSelectedExporter())
  169. {
  170. for (Project::ExporterIterator exporter (*project); exporter.next();)
  171. if (exporter->canLaunchProject() && exporter->getUniqueName() == selectedExporter->getUniqueName())
  172. return true;
  173. }
  174. }
  175. return false;
  176. }
  177. //==============================================================================
  178. void HeaderComponent::sidebarTabsWidthChanged (int newWidth)
  179. {
  180. tabsWidth = newWidth;
  181. resized();
  182. }
  183. void HeaderComponent::liveBuildEnablementChanged (bool isEnabled)
  184. {
  185. runAppButton.setVisible (isEnabled);
  186. }
  187. //==============================================================================
  188. void HeaderComponent::changeListenerCallback (ChangeBroadcaster* source)
  189. {
  190. if (source == &userAvatar)
  191. {
  192. resized();
  193. }
  194. else if (childProcess != nullptr && source == &childProcess->activityList)
  195. {
  196. if (childProcess->activityList.getNumActivities() > 0)
  197. buildPing();
  198. else
  199. buildFinished (childProcess->errorList.getNumErrors() == 0);
  200. }
  201. }
  202. void HeaderComponent::valueChanged (Value&)
  203. {
  204. updateName();
  205. }
  206. void HeaderComponent::timerCallback()
  207. {
  208. repaint();
  209. }
  210. //==============================================================================
  211. void HeaderComponent::initialiseButtons()
  212. {
  213. addAndMakeVisible (projectSettingsButton);
  214. projectSettingsButton.onClick = [this] { projectContentComponent->showProjectSettings(); };
  215. addAndMakeVisible (saveAndOpenInIDEButton);
  216. saveAndOpenInIDEButton.setBackgroundColour (Colours::white);
  217. saveAndOpenInIDEButton.setIconInset (7);
  218. saveAndOpenInIDEButton.onClick = [this]
  219. {
  220. if (project != nullptr)
  221. {
  222. if (project->isSaveAndExportDisabled())
  223. {
  224. auto setWarningVisible = [this] (const Identifier& identifier)
  225. {
  226. auto child = project->getProjectMessages().getChildWithName (ProjectMessages::Ids::warning)
  227. .getChildWithName (identifier);
  228. if (child.isValid())
  229. child.setProperty (ProjectMessages::Ids::isVisible, true, nullptr);
  230. };
  231. if (project->hasIncompatibleLicenseTypeAndSplashScreenSetting())
  232. setWarningVisible (ProjectMessages::Ids::incompatibleLicense);
  233. if (project->isFileModificationCheckPending())
  234. setWarningVisible (ProjectMessages::Ids::jucerFileModified);
  235. }
  236. else
  237. {
  238. if (auto exporter = getSelectedExporter())
  239. project->openProjectInIDE (*exporter, true);
  240. }
  241. }
  242. };
  243. addAndMakeVisible (runAppButton);
  244. runAppButton.setIconInset (7);
  245. runAppButton.onClick = [this]
  246. {
  247. if (childProcess != nullptr)
  248. childProcess->launchApp();
  249. };
  250. updateExporterButton();
  251. }
  252. void HeaderComponent::updateName()
  253. {
  254. if (project != nullptr)
  255. projectNameLabel.setText (project->getDocumentTitle(), dontSendNotification);
  256. }
  257. void HeaderComponent::updateExporterButton()
  258. {
  259. if (auto selectedExporter = getSelectedExporter())
  260. {
  261. auto selectedName = selectedExporter->getUniqueName();
  262. for (auto info : ProjectExporter::getExporterTypeInfos())
  263. {
  264. if (selectedName.contains (info.displayName))
  265. {
  266. saveAndOpenInIDEButton.setImage (info.icon);
  267. saveAndOpenInIDEButton.repaint();
  268. saveAndOpenInIDEButton.setEnabled (canCurrentExporterLaunchProject());
  269. }
  270. }
  271. }
  272. }
  273. //==============================================================================
  274. void HeaderComponent::buildPing()
  275. {
  276. if (! isTimerRunning())
  277. {
  278. isBuilding = true;
  279. runAppButton.setEnabled (false);
  280. runAppButton.setTooltip ("Building...");
  281. startTimer (50);
  282. }
  283. }
  284. void HeaderComponent::buildFinished (bool success)
  285. {
  286. stopTimer();
  287. isBuilding = false;
  288. repaint();
  289. setRunAppButtonState (success);
  290. }
  291. void HeaderComponent::setRunAppButtonState (bool buildWasSuccessful)
  292. {
  293. bool shouldEnableButton = false;
  294. if (buildWasSuccessful)
  295. {
  296. if (childProcess != nullptr)
  297. {
  298. if (childProcess->isAppRunning() || (! childProcess->isAppRunning() && childProcess->canLaunchApp()))
  299. {
  300. runAppButton.setTooltip ("Launch application");
  301. shouldEnableButton = true;
  302. }
  303. else
  304. {
  305. runAppButton.setTooltip ("Application can't be launched");
  306. }
  307. }
  308. else
  309. {
  310. runAppButton.setTooltip ("Enable live-build engine to launch application");
  311. }
  312. }
  313. else
  314. {
  315. runAppButton.setTooltip ("Error building application");
  316. }
  317. runAppButton.setEnabled (shouldEnableButton);
  318. }