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.

534 lines
18KB

  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. struct IconButton : public Button
  18. {
  19. IconButton (String name, const Path* p)
  20. : Button (name),
  21. icon (p, Colours::transparentBlack)
  22. {
  23. lookAndFeelChanged();
  24. setTooltip (name);
  25. }
  26. void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override
  27. {
  28. auto alpha = 1.0f;
  29. if (! isEnabled())
  30. {
  31. isMouseOverButton = false;
  32. isButtonDown = false;
  33. alpha = 0.2f;
  34. }
  35. auto backgroundColour = isIDEButton ? Colours::white
  36. : isUserButton ? findColour (userButtonBackgroundColourId)
  37. : findColour (defaultButtonBackgroundColourId);
  38. backgroundColour = isButtonDown ? backgroundColour.darker (0.5f)
  39. : isMouseOverButton ? backgroundColour.darker (0.2f)
  40. : backgroundColour;
  41. auto bounds = getLocalBounds().toFloat();
  42. if (isButtonDown)
  43. bounds.reduce (2, 2);
  44. Path ellipse;
  45. ellipse.addEllipse (bounds);
  46. g.reduceClipRegion(ellipse);
  47. g.setColour (backgroundColour.withAlpha (alpha));
  48. g.fillAll();
  49. if (iconImage != Image())
  50. {
  51. if (isIDEButton)
  52. bounds.reduce (7, 7);
  53. g.setOpacity (alpha);
  54. g.drawImage (iconImage, bounds, RectanglePlacement::fillDestination, false);
  55. }
  56. else
  57. {
  58. icon.withColour (findColour (defaultIconColourId).withAlpha (alpha)).draw (g, bounds.reduced (2, 2), false);
  59. }
  60. }
  61. Icon icon;
  62. Image iconImage;
  63. bool isIDEButton = false;
  64. bool isUserButton = false;
  65. };
  66. //==============================================================================
  67. class UserSettingsPopup : public Component
  68. #if ! JUCER_ENABLE_GPL_MODE
  69. , private Button::Listener,
  70. private LicenseController::StateChangedCallback
  71. #endif
  72. {
  73. public:
  74. UserSettingsPopup (bool isShownInsideWebview)
  75. #if ! JUCER_ENABLE_GPL_MODE
  76. : isInsideWebview (isShownInsideWebview)
  77. #endif
  78. {
  79. #if JUCER_ENABLE_GPL_MODE
  80. ignoreUnused (isShownInsideWebview);
  81. #endif
  82. auto standardFont = Font (12.0f);
  83. addAndMakeVisible (loggedInUsernameLabel = new Label ("Username Label"));
  84. loggedInUsernameLabel->setFont (standardFont);
  85. loggedInUsernameLabel->setJustificationType (Justification::centred);
  86. loggedInUsernameLabel->setMinimumHorizontalScale (0.75f);
  87. #if JUCER_ENABLE_GPL_MODE
  88. loggedInUsernameLabel->setText ("GPL Mode: Re-compile with JUCER_ENABLE_GPL_MODE=0 to enable login!",
  89. NotificationType::dontSendNotification);
  90. #else
  91. addAndMakeVisible (licenseTypeLabel = new Label ("License Type Label"));
  92. licenseTypeLabel->setFont (standardFont);
  93. licenseTypeLabel->setJustificationType (Justification::centred);
  94. licenseTypeLabel->setMinimumHorizontalScale (1.0f);
  95. addAndMakeVisible (logoutButton = new TextButton (isInsideWebview ? "Select different account..." : "Logout"));
  96. logoutButton->addListener (this);
  97. logoutButton->setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId));
  98. if (! isInsideWebview)
  99. {
  100. addAndMakeVisible (switchLicenseButton = new TextButton ("Switch License"));
  101. switchLicenseButton->addListener (this);
  102. }
  103. if (LicenseController* controller = ProjucerApplication::getApp().licenseController)
  104. licenseStateChanged (controller->getState());
  105. #endif
  106. }
  107. void paint (Graphics& g) override
  108. {
  109. g.fillAll (findColour (secondaryBackgroundColourId));
  110. }
  111. void resized() override
  112. {
  113. auto bounds = getLocalBounds().reduced (10, 20);
  114. #if JUCER_ENABLE_GPL_MODE
  115. loggedInUsernameLabel->setBounds (bounds);
  116. #else
  117. loggedInUsernameLabel->setBounds (bounds.removeFromTop (25));
  118. if (hasLicenseType)
  119. {
  120. bounds.removeFromTop (10);
  121. licenseTypeLabel->setBounds (bounds.removeFromTop (25));
  122. }
  123. bounds.removeFromBottom (5);
  124. auto buttonArea = bounds.removeFromBottom (30);
  125. if (! isInsideWebview)
  126. switchLicenseButton->setBounds (buttonArea.removeFromRight (buttonArea.getWidth() / 2).reduced (2));
  127. logoutButton->setBounds (buttonArea.reduced (2));
  128. #endif
  129. }
  130. private:
  131. //==============================================================================
  132. #if ! JUCER_ENABLE_GPL_MODE
  133. void buttonClicked (Button* b) override
  134. {
  135. if (b == logoutButton)
  136. {
  137. dismissCalloutBox();
  138. ProjucerApplication::getApp().doLogout();
  139. }
  140. else if (b == switchLicenseButton)
  141. {
  142. dismissCalloutBox();
  143. if (LicenseController* controller = ProjucerApplication::getApp().licenseController)
  144. controller->chooseNewLicense();
  145. }
  146. }
  147. void licenseStateChanged (const LicenseState& state) override
  148. {
  149. hasLicenseType = (state.type != LicenseState::Type::noLicenseChosenYet);
  150. licenseTypeLabel->setVisible (hasLicenseType);
  151. loggedInUsernameLabel->setText (state.username, NotificationType::dontSendNotification);
  152. licenseTypeLabel->setText (LicenseState::licenseTypeToString (state.type), NotificationType::dontSendNotification);
  153. }
  154. void dismissCalloutBox()
  155. {
  156. if (auto* parent = findParentComponentOfClass<CallOutBox>())
  157. parent->dismiss();
  158. }
  159. void lookAndFeelChanged() override
  160. {
  161. if (logoutButton != nullptr)
  162. logoutButton->setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId));
  163. }
  164. #endif
  165. //==============================================================================
  166. ScopedPointer<Label> loggedInUsernameLabel;
  167. #if ! JUCER_ENABLE_GPL_MODE
  168. ScopedPointer<Label> licenseTypeLabel;
  169. ScopedPointer<TextButton> logoutButton, switchLicenseButton;
  170. bool hasLicenseType = false;
  171. bool isInsideWebview;
  172. #endif
  173. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UserSettingsPopup)
  174. };
  175. //==============================================================================
  176. class HeaderComponent : public Component,
  177. private Button::Listener,
  178. private ComboBox::Listener,
  179. private ValueTree::Listener,
  180. private ChangeListener
  181. {
  182. public:
  183. HeaderComponent()
  184. : configLabel ("Config Label", "Selected exporter")
  185. {
  186. addAndMakeVisible (configLabel);
  187. addAndMakeVisible (exporterBox);
  188. exporterBox.addListener (this);
  189. addAndMakeVisible (juceIcon = new ImageComponent ("icon"));
  190. juceIcon->setImage (ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize),
  191. RectanglePlacement::centred);
  192. projectNameLabel.setText (String(), dontSendNotification);
  193. addAndMakeVisible (projectNameLabel);
  194. initialiseButtons();
  195. }
  196. ~HeaderComponent()
  197. {
  198. if (userSettingsWindow != nullptr)
  199. userSettingsWindow->dismiss();
  200. }
  201. void resized() override
  202. {
  203. auto bounds = getLocalBounds();
  204. configLabel.setFont (Font (bounds.getHeight() / 3.0f));
  205. //======================================================================
  206. auto projectHeaderBounds = bounds.removeFromLeft (tabsWidth);
  207. juceIcon->setBounds (projectHeaderBounds.removeFromLeft (projectHeaderBounds.getHeight()).reduced (5, 5));
  208. projectSettingsButton->setBounds (projectHeaderBounds.removeFromRight (projectHeaderBounds.getHeight()).reduced (2, 2));
  209. projectNameLabel.setBounds (projectHeaderBounds);
  210. //======================================================================
  211. bounds.removeFromLeft (33);
  212. continuousRebuildButton->setBounds (bounds.removeFromLeft (bounds.getHeight()).reduced (2, 2));
  213. bounds.removeFromLeft (5);
  214. buildNowButton->setBounds (bounds.removeFromLeft (bounds.getHeight()).reduced (2, 2));
  215. bounds.removeFromRight (5);
  216. userSettingsButton->setBounds (bounds.removeFromRight (bounds.getHeight()).reduced (2, 2));
  217. auto exporterWidth = jmax (250, bounds.getWidth() / 2);
  218. auto spacing = bounds.getWidth() - exporterWidth;
  219. auto leftSpacing = jmax (20, spacing / 3);
  220. auto rightSpacing = jmax (40, 2 * (spacing / 3));
  221. bounds.removeFromLeft (leftSpacing);
  222. bounds.removeFromRight (rightSpacing);
  223. saveAndOpenInIDEButton->setBounds (bounds.removeFromRight (bounds.getHeight()).reduced (2, 2));
  224. bounds.removeFromRight (5);
  225. exporterSettingsButton->setBounds (bounds.removeFromRight (bounds.getHeight()).reduced (2, 2));
  226. bounds.removeFromRight (10);
  227. exporterBox.setBounds (bounds.removeFromBottom (roundToInt (bounds.getHeight() / 1.8f)));
  228. configLabel.setBounds (bounds);
  229. }
  230. void paint (Graphics& g) override
  231. {
  232. g.fillAll (findColour (backgroundColourId));
  233. }
  234. void setCurrentProject (Project* p)
  235. {
  236. project = p;
  237. exportersTree = project->getExporters();
  238. exportersTree.addListener (this);
  239. updateExporters();
  240. project->addChangeListener (this);
  241. updateName();
  242. continuousRebuildButton->setEnabled (true);
  243. updateContinuousRebuildButtonIcon();
  244. if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>())
  245. buildNowButton->setEnabled (! pcc->isContinuousRebuildEnabled() || ! pcc->isBuildEnabled());
  246. }
  247. void updateExporters()
  248. {
  249. auto selectedName = getSelectedExporterName();
  250. exporterBox.clear();
  251. int i = 0;
  252. for (Project::ExporterIterator exporter (*project); exporter.next(); ++i)
  253. {
  254. exporterBox.addItem (exporter->getName(), i + 1);
  255. if (selectedName == exporter->getName())
  256. exporterBox.setSelectedId (i + 1);
  257. }
  258. if (exporterBox.getSelectedItemIndex() == -1)
  259. exporterBox.setSelectedItemIndex (0);
  260. updateExporterButton();
  261. }
  262. String getSelectedExporterName()
  263. {
  264. return exporterBox.getItemText (exporterBox.getSelectedItemIndex());
  265. }
  266. bool canCurrentExporterLaunchProject()
  267. {
  268. for (Project::ExporterIterator exporter (*project); exporter.next();)
  269. if (exporter->getName() == getSelectedExporterName() && exporter->canLaunchProject())
  270. return true;
  271. return false;
  272. }
  273. int getUserButtonWidth() { return userSettingsButton->getWidth(); }
  274. void sidebarTabsWidthChanged (int newWidth)
  275. {
  276. tabsWidth = newWidth;
  277. resized();
  278. }
  279. void showUserSettings()
  280. {
  281. #if JUCER_ENABLE_GPL_MODE
  282. const int settingsPopupHeight = 75;
  283. #else
  284. const int settingsPopupHeight = 150;
  285. #endif
  286. auto* content = new UserSettingsPopup (false);
  287. content->setSize (200, settingsPopupHeight);
  288. userSettingsWindow = &CallOutBox::launchAsynchronously (content, userSettingsButton->getScreenBounds(), nullptr);
  289. }
  290. void lookAndFeelChanged() override
  291. {
  292. if (userSettingsWindow != nullptr)
  293. userSettingsWindow->sendLookAndFeelChange();
  294. }
  295. private:
  296. Project* project = nullptr;
  297. ValueTree exportersTree;
  298. Label configLabel, projectNameLabel;
  299. ComboBox exporterBox;
  300. ScopedPointer<ImageComponent> juceIcon;
  301. ScopedPointer<IconButton> projectSettingsButton, continuousRebuildButton, buildNowButton,
  302. exporterSettingsButton, saveAndOpenInIDEButton, userSettingsButton;
  303. SafePointer<CallOutBox> userSettingsWindow;
  304. int tabsWidth = 200;
  305. //==========================================================================
  306. void buttonClicked (Button* b) override
  307. {
  308. auto* pcc = findParentComponentOfClass<ProjectContentComponent>();
  309. if (b == projectSettingsButton)
  310. {
  311. pcc->showProjectSettings();
  312. }
  313. else if (b == continuousRebuildButton)
  314. {
  315. if (! pcc->isBuildEnabled())
  316. pcc->setBuildEnabled (true);
  317. auto newState = ! pcc->isContinuousRebuildEnabled();
  318. pcc->setContinuousRebuildEnabled (newState);
  319. updateContinuousRebuildButtonIcon();
  320. buildNowButton->setEnabled (! pcc->isContinuousRebuildEnabled() || ! pcc->isBuildEnabled());
  321. }
  322. else if (b == buildNowButton)
  323. {
  324. if (! pcc->isBuildEnabled())
  325. pcc->setBuildEnabled (true);
  326. pcc->rebuildNow();
  327. }
  328. else if (b == exporterSettingsButton)
  329. {
  330. pcc->showExporterSettings (getSelectedExporterName());
  331. }
  332. else if (b == saveAndOpenInIDEButton)
  333. {
  334. pcc->openInSelectedIDE (true);
  335. }
  336. else if (b == userSettingsButton)
  337. {
  338. showUserSettings();
  339. }
  340. }
  341. void comboBoxChanged (ComboBox* c) override
  342. {
  343. if (c == &exporterBox)
  344. updateExporterButton();
  345. }
  346. void changeListenerCallback (ChangeBroadcaster* source) override
  347. {
  348. if (source == project)
  349. updateName();
  350. }
  351. void valueTreePropertyChanged (ValueTree&, const Identifier&) override {}
  352. void valueTreeParentChanged (ValueTree&) override {}
  353. void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override { updateIfNeeded (parentTree); }
  354. void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override { updateIfNeeded (parentTree); }
  355. void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override { updateIfNeeded (parentTree); }
  356. void initialiseButtons()
  357. {
  358. auto& icons = getIcons();
  359. addAndMakeVisible (projectSettingsButton = new IconButton ("Project Settings", &icons.settings));
  360. projectSettingsButton->addListener (this);
  361. addAndMakeVisible (continuousRebuildButton = new IconButton ("Continuous Rebuild", &icons.continuousBuildStart));
  362. continuousRebuildButton->addListener (this);
  363. continuousRebuildButton->setEnabled (false);
  364. addAndMakeVisible (buildNowButton = new IconButton ("Build Now", &icons.buildNow));
  365. buildNowButton->addListener (this);
  366. buildNowButton->setEnabled (false);
  367. addAndMakeVisible (exporterSettingsButton = new IconButton ("Exporter Settings", &icons.edit));
  368. exporterSettingsButton->addListener (this);
  369. addAndMakeVisible (saveAndOpenInIDEButton = new IconButton ("Save and Open in IDE", nullptr));
  370. saveAndOpenInIDEButton->addListener (this);
  371. saveAndOpenInIDEButton->isIDEButton = true;
  372. addAndMakeVisible (userSettingsButton = new IconButton ("User Settings", &icons.user));
  373. userSettingsButton->addListener (this);
  374. userSettingsButton->isUserButton = true;
  375. updateExporterButton();
  376. updateUserAvatar();
  377. }
  378. void updateIfNeeded (ValueTree tree)
  379. {
  380. if (tree == exportersTree)
  381. updateExporters();
  382. }
  383. void updateName()
  384. {
  385. projectNameLabel.setText (project->getDocumentTitle(), dontSendNotification);
  386. }
  387. void updateExporterButton()
  388. {
  389. auto currentExporterName = getSelectedExporterName();
  390. for (auto info : ProjectExporter::getExporterTypes())
  391. {
  392. if (info.name == currentExporterName)
  393. {
  394. saveAndOpenInIDEButton->iconImage = info.getIcon();
  395. saveAndOpenInIDEButton->repaint();
  396. saveAndOpenInIDEButton->setEnabled (canCurrentExporterLaunchProject());
  397. }
  398. }
  399. }
  400. void updateUserAvatar()
  401. {
  402. if (LicenseController* controller = ProjucerApplication::getApp().licenseController)
  403. {
  404. auto& state = controller->getState();
  405. userSettingsButton->iconImage = state.avatar;
  406. userSettingsButton->repaint();
  407. }
  408. }
  409. void updateContinuousRebuildButtonIcon()
  410. {
  411. if (auto* pcc = findParentComponentOfClass<ProjectContentComponent>())
  412. {
  413. continuousRebuildButton->setEnabled (pcc->isBuildEnabled());
  414. continuousRebuildButton->icon = Icon (pcc->isContinuousRebuildEnabled() ? &getIcons().continuousBuildStop
  415. : &getIcons().continuousBuildStart, Colours::transparentBlack);
  416. }
  417. }
  418. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HeaderComponent)
  419. };