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.

272 lines
10KB

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