/* ============================================================================== This file is part of the JUCE 6 technical preview. Copyright (c) 2017 - ROLI Ltd. You may use this code under the terms of the GPL v3 (see www.gnu.org/licenses). For this technical preview, this file is not subject to commercial licensing. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ #pragma once //============================================================================== class LicenseWebview : public DialogWindow { public: LicenseWebview (ModalComponentManager::Callback* callbackToUse, const String& request) : DialogWindow ("Log-in to Projucer", Colour (0xfff1f1f1), true, true) { LicenseWebviewContent* content; setUsingNativeTitleBar (true); setContentOwned (content = new LicenseWebviewContent (*this, callbackToUse), true); centreWithSize (getWidth(), getHeight()); content->goToURL (request); } void goToURL (const String& request) { reinterpret_cast (getContentComponent())->goToURL (request); } void setPageCallback (const std::function&)>& cb) { reinterpret_cast (getContentComponent())->pageCallback = cb; } void setNewWindowCallback (const std::function& cb) { reinterpret_cast (getContentComponent())->newWindowCallback = cb; } void closeButtonPressed() override { exitModalState (-1); } private: class LicenseWebviewContent : public Component { //============================================================================== struct RedirectWebBrowserComponent : public WebBrowserComponent { RedirectWebBrowserComponent (LicenseWebviewContent& controller) : WebBrowserComponent (false), owner (controller) {} ~RedirectWebBrowserComponent() override {} bool pageAboutToLoad (const String& url) override { return owner.pageAboutToLoad (url); } void pageFinishedLoading (const String& url) override { owner.pageFinishedLoading (url); } void newWindowAttemptingToLoad (const String& url) override { owner.newWindowAttemptingToLoad (url); } bool pageLoadHadNetworkError (const String& err) override { return owner.pageLoadHadNetworkError (err); } LicenseWebviewContent& owner; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RedirectWebBrowserComponent) }; //============================================================================== struct Header : public Component, private LicenseController::StateChangedCallback { Header() : avatarButton ("User Settings", &getIcons().user) { setOpaque (true); addChildComponent (avatarButton); avatarButton.onClick = [this] { showAvatarWindow(); }; if (auto* licenseController = ProjucerApplication::getApp().licenseController.get()) { licenseController->addLicenseStatusChangedCallback (this); licenseStateChanged (licenseController->getState()); } } ~Header() override { if (auto* licenseController = ProjucerApplication::getApp().licenseController.get()) licenseController->removeLicenseStatusChangedCallback (this); } void resized() override { auto r = getLocalBounds().reduced (30, 20); avatarButton.setBounds (r.removeFromRight (r.getHeight())); } void paint (Graphics& g) override { auto r = getLocalBounds().reduced (30, 20); g.fillAll (Colour (backgroundColour)); if (juceLogo != nullptr) juceLogo->drawWithin (g, r.toFloat(), RectanglePlacement::xLeft + RectanglePlacement::yMid, 1.0); } void licenseStateChanged (const LicenseState& state) override { avatarButton.iconImage = state.avatar; avatarButton.setVisible (state.type != LicenseState::Type::notLoggedIn && state.type != LicenseState::Type::GPL); avatarButton.repaint(); } void showAvatarWindow() { if (auto* licenseController = ProjucerApplication::getApp().licenseController.get()) { auto type = licenseController->getState().type; auto* content = new UserSettingsPopup (true); content->setSize (200, (type == LicenseState::Type::noLicenseChosenYet ? 100 : 150)); CallOutBox::launchAsynchronously (content, avatarButton.getScreenBounds(), nullptr); } } const uint32 backgroundColour = 0xff414141; std::unique_ptr juceLogo { Drawable::createFromImageData (BinaryData::jucelogowithtext_svg, BinaryData::jucelogowithtext_svgSize) }; IconButton avatarButton; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Header) }; //============================================================================== public: LicenseWebviewContent (LicenseWebview& parentWindowToUse, ModalComponentManager::Callback* callbackToUse) : parentWindow (parentWindowToUse), modalCallback (callbackToUse), webview (*this) { addAndMakeVisible (header); addAndMakeVisible (webview); setOpaque (true); setSize (978, 718); #if JUCE_WINDOWS // windows needs the webcomponent be visible parentWindow.enterModalState (true, modalCallback.release(), true); #endif } void goToURL (const String& request) { lastURL = request; webview.goToURL (lastURL); } void paint (Graphics& g) override { g.fillAll (Colours::lightblue); } void resized() override { auto r = getLocalBounds(); header.setBounds (r.removeFromTop (78)); webview.setBounds (r); } bool pageAboutToLoad (const String& page) { URL url (page); if (page == "about:blank" || page.startsWith ("file://") || page.startsWith ("data:text/html")) { if (page != lastErrorPageURI) lastURL = page; return true; } else if (url.getScheme() == "projucer") { HashMap params; auto n = url.getParameterNames().size(); for (int i = 0; i < n; ++i) params.set (url.getParameterNames()[i], url.getParameterValues()[i]); String cmd (url.getDomain()); if (n == 0 && cmd.containsChar (L'=')) { // old-style callback StringArray domainTokens (StringArray::fromTokens (cmd, "=", "")); cmd = domainTokens[0]; params.set (cmd, domainTokens[1]); } if (pageCallback) pageCallback (cmd, params); return false; } if (isValidURL (url)) lastURL = page; return true; } void pageFinishedLoading (const String& page) { URL url (page); if ((isValidURL (url) || page.startsWith ("file://") || page.startsWith ("data:text/html")) && ! parentWindow.isCurrentlyModal()) parentWindow.enterModalState (true, modalCallback.release(), true); } void newWindowAttemptingToLoad (const String& page) { URL url (page); bool isGitHub = url.getDomain().endsWith ("github.com"); if (isValidURL (url) || isGitHub) { url.launchInDefaultBrowser(); if (newWindowCallback && ! isGitHub) newWindowCallback (page); } } bool pageLoadHadNetworkError (const String&) { String errorPageSource = String (BinaryData::offlinepage_html, BinaryData::offlinepage_htmlSize) .replace ("__URL_PLACEHOLDER__", lastURL); #if JUCE_WINDOWS auto tmpFile = File::createTempFile (".html"); tmpFile.replaceWithText (errorPageSource, true); lastErrorPageURI = "file://" + tmpFile.getFullPathName(); #else lastErrorPageURI = "data:text/html;base64," + Base64::toBase64 (errorPageSource); #endif goToURL (lastErrorPageURI); return false; } static bool isValidURL (const URL& url) { return (url.getDomain().endsWith ("roli.com") || url.getDomain().endsWith ("juce.com")); } //============================================================================== LicenseWebview& parentWindow; std::unique_ptr modalCallback; Header header; RedirectWebBrowserComponent webview; std::function&)> pageCallback; std::function newWindowCallback; String lastURL, lastErrorPageURI; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LicenseWebviewContent) }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LicenseWebview) };