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.

285 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - 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. #pragma once
  18. //==============================================================================
  19. class LicenseWebview : public DialogWindow
  20. {
  21. public:
  22. LicenseWebview (ModalComponentManager::Callback* callbackToUse, const String& request)
  23. : DialogWindow ("Log-in to Projucer", Colour (0xfff1f1f1), true, true)
  24. {
  25. LicenseWebviewContent* content;
  26. setUsingNativeTitleBar (true);
  27. setContentOwned (content = new LicenseWebviewContent (*this, callbackToUse), true);
  28. centreWithSize (getWidth(), getHeight());
  29. content->goToURL (request);
  30. }
  31. void goToURL (const String& request) { reinterpret_cast<LicenseWebviewContent*> (getContentComponent())->goToURL (request); }
  32. void setPageCallback (const std::function<void (const String&, const HashMap<String, String>&)>& cb)
  33. {
  34. reinterpret_cast<LicenseWebviewContent*> (getContentComponent())->pageCallback = cb;
  35. }
  36. void setNewWindowCallback (const std::function<void (const String&)>& cb)
  37. {
  38. reinterpret_cast<LicenseWebviewContent*> (getContentComponent())->newWindowCallback = cb;
  39. }
  40. void closeButtonPressed() override { exitModalState (-1); }
  41. private:
  42. class LicenseWebviewContent : public Component
  43. {
  44. //==============================================================================
  45. struct RedirectWebBrowserComponent : public WebBrowserComponent
  46. {
  47. RedirectWebBrowserComponent (LicenseWebviewContent& controller) : WebBrowserComponent (false), owner (controller) {}
  48. virtual ~RedirectWebBrowserComponent() {}
  49. bool pageAboutToLoad (const String& url) override { return owner.pageAboutToLoad (url); }
  50. void pageFinishedLoading (const String& url) override { owner.pageFinishedLoading (url); }
  51. void newWindowAttemptingToLoad (const String& url) override { owner.newWindowAttemptingToLoad (url); }
  52. bool pageLoadHadNetworkError (const String& err) override { return owner.pageLoadHadNetworkError (err); }
  53. LicenseWebviewContent& owner;
  54. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RedirectWebBrowserComponent)
  55. };
  56. //==============================================================================
  57. struct Header : public Component, private LicenseController::StateChangedCallback,
  58. private Button::Listener
  59. {
  60. Header ()
  61. : avatarButton ("User Settings", &getIcons().user)
  62. {
  63. setOpaque (true);
  64. addChildComponent (avatarButton);
  65. avatarButton.addListener (this);
  66. if (LicenseController* licenseController = ProjucerApplication::getApp().licenseController)
  67. {
  68. licenseController->addLicenseStatusChangedCallback (this);
  69. licenseStateChanged (licenseController->getState());
  70. }
  71. }
  72. virtual ~Header()
  73. {
  74. avatarButton.removeListener (this);
  75. if (LicenseController* licenseController = ProjucerApplication::getApp().licenseController)
  76. licenseController->removeLicenseStatusChangedCallback (this);
  77. }
  78. void resized() override
  79. {
  80. auto r = getLocalBounds().reduced (30, 20);
  81. avatarButton.setBounds (r.removeFromRight (r.getHeight()));
  82. }
  83. void paint (Graphics& g) override
  84. {
  85. auto r = getLocalBounds().reduced (30, 20);
  86. g.fillAll (Colour (backgroundColour));
  87. if (juceLogo != nullptr)
  88. juceLogo->drawWithin (g, r.toFloat(), RectanglePlacement::xLeft + RectanglePlacement::yMid, 1.0);
  89. }
  90. void licenseStateChanged (const LicenseState& state) override
  91. {
  92. avatarButton.iconImage = state.avatar;
  93. avatarButton.setVisible (state.type != LicenseState::Type::notLoggedIn && state.type != LicenseState::Type::GPL);
  94. avatarButton.repaint();
  95. }
  96. void buttonClicked (Button*) override
  97. {
  98. if (LicenseController* licenseController = ProjucerApplication::getApp().licenseController)
  99. {
  100. const LicenseState::Type type = licenseController->getState().type;
  101. auto* content = new UserSettingsPopup (true);
  102. content->setSize (200, (type == LicenseState::Type::noLicenseChosenYet ? 100 : 150));
  103. CallOutBox::launchAsynchronously (content, avatarButton.getScreenBounds(), nullptr);
  104. }
  105. }
  106. static constexpr uint32 backgroundColour = 0xff414141;
  107. ScopedPointer<Drawable> juceLogo
  108. = Drawable::createFromImageData (BinaryData::jucelogowithtext_svg,
  109. BinaryData::jucelogowithtext_svgSize);
  110. IconButton avatarButton;
  111. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Header)
  112. };
  113. //==============================================================================
  114. //==============================================================================
  115. public:
  116. LicenseWebviewContent (LicenseWebview& parentWindowToUse, ModalComponentManager::Callback* callbackToUse)
  117. : parentWindow (parentWindowToUse), modalCallback (callbackToUse), webview (*this)
  118. {
  119. addAndMakeVisible (header);
  120. addAndMakeVisible (webview);
  121. setOpaque (true);
  122. setSize (978, 718);
  123. #if JUCE_WINDOWS // windows needs the webcomponent be visible
  124. parentWindow.enterModalState (true, modalCallback.release(), true);
  125. #endif
  126. }
  127. void goToURL (const String& request)
  128. {
  129. lastURL = request;
  130. webview.goToURL (lastURL);
  131. }
  132. void paint (Graphics& g) override { g.fillAll (Colours::lightblue); }
  133. void resized() override
  134. {
  135. auto r = getLocalBounds();
  136. header.setBounds (r.removeFromTop (78));
  137. webview.setBounds (r);
  138. }
  139. bool pageAboutToLoad (const String& page)
  140. {
  141. URL url (page);
  142. if (page == "about:blank" || page.startsWith ("file://") || page.startsWith ("data:text/html"))
  143. {
  144. if (page != lastErrorPageURI)
  145. lastURL = page;
  146. return true;
  147. }
  148. else if (url.getScheme() == "projucer")
  149. {
  150. HashMap<String, String> params;
  151. auto n = url.getParameterNames().size();
  152. for (int i = 0; i < n; ++i)
  153. params.set (url.getParameterNames()[i], url.getParameterValues()[i]);
  154. String cmd (url.getDomain());
  155. if (n == 0 && cmd.containsChar (L'='))
  156. {
  157. // old-style callback
  158. StringArray domainTokens (StringArray::fromTokens (cmd, "=", ""));
  159. cmd = domainTokens[0];
  160. params.set (cmd, domainTokens[1]);
  161. }
  162. if (pageCallback)
  163. pageCallback (cmd, params);
  164. return false;
  165. }
  166. bool isValid = (url.getDomain().endsWith ("roli.com") || url.getDomain().endsWith ("juce.com"));
  167. if (isValid)
  168. lastURL = page;
  169. return true;
  170. }
  171. void pageFinishedLoading (const String& page)
  172. {
  173. URL url (page);
  174. if ((isValidURL (url)
  175. || page.startsWith ("file://") || page.startsWith ("data:text/html"))
  176. && ! parentWindow.isCurrentlyModal())
  177. parentWindow.enterModalState (true, modalCallback.release(), true);
  178. }
  179. void newWindowAttemptingToLoad (const String& page)
  180. {
  181. URL url (page);
  182. bool isGitHub = url.getDomain().endsWith ("github.com");
  183. if (url.getDomain().endsWith ("roli.com")
  184. || url.getDomain().endsWith ("juce.com")
  185. || isGitHub)
  186. {
  187. url.launchInDefaultBrowser();
  188. if (newWindowCallback && ! isGitHub)
  189. newWindowCallback (page);
  190. }
  191. }
  192. bool pageLoadHadNetworkError (const String&)
  193. {
  194. String errorPageSource = String (BinaryData::offlinepage_html, BinaryData::offlinepage_htmlSize)
  195. .replace ("__URL_PLACEHOLDER__", lastURL);
  196. #if JUCE_WINDOWS
  197. auto tmpFile = File::createTempFile (".html");
  198. tmpFile.replaceWithText (errorPageSource, true);
  199. lastErrorPageURI = "file://" + tmpFile.getFullPathName();
  200. #else
  201. lastErrorPageURI = "data:text/html;base64," + Base64::toBase64 (errorPageSource);
  202. #endif
  203. goToURL (lastErrorPageURI);
  204. return false;
  205. }
  206. static bool isValidURL (const URL& url) { return (url.getDomain().endsWith ("roli.com") || url.getDomain().endsWith ("juce.com")); }
  207. //==============================================================================
  208. LicenseWebview& parentWindow;
  209. ScopedPointer<ModalComponentManager::Callback> modalCallback;
  210. Header header;
  211. RedirectWebBrowserComponent webview;
  212. std::function<void (const String&, const HashMap<String, String>&)> pageCallback;
  213. std::function<void (const String&)> newWindowCallback;
  214. String lastURL, lastErrorPageURI;
  215. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LicenseWebviewContent)
  216. };
  217. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LicenseWebview)
  218. };