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.

287 lines
11KB

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