migrating to the latest JUCE version
This commit is contained in:
		@@ -1,369 +1,369 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             AnalyticsCollectionDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Collects analytics data.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_analytics, juce_core, juce_data_structures, juce_events,
 | 
			
		||||
                   juce_graphics, juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2019, linux_make, xcode_iphone, androidstudio
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        AnalyticsCollectionDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
enum DemoAnalyticsEventTypes
 | 
			
		||||
{
 | 
			
		||||
    event,
 | 
			
		||||
    sessionStart,
 | 
			
		||||
    sessionEnd,
 | 
			
		||||
    screenView,
 | 
			
		||||
    exception
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class GoogleAnalyticsDestination  : public ThreadedAnalyticsDestination
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    GoogleAnalyticsDestination()
 | 
			
		||||
        : ThreadedAnalyticsDestination ("GoogleAnalyticsThread")
 | 
			
		||||
    {
 | 
			
		||||
        {
 | 
			
		||||
            // Choose where to save any unsent events.
 | 
			
		||||
 | 
			
		||||
            auto appDataDir = File::getSpecialLocation (File::userApplicationDataDirectory)
 | 
			
		||||
                                   .getChildFile (JUCEApplication::getInstance()->getApplicationName());
 | 
			
		||||
 | 
			
		||||
            if (! appDataDir.exists())
 | 
			
		||||
                appDataDir.createDirectory();
 | 
			
		||||
 | 
			
		||||
            savedEventsFile = appDataDir.getChildFile ("analytics_events.xml");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            // It's often a good idea to construct any analytics service API keys
 | 
			
		||||
            // at runtime, so they're not searchable in the binary distribution of
 | 
			
		||||
            // your application (but we've not done this here). You should replace
 | 
			
		||||
            // the following key with your own to get this example application
 | 
			
		||||
            // fully working.
 | 
			
		||||
 | 
			
		||||
            apiKey = "UA-XXXXXXXXX-1";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        startAnalyticsThread (initialPeriodMs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~GoogleAnalyticsDestination() override
 | 
			
		||||
    {
 | 
			
		||||
        // Here we sleep so that our background thread has a chance to send the
 | 
			
		||||
        // last lot of batched events. Be careful - if your app takes too long to
 | 
			
		||||
        // shut down then some operating systems will kill it forcibly!
 | 
			
		||||
        Thread::sleep (initialPeriodMs);
 | 
			
		||||
 | 
			
		||||
        stopAnalyticsThread (1000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int getMaximumBatchSize() override   { return 20; }
 | 
			
		||||
 | 
			
		||||
    bool logBatchedEvents (const Array<AnalyticsEvent>& events) override
 | 
			
		||||
    {
 | 
			
		||||
        // Send events to Google Analytics.
 | 
			
		||||
 | 
			
		||||
        String appData ("v=1&aip=1&tid=" + apiKey);
 | 
			
		||||
 | 
			
		||||
        StringArray postData;
 | 
			
		||||
 | 
			
		||||
        for (auto& event : events)
 | 
			
		||||
        {
 | 
			
		||||
            StringPairArray data;
 | 
			
		||||
 | 
			
		||||
            switch (event.eventType)
 | 
			
		||||
            {
 | 
			
		||||
                case (DemoAnalyticsEventTypes::event):
 | 
			
		||||
                {
 | 
			
		||||
                    data.set ("t", "event");
 | 
			
		||||
 | 
			
		||||
                    if (event.name == "startup")
 | 
			
		||||
                    {
 | 
			
		||||
                        data.set ("ec",  "info");
 | 
			
		||||
                        data.set ("ea",  "appStarted");
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (event.name == "shutdown")
 | 
			
		||||
                    {
 | 
			
		||||
                        data.set ("ec",  "info");
 | 
			
		||||
                        data.set ("ea",  "appStopped");
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (event.name == "button_press")
 | 
			
		||||
                    {
 | 
			
		||||
                        data.set ("ec",  "button_press");
 | 
			
		||||
                        data.set ("ea",  event.parameters["id"]);
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (event.name == "crash")
 | 
			
		||||
                    {
 | 
			
		||||
                        data.set ("ec",  "crash");
 | 
			
		||||
                        data.set ("ea",  "crash");
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        jassertfalse;
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                default:
 | 
			
		||||
                {
 | 
			
		||||
                    // Unknown event type! In this demo app we're just using a
 | 
			
		||||
                    // single event type, but in a real app you probably want to
 | 
			
		||||
                    // handle multiple ones.
 | 
			
		||||
                    jassertfalse;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            data.set ("cid", event.userID);
 | 
			
		||||
 | 
			
		||||
            StringArray eventData;
 | 
			
		||||
 | 
			
		||||
            for (auto& key : data.getAllKeys())
 | 
			
		||||
                eventData.add (key + "=" + URL::addEscapeChars (data[key], true));
 | 
			
		||||
 | 
			
		||||
            postData.add (appData + "&" + eventData.joinIntoString ("&"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto url = URL ("https://www.google-analytics.com/batch")
 | 
			
		||||
                       .withPOSTData (postData.joinIntoString ("\n"));
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            const ScopedLock lock (webStreamCreation);
 | 
			
		||||
 | 
			
		||||
            if (shouldExit)
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            webStream.reset (new WebInputStream (url, true));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto success = webStream->connect (nullptr);
 | 
			
		||||
 | 
			
		||||
        // Do an exponential backoff if we failed to connect.
 | 
			
		||||
        if (success)
 | 
			
		||||
            periodMs = initialPeriodMs;
 | 
			
		||||
        else
 | 
			
		||||
            periodMs *= 2;
 | 
			
		||||
 | 
			
		||||
        setBatchPeriod (periodMs);
 | 
			
		||||
 | 
			
		||||
        return success;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void stopLoggingEvents() override
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock lock (webStreamCreation);
 | 
			
		||||
 | 
			
		||||
        shouldExit = true;
 | 
			
		||||
 | 
			
		||||
        if (webStream.get() != nullptr)
 | 
			
		||||
            webStream->cancel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void saveUnloggedEvents (const std::deque<AnalyticsEvent>& eventsToSave) override
 | 
			
		||||
    {
 | 
			
		||||
        // Save unsent events to disk. Here we use XML as a serialisation format, but
 | 
			
		||||
        // you can use anything else as long as the restoreUnloggedEvents method can
 | 
			
		||||
        // restore events from disk. If you're saving very large numbers of events then
 | 
			
		||||
        // a binary format may be more suitable if it is faster - remember that this
 | 
			
		||||
        // method is called on app shutdown so it needs to complete quickly!
 | 
			
		||||
 | 
			
		||||
        auto xml = parseXMLIfTagMatches (savedEventsFile, "events");
 | 
			
		||||
 | 
			
		||||
        if (xml == nullptr)
 | 
			
		||||
            xml = std::make_unique<XmlElement> ("events");
 | 
			
		||||
 | 
			
		||||
        for (auto& event : eventsToSave)
 | 
			
		||||
        {
 | 
			
		||||
            auto* xmlEvent = new XmlElement ("google_analytics_event");
 | 
			
		||||
            xmlEvent->setAttribute ("name", event.name);
 | 
			
		||||
            xmlEvent->setAttribute ("type", event.eventType);
 | 
			
		||||
            xmlEvent->setAttribute ("timestamp", (int) event.timestamp);
 | 
			
		||||
            xmlEvent->setAttribute ("user_id", event.userID);
 | 
			
		||||
 | 
			
		||||
            auto* parameters = new XmlElement ("parameters");
 | 
			
		||||
 | 
			
		||||
            for (auto& key : event.parameters.getAllKeys())
 | 
			
		||||
                parameters->setAttribute (key, event.parameters[key]);
 | 
			
		||||
 | 
			
		||||
            xmlEvent->addChildElement (parameters);
 | 
			
		||||
 | 
			
		||||
            auto* userProperties = new XmlElement ("user_properties");
 | 
			
		||||
 | 
			
		||||
            for (auto& key : event.userProperties.getAllKeys())
 | 
			
		||||
                userProperties->setAttribute (key, event.userProperties[key]);
 | 
			
		||||
 | 
			
		||||
            xmlEvent->addChildElement (userProperties);
 | 
			
		||||
 | 
			
		||||
            xml->addChildElement (xmlEvent);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        xml->writeTo (savedEventsFile, {});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void restoreUnloggedEvents (std::deque<AnalyticsEvent>& restoredEventQueue) override
 | 
			
		||||
    {
 | 
			
		||||
        if (auto xml = parseXMLIfTagMatches (savedEventsFile, "events"))
 | 
			
		||||
        {
 | 
			
		||||
            auto numEvents = xml->getNumChildElements();
 | 
			
		||||
 | 
			
		||||
            for (auto iEvent = 0; iEvent < numEvents; ++iEvent)
 | 
			
		||||
            {
 | 
			
		||||
                auto* xmlEvent = xml->getChildElement (iEvent);
 | 
			
		||||
 | 
			
		||||
                StringPairArray parameters;
 | 
			
		||||
                auto* xmlParameters = xmlEvent->getChildByName ("parameters");
 | 
			
		||||
                auto numParameters = xmlParameters->getNumAttributes();
 | 
			
		||||
 | 
			
		||||
                for (auto iParam = 0; iParam < numParameters; ++iParam)
 | 
			
		||||
                    parameters.set (xmlParameters->getAttributeName (iParam),
 | 
			
		||||
                                    xmlParameters->getAttributeValue (iParam));
 | 
			
		||||
 | 
			
		||||
                StringPairArray userProperties;
 | 
			
		||||
                auto* xmlUserProperties = xmlEvent->getChildByName ("user_properties");
 | 
			
		||||
                auto numUserProperties = xmlUserProperties->getNumAttributes();
 | 
			
		||||
 | 
			
		||||
                for (auto iProp = 0; iProp < numUserProperties; ++iProp)
 | 
			
		||||
                    userProperties.set (xmlUserProperties->getAttributeName (iProp),
 | 
			
		||||
                                        xmlUserProperties->getAttributeValue (iProp));
 | 
			
		||||
 | 
			
		||||
                restoredEventQueue.push_back ({
 | 
			
		||||
                    xmlEvent->getStringAttribute ("name"),
 | 
			
		||||
                    xmlEvent->getIntAttribute ("type"),
 | 
			
		||||
                    static_cast<uint32> (xmlEvent->getIntAttribute ("timestamp")),
 | 
			
		||||
                    parameters,
 | 
			
		||||
                    xmlEvent->getStringAttribute ("user_id"),
 | 
			
		||||
                    userProperties
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            savedEventsFile.deleteFile();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const int initialPeriodMs = 1000;
 | 
			
		||||
    int periodMs = initialPeriodMs;
 | 
			
		||||
 | 
			
		||||
    CriticalSection webStreamCreation;
 | 
			
		||||
    bool shouldExit = false;
 | 
			
		||||
    std::unique_ptr<WebInputStream> webStream;
 | 
			
		||||
 | 
			
		||||
    String apiKey;
 | 
			
		||||
 | 
			
		||||
    File savedEventsFile;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class AnalyticsCollectionDemo   : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AnalyticsCollectionDemo()
 | 
			
		||||
    {
 | 
			
		||||
        // Add an analytics identifier for the user. Make sure you don't accidentally
 | 
			
		||||
        // collect identifiable information if you haven't asked for permission!
 | 
			
		||||
        Analytics::getInstance()->setUserId ("AnonUser1234");
 | 
			
		||||
 | 
			
		||||
        // Add any other constant user information.
 | 
			
		||||
        StringPairArray userData;
 | 
			
		||||
        userData.set ("group", "beta");
 | 
			
		||||
        Analytics::getInstance()->setUserProperties (userData);
 | 
			
		||||
 | 
			
		||||
        // Add any analytics destinations we want to use to the Analytics singleton.
 | 
			
		||||
        Analytics::getInstance()->addDestination (new GoogleAnalyticsDestination());
 | 
			
		||||
 | 
			
		||||
        // The event type here should probably be DemoAnalyticsEventTypes::sessionStart
 | 
			
		||||
        // in a more advanced app.
 | 
			
		||||
        Analytics::getInstance()->logEvent ("startup", {}, DemoAnalyticsEventTypes::event);
 | 
			
		||||
 | 
			
		||||
        crashButton.onClick = [this] { sendCrash(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (eventButton);
 | 
			
		||||
        addAndMakeVisible (crashButton);
 | 
			
		||||
 | 
			
		||||
        setSize (300, 200);
 | 
			
		||||
 | 
			
		||||
        StringPairArray logButtonPressParameters;
 | 
			
		||||
        logButtonPressParameters.set ("id", "a");
 | 
			
		||||
        logEventButtonPress.reset (new ButtonTracker (eventButton, "button_press", logButtonPressParameters));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~AnalyticsCollectionDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        // The event type here should probably be DemoAnalyticsEventTypes::sessionEnd
 | 
			
		||||
        // in a more advanced app.
 | 
			
		||||
        Analytics::getInstance()->logEvent ("shutdown", {}, DemoAnalyticsEventTypes::event);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        eventButton.centreWithSize (100, 40);
 | 
			
		||||
        eventButton.setBounds (eventButton.getBounds().translated (0, 25));
 | 
			
		||||
        crashButton.setBounds (eventButton.getBounds().translated (0, -50));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void sendCrash()
 | 
			
		||||
    {
 | 
			
		||||
        // In a more advanced application you would probably use a different event
 | 
			
		||||
        // type here.
 | 
			
		||||
        Analytics::getInstance()->logEvent ("crash", {}, DemoAnalyticsEventTypes::event);
 | 
			
		||||
        Analytics::getInstance()->getDestinations().clear();
 | 
			
		||||
        JUCEApplication::getInstance()->shutdown();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TextButton eventButton { "Press me!" }, crashButton { "Simulate crash!" };
 | 
			
		||||
    std::unique_ptr<ButtonTracker> logEventButtonPress;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnalyticsCollectionDemo)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             AnalyticsCollectionDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Collects analytics data.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_analytics, juce_core, juce_data_structures, juce_events,
 | 
			
		||||
                   juce_graphics, juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2022, linux_make, xcode_iphone, androidstudio
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        AnalyticsCollectionDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
enum DemoAnalyticsEventTypes
 | 
			
		||||
{
 | 
			
		||||
    event,
 | 
			
		||||
    sessionStart,
 | 
			
		||||
    sessionEnd,
 | 
			
		||||
    screenView,
 | 
			
		||||
    exception
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class GoogleAnalyticsDestination  : public ThreadedAnalyticsDestination
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    GoogleAnalyticsDestination()
 | 
			
		||||
        : ThreadedAnalyticsDestination ("GoogleAnalyticsThread")
 | 
			
		||||
    {
 | 
			
		||||
        {
 | 
			
		||||
            // Choose where to save any unsent events.
 | 
			
		||||
 | 
			
		||||
            auto appDataDir = File::getSpecialLocation (File::userApplicationDataDirectory)
 | 
			
		||||
                                   .getChildFile (JUCEApplication::getInstance()->getApplicationName());
 | 
			
		||||
 | 
			
		||||
            if (! appDataDir.exists())
 | 
			
		||||
                appDataDir.createDirectory();
 | 
			
		||||
 | 
			
		||||
            savedEventsFile = appDataDir.getChildFile ("analytics_events.xml");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            // It's often a good idea to construct any analytics service API keys
 | 
			
		||||
            // at runtime, so they're not searchable in the binary distribution of
 | 
			
		||||
            // your application (but we've not done this here). You should replace
 | 
			
		||||
            // the following key with your own to get this example application
 | 
			
		||||
            // fully working.
 | 
			
		||||
 | 
			
		||||
            apiKey = "UA-XXXXXXXXX-1";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        startAnalyticsThread (initialPeriodMs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~GoogleAnalyticsDestination() override
 | 
			
		||||
    {
 | 
			
		||||
        // Here we sleep so that our background thread has a chance to send the
 | 
			
		||||
        // last lot of batched events. Be careful - if your app takes too long to
 | 
			
		||||
        // shut down then some operating systems will kill it forcibly!
 | 
			
		||||
        Thread::sleep (initialPeriodMs);
 | 
			
		||||
 | 
			
		||||
        stopAnalyticsThread (1000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int getMaximumBatchSize() override   { return 20; }
 | 
			
		||||
 | 
			
		||||
    bool logBatchedEvents (const Array<AnalyticsEvent>& events) override
 | 
			
		||||
    {
 | 
			
		||||
        // Send events to Google Analytics.
 | 
			
		||||
 | 
			
		||||
        String appData ("v=1&aip=1&tid=" + apiKey);
 | 
			
		||||
 | 
			
		||||
        StringArray postData;
 | 
			
		||||
 | 
			
		||||
        for (auto& event : events)
 | 
			
		||||
        {
 | 
			
		||||
            StringPairArray data;
 | 
			
		||||
 | 
			
		||||
            switch (event.eventType)
 | 
			
		||||
            {
 | 
			
		||||
                case (DemoAnalyticsEventTypes::event):
 | 
			
		||||
                {
 | 
			
		||||
                    data.set ("t", "event");
 | 
			
		||||
 | 
			
		||||
                    if (event.name == "startup")
 | 
			
		||||
                    {
 | 
			
		||||
                        data.set ("ec",  "info");
 | 
			
		||||
                        data.set ("ea",  "appStarted");
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (event.name == "shutdown")
 | 
			
		||||
                    {
 | 
			
		||||
                        data.set ("ec",  "info");
 | 
			
		||||
                        data.set ("ea",  "appStopped");
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (event.name == "button_press")
 | 
			
		||||
                    {
 | 
			
		||||
                        data.set ("ec",  "button_press");
 | 
			
		||||
                        data.set ("ea",  event.parameters["id"]);
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (event.name == "crash")
 | 
			
		||||
                    {
 | 
			
		||||
                        data.set ("ec",  "crash");
 | 
			
		||||
                        data.set ("ea",  "crash");
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        jassertfalse;
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                default:
 | 
			
		||||
                {
 | 
			
		||||
                    // Unknown event type! In this demo app we're just using a
 | 
			
		||||
                    // single event type, but in a real app you probably want to
 | 
			
		||||
                    // handle multiple ones.
 | 
			
		||||
                    jassertfalse;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            data.set ("cid", event.userID);
 | 
			
		||||
 | 
			
		||||
            StringArray eventData;
 | 
			
		||||
 | 
			
		||||
            for (auto& key : data.getAllKeys())
 | 
			
		||||
                eventData.add (key + "=" + URL::addEscapeChars (data[key], true));
 | 
			
		||||
 | 
			
		||||
            postData.add (appData + "&" + eventData.joinIntoString ("&"));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto url = URL ("https://www.google-analytics.com/batch")
 | 
			
		||||
                       .withPOSTData (postData.joinIntoString ("\n"));
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            const ScopedLock lock (webStreamCreation);
 | 
			
		||||
 | 
			
		||||
            if (shouldExit)
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            webStream.reset (new WebInputStream (url, true));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto success = webStream->connect (nullptr);
 | 
			
		||||
 | 
			
		||||
        // Do an exponential backoff if we failed to connect.
 | 
			
		||||
        if (success)
 | 
			
		||||
            periodMs = initialPeriodMs;
 | 
			
		||||
        else
 | 
			
		||||
            periodMs *= 2;
 | 
			
		||||
 | 
			
		||||
        setBatchPeriod (periodMs);
 | 
			
		||||
 | 
			
		||||
        return success;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void stopLoggingEvents() override
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock lock (webStreamCreation);
 | 
			
		||||
 | 
			
		||||
        shouldExit = true;
 | 
			
		||||
 | 
			
		||||
        if (webStream.get() != nullptr)
 | 
			
		||||
            webStream->cancel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void saveUnloggedEvents (const std::deque<AnalyticsEvent>& eventsToSave) override
 | 
			
		||||
    {
 | 
			
		||||
        // Save unsent events to disk. Here we use XML as a serialisation format, but
 | 
			
		||||
        // you can use anything else as long as the restoreUnloggedEvents method can
 | 
			
		||||
        // restore events from disk. If you're saving very large numbers of events then
 | 
			
		||||
        // a binary format may be more suitable if it is faster - remember that this
 | 
			
		||||
        // method is called on app shutdown so it needs to complete quickly!
 | 
			
		||||
 | 
			
		||||
        auto xml = parseXMLIfTagMatches (savedEventsFile, "events");
 | 
			
		||||
 | 
			
		||||
        if (xml == nullptr)
 | 
			
		||||
            xml = std::make_unique<XmlElement> ("events");
 | 
			
		||||
 | 
			
		||||
        for (auto& event : eventsToSave)
 | 
			
		||||
        {
 | 
			
		||||
            auto* xmlEvent = new XmlElement ("google_analytics_event");
 | 
			
		||||
            xmlEvent->setAttribute ("name", event.name);
 | 
			
		||||
            xmlEvent->setAttribute ("type", event.eventType);
 | 
			
		||||
            xmlEvent->setAttribute ("timestamp", (int) event.timestamp);
 | 
			
		||||
            xmlEvent->setAttribute ("user_id", event.userID);
 | 
			
		||||
 | 
			
		||||
            auto* parameters = new XmlElement ("parameters");
 | 
			
		||||
 | 
			
		||||
            for (auto& key : event.parameters.getAllKeys())
 | 
			
		||||
                parameters->setAttribute (key, event.parameters[key]);
 | 
			
		||||
 | 
			
		||||
            xmlEvent->addChildElement (parameters);
 | 
			
		||||
 | 
			
		||||
            auto* userProperties = new XmlElement ("user_properties");
 | 
			
		||||
 | 
			
		||||
            for (auto& key : event.userProperties.getAllKeys())
 | 
			
		||||
                userProperties->setAttribute (key, event.userProperties[key]);
 | 
			
		||||
 | 
			
		||||
            xmlEvent->addChildElement (userProperties);
 | 
			
		||||
 | 
			
		||||
            xml->addChildElement (xmlEvent);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        xml->writeTo (savedEventsFile, {});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void restoreUnloggedEvents (std::deque<AnalyticsEvent>& restoredEventQueue) override
 | 
			
		||||
    {
 | 
			
		||||
        if (auto xml = parseXMLIfTagMatches (savedEventsFile, "events"))
 | 
			
		||||
        {
 | 
			
		||||
            auto numEvents = xml->getNumChildElements();
 | 
			
		||||
 | 
			
		||||
            for (auto iEvent = 0; iEvent < numEvents; ++iEvent)
 | 
			
		||||
            {
 | 
			
		||||
                auto* xmlEvent = xml->getChildElement (iEvent);
 | 
			
		||||
 | 
			
		||||
                StringPairArray parameters;
 | 
			
		||||
                auto* xmlParameters = xmlEvent->getChildByName ("parameters");
 | 
			
		||||
                auto numParameters = xmlParameters->getNumAttributes();
 | 
			
		||||
 | 
			
		||||
                for (auto iParam = 0; iParam < numParameters; ++iParam)
 | 
			
		||||
                    parameters.set (xmlParameters->getAttributeName (iParam),
 | 
			
		||||
                                    xmlParameters->getAttributeValue (iParam));
 | 
			
		||||
 | 
			
		||||
                StringPairArray userProperties;
 | 
			
		||||
                auto* xmlUserProperties = xmlEvent->getChildByName ("user_properties");
 | 
			
		||||
                auto numUserProperties = xmlUserProperties->getNumAttributes();
 | 
			
		||||
 | 
			
		||||
                for (auto iProp = 0; iProp < numUserProperties; ++iProp)
 | 
			
		||||
                    userProperties.set (xmlUserProperties->getAttributeName (iProp),
 | 
			
		||||
                                        xmlUserProperties->getAttributeValue (iProp));
 | 
			
		||||
 | 
			
		||||
                restoredEventQueue.push_back ({
 | 
			
		||||
                    xmlEvent->getStringAttribute ("name"),
 | 
			
		||||
                    xmlEvent->getIntAttribute ("type"),
 | 
			
		||||
                    static_cast<uint32> (xmlEvent->getIntAttribute ("timestamp")),
 | 
			
		||||
                    parameters,
 | 
			
		||||
                    xmlEvent->getStringAttribute ("user_id"),
 | 
			
		||||
                    userProperties
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            savedEventsFile.deleteFile();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const int initialPeriodMs = 1000;
 | 
			
		||||
    int periodMs = initialPeriodMs;
 | 
			
		||||
 | 
			
		||||
    CriticalSection webStreamCreation;
 | 
			
		||||
    bool shouldExit = false;
 | 
			
		||||
    std::unique_ptr<WebInputStream> webStream;
 | 
			
		||||
 | 
			
		||||
    String apiKey;
 | 
			
		||||
 | 
			
		||||
    File savedEventsFile;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class AnalyticsCollectionDemo   : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    AnalyticsCollectionDemo()
 | 
			
		||||
    {
 | 
			
		||||
        // Add an analytics identifier for the user. Make sure you don't accidentally
 | 
			
		||||
        // collect identifiable information if you haven't asked for permission!
 | 
			
		||||
        Analytics::getInstance()->setUserId ("AnonUser1234");
 | 
			
		||||
 | 
			
		||||
        // Add any other constant user information.
 | 
			
		||||
        StringPairArray userData;
 | 
			
		||||
        userData.set ("group", "beta");
 | 
			
		||||
        Analytics::getInstance()->setUserProperties (userData);
 | 
			
		||||
 | 
			
		||||
        // Add any analytics destinations we want to use to the Analytics singleton.
 | 
			
		||||
        Analytics::getInstance()->addDestination (new GoogleAnalyticsDestination());
 | 
			
		||||
 | 
			
		||||
        // The event type here should probably be DemoAnalyticsEventTypes::sessionStart
 | 
			
		||||
        // in a more advanced app.
 | 
			
		||||
        Analytics::getInstance()->logEvent ("startup", {}, DemoAnalyticsEventTypes::event);
 | 
			
		||||
 | 
			
		||||
        crashButton.onClick = [this] { sendCrash(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (eventButton);
 | 
			
		||||
        addAndMakeVisible (crashButton);
 | 
			
		||||
 | 
			
		||||
        setSize (300, 200);
 | 
			
		||||
 | 
			
		||||
        StringPairArray logButtonPressParameters;
 | 
			
		||||
        logButtonPressParameters.set ("id", "a");
 | 
			
		||||
        logEventButtonPress.reset (new ButtonTracker (eventButton, "button_press", logButtonPressParameters));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~AnalyticsCollectionDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        // The event type here should probably be DemoAnalyticsEventTypes::sessionEnd
 | 
			
		||||
        // in a more advanced app.
 | 
			
		||||
        Analytics::getInstance()->logEvent ("shutdown", {}, DemoAnalyticsEventTypes::event);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        eventButton.centreWithSize (100, 40);
 | 
			
		||||
        eventButton.setBounds (eventButton.getBounds().translated (0, 25));
 | 
			
		||||
        crashButton.setBounds (eventButton.getBounds().translated (0, -50));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void sendCrash()
 | 
			
		||||
    {
 | 
			
		||||
        // In a more advanced application you would probably use a different event
 | 
			
		||||
        // type here.
 | 
			
		||||
        Analytics::getInstance()->logEvent ("crash", {}, DemoAnalyticsEventTypes::event);
 | 
			
		||||
        Analytics::getInstance()->getDestinations().clear();
 | 
			
		||||
        JUCEApplication::getInstance()->shutdown();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    TextButton eventButton { "Press me!" }, crashButton { "Simulate crash!" };
 | 
			
		||||
    std::unique_ptr<ButtonTracker> logEventButtonPress;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnalyticsCollectionDemo)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										666
									
								
								deps/juce/examples/Utilities/Box2DDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										666
									
								
								deps/juce/examples/Utilities/Box2DDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,333 +1,333 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             Box2DDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Showcases 2D graphics features.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_box2d, juce_core, juce_data_structures, juce_events,
 | 
			
		||||
                   juce_graphics, juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        Box2DDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// (These classes and random functions are used inside the 3rd-party Box2D demo code)
 | 
			
		||||
inline float32 RandomFloat()                           { return Random::getSystemRandom().nextFloat() * 2.0f - 1.0f; }
 | 
			
		||||
inline float32 RandomFloat (float32 lo, float32 hi)    { return Random::getSystemRandom().nextFloat() * (hi - lo) + lo; }
 | 
			
		||||
 | 
			
		||||
struct Settings
 | 
			
		||||
{
 | 
			
		||||
    b2Vec2 viewCenter  { 0.0f, 20.0f };
 | 
			
		||||
    float32 hz = 60.0f;
 | 
			
		||||
    int velocityIterations = 8;
 | 
			
		||||
    int positionIterations = 3;
 | 
			
		||||
    int drawShapes         = 1;
 | 
			
		||||
    int drawJoints         = 1;
 | 
			
		||||
    int drawAABBs          = 0;
 | 
			
		||||
    int drawPairs          = 0;
 | 
			
		||||
    int drawContactPoints  = 0;
 | 
			
		||||
    int drawContactNormals = 0;
 | 
			
		||||
    int drawContactForces  = 0;
 | 
			
		||||
    int drawFrictionForces = 0;
 | 
			
		||||
    int drawCOMs           = 0;
 | 
			
		||||
    int drawStats          = 0;
 | 
			
		||||
    int drawProfile        = 0;
 | 
			
		||||
    int enableWarmStarting = 1;
 | 
			
		||||
    int enableContinuous   = 1;
 | 
			
		||||
    int enableSubStepping  = 0;
 | 
			
		||||
    int pause              = 0;
 | 
			
		||||
    int singleStep         = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Test
 | 
			
		||||
{
 | 
			
		||||
    Test()          {}
 | 
			
		||||
    virtual ~Test() {}
 | 
			
		||||
 | 
			
		||||
    virtual void Keyboard (unsigned char /*key*/)   {}
 | 
			
		||||
    virtual void KeyboardUp (unsigned char /*key*/) {}
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<b2World> m_world  { new b2World (b2Vec2 (0.0f, -10.0f)) };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wimplicit-int-float-conversion")
 | 
			
		||||
 | 
			
		||||
#include "../Assets/Box2DTests/AddPair.h"
 | 
			
		||||
#include "../Assets/Box2DTests/ApplyForce.h"
 | 
			
		||||
#include "../Assets/Box2DTests/Dominos.h"
 | 
			
		||||
#include "../Assets/Box2DTests/Chain.h"
 | 
			
		||||
 | 
			
		||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** This list box just displays a StringArray and broadcasts a change message when the
 | 
			
		||||
    selected row changes.
 | 
			
		||||
*/
 | 
			
		||||
class Box2DTestList : public ListBoxModel,
 | 
			
		||||
                      public ChangeBroadcaster
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    Box2DTestList (const StringArray& testList)
 | 
			
		||||
        : tests (testList)
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    int getNumRows() override                                      { return tests.size(); }
 | 
			
		||||
 | 
			
		||||
    void selectedRowsChanged (int /*lastRowSelected*/) override    { sendChangeMessage(); }
 | 
			
		||||
 | 
			
		||||
    void paintListBoxItem (int row, Graphics& g, int w, int h, bool rowIsSelected) override
 | 
			
		||||
    {
 | 
			
		||||
        auto& lf = LookAndFeel::getDefaultLookAndFeel();
 | 
			
		||||
 | 
			
		||||
        if (rowIsSelected)
 | 
			
		||||
            g.fillAll (Colour::contrasting (lf.findColour (ListBox::textColourId),
 | 
			
		||||
                                            lf.findColour (ListBox::backgroundColourId)));
 | 
			
		||||
 | 
			
		||||
        g.setColour (lf.findColour (ListBox::textColourId));
 | 
			
		||||
        g.setFont ((float) h * 0.7f);
 | 
			
		||||
        g.drawText (tests[row], Rectangle<int> (0, 0, w, h).reduced (2),
 | 
			
		||||
                    Justification::centredLeft, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    StringArray tests;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Box2DTestList)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
struct Box2DRenderComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
    Box2DRenderComponent()
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (Colours::white);
 | 
			
		||||
 | 
			
		||||
        if (currentTest.get() != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            Box2DRenderer renderer;
 | 
			
		||||
 | 
			
		||||
            renderer.render (g, *currentTest->m_world,
 | 
			
		||||
                             -16.0f, 30.0f, 16.0f, -1.0f,
 | 
			
		||||
                             getLocalBounds().toFloat().reduced (8.0f));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<Test> currentTest;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class Box2DDemo : public Component,
 | 
			
		||||
                  private Timer,
 | 
			
		||||
                  private ChangeListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    enum Demos
 | 
			
		||||
    {
 | 
			
		||||
        addPair = 0,
 | 
			
		||||
        applyForce,
 | 
			
		||||
        dominoes,
 | 
			
		||||
        chain,
 | 
			
		||||
        numTests
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Box2DDemo()
 | 
			
		||||
        : testsList (getTestsList())
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
        setWantsKeyboardFocus (true);
 | 
			
		||||
 | 
			
		||||
        testsListModel.addChangeListener (this);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (renderComponent);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (testsListBox);
 | 
			
		||||
        testsListBox.setModel (&testsListModel);
 | 
			
		||||
        testsListBox.selectRow (dominoes);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (instructions);
 | 
			
		||||
        instructions.setMultiLine (true);
 | 
			
		||||
        instructions.setReadOnly (true);
 | 
			
		||||
 | 
			
		||||
        startTimerHz (60);
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~Box2DDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        testsListModel.removeChangeListener (this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto r = getLocalBounds().reduced (4);
 | 
			
		||||
 | 
			
		||||
        auto area = r.removeFromBottom (150);
 | 
			
		||||
        testsListBox.setBounds (area.removeFromLeft (150));
 | 
			
		||||
 | 
			
		||||
        area.removeFromLeft (4);
 | 
			
		||||
        instructions.setBounds (area);
 | 
			
		||||
 | 
			
		||||
        r.removeFromBottom (6);
 | 
			
		||||
        renderComponent.setBounds (r);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool keyPressed (const KeyPress& key) override
 | 
			
		||||
    {
 | 
			
		||||
        if (renderComponent.currentTest.get() != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            // We override this to avoid the system beeping for an unused keypress
 | 
			
		||||
            switch (key.getTextCharacter())
 | 
			
		||||
            {
 | 
			
		||||
                case 'a':
 | 
			
		||||
                case 'w':
 | 
			
		||||
                case 'd':
 | 
			
		||||
                    return true;
 | 
			
		||||
 | 
			
		||||
                default:
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    StringArray testsList;
 | 
			
		||||
    Box2DTestList testsListModel  { testsList };
 | 
			
		||||
 | 
			
		||||
    Box2DRenderComponent renderComponent;
 | 
			
		||||
    ListBox testsListBox;
 | 
			
		||||
    TextEditor instructions;
 | 
			
		||||
 | 
			
		||||
    static Test* createTest (int index)
 | 
			
		||||
    {
 | 
			
		||||
        switch (index)
 | 
			
		||||
        {
 | 
			
		||||
            case addPair:       return new AddPair();
 | 
			
		||||
            case applyForce:    return new ApplyForce();
 | 
			
		||||
            case dominoes:      return new Dominos();
 | 
			
		||||
            case chain:         return new Chain();
 | 
			
		||||
            default:            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static String getInstructions (int index)
 | 
			
		||||
    {
 | 
			
		||||
        switch (index)
 | 
			
		||||
        {
 | 
			
		||||
            case applyForce:
 | 
			
		||||
                return String ("Keys:") + newLine + "Left: \'a\'" + newLine
 | 
			
		||||
                        + "Right: \'d\'" + newLine + "Forward: \'w\'";
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void checkKeys()
 | 
			
		||||
    {
 | 
			
		||||
        if (renderComponent.currentTest.get() == nullptr)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        checkKeyCode ('a');
 | 
			
		||||
        checkKeyCode ('w');
 | 
			
		||||
        checkKeyCode ('d');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void checkKeyCode (const int keyCode)
 | 
			
		||||
    {
 | 
			
		||||
        if (KeyPress::isKeyCurrentlyDown (keyCode))
 | 
			
		||||
            renderComponent.currentTest->Keyboard ((unsigned char) keyCode);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        if (renderComponent.currentTest.get() == nullptr)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (isShowing())
 | 
			
		||||
            grabKeyboardFocus();
 | 
			
		||||
 | 
			
		||||
        checkKeys();
 | 
			
		||||
        renderComponent.currentTest->m_world->Step (1.0f / 60.0f, 6, 2);
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void changeListenerCallback (ChangeBroadcaster* source) override
 | 
			
		||||
    {
 | 
			
		||||
        if (source == &testsListModel)
 | 
			
		||||
        {
 | 
			
		||||
            auto index = testsListBox.getSelectedRow();
 | 
			
		||||
 | 
			
		||||
            renderComponent.currentTest.reset (createTest (index));
 | 
			
		||||
            instructions.setText (getInstructions (index));
 | 
			
		||||
 | 
			
		||||
            repaint();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        instructions.applyFontToAllText (instructions.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static StringArray getTestsList()
 | 
			
		||||
    {
 | 
			
		||||
        return { "Add Pair Stress Test", "Apply Force", "Dominoes", "Chain" };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Box2DDemo)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             Box2DDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Showcases 2D graphics features.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_box2d, juce_core, juce_data_structures, juce_events,
 | 
			
		||||
                   juce_graphics, juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        Box2DDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// (These classes and random functions are used inside the 3rd-party Box2D demo code)
 | 
			
		||||
inline float32 RandomFloat()                           { return Random::getSystemRandom().nextFloat() * 2.0f - 1.0f; }
 | 
			
		||||
inline float32 RandomFloat (float32 lo, float32 hi)    { return Random::getSystemRandom().nextFloat() * (hi - lo) + lo; }
 | 
			
		||||
 | 
			
		||||
struct Settings
 | 
			
		||||
{
 | 
			
		||||
    b2Vec2 viewCenter  { 0.0f, 20.0f };
 | 
			
		||||
    float32 hz = 60.0f;
 | 
			
		||||
    int velocityIterations = 8;
 | 
			
		||||
    int positionIterations = 3;
 | 
			
		||||
    int drawShapes         = 1;
 | 
			
		||||
    int drawJoints         = 1;
 | 
			
		||||
    int drawAABBs          = 0;
 | 
			
		||||
    int drawPairs          = 0;
 | 
			
		||||
    int drawContactPoints  = 0;
 | 
			
		||||
    int drawContactNormals = 0;
 | 
			
		||||
    int drawContactForces  = 0;
 | 
			
		||||
    int drawFrictionForces = 0;
 | 
			
		||||
    int drawCOMs           = 0;
 | 
			
		||||
    int drawStats          = 0;
 | 
			
		||||
    int drawProfile        = 0;
 | 
			
		||||
    int enableWarmStarting = 1;
 | 
			
		||||
    int enableContinuous   = 1;
 | 
			
		||||
    int enableSubStepping  = 0;
 | 
			
		||||
    int pause              = 0;
 | 
			
		||||
    int singleStep         = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct Test
 | 
			
		||||
{
 | 
			
		||||
    Test()          {}
 | 
			
		||||
    virtual ~Test() {}
 | 
			
		||||
 | 
			
		||||
    virtual void Keyboard (unsigned char /*key*/)   {}
 | 
			
		||||
    virtual void KeyboardUp (unsigned char /*key*/) {}
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<b2World> m_world  { new b2World (b2Vec2 (0.0f, -10.0f)) };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wimplicit-int-float-conversion")
 | 
			
		||||
 | 
			
		||||
#include "../Assets/Box2DTests/AddPair.h"
 | 
			
		||||
#include "../Assets/Box2DTests/ApplyForce.h"
 | 
			
		||||
#include "../Assets/Box2DTests/Dominos.h"
 | 
			
		||||
#include "../Assets/Box2DTests/Chain.h"
 | 
			
		||||
 | 
			
		||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** This list box just displays a StringArray and broadcasts a change message when the
 | 
			
		||||
    selected row changes.
 | 
			
		||||
*/
 | 
			
		||||
class Box2DTestList : public ListBoxModel,
 | 
			
		||||
                      public ChangeBroadcaster
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    Box2DTestList (const StringArray& testList)
 | 
			
		||||
        : tests (testList)
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    int getNumRows() override                                      { return tests.size(); }
 | 
			
		||||
 | 
			
		||||
    void selectedRowsChanged (int /*lastRowSelected*/) override    { sendChangeMessage(); }
 | 
			
		||||
 | 
			
		||||
    void paintListBoxItem (int row, Graphics& g, int w, int h, bool rowIsSelected) override
 | 
			
		||||
    {
 | 
			
		||||
        auto& lf = LookAndFeel::getDefaultLookAndFeel();
 | 
			
		||||
 | 
			
		||||
        if (rowIsSelected)
 | 
			
		||||
            g.fillAll (Colour::contrasting (lf.findColour (ListBox::textColourId),
 | 
			
		||||
                                            lf.findColour (ListBox::backgroundColourId)));
 | 
			
		||||
 | 
			
		||||
        g.setColour (lf.findColour (ListBox::textColourId));
 | 
			
		||||
        g.setFont ((float) h * 0.7f);
 | 
			
		||||
        g.drawText (tests[row], Rectangle<int> (0, 0, w, h).reduced (2),
 | 
			
		||||
                    Justification::centredLeft, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    StringArray tests;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Box2DTestList)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
struct Box2DRenderComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
    Box2DRenderComponent()
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (Colours::white);
 | 
			
		||||
 | 
			
		||||
        if (currentTest.get() != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            Box2DRenderer renderer;
 | 
			
		||||
 | 
			
		||||
            renderer.render (g, *currentTest->m_world,
 | 
			
		||||
                             -16.0f, 30.0f, 16.0f, -1.0f,
 | 
			
		||||
                             getLocalBounds().toFloat().reduced (8.0f));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<Test> currentTest;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class Box2DDemo : public Component,
 | 
			
		||||
                  private Timer,
 | 
			
		||||
                  private ChangeListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    enum Demos
 | 
			
		||||
    {
 | 
			
		||||
        addPair = 0,
 | 
			
		||||
        applyForce,
 | 
			
		||||
        dominoes,
 | 
			
		||||
        chain,
 | 
			
		||||
        numTests
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Box2DDemo()
 | 
			
		||||
        : testsList (getTestsList())
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
        setWantsKeyboardFocus (true);
 | 
			
		||||
 | 
			
		||||
        testsListModel.addChangeListener (this);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (renderComponent);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (testsListBox);
 | 
			
		||||
        testsListBox.setModel (&testsListModel);
 | 
			
		||||
        testsListBox.selectRow (dominoes);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (instructions);
 | 
			
		||||
        instructions.setMultiLine (true);
 | 
			
		||||
        instructions.setReadOnly (true);
 | 
			
		||||
 | 
			
		||||
        startTimerHz (60);
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~Box2DDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        testsListModel.removeChangeListener (this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto r = getLocalBounds().reduced (4);
 | 
			
		||||
 | 
			
		||||
        auto area = r.removeFromBottom (150);
 | 
			
		||||
        testsListBox.setBounds (area.removeFromLeft (150));
 | 
			
		||||
 | 
			
		||||
        area.removeFromLeft (4);
 | 
			
		||||
        instructions.setBounds (area);
 | 
			
		||||
 | 
			
		||||
        r.removeFromBottom (6);
 | 
			
		||||
        renderComponent.setBounds (r);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool keyPressed (const KeyPress& key) override
 | 
			
		||||
    {
 | 
			
		||||
        if (renderComponent.currentTest.get() != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            // We override this to avoid the system beeping for an unused keypress
 | 
			
		||||
            switch (key.getTextCharacter())
 | 
			
		||||
            {
 | 
			
		||||
                case 'a':
 | 
			
		||||
                case 'w':
 | 
			
		||||
                case 'd':
 | 
			
		||||
                    return true;
 | 
			
		||||
 | 
			
		||||
                default:
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    StringArray testsList;
 | 
			
		||||
    Box2DTestList testsListModel  { testsList };
 | 
			
		||||
 | 
			
		||||
    Box2DRenderComponent renderComponent;
 | 
			
		||||
    ListBox testsListBox;
 | 
			
		||||
    TextEditor instructions;
 | 
			
		||||
 | 
			
		||||
    static Test* createTest (int index)
 | 
			
		||||
    {
 | 
			
		||||
        switch (index)
 | 
			
		||||
        {
 | 
			
		||||
            case addPair:       return new AddPair();
 | 
			
		||||
            case applyForce:    return new ApplyForce();
 | 
			
		||||
            case dominoes:      return new Dominos();
 | 
			
		||||
            case chain:         return new Chain();
 | 
			
		||||
            default:            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static String getInstructions (int index)
 | 
			
		||||
    {
 | 
			
		||||
        switch (index)
 | 
			
		||||
        {
 | 
			
		||||
            case applyForce:
 | 
			
		||||
                return String ("Keys:") + newLine + "Left: \'a\'" + newLine
 | 
			
		||||
                        + "Right: \'d\'" + newLine + "Forward: \'w\'";
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void checkKeys()
 | 
			
		||||
    {
 | 
			
		||||
        if (renderComponent.currentTest.get() == nullptr)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        checkKeyCode ('a');
 | 
			
		||||
        checkKeyCode ('w');
 | 
			
		||||
        checkKeyCode ('d');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void checkKeyCode (const int keyCode)
 | 
			
		||||
    {
 | 
			
		||||
        if (KeyPress::isKeyCurrentlyDown (keyCode))
 | 
			
		||||
            renderComponent.currentTest->Keyboard ((unsigned char) keyCode);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        if (renderComponent.currentTest.get() == nullptr)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (isShowing())
 | 
			
		||||
            grabKeyboardFocus();
 | 
			
		||||
 | 
			
		||||
        checkKeys();
 | 
			
		||||
        renderComponent.currentTest->m_world->Step (1.0f / 60.0f, 6, 2);
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void changeListenerCallback (ChangeBroadcaster* source) override
 | 
			
		||||
    {
 | 
			
		||||
        if (source == &testsListModel)
 | 
			
		||||
        {
 | 
			
		||||
            auto index = testsListBox.getSelectedRow();
 | 
			
		||||
 | 
			
		||||
            renderComponent.currentTest.reset (createTest (index));
 | 
			
		||||
            instructions.setText (getInstructions (index));
 | 
			
		||||
 | 
			
		||||
            repaint();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        instructions.applyFontToAllText (instructions.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static StringArray getTestsList()
 | 
			
		||||
    {
 | 
			
		||||
        return { "Add Pair Stress Test", "Apply Force", "Dominoes", "Chain" };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Box2DDemo)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								deps/juce/examples/Utilities/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								deps/juce/examples/Utilities/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							@@ -1,15 +1,15 @@
 | 
			
		||||
# ==============================================================================
 | 
			
		||||
#
 | 
			
		||||
#  This file is part of the JUCE library.
 | 
			
		||||
#  Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
#  Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
#
 | 
			
		||||
#  JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
#  licensing.
 | 
			
		||||
#
 | 
			
		||||
#  By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
#  Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
#  By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
#  Agreement and JUCE Privacy Policy.
 | 
			
		||||
#
 | 
			
		||||
#  End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
#  End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
#  Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
#
 | 
			
		||||
#  Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										732
									
								
								deps/juce/examples/Utilities/ChildProcessDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										732
									
								
								deps/juce/examples/Utilities/ChildProcessDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,355 +1,377 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             ChildProcessDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Launches applications as child processes.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2019, linux_make
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Console
 | 
			
		||||
 mainClass:        ChildProcessDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// This is a token that's used at both ends of our parent-child processes, to
 | 
			
		||||
// act as a unique token in the command line arguments.
 | 
			
		||||
static const char* demoCommandLineUID = "demoUID";
 | 
			
		||||
 | 
			
		||||
// A few quick utility functions to convert between raw data and ValueTrees
 | 
			
		||||
static ValueTree memoryBlockToValueTree (const MemoryBlock& mb)
 | 
			
		||||
{
 | 
			
		||||
    return ValueTree::readFromData (mb.getData(), mb.getSize());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static MemoryBlock valueTreeToMemoryBlock (const ValueTree& v)
 | 
			
		||||
{
 | 
			
		||||
    MemoryOutputStream mo;
 | 
			
		||||
    v.writeToStream (mo);
 | 
			
		||||
 | 
			
		||||
    return mo.getMemoryBlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static String valueTreeToString (const ValueTree& v)
 | 
			
		||||
{
 | 
			
		||||
    if (auto xml = v.createXml())
 | 
			
		||||
        return xml->toString (XmlElement::TextFormat().singleLine().withoutHeader());
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class ChildProcessDemo   : public Component,
 | 
			
		||||
                           private MessageListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    ChildProcessDemo()
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (launchButton);
 | 
			
		||||
        launchButton.onClick = [this] { launchChildProcess(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (pingButton);
 | 
			
		||||
        pingButton.onClick = [this] { pingChildProcess(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (killButton);
 | 
			
		||||
        killButton.onClick = [this] { killChildProcess(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (testResultsBox);
 | 
			
		||||
        testResultsBox.setMultiLine (true);
 | 
			
		||||
        testResultsBox.setFont ({ Font::getDefaultMonospacedFontName(), 12.0f, Font::plain });
 | 
			
		||||
 | 
			
		||||
        logMessage (String ("This demo uses the ChildProcessMaster and ChildProcessSlave classes to launch and communicate "
 | 
			
		||||
                            "with a child process, sending messages in the form of serialised ValueTree objects.") + newLine);
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~ChildProcessDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        masterProcess.reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto area = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        auto top = area.removeFromTop (40);
 | 
			
		||||
        launchButton.setBounds (top.removeFromLeft (180).reduced (8));
 | 
			
		||||
        pingButton  .setBounds (top.removeFromLeft (180).reduced (8));
 | 
			
		||||
        killButton  .setBounds (top.removeFromLeft (180).reduced (8));
 | 
			
		||||
 | 
			
		||||
        testResultsBox.setBounds (area.reduced (8));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Appends a message to the textbox that's shown in the demo as the console
 | 
			
		||||
    void logMessage (const String& message)
 | 
			
		||||
    {
 | 
			
		||||
        postMessage (new LogMessage (message));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // invoked by the 'launch' button.
 | 
			
		||||
    void launchChildProcess()
 | 
			
		||||
    {
 | 
			
		||||
        if (masterProcess.get() == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            masterProcess.reset (new DemoMasterProcess (*this));
 | 
			
		||||
 | 
			
		||||
            if (masterProcess->launchSlaveProcess (File::getSpecialLocation (File::currentExecutableFile), demoCommandLineUID))
 | 
			
		||||
                logMessage ("Child process started");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // invoked by the 'ping' button.
 | 
			
		||||
    void pingChildProcess()
 | 
			
		||||
    {
 | 
			
		||||
        if (masterProcess.get() != nullptr)
 | 
			
		||||
            masterProcess->sendPingMessageToSlave();
 | 
			
		||||
        else
 | 
			
		||||
            logMessage ("Child process is not running!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // invoked by the 'kill' button.
 | 
			
		||||
    void killChildProcess()
 | 
			
		||||
    {
 | 
			
		||||
        if (masterProcess.get() != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            masterProcess.reset();
 | 
			
		||||
            logMessage ("Child process killed");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    // This class is used by the main process, acting as the master and receiving messages
 | 
			
		||||
    // from the slave process.
 | 
			
		||||
    class DemoMasterProcess  : public ChildProcessMaster,
 | 
			
		||||
                               private DeletedAtShutdown
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        DemoMasterProcess (ChildProcessDemo& d) : demo (d) {}
 | 
			
		||||
 | 
			
		||||
        // This gets called when a message arrives from the slave process..
 | 
			
		||||
        void handleMessageFromSlave (const MemoryBlock& mb) override
 | 
			
		||||
        {
 | 
			
		||||
            auto incomingMessage = memoryBlockToValueTree (mb);
 | 
			
		||||
 | 
			
		||||
            demo.logMessage ("Received: " + valueTreeToString (incomingMessage));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // This gets called if the slave process dies.
 | 
			
		||||
        void handleConnectionLost() override
 | 
			
		||||
        {
 | 
			
		||||
            demo.logMessage ("Connection lost to child process!");
 | 
			
		||||
            demo.killChildProcess();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void sendPingMessageToSlave()
 | 
			
		||||
        {
 | 
			
		||||
            ValueTree message ("MESSAGE");
 | 
			
		||||
            message.setProperty ("count", count++, nullptr);
 | 
			
		||||
 | 
			
		||||
            demo.logMessage ("Sending: " + valueTreeToString (message));
 | 
			
		||||
 | 
			
		||||
            sendMessageToSlave (valueTreeToMemoryBlock (message));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ChildProcessDemo& demo;
 | 
			
		||||
        int count = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    std::unique_ptr<DemoMasterProcess> masterProcess;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    TextButton launchButton  { "Launch Child Process" };
 | 
			
		||||
    TextButton pingButton    { "Send Ping" };
 | 
			
		||||
    TextButton killButton    { "Kill Child Process" };
 | 
			
		||||
 | 
			
		||||
    TextEditor testResultsBox;
 | 
			
		||||
 | 
			
		||||
    struct LogMessage  : public Message
 | 
			
		||||
    {
 | 
			
		||||
        LogMessage (const String& m) : message (m) {}
 | 
			
		||||
 | 
			
		||||
        String message;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void handleMessage (const Message& message) override
 | 
			
		||||
    {
 | 
			
		||||
        testResultsBox.moveCaretToEnd();
 | 
			
		||||
        testResultsBox.insertTextAtCaret (static_cast<const LogMessage&> (message).message + newLine);
 | 
			
		||||
        testResultsBox.moveCaretToEnd();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        testResultsBox.applyFontToAllText (testResultsBox.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessDemo)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/*  This class gets instantiated in the child process, and receives messages from
 | 
			
		||||
    the master process.
 | 
			
		||||
*/
 | 
			
		||||
class DemoSlaveProcess  : public ChildProcessSlave,
 | 
			
		||||
                          private DeletedAtShutdown
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    DemoSlaveProcess() {}
 | 
			
		||||
 | 
			
		||||
    void handleMessageFromMaster (const MemoryBlock& mb) override
 | 
			
		||||
    {
 | 
			
		||||
        ValueTree incomingMessage (memoryBlockToValueTree (mb));
 | 
			
		||||
 | 
			
		||||
        /*  In this demo we're only expecting one type of message, which will contain a 'count' parameter -
 | 
			
		||||
            we'll just increment that number and send back a new message containing the new number.
 | 
			
		||||
 | 
			
		||||
            Obviously in a real app you'll probably want to look at the type of the message, and do
 | 
			
		||||
            some more interesting behaviour.
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        ValueTree reply ("REPLY");
 | 
			
		||||
        reply.setProperty ("countPlusOne", static_cast<int> (incomingMessage["count"]) + 1, nullptr);
 | 
			
		||||
 | 
			
		||||
        sendMessageToMaster (valueTreeToMemoryBlock (reply));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void handleConnectionMade() override
 | 
			
		||||
    {
 | 
			
		||||
        // This method is called when the connection is established, and in response, we'll just
 | 
			
		||||
        // send off a message to say hello.
 | 
			
		||||
        ValueTree reply ("HelloWorld");
 | 
			
		||||
        sendMessageToMaster (valueTreeToMemoryBlock (reply));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If no pings are received from the master process for a number of seconds, then this will get invoked.
 | 
			
		||||
       Typically you'll want to use this as a signal to kill the process as quickly as possible, as you
 | 
			
		||||
       don't want to leave it hanging around as a zombie..
 | 
			
		||||
    */
 | 
			
		||||
    void handleConnectionLost() override
 | 
			
		||||
    {
 | 
			
		||||
        JUCEApplication::quit();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/*  The JUCEApplication::initialise method calls this function to allow the
 | 
			
		||||
    child process to launch when the command line parameters indicate that we're
 | 
			
		||||
    being asked to run as a child process..
 | 
			
		||||
*/
 | 
			
		||||
bool invokeChildProcessDemo (const String& commandLine)
 | 
			
		||||
{
 | 
			
		||||
    std::unique_ptr<DemoSlaveProcess> slave (new DemoSlaveProcess());
 | 
			
		||||
 | 
			
		||||
    if (slave->initialiseFromCommandLine (commandLine, demoCommandLineUID))
 | 
			
		||||
    {
 | 
			
		||||
        slave.release(); // allow the slave object to stay alive - it'll handle its own deletion.
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef JUCE_DEMO_RUNNER
 | 
			
		||||
 //==============================================================================
 | 
			
		||||
 // As we need to modify the JUCEApplication::initialise method to launch the child process
 | 
			
		||||
 // based on the command line parameters, we can't just use the normal auto-generated Main.cpp.
 | 
			
		||||
 // Instead, we don't do anything in Main.cpp and create a JUCEApplication subclass here with
 | 
			
		||||
 // the necessary modifications.
 | 
			
		||||
 class Application    : public JUCEApplication
 | 
			
		||||
 {
 | 
			
		||||
 public:
 | 
			
		||||
     //==============================================================================
 | 
			
		||||
     Application() {}
 | 
			
		||||
 | 
			
		||||
     const String getApplicationName() override              { return "ChildProcessDemo"; }
 | 
			
		||||
     const String getApplicationVersion() override           { return "1.0.0"; }
 | 
			
		||||
 | 
			
		||||
     void initialise (const String& commandLine) override
 | 
			
		||||
     {
 | 
			
		||||
         // launches the child process if the command line parameters contain the demo UID
 | 
			
		||||
         if (invokeChildProcessDemo (commandLine))
 | 
			
		||||
             return;
 | 
			
		||||
 | 
			
		||||
         mainWindow.reset (new MainWindow ("ChildProcessDemo", new ChildProcessDemo()));
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
     void shutdown() override                                { mainWindow = nullptr; }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
     class MainWindow    : public DocumentWindow
 | 
			
		||||
     {
 | 
			
		||||
     public:
 | 
			
		||||
         MainWindow (const String& name, Component* c)  : DocumentWindow (name,
 | 
			
		||||
                                                                          Desktop::getInstance().getDefaultLookAndFeel()
 | 
			
		||||
                                                                                                .findColour (ResizableWindow::backgroundColourId),
 | 
			
		||||
                                                                          DocumentWindow::allButtons)
 | 
			
		||||
         {
 | 
			
		||||
             setUsingNativeTitleBar (true);
 | 
			
		||||
             setContentOwned (c, true);
 | 
			
		||||
 | 
			
		||||
             centreWithSize (getWidth(), getHeight());
 | 
			
		||||
 | 
			
		||||
             setVisible (true);
 | 
			
		||||
         }
 | 
			
		||||
 | 
			
		||||
         void closeButtonPressed() override
 | 
			
		||||
         {
 | 
			
		||||
             JUCEApplication::getInstance()->systemRequestedQuit();
 | 
			
		||||
         }
 | 
			
		||||
 | 
			
		||||
     private:
 | 
			
		||||
         JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
 | 
			
		||||
     };
 | 
			
		||||
 | 
			
		||||
     std::unique_ptr<MainWindow> mainWindow;
 | 
			
		||||
 };
 | 
			
		||||
 | 
			
		||||
 //==============================================================================
 | 
			
		||||
 START_JUCE_APPLICATION (Application)
 | 
			
		||||
#endif
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             ChildProcessDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Launches applications as child processes.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2022, linux_make
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Console
 | 
			
		||||
 mainClass:        ChildProcessDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
// This is a token that's used at both ends of our parent-child processes, to
 | 
			
		||||
// act as a unique token in the command line arguments.
 | 
			
		||||
static const char* demoCommandLineUID = "demoUID";
 | 
			
		||||
 | 
			
		||||
// A few quick utility functions to convert between raw data and ValueTrees
 | 
			
		||||
static ValueTree memoryBlockToValueTree (const MemoryBlock& mb)
 | 
			
		||||
{
 | 
			
		||||
    return ValueTree::readFromData (mb.getData(), mb.getSize());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static MemoryBlock valueTreeToMemoryBlock (const ValueTree& v)
 | 
			
		||||
{
 | 
			
		||||
    MemoryOutputStream mo;
 | 
			
		||||
    v.writeToStream (mo);
 | 
			
		||||
 | 
			
		||||
    return mo.getMemoryBlock();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static String valueTreeToString (const ValueTree& v)
 | 
			
		||||
{
 | 
			
		||||
    if (auto xml = v.createXml())
 | 
			
		||||
        return xml->toString (XmlElement::TextFormat().singleLine().withoutHeader());
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class ChildProcessDemo   : public Component,
 | 
			
		||||
                           private MessageListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    ChildProcessDemo()
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (launchButton);
 | 
			
		||||
        launchButton.onClick = [this] { launchChildProcess(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (pingButton);
 | 
			
		||||
        pingButton.onClick = [this] { pingChildProcess(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (killButton);
 | 
			
		||||
        killButton.onClick = [this] { killChildProcess(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (testResultsBox);
 | 
			
		||||
        testResultsBox.setMultiLine (true);
 | 
			
		||||
        testResultsBox.setFont ({ Font::getDefaultMonospacedFontName(), 12.0f, Font::plain });
 | 
			
		||||
 | 
			
		||||
        logMessage (String ("This demo uses the ChildProcessCoordinator and ChildProcessWorker classes to launch and communicate "
 | 
			
		||||
                            "with a child process, sending messages in the form of serialised ValueTree objects.") + newLine
 | 
			
		||||
                  + String ("In this demo, the child process will automatically quit if it fails to receive a ping message at least every ")
 | 
			
		||||
                  + String (timeoutSeconds)
 | 
			
		||||
                  + String (" seconds. To keep the process alive, press the \"")
 | 
			
		||||
                  + pingButton.getButtonText()
 | 
			
		||||
                  + String ("\" button periodically.") + newLine);
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~ChildProcessDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        coordinatorProcess.reset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto area = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        auto top = area.removeFromTop (40);
 | 
			
		||||
        launchButton.setBounds (top.removeFromLeft (180).reduced (8));
 | 
			
		||||
        pingButton  .setBounds (top.removeFromLeft (180).reduced (8));
 | 
			
		||||
        killButton  .setBounds (top.removeFromLeft (180).reduced (8));
 | 
			
		||||
 | 
			
		||||
        testResultsBox.setBounds (area.reduced (8));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Appends a message to the textbox that's shown in the demo as the console
 | 
			
		||||
    void logMessage (const String& message)
 | 
			
		||||
    {
 | 
			
		||||
        postMessage (new LogMessage (message));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // invoked by the 'launch' button.
 | 
			
		||||
    void launchChildProcess()
 | 
			
		||||
    {
 | 
			
		||||
        if (coordinatorProcess.get() == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            coordinatorProcess = std::make_unique<DemoCoordinatorProcess> (*this);
 | 
			
		||||
 | 
			
		||||
            if (coordinatorProcess->launchWorkerProcess (File::getSpecialLocation (File::currentExecutableFile),
 | 
			
		||||
                                                         demoCommandLineUID,
 | 
			
		||||
                                                         timeoutMillis))
 | 
			
		||||
            {
 | 
			
		||||
                logMessage ("Child process started");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // invoked by the 'ping' button.
 | 
			
		||||
    void pingChildProcess()
 | 
			
		||||
    {
 | 
			
		||||
        if (coordinatorProcess.get() != nullptr)
 | 
			
		||||
            coordinatorProcess->sendPingMessageToWorker();
 | 
			
		||||
        else
 | 
			
		||||
            logMessage ("Child process is not running!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // invoked by the 'kill' button.
 | 
			
		||||
    void killChildProcess()
 | 
			
		||||
    {
 | 
			
		||||
        if (coordinatorProcess.get() != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            coordinatorProcess.reset();
 | 
			
		||||
            logMessage ("Child process killed");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    // This class is used by the main process, acting as the coordinator and receiving messages
 | 
			
		||||
    // from the worker process.
 | 
			
		||||
    class DemoCoordinatorProcess  : public ChildProcessCoordinator,
 | 
			
		||||
                                    private DeletedAtShutdown,
 | 
			
		||||
                                    private AsyncUpdater
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        DemoCoordinatorProcess (ChildProcessDemo& d) : demo (d) {}
 | 
			
		||||
 | 
			
		||||
        ~DemoCoordinatorProcess() override { cancelPendingUpdate(); }
 | 
			
		||||
 | 
			
		||||
        // This gets called when a message arrives from the worker process..
 | 
			
		||||
        void handleMessageFromWorker (const MemoryBlock& mb) override
 | 
			
		||||
        {
 | 
			
		||||
            auto incomingMessage = memoryBlockToValueTree (mb);
 | 
			
		||||
 | 
			
		||||
            demo.logMessage ("Received: " + valueTreeToString (incomingMessage));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // This gets called if the worker process dies.
 | 
			
		||||
        void handleConnectionLost() override
 | 
			
		||||
        {
 | 
			
		||||
            demo.logMessage ("Connection lost to child process!");
 | 
			
		||||
            triggerAsyncUpdate();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void handleAsyncUpdate() override
 | 
			
		||||
        {
 | 
			
		||||
            demo.killChildProcess();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void sendPingMessageToWorker()
 | 
			
		||||
        {
 | 
			
		||||
            ValueTree message ("MESSAGE");
 | 
			
		||||
            message.setProperty ("count", count++, nullptr);
 | 
			
		||||
 | 
			
		||||
            demo.logMessage ("Sending: " + valueTreeToString (message));
 | 
			
		||||
 | 
			
		||||
            sendMessageToWorker (valueTreeToMemoryBlock (message));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ChildProcessDemo& demo;
 | 
			
		||||
        int count = 0;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    std::unique_ptr<DemoCoordinatorProcess> coordinatorProcess;
 | 
			
		||||
 | 
			
		||||
    static constexpr auto timeoutSeconds = 10;
 | 
			
		||||
    static constexpr auto timeoutMillis = timeoutSeconds * 1000;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
    TextButton launchButton  { "Launch Child Process" };
 | 
			
		||||
    TextButton pingButton    { "Send Ping" };
 | 
			
		||||
    TextButton killButton    { "Kill Child Process" };
 | 
			
		||||
 | 
			
		||||
    TextEditor testResultsBox;
 | 
			
		||||
 | 
			
		||||
    struct LogMessage  : public Message
 | 
			
		||||
    {
 | 
			
		||||
        LogMessage (const String& m) : message (m) {}
 | 
			
		||||
 | 
			
		||||
        String message;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    void handleMessage (const Message& message) override
 | 
			
		||||
    {
 | 
			
		||||
        testResultsBox.moveCaretToEnd();
 | 
			
		||||
        testResultsBox.insertTextAtCaret (static_cast<const LogMessage&> (message).message + newLine);
 | 
			
		||||
        testResultsBox.moveCaretToEnd();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        testResultsBox.applyFontToAllText (testResultsBox.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessDemo)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/*  This class gets instantiated in the child process, and receives messages from
 | 
			
		||||
    the coordinator process.
 | 
			
		||||
*/
 | 
			
		||||
class DemoWorkerProcess  : public ChildProcessWorker,
 | 
			
		||||
                           private DeletedAtShutdown
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    DemoWorkerProcess() = default;
 | 
			
		||||
 | 
			
		||||
    void handleMessageFromCoordinator (const MemoryBlock& mb) override
 | 
			
		||||
    {
 | 
			
		||||
        ValueTree incomingMessage (memoryBlockToValueTree (mb));
 | 
			
		||||
 | 
			
		||||
        /*  In this demo we're only expecting one type of message, which will contain a 'count' parameter -
 | 
			
		||||
            we'll just increment that number and send back a new message containing the new number.
 | 
			
		||||
 | 
			
		||||
            Obviously in a real app you'll probably want to look at the type of the message, and do
 | 
			
		||||
            some more interesting behaviour.
 | 
			
		||||
        */
 | 
			
		||||
 | 
			
		||||
        ValueTree reply ("REPLY");
 | 
			
		||||
        reply.setProperty ("countPlusOne", static_cast<int> (incomingMessage["count"]) + 1, nullptr);
 | 
			
		||||
 | 
			
		||||
        sendMessageToCoordinator (valueTreeToMemoryBlock (reply));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void handleConnectionMade() override
 | 
			
		||||
    {
 | 
			
		||||
        // This method is called when the connection is established, and in response, we'll just
 | 
			
		||||
        // send off a message to say hello.
 | 
			
		||||
        ValueTree reply ("HelloWorld");
 | 
			
		||||
        sendMessageToCoordinator (valueTreeToMemoryBlock (reply));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* If no pings are received from the coordinator process for a number of seconds, then this will get invoked.
 | 
			
		||||
       Typically, you'll want to use this as a signal to kill the process as quickly as possible, as you
 | 
			
		||||
       don't want to leave it hanging around as a zombie.
 | 
			
		||||
    */
 | 
			
		||||
    void handleConnectionLost() override
 | 
			
		||||
    {
 | 
			
		||||
        JUCEApplication::quit();
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/*  The JUCEApplication::initialise method calls this function to allow the
 | 
			
		||||
    child process to launch when the command line parameters indicate that we're
 | 
			
		||||
    being asked to run as a child process.
 | 
			
		||||
*/
 | 
			
		||||
inline bool invokeChildProcessDemo (const String& commandLine)
 | 
			
		||||
{
 | 
			
		||||
    auto worker = std::make_unique<DemoWorkerProcess>();
 | 
			
		||||
 | 
			
		||||
    if (worker->initialiseFromCommandLine (commandLine, demoCommandLineUID, ChildProcessDemo::timeoutMillis))
 | 
			
		||||
    {
 | 
			
		||||
        worker.release(); // allow the worker object to stay alive - it'll handle its own deletion.
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef JUCE_DEMO_RUNNER
 | 
			
		||||
 //==============================================================================
 | 
			
		||||
 // As we need to modify the JUCEApplication::initialise method to launch the child process
 | 
			
		||||
 // based on the command line parameters, we can't just use the normal auto-generated Main.cpp.
 | 
			
		||||
 // Instead, we don't do anything in Main.cpp and create a JUCEApplication subclass here with
 | 
			
		||||
 // the necessary modifications.
 | 
			
		||||
 class Application    : public JUCEApplication
 | 
			
		||||
 {
 | 
			
		||||
 public:
 | 
			
		||||
     //==============================================================================
 | 
			
		||||
     Application() {}
 | 
			
		||||
 | 
			
		||||
     const String getApplicationName() override              { return "ChildProcessDemo"; }
 | 
			
		||||
     const String getApplicationVersion() override           { return "1.0.0"; }
 | 
			
		||||
 | 
			
		||||
     void initialise (const String& commandLine) override
 | 
			
		||||
     {
 | 
			
		||||
         // launches the child process if the command line parameters contain the demo UID
 | 
			
		||||
         if (invokeChildProcessDemo (commandLine))
 | 
			
		||||
             return;
 | 
			
		||||
 | 
			
		||||
         mainWindow = std::make_unique<MainWindow> ("ChildProcessDemo", std::make_unique<ChildProcessDemo>());
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
     void shutdown() override                                { mainWindow = nullptr; }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
     class MainWindow    : public DocumentWindow
 | 
			
		||||
     {
 | 
			
		||||
     public:
 | 
			
		||||
         MainWindow (const String& name, std::unique_ptr<Component> c)
 | 
			
		||||
            : DocumentWindow (name,
 | 
			
		||||
                              Desktop::getInstance().getDefaultLookAndFeel()
 | 
			
		||||
                                                    .findColour (ResizableWindow::backgroundColourId),
 | 
			
		||||
                              DocumentWindow::allButtons)
 | 
			
		||||
         {
 | 
			
		||||
             setUsingNativeTitleBar (true);
 | 
			
		||||
             setContentOwned (c.release(), true);
 | 
			
		||||
 | 
			
		||||
             centreWithSize (getWidth(), getHeight());
 | 
			
		||||
 | 
			
		||||
             setVisible (true);
 | 
			
		||||
         }
 | 
			
		||||
 | 
			
		||||
         void closeButtonPressed() override
 | 
			
		||||
         {
 | 
			
		||||
             JUCEApplication::getInstance()->systemRequestedQuit();
 | 
			
		||||
         }
 | 
			
		||||
 | 
			
		||||
     private:
 | 
			
		||||
         JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow)
 | 
			
		||||
     };
 | 
			
		||||
 | 
			
		||||
     std::unique_ptr<MainWindow> mainWindow;
 | 
			
		||||
 };
 | 
			
		||||
 | 
			
		||||
 //==============================================================================
 | 
			
		||||
 START_JUCE_APPLICATION (Application)
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										542
									
								
								deps/juce/examples/Utilities/CryptographyDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										542
									
								
								deps/juce/examples/Utilities/CryptographyDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,271 +1,271 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             CryptographyDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Encrypts and decrypts data.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_cryptography, juce_data_structures, juce_events,
 | 
			
		||||
                   juce_graphics, juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        CryptographyDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class RSAComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    RSAComponent()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (rsaGroup);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (bitSize);
 | 
			
		||||
        bitSize.setText (String (256));
 | 
			
		||||
        bitSizeLabel.attachToComponent (&bitSize, true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (generateRSAButton);
 | 
			
		||||
        generateRSAButton.onClick = [this] { createRSAKey(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (rsaResultBox);
 | 
			
		||||
        rsaResultBox.setReadOnly (true);
 | 
			
		||||
        rsaResultBox.setMultiLine (true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto area = getLocalBounds();
 | 
			
		||||
        rsaGroup.setBounds (area);
 | 
			
		||||
        area.removeFromTop (10);
 | 
			
		||||
        area.reduce (5, 5);
 | 
			
		||||
 | 
			
		||||
        auto topArea = area.removeFromTop (34);
 | 
			
		||||
        topArea.removeFromLeft (110);
 | 
			
		||||
        bitSize.setBounds (topArea.removeFromLeft (topArea.getWidth() / 2).reduced (5));
 | 
			
		||||
        generateRSAButton.setBounds (topArea.reduced (5));
 | 
			
		||||
 | 
			
		||||
        rsaResultBox.setBounds (area.reduced (5));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void createRSAKey()
 | 
			
		||||
    {
 | 
			
		||||
        auto bits = jlimit (32, 1024, bitSize.getText().getIntValue());
 | 
			
		||||
        bitSize.setText (String (bits), dontSendNotification);
 | 
			
		||||
 | 
			
		||||
        // Create a key-pair...
 | 
			
		||||
        RSAKey publicKey, privateKey;
 | 
			
		||||
        RSAKey::createKeyPair (publicKey, privateKey, bits);
 | 
			
		||||
 | 
			
		||||
        // Test the new key on a piece of data...
 | 
			
		||||
        BigInteger testValue;
 | 
			
		||||
        testValue.parseString ("1234567890abcdef", 16);
 | 
			
		||||
 | 
			
		||||
        auto encodedValue = testValue;
 | 
			
		||||
        publicKey.applyToValue (encodedValue);
 | 
			
		||||
 | 
			
		||||
        auto decodedValue = encodedValue;
 | 
			
		||||
        privateKey.applyToValue (decodedValue);
 | 
			
		||||
 | 
			
		||||
        // ..and show the results..
 | 
			
		||||
        String message;
 | 
			
		||||
        message << "Number of bits: " << bits << newLine
 | 
			
		||||
                << "Public Key: "  << publicKey .toString() << newLine
 | 
			
		||||
                << "Private Key: " << privateKey.toString() << newLine
 | 
			
		||||
                << newLine
 | 
			
		||||
                << "Test input: " << testValue.toString (16) << newLine
 | 
			
		||||
                << "Encoded: " << encodedValue.toString (16) << newLine
 | 
			
		||||
                << "Decoded: " << decodedValue.toString (16) << newLine;
 | 
			
		||||
 | 
			
		||||
        rsaResultBox.setText (message, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    GroupComponent rsaGroup       { {}, "RSA Encryption" };
 | 
			
		||||
    TextButton generateRSAButton  { "Generate RSA" };
 | 
			
		||||
    Label bitSizeLabel            { {}, "Num Bits to Use:" };
 | 
			
		||||
    TextEditor bitSize, rsaResultBox;
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        rsaGroup.setColour (GroupComponent::outlineColourId,
 | 
			
		||||
                            getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::outline,
 | 
			
		||||
                                                    Colours::grey));
 | 
			
		||||
        rsaGroup.setColour (GroupComponent::textColourId,
 | 
			
		||||
                            getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::defaultText,
 | 
			
		||||
                                                    Colours::white));
 | 
			
		||||
        rsaResultBox.setColour (TextEditor::backgroundColourId,
 | 
			
		||||
                                getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::widgetBackground,
 | 
			
		||||
                                                        Colours::white.withAlpha (0.5f)));
 | 
			
		||||
 | 
			
		||||
        bitSize.applyFontToAllText (bitSize.getFont());
 | 
			
		||||
        rsaResultBox.applyFontToAllText (rsaResultBox.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RSAComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class HashesComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    HashesComponent()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (hashGroup);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (hashEntryBox);
 | 
			
		||||
        hashEntryBox.setMultiLine (true);
 | 
			
		||||
 | 
			
		||||
        hashEntryBox.setReturnKeyStartsNewLine (true);
 | 
			
		||||
        hashEntryBox.setText ("Type some text in this box and the resulting MD5, SHA and Whirlpool hashes will update below");
 | 
			
		||||
 | 
			
		||||
        auto updateHashes = [this]
 | 
			
		||||
        {
 | 
			
		||||
            auto text = hashEntryBox.getText();
 | 
			
		||||
 | 
			
		||||
            updateMD5Result       (text.toUTF8());
 | 
			
		||||
            updateSHA256Result    (text.toUTF8());
 | 
			
		||||
            updateWhirlpoolResult (text.toUTF8());
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        hashEntryBox.onTextChange = updateHashes;
 | 
			
		||||
        hashEntryBox.onReturnKey  = updateHashes;
 | 
			
		||||
 | 
			
		||||
        hashLabel1.attachToComponent (&hashEntryBox,    true);
 | 
			
		||||
        hashLabel2.attachToComponent (&md5Result,       true);
 | 
			
		||||
        hashLabel3.attachToComponent (&shaResult,       true);
 | 
			
		||||
        hashLabel4.attachToComponent (&whirlpoolResult, true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (md5Result);
 | 
			
		||||
        addAndMakeVisible (shaResult);
 | 
			
		||||
        addAndMakeVisible (whirlpoolResult);
 | 
			
		||||
 | 
			
		||||
        updateHashes();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void updateMD5Result (CharPointer_UTF8 text)
 | 
			
		||||
    {
 | 
			
		||||
        md5Result.setText (MD5 (text).toHexString(), dontSendNotification);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void updateSHA256Result (CharPointer_UTF8 text)
 | 
			
		||||
    {
 | 
			
		||||
        shaResult.setText (SHA256 (text).toHexString(), dontSendNotification);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void updateWhirlpoolResult (CharPointer_UTF8 text)
 | 
			
		||||
    {
 | 
			
		||||
        whirlpoolResult.setText (Whirlpool (text).toHexString(), dontSendNotification);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto area = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        hashGroup.setBounds (area);
 | 
			
		||||
 | 
			
		||||
        area.removeFromLeft (120);
 | 
			
		||||
        area.removeFromTop (10);
 | 
			
		||||
        area.reduce (5, 5);
 | 
			
		||||
 | 
			
		||||
        whirlpoolResult.setBounds (area.removeFromBottom (34));
 | 
			
		||||
        shaResult      .setBounds (area.removeFromBottom (34));
 | 
			
		||||
        md5Result      .setBounds (area.removeFromBottom (34));
 | 
			
		||||
        hashEntryBox   .setBounds (area.reduced (5));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    GroupComponent hashGroup { {}, "Hashes" };
 | 
			
		||||
    TextEditor hashEntryBox;
 | 
			
		||||
    Label md5Result, shaResult, whirlpoolResult;
 | 
			
		||||
 | 
			
		||||
    Label hashLabel1  { {}, "Text to Hash:" };
 | 
			
		||||
    Label hashLabel2  { {}, "MD5 Result:" };
 | 
			
		||||
    Label hashLabel3  { {}, "SHA Result:" };
 | 
			
		||||
    Label hashLabel4  { {}, "Whirlpool Result:" };
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        hashGroup.setColour (GroupComponent::outlineColourId,
 | 
			
		||||
                             getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::outline,
 | 
			
		||||
                                                     Colours::grey));
 | 
			
		||||
        hashGroup.setColour (GroupComponent::textColourId,
 | 
			
		||||
                             getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::defaultText,
 | 
			
		||||
                                                     Colours::white));
 | 
			
		||||
        hashEntryBox.setColour (TextEditor::backgroundColourId,
 | 
			
		||||
                                getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::widgetBackground,
 | 
			
		||||
                                                        Colours::white.withAlpha (0.5f)));
 | 
			
		||||
 | 
			
		||||
        hashEntryBox.applyFontToAllText (hashEntryBox.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HashesComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class CryptographyDemo  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    CryptographyDemo()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (rsaDemo);
 | 
			
		||||
        addAndMakeVisible (hashDemo);
 | 
			
		||||
 | 
			
		||||
        setSize (750, 750);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
 | 
			
		||||
                                           Colour::greyLevel (0.4f)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto area = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        rsaDemo .setBounds (area.removeFromTop (getHeight() / 2).reduced (5));
 | 
			
		||||
        hashDemo.setBounds (area.reduced (5));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    RSAComponent rsaDemo;
 | 
			
		||||
    HashesComponent hashDemo;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CryptographyDemo)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             CryptographyDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Encrypts and decrypts data.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_cryptography, juce_data_structures, juce_events,
 | 
			
		||||
                   juce_graphics, juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        CryptographyDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class RSAComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    RSAComponent()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (rsaGroup);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (bitSize);
 | 
			
		||||
        bitSize.setText (String (256));
 | 
			
		||||
        bitSizeLabel.attachToComponent (&bitSize, true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (generateRSAButton);
 | 
			
		||||
        generateRSAButton.onClick = [this] { createRSAKey(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (rsaResultBox);
 | 
			
		||||
        rsaResultBox.setReadOnly (true);
 | 
			
		||||
        rsaResultBox.setMultiLine (true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto area = getLocalBounds();
 | 
			
		||||
        rsaGroup.setBounds (area);
 | 
			
		||||
        area.removeFromTop (10);
 | 
			
		||||
        area.reduce (5, 5);
 | 
			
		||||
 | 
			
		||||
        auto topArea = area.removeFromTop (34);
 | 
			
		||||
        topArea.removeFromLeft (110);
 | 
			
		||||
        bitSize.setBounds (topArea.removeFromLeft (topArea.getWidth() / 2).reduced (5));
 | 
			
		||||
        generateRSAButton.setBounds (topArea.reduced (5));
 | 
			
		||||
 | 
			
		||||
        rsaResultBox.setBounds (area.reduced (5));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void createRSAKey()
 | 
			
		||||
    {
 | 
			
		||||
        auto bits = jlimit (32, 1024, bitSize.getText().getIntValue());
 | 
			
		||||
        bitSize.setText (String (bits), dontSendNotification);
 | 
			
		||||
 | 
			
		||||
        // Create a key-pair...
 | 
			
		||||
        RSAKey publicKey, privateKey;
 | 
			
		||||
        RSAKey::createKeyPair (publicKey, privateKey, bits);
 | 
			
		||||
 | 
			
		||||
        // Test the new key on a piece of data...
 | 
			
		||||
        BigInteger testValue;
 | 
			
		||||
        testValue.parseString ("1234567890abcdef", 16);
 | 
			
		||||
 | 
			
		||||
        auto encodedValue = testValue;
 | 
			
		||||
        publicKey.applyToValue (encodedValue);
 | 
			
		||||
 | 
			
		||||
        auto decodedValue = encodedValue;
 | 
			
		||||
        privateKey.applyToValue (decodedValue);
 | 
			
		||||
 | 
			
		||||
        // ..and show the results..
 | 
			
		||||
        String message;
 | 
			
		||||
        message << "Number of bits: " << bits << newLine
 | 
			
		||||
                << "Public Key: "  << publicKey .toString() << newLine
 | 
			
		||||
                << "Private Key: " << privateKey.toString() << newLine
 | 
			
		||||
                << newLine
 | 
			
		||||
                << "Test input: " << testValue.toString (16) << newLine
 | 
			
		||||
                << "Encoded: " << encodedValue.toString (16) << newLine
 | 
			
		||||
                << "Decoded: " << decodedValue.toString (16) << newLine;
 | 
			
		||||
 | 
			
		||||
        rsaResultBox.setText (message, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    GroupComponent rsaGroup       { {}, "RSA Encryption" };
 | 
			
		||||
    TextButton generateRSAButton  { "Generate RSA" };
 | 
			
		||||
    Label bitSizeLabel            { {}, "Num Bits to Use:" };
 | 
			
		||||
    TextEditor bitSize, rsaResultBox;
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        rsaGroup.setColour (GroupComponent::outlineColourId,
 | 
			
		||||
                            getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::outline,
 | 
			
		||||
                                                    Colours::grey));
 | 
			
		||||
        rsaGroup.setColour (GroupComponent::textColourId,
 | 
			
		||||
                            getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::defaultText,
 | 
			
		||||
                                                    Colours::white));
 | 
			
		||||
        rsaResultBox.setColour (TextEditor::backgroundColourId,
 | 
			
		||||
                                getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::widgetBackground,
 | 
			
		||||
                                                        Colours::white.withAlpha (0.5f)));
 | 
			
		||||
 | 
			
		||||
        bitSize.applyFontToAllText (bitSize.getFont());
 | 
			
		||||
        rsaResultBox.applyFontToAllText (rsaResultBox.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RSAComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class HashesComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    HashesComponent()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (hashGroup);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (hashEntryBox);
 | 
			
		||||
        hashEntryBox.setMultiLine (true);
 | 
			
		||||
 | 
			
		||||
        hashEntryBox.setReturnKeyStartsNewLine (true);
 | 
			
		||||
        hashEntryBox.setText ("Type some text in this box and the resulting MD5, SHA and Whirlpool hashes will update below");
 | 
			
		||||
 | 
			
		||||
        auto updateHashes = [this]
 | 
			
		||||
        {
 | 
			
		||||
            auto text = hashEntryBox.getText();
 | 
			
		||||
 | 
			
		||||
            updateMD5Result       (text.toUTF8());
 | 
			
		||||
            updateSHA256Result    (text.toUTF8());
 | 
			
		||||
            updateWhirlpoolResult (text.toUTF8());
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        hashEntryBox.onTextChange = updateHashes;
 | 
			
		||||
        hashEntryBox.onReturnKey  = updateHashes;
 | 
			
		||||
 | 
			
		||||
        hashLabel1.attachToComponent (&hashEntryBox,    true);
 | 
			
		||||
        hashLabel2.attachToComponent (&md5Result,       true);
 | 
			
		||||
        hashLabel3.attachToComponent (&shaResult,       true);
 | 
			
		||||
        hashLabel4.attachToComponent (&whirlpoolResult, true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (md5Result);
 | 
			
		||||
        addAndMakeVisible (shaResult);
 | 
			
		||||
        addAndMakeVisible (whirlpoolResult);
 | 
			
		||||
 | 
			
		||||
        updateHashes();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void updateMD5Result (CharPointer_UTF8 text)
 | 
			
		||||
    {
 | 
			
		||||
        md5Result.setText (MD5 (text).toHexString(), dontSendNotification);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void updateSHA256Result (CharPointer_UTF8 text)
 | 
			
		||||
    {
 | 
			
		||||
        shaResult.setText (SHA256 (text).toHexString(), dontSendNotification);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void updateWhirlpoolResult (CharPointer_UTF8 text)
 | 
			
		||||
    {
 | 
			
		||||
        whirlpoolResult.setText (Whirlpool (text).toHexString(), dontSendNotification);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto area = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        hashGroup.setBounds (area);
 | 
			
		||||
 | 
			
		||||
        area.removeFromLeft (120);
 | 
			
		||||
        area.removeFromTop (10);
 | 
			
		||||
        area.reduce (5, 5);
 | 
			
		||||
 | 
			
		||||
        whirlpoolResult.setBounds (area.removeFromBottom (34));
 | 
			
		||||
        shaResult      .setBounds (area.removeFromBottom (34));
 | 
			
		||||
        md5Result      .setBounds (area.removeFromBottom (34));
 | 
			
		||||
        hashEntryBox   .setBounds (area.reduced (5));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    GroupComponent hashGroup { {}, "Hashes" };
 | 
			
		||||
    TextEditor hashEntryBox;
 | 
			
		||||
    Label md5Result, shaResult, whirlpoolResult;
 | 
			
		||||
 | 
			
		||||
    Label hashLabel1  { {}, "Text to Hash:" };
 | 
			
		||||
    Label hashLabel2  { {}, "MD5 Result:" };
 | 
			
		||||
    Label hashLabel3  { {}, "SHA Result:" };
 | 
			
		||||
    Label hashLabel4  { {}, "Whirlpool Result:" };
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        hashGroup.setColour (GroupComponent::outlineColourId,
 | 
			
		||||
                             getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::outline,
 | 
			
		||||
                                                     Colours::grey));
 | 
			
		||||
        hashGroup.setColour (GroupComponent::textColourId,
 | 
			
		||||
                             getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::defaultText,
 | 
			
		||||
                                                     Colours::white));
 | 
			
		||||
        hashEntryBox.setColour (TextEditor::backgroundColourId,
 | 
			
		||||
                                getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::widgetBackground,
 | 
			
		||||
                                                        Colours::white.withAlpha (0.5f)));
 | 
			
		||||
 | 
			
		||||
        hashEntryBox.applyFontToAllText (hashEntryBox.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HashesComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class CryptographyDemo  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    CryptographyDemo()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (rsaDemo);
 | 
			
		||||
        addAndMakeVisible (hashDemo);
 | 
			
		||||
 | 
			
		||||
        setSize (750, 750);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
 | 
			
		||||
                                           Colour::greyLevel (0.4f)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto area = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        rsaDemo .setBounds (area.removeFromTop (getHeight() / 2).reduced (5));
 | 
			
		||||
        hashDemo.setBounds (area.reduced (5));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    RSAComponent rsaDemo;
 | 
			
		||||
    HashesComponent hashDemo;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CryptographyDemo)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1203
									
								
								deps/juce/examples/Utilities/InAppPurchasesDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1203
									
								
								deps/juce/examples/Utilities/InAppPurchasesDemo.h
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										370
									
								
								deps/juce/examples/Utilities/JavaScriptDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										370
									
								
								deps/juce/examples/Utilities/JavaScriptDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,185 +1,185 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             JavaScriptDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Showcases JavaScript features.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics, juce_gui_extra
 | 
			
		||||
 exporters:        xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        JavaScriptDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class JavaScriptDemo    : public Component,
 | 
			
		||||
                          private CodeDocument::Listener,
 | 
			
		||||
                          private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    JavaScriptDemo()
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
 | 
			
		||||
        editor.reset (new CodeEditorComponent (codeDocument, nullptr));
 | 
			
		||||
        addAndMakeVisible (editor.get());
 | 
			
		||||
        editor->setFont ({ Font::getDefaultMonospacedFontName(), 14.0f, Font::plain });
 | 
			
		||||
        editor->setTabSize (4, true);
 | 
			
		||||
 | 
			
		||||
        outputDisplay.setMultiLine (true);
 | 
			
		||||
        outputDisplay.setReadOnly (true);
 | 
			
		||||
        outputDisplay.setCaretVisible (false);
 | 
			
		||||
        outputDisplay.setFont ({ Font::getDefaultMonospacedFontName(), 14.0f, Font::plain });
 | 
			
		||||
        addAndMakeVisible (outputDisplay);
 | 
			
		||||
 | 
			
		||||
        codeDocument.addListener (this);
 | 
			
		||||
 | 
			
		||||
        editor->loadContent (
 | 
			
		||||
            "/*\n"
 | 
			
		||||
            "    Javascript! In this simple demo, the native\n"
 | 
			
		||||
            "    code provides an object called \'Demo\' which\n"
 | 
			
		||||
            "    has a method \'print\' that writes to the\n"
 | 
			
		||||
            "    console below...\n"
 | 
			
		||||
            "*/\n"
 | 
			
		||||
            "\n"
 | 
			
		||||
            "Demo.print (\"Hello World in JUCE + Javascript!\");\n"
 | 
			
		||||
            "Demo.print (\"\");\n"
 | 
			
		||||
            "\n"
 | 
			
		||||
            "function factorial (n)\n"
 | 
			
		||||
            "{\n"
 | 
			
		||||
            "    var total = 1;\n"
 | 
			
		||||
            "    while (n > 0)\n"
 | 
			
		||||
            "        total = total * n--;\n"
 | 
			
		||||
            "    return total;\n"
 | 
			
		||||
            "}\n"
 | 
			
		||||
            "\n"
 | 
			
		||||
            "for (var i = 1; i < 10; ++i)\n"
 | 
			
		||||
            "    Demo.print (\"Factorial of \" + i \n"
 | 
			
		||||
            "                   + \" = \" + factorial (i));\n");
 | 
			
		||||
 | 
			
		||||
        setSize (600, 750);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void runScript()
 | 
			
		||||
    {
 | 
			
		||||
        outputDisplay.clear();
 | 
			
		||||
 | 
			
		||||
        JavascriptEngine engine;
 | 
			
		||||
        engine.maximumExecutionTime = RelativeTime::seconds (5);
 | 
			
		||||
        engine.registerNativeObject ("Demo", new DemoClass (*this));
 | 
			
		||||
 | 
			
		||||
        auto startTime = Time::getMillisecondCounterHiRes();
 | 
			
		||||
 | 
			
		||||
        auto result = engine.execute (codeDocument.getAllContent());
 | 
			
		||||
 | 
			
		||||
        auto elapsedMs = Time::getMillisecondCounterHiRes() - startTime;
 | 
			
		||||
 | 
			
		||||
        if (result.failed())
 | 
			
		||||
            outputDisplay.setText (result.getErrorMessage());
 | 
			
		||||
        else
 | 
			
		||||
            outputDisplay.insertTextAtCaret ("\n(Execution time: " + String (elapsedMs, 2) + " milliseconds)");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void consoleOutput (const String& message)
 | 
			
		||||
    {
 | 
			
		||||
        outputDisplay.moveCaretToEnd();
 | 
			
		||||
        outputDisplay.insertTextAtCaret (message + newLine);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    // This class is used by the script, and provides methods that the JS can call.
 | 
			
		||||
    struct DemoClass  : public DynamicObject
 | 
			
		||||
    {
 | 
			
		||||
        DemoClass (JavaScriptDemo& demo) : owner (demo)
 | 
			
		||||
        {
 | 
			
		||||
            setMethod ("print", print);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static Identifier getClassName()    { return "Demo"; }
 | 
			
		||||
 | 
			
		||||
        static var print (const var::NativeFunctionArgs& args)
 | 
			
		||||
        {
 | 
			
		||||
            if (args.numArguments > 0)
 | 
			
		||||
                if (auto* thisObject = dynamic_cast<DemoClass*> (args.thisObject.getObject()))
 | 
			
		||||
                    thisObject->owner.consoleOutput (args.arguments[0].toString());
 | 
			
		||||
 | 
			
		||||
            return var::undefined();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        JavaScriptDemo& owner;
 | 
			
		||||
 | 
			
		||||
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoClass)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    CodeDocument codeDocument;
 | 
			
		||||
    std::unique_ptr<CodeEditorComponent> editor;
 | 
			
		||||
    TextEditor outputDisplay;
 | 
			
		||||
 | 
			
		||||
    void codeDocumentTextInserted (const String&, int) override    { startTimer (300); }
 | 
			
		||||
    void codeDocumentTextDeleted (int, int) override               { startTimer (300); }
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        stopTimer();
 | 
			
		||||
        runScript();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto r = getLocalBounds().reduced (8);
 | 
			
		||||
 | 
			
		||||
        editor->setBounds       (r.removeFromTop (proportionOfHeight (0.6f)));
 | 
			
		||||
        outputDisplay.setBounds (r.withTrimmedTop (8));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        outputDisplay.applyFontToAllText (outputDisplay.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JavaScriptDemo)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             JavaScriptDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Showcases JavaScript features.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics, juce_gui_extra
 | 
			
		||||
 exporters:        xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        JavaScriptDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class JavaScriptDemo    : public Component,
 | 
			
		||||
                          private CodeDocument::Listener,
 | 
			
		||||
                          private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    JavaScriptDemo()
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
 | 
			
		||||
        editor.reset (new CodeEditorComponent (codeDocument, nullptr));
 | 
			
		||||
        addAndMakeVisible (editor.get());
 | 
			
		||||
        editor->setFont ({ Font::getDefaultMonospacedFontName(), 14.0f, Font::plain });
 | 
			
		||||
        editor->setTabSize (4, true);
 | 
			
		||||
 | 
			
		||||
        outputDisplay.setMultiLine (true);
 | 
			
		||||
        outputDisplay.setReadOnly (true);
 | 
			
		||||
        outputDisplay.setCaretVisible (false);
 | 
			
		||||
        outputDisplay.setFont ({ Font::getDefaultMonospacedFontName(), 14.0f, Font::plain });
 | 
			
		||||
        addAndMakeVisible (outputDisplay);
 | 
			
		||||
 | 
			
		||||
        codeDocument.addListener (this);
 | 
			
		||||
 | 
			
		||||
        editor->loadContent (
 | 
			
		||||
            "/*\n"
 | 
			
		||||
            "    Javascript! In this simple demo, the native\n"
 | 
			
		||||
            "    code provides an object called \'Demo\' which\n"
 | 
			
		||||
            "    has a method \'print\' that writes to the\n"
 | 
			
		||||
            "    console below...\n"
 | 
			
		||||
            "*/\n"
 | 
			
		||||
            "\n"
 | 
			
		||||
            "Demo.print (\"Hello World in JUCE + Javascript!\");\n"
 | 
			
		||||
            "Demo.print (\"\");\n"
 | 
			
		||||
            "\n"
 | 
			
		||||
            "function factorial (n)\n"
 | 
			
		||||
            "{\n"
 | 
			
		||||
            "    var total = 1;\n"
 | 
			
		||||
            "    while (n > 0)\n"
 | 
			
		||||
            "        total = total * n--;\n"
 | 
			
		||||
            "    return total;\n"
 | 
			
		||||
            "}\n"
 | 
			
		||||
            "\n"
 | 
			
		||||
            "for (var i = 1; i < 10; ++i)\n"
 | 
			
		||||
            "    Demo.print (\"Factorial of \" + i \n"
 | 
			
		||||
            "                   + \" = \" + factorial (i));\n");
 | 
			
		||||
 | 
			
		||||
        setSize (600, 750);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void runScript()
 | 
			
		||||
    {
 | 
			
		||||
        outputDisplay.clear();
 | 
			
		||||
 | 
			
		||||
        JavascriptEngine engine;
 | 
			
		||||
        engine.maximumExecutionTime = RelativeTime::seconds (5);
 | 
			
		||||
        engine.registerNativeObject ("Demo", new DemoClass (*this));
 | 
			
		||||
 | 
			
		||||
        auto startTime = Time::getMillisecondCounterHiRes();
 | 
			
		||||
 | 
			
		||||
        auto result = engine.execute (codeDocument.getAllContent());
 | 
			
		||||
 | 
			
		||||
        auto elapsedMs = Time::getMillisecondCounterHiRes() - startTime;
 | 
			
		||||
 | 
			
		||||
        if (result.failed())
 | 
			
		||||
            outputDisplay.setText (result.getErrorMessage());
 | 
			
		||||
        else
 | 
			
		||||
            outputDisplay.insertTextAtCaret ("\n(Execution time: " + String (elapsedMs, 2) + " milliseconds)");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void consoleOutput (const String& message)
 | 
			
		||||
    {
 | 
			
		||||
        outputDisplay.moveCaretToEnd();
 | 
			
		||||
        outputDisplay.insertTextAtCaret (message + newLine);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    // This class is used by the script, and provides methods that the JS can call.
 | 
			
		||||
    struct DemoClass  : public DynamicObject
 | 
			
		||||
    {
 | 
			
		||||
        DemoClass (JavaScriptDemo& demo) : owner (demo)
 | 
			
		||||
        {
 | 
			
		||||
            setMethod ("print", print);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static Identifier getClassName()    { return "Demo"; }
 | 
			
		||||
 | 
			
		||||
        static var print (const var::NativeFunctionArgs& args)
 | 
			
		||||
        {
 | 
			
		||||
            if (args.numArguments > 0)
 | 
			
		||||
                if (auto* thisObject = dynamic_cast<DemoClass*> (args.thisObject.getObject()))
 | 
			
		||||
                    thisObject->owner.consoleOutput (args.arguments[0].toString());
 | 
			
		||||
 | 
			
		||||
            return var::undefined();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        JavaScriptDemo& owner;
 | 
			
		||||
 | 
			
		||||
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoClass)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    CodeDocument codeDocument;
 | 
			
		||||
    std::unique_ptr<CodeEditorComponent> editor;
 | 
			
		||||
    TextEditor outputDisplay;
 | 
			
		||||
 | 
			
		||||
    void codeDocumentTextInserted (const String&, int) override    { startTimer (300); }
 | 
			
		||||
    void codeDocumentTextDeleted (int, int) override               { startTimer (300); }
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        stopTimer();
 | 
			
		||||
        runScript();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto r = getLocalBounds().reduced (8);
 | 
			
		||||
 | 
			
		||||
        editor->setBounds       (r.removeFromTop (proportionOfHeight (0.6f)));
 | 
			
		||||
        outputDisplay.setBounds (r.withTrimmedTop (8));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        outputDisplay.applyFontToAllText (outputDisplay.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JavaScriptDemo)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										265
									
								
								deps/juce/examples/Utilities/LiveConstantDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										265
									
								
								deps/juce/examples/Utilities/LiveConstantDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,132 +1,133 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             LiveConstantDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Demonstrates the live constant macro.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics, juce_gui_extra
 | 
			
		||||
 exporters:        xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        LiveConstantDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
struct LiveConstantDemoComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
    LiveConstantDemoComponent() {}
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (JUCE_LIVE_CONSTANT (Colour (0xffe5e7a7)));
 | 
			
		||||
 | 
			
		||||
        g.setColour (JUCE_LIVE_CONSTANT (Colours::red.withAlpha (0.2f)));
 | 
			
		||||
        auto blockWidth  = JUCE_LIVE_CONSTANT (0x120);
 | 
			
		||||
        auto blockHeight = JUCE_LIVE_CONSTANT (200);
 | 
			
		||||
        g.fillRect ((getWidth() - blockWidth) / 2, (getHeight() - blockHeight) / 2, blockWidth, blockHeight);
 | 
			
		||||
 | 
			
		||||
        auto fontColour = JUCE_LIVE_CONSTANT (Colour (0xff000a55));
 | 
			
		||||
        auto fontSize   = JUCE_LIVE_CONSTANT (30.0f);
 | 
			
		||||
 | 
			
		||||
        g.setColour (fontColour);
 | 
			
		||||
        g.setFont (fontSize);
 | 
			
		||||
 | 
			
		||||
        g.drawFittedText (getDemoText(), getLocalBounds(), Justification::centred, 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static String getDemoText()
 | 
			
		||||
    {
 | 
			
		||||
        return JUCE_LIVE_CONSTANT ("Hello world!");
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class LiveConstantDemo   : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    LiveConstantDemo()
 | 
			
		||||
    {
 | 
			
		||||
        descriptionLabel.setMinimumHorizontalScale (1.0f);
 | 
			
		||||
        descriptionLabel.setText ("This demonstrates the JUCE_LIVE_CONSTANT macro, which allows you to quickly "
 | 
			
		||||
                                  "adjust primitive values at runtime by just wrapping them in a macro.\n\n"
 | 
			
		||||
                                  "To understand what's going on in this demo, you should have a look at the "
 | 
			
		||||
                                  "LiveConstantDemoComponent class, where you can see the code that's invoking the demo below...",
 | 
			
		||||
                                  dontSendNotification);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (descriptionLabel);
 | 
			
		||||
        addAndMakeVisible (startButton);
 | 
			
		||||
        addChildComponent (demoComp);
 | 
			
		||||
        startButton.onClick = [this] { start(); };
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto r = getLocalBounds().reduced (10);
 | 
			
		||||
 | 
			
		||||
        descriptionLabel.setBounds (r.removeFromTop (200));
 | 
			
		||||
        startButton     .setBounds (r.removeFromTop (22).removeFromLeft (250));
 | 
			
		||||
        demoComp        .setBounds (r.withTrimmedTop (10));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void start()
 | 
			
		||||
    {
 | 
			
		||||
        startButton.setVisible (false);
 | 
			
		||||
        demoComp   .setVisible (true);
 | 
			
		||||
 | 
			
		||||
        descriptionLabel.setText ("Tweak some of the colours and values in the pop-up window to see what "
 | 
			
		||||
                                  "the effect of your changes would be on the component below...",
 | 
			
		||||
                                  dontSendNotification);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    Label descriptionLabel;
 | 
			
		||||
    TextButton startButton  { "Begin Demo" };
 | 
			
		||||
 | 
			
		||||
    LiveConstantDemoComponent demoComp;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LiveConstantDemo)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             LiveConstantDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Demonstrates the live constant macro.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics, juce_gui_extra
 | 
			
		||||
 exporters:        xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        LiveConstantDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
struct LiveConstantDemoComponent  : public Component
 | 
			
		||||
{
 | 
			
		||||
    LiveConstantDemoComponent() {}
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (JUCE_LIVE_CONSTANT (Colour (0xffe5e7a7)));
 | 
			
		||||
 | 
			
		||||
        g.setColour (JUCE_LIVE_CONSTANT (Colours::red.withAlpha (0.2f)));
 | 
			
		||||
        auto blockWidth  = JUCE_LIVE_CONSTANT (0x120);
 | 
			
		||||
        auto blockHeight = JUCE_LIVE_CONSTANT (200);
 | 
			
		||||
        g.fillRect ((getWidth() - blockWidth) / 2, (getHeight() - blockHeight) / 2, blockWidth, blockHeight);
 | 
			
		||||
 | 
			
		||||
        auto fontColour = JUCE_LIVE_CONSTANT (Colour (0xff000a55));
 | 
			
		||||
        auto fontSize   = JUCE_LIVE_CONSTANT (30.0f);
 | 
			
		||||
 | 
			
		||||
        g.setColour (fontColour);
 | 
			
		||||
        g.setFont (fontSize);
 | 
			
		||||
 | 
			
		||||
        g.drawFittedText (getDemoText(), getLocalBounds(), Justification::centred, 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static String getDemoText()
 | 
			
		||||
    {
 | 
			
		||||
        return JUCE_LIVE_CONSTANT ("Hello world!");
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class LiveConstantDemo   : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    LiveConstantDemo()
 | 
			
		||||
    {
 | 
			
		||||
        descriptionLabel.setMinimumHorizontalScale (1.0f);
 | 
			
		||||
        descriptionLabel.setText ("This demonstrates the JUCE_LIVE_CONSTANT macro, which allows you to quickly "
 | 
			
		||||
                                  "adjust primitive values at runtime by just wrapping them in a macro.\n\n"
 | 
			
		||||
                                  "Editing JUCE_LIVE_CONSTANT values is only enabled in debug builds.\n\n"
 | 
			
		||||
                                  "To understand what's going on in this demo, you should have a look at the "
 | 
			
		||||
                                  "LiveConstantDemoComponent class, where you can see the code that's invoking the demo below.",
 | 
			
		||||
                                  dontSendNotification);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (descriptionLabel);
 | 
			
		||||
        addAndMakeVisible (startButton);
 | 
			
		||||
        addChildComponent (demoComp);
 | 
			
		||||
        startButton.onClick = [this] { start(); };
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto r = getLocalBounds().reduced (10);
 | 
			
		||||
 | 
			
		||||
        descriptionLabel.setBounds (r.removeFromTop (200));
 | 
			
		||||
        startButton     .setBounds (r.removeFromTop (22).removeFromLeft (250));
 | 
			
		||||
        demoComp        .setBounds (r.withTrimmedTop (10));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void start()
 | 
			
		||||
    {
 | 
			
		||||
        startButton.setVisible (false);
 | 
			
		||||
        demoComp   .setVisible (true);
 | 
			
		||||
 | 
			
		||||
        descriptionLabel.setText ("Tweak some of the colours and values in the pop-up window to see what "
 | 
			
		||||
                                  "the effect of your changes would be on the component below...",
 | 
			
		||||
                                  dontSendNotification);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    Label descriptionLabel;
 | 
			
		||||
    TextButton startButton  { "Begin Demo" };
 | 
			
		||||
 | 
			
		||||
    LiveConstantDemoComponent demoComp;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LiveConstantDemo)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										658
									
								
								deps/juce/examples/Utilities/MultithreadingDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										658
									
								
								deps/juce/examples/Utilities/MultithreadingDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,329 +1,329 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             MultithreadingDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Demonstrates multi-threading.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        MultithreadingDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class BouncingBall   : private ComponentListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    BouncingBall (Component& comp)
 | 
			
		||||
        : containerComponent (comp)
 | 
			
		||||
    {
 | 
			
		||||
        containerComponent.addComponentListener (this);
 | 
			
		||||
 | 
			
		||||
        auto speed = 5.0f; // give each ball a fixed speed so we can
 | 
			
		||||
                           // see the effects of thread priority on how fast
 | 
			
		||||
                           // they actually go.
 | 
			
		||||
 | 
			
		||||
        auto angle = Random::getSystemRandom().nextFloat() * MathConstants<float>::twoPi;
 | 
			
		||||
 | 
			
		||||
        dx = std::sin (angle) * speed;
 | 
			
		||||
        dy = std::cos (angle) * speed;
 | 
			
		||||
 | 
			
		||||
        colour = Colour ((juce::uint32) Random::getSystemRandom().nextInt())
 | 
			
		||||
                    .withAlpha (0.5f)
 | 
			
		||||
                    .withBrightness (0.7f);
 | 
			
		||||
 | 
			
		||||
        componentMovedOrResized (containerComponent, true, true);
 | 
			
		||||
 | 
			
		||||
        x = Random::getSystemRandom().nextFloat() * parentWidth;
 | 
			
		||||
        y = Random::getSystemRandom().nextFloat() * parentHeight;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~BouncingBall() override
 | 
			
		||||
    {
 | 
			
		||||
        containerComponent.removeComponentListener (this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // This will be called from the message thread
 | 
			
		||||
    void draw (Graphics& g)
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock lock (drawing);
 | 
			
		||||
 | 
			
		||||
        g.setColour (colour);
 | 
			
		||||
        g.fillEllipse (x, y, size, size);
 | 
			
		||||
 | 
			
		||||
        g.setColour (Colours::black);
 | 
			
		||||
        g.setFont (10.0f);
 | 
			
		||||
        g.drawText (String::toHexString ((int64) threadId), Rectangle<float> (x, y, size, size), Justification::centred, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void moveBall()
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock lock (drawing);
 | 
			
		||||
 | 
			
		||||
        threadId = Thread::getCurrentThreadId(); // this is so the component can print the thread ID inside the ball
 | 
			
		||||
 | 
			
		||||
        x += dx;
 | 
			
		||||
        y += dy;
 | 
			
		||||
 | 
			
		||||
        if (x < 0)
 | 
			
		||||
            dx = std::abs (dx);
 | 
			
		||||
 | 
			
		||||
        if (x > parentWidth)
 | 
			
		||||
            dx = -std::abs (dx);
 | 
			
		||||
 | 
			
		||||
        if (y < 0)
 | 
			
		||||
            dy = std::abs (dy);
 | 
			
		||||
 | 
			
		||||
        if (y > parentHeight)
 | 
			
		||||
            dy = -std::abs (dy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void componentMovedOrResized (Component& comp, bool, bool) override
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock lock (drawing);
 | 
			
		||||
 | 
			
		||||
        parentWidth  = (float) comp.getWidth()  - size;
 | 
			
		||||
        parentHeight = (float) comp.getHeight() - size;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    float x = 0.0f, y = 0.0f,
 | 
			
		||||
          size = Random::getSystemRandom().nextFloat() * 30.0f + 30.0f,
 | 
			
		||||
          dx = 0.0f, dy = 0.0f,
 | 
			
		||||
          parentWidth = 50.0f, parentHeight = 50.0f;
 | 
			
		||||
 | 
			
		||||
    Colour colour;
 | 
			
		||||
    Thread::ThreadID threadId = {};
 | 
			
		||||
    CriticalSection drawing;
 | 
			
		||||
 | 
			
		||||
    Component& containerComponent;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BouncingBall)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class DemoThread    : public BouncingBall,
 | 
			
		||||
                      public Thread
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    DemoThread (Component& containerComp)
 | 
			
		||||
        : BouncingBall (containerComp),
 | 
			
		||||
          Thread ("JUCE Demo Thread")
 | 
			
		||||
    {
 | 
			
		||||
        // give the threads a random priority, so some will move more
 | 
			
		||||
        // smoothly than others..
 | 
			
		||||
        startThread (Random::getSystemRandom().nextInt (3) + 3);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~DemoThread() override
 | 
			
		||||
    {
 | 
			
		||||
        // allow the thread 2 seconds to stop cleanly - should be plenty of time.
 | 
			
		||||
        stopThread (2000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void run() override
 | 
			
		||||
    {
 | 
			
		||||
        // this is the code that runs this thread - we'll loop continuously,
 | 
			
		||||
        // updating the coordinates of our blob.
 | 
			
		||||
 | 
			
		||||
        // threadShouldExit() returns true when the stopThread() method has been
 | 
			
		||||
        // called, so we should check it often, and exit as soon as it gets flagged.
 | 
			
		||||
        while (! threadShouldExit())
 | 
			
		||||
        {
 | 
			
		||||
            // sleep a bit so the threads don't all grind the CPU to a halt..
 | 
			
		||||
            wait (interval);
 | 
			
		||||
 | 
			
		||||
            // because this is a background thread, we mustn't do any UI work without
 | 
			
		||||
            // first grabbing a MessageManagerLock..
 | 
			
		||||
            const MessageManagerLock mml (Thread::getCurrentThread());
 | 
			
		||||
 | 
			
		||||
            if (! mml.lockWasGained())  // if something is trying to kill this job, the lock
 | 
			
		||||
                return;                 // will fail, in which case we'd better return..
 | 
			
		||||
 | 
			
		||||
            // now we've got the UI thread locked, we can mess about with the components
 | 
			
		||||
            moveBall();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    int interval = Random::getSystemRandom().nextInt (50) + 6;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoThread)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class DemoThreadPoolJob  : public BouncingBall,
 | 
			
		||||
                           public ThreadPoolJob
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    DemoThreadPoolJob (Component& containerComp)
 | 
			
		||||
        : BouncingBall (containerComp),
 | 
			
		||||
          ThreadPoolJob ("Demo Threadpool Job")
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    JobStatus runJob() override
 | 
			
		||||
    {
 | 
			
		||||
        // this is the code that runs this job. It'll be repeatedly called until we return
 | 
			
		||||
        // jobHasFinished instead of jobNeedsRunningAgain.
 | 
			
		||||
        Thread::sleep (30);
 | 
			
		||||
 | 
			
		||||
        // because this is a background thread, we mustn't do any UI work without
 | 
			
		||||
        // first grabbing a MessageManagerLock..
 | 
			
		||||
        const MessageManagerLock mml (this);
 | 
			
		||||
 | 
			
		||||
        // before moving the ball, we need to check whether the lock was actually gained, because
 | 
			
		||||
        // if something is trying to stop this job, it will have failed..
 | 
			
		||||
        if (mml.lockWasGained())
 | 
			
		||||
            moveBall();
 | 
			
		||||
 | 
			
		||||
        return jobNeedsRunningAgain;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void removedFromQueue()
 | 
			
		||||
    {
 | 
			
		||||
        // This is called to tell us that our job has been removed from the pool.
 | 
			
		||||
        // In this case there's no need to do anything here.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoThreadPoolJob)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class MultithreadingDemo   : public Component,
 | 
			
		||||
                             private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    MultithreadingDemo()
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (controlButton);
 | 
			
		||||
        controlButton.changeWidthToFitText (24);
 | 
			
		||||
        controlButton.setTopLeftPosition (20, 20);
 | 
			
		||||
        controlButton.setTriggeredOnMouseDown (true);
 | 
			
		||||
        controlButton.setAlwaysOnTop (true);
 | 
			
		||||
        controlButton.onClick = [this] { showMenu(); };
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
 | 
			
		||||
        resetAllBalls();
 | 
			
		||||
 | 
			
		||||
        startTimerHz (60);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~MultithreadingDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        pool.removeAllJobs (true, 2000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resetAllBalls()
 | 
			
		||||
    {
 | 
			
		||||
        pool.removeAllJobs (true, 4000);
 | 
			
		||||
        balls.clear();
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < 5; ++i)
 | 
			
		||||
            addABall();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
 | 
			
		||||
        for (auto* ball : balls)
 | 
			
		||||
            ball->draw (g);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void setUsingPool (bool usePool)
 | 
			
		||||
    {
 | 
			
		||||
        isUsingPool = usePool;
 | 
			
		||||
        resetAllBalls();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void addABall()
 | 
			
		||||
    {
 | 
			
		||||
        if (isUsingPool)
 | 
			
		||||
        {
 | 
			
		||||
            auto newBall = std::make_unique<DemoThreadPoolJob> (*this);
 | 
			
		||||
            pool.addJob (newBall.get(), false);
 | 
			
		||||
            balls.add (newBall.release());
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            balls.add (new DemoThread (*this));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void showMenu()
 | 
			
		||||
    {
 | 
			
		||||
        PopupMenu m;
 | 
			
		||||
        m.addItem (1, "Use one thread per ball", true, ! isUsingPool);
 | 
			
		||||
        m.addItem (2, "Use a thread pool",       true,   isUsingPool);
 | 
			
		||||
 | 
			
		||||
        m.showMenuAsync (PopupMenu::Options().withTargetComponent (controlButton),
 | 
			
		||||
                         ModalCallbackFunction::forComponent (menuItemChosenCallback, this));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void menuItemChosenCallback (int result, MultithreadingDemo* demoComponent)
 | 
			
		||||
    {
 | 
			
		||||
        if (result != 0 && demoComponent != nullptr)
 | 
			
		||||
            demoComponent->setUsingPool (result == 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    ThreadPool pool           { 3 };
 | 
			
		||||
    TextButton controlButton  { "Thread type" };
 | 
			
		||||
    bool isUsingPool = false;
 | 
			
		||||
 | 
			
		||||
    OwnedArray<BouncingBall> balls;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultithreadingDemo)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             MultithreadingDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Demonstrates multi-threading.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        MultithreadingDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class BouncingBall   : private ComponentListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    BouncingBall (Component& comp)
 | 
			
		||||
        : containerComponent (comp)
 | 
			
		||||
    {
 | 
			
		||||
        containerComponent.addComponentListener (this);
 | 
			
		||||
 | 
			
		||||
        auto speed = 5.0f; // give each ball a fixed speed so we can
 | 
			
		||||
                           // see the effects of thread priority on how fast
 | 
			
		||||
                           // they actually go.
 | 
			
		||||
 | 
			
		||||
        auto angle = Random::getSystemRandom().nextFloat() * MathConstants<float>::twoPi;
 | 
			
		||||
 | 
			
		||||
        dx = std::sin (angle) * speed;
 | 
			
		||||
        dy = std::cos (angle) * speed;
 | 
			
		||||
 | 
			
		||||
        colour = Colour ((juce::uint32) Random::getSystemRandom().nextInt())
 | 
			
		||||
                    .withAlpha (0.5f)
 | 
			
		||||
                    .withBrightness (0.7f);
 | 
			
		||||
 | 
			
		||||
        componentMovedOrResized (containerComponent, true, true);
 | 
			
		||||
 | 
			
		||||
        x = Random::getSystemRandom().nextFloat() * parentWidth;
 | 
			
		||||
        y = Random::getSystemRandom().nextFloat() * parentHeight;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~BouncingBall() override
 | 
			
		||||
    {
 | 
			
		||||
        containerComponent.removeComponentListener (this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // This will be called from the message thread
 | 
			
		||||
    void draw (Graphics& g)
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock lock (drawing);
 | 
			
		||||
 | 
			
		||||
        g.setColour (colour);
 | 
			
		||||
        g.fillEllipse (x, y, size, size);
 | 
			
		||||
 | 
			
		||||
        g.setColour (Colours::black);
 | 
			
		||||
        g.setFont (10.0f);
 | 
			
		||||
        g.drawText (String::toHexString ((int64) threadId), Rectangle<float> (x, y, size, size), Justification::centred, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void moveBall()
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock lock (drawing);
 | 
			
		||||
 | 
			
		||||
        threadId = Thread::getCurrentThreadId(); // this is so the component can print the thread ID inside the ball
 | 
			
		||||
 | 
			
		||||
        x += dx;
 | 
			
		||||
        y += dy;
 | 
			
		||||
 | 
			
		||||
        if (x < 0)
 | 
			
		||||
            dx = std::abs (dx);
 | 
			
		||||
 | 
			
		||||
        if (x > parentWidth)
 | 
			
		||||
            dx = -std::abs (dx);
 | 
			
		||||
 | 
			
		||||
        if (y < 0)
 | 
			
		||||
            dy = std::abs (dy);
 | 
			
		||||
 | 
			
		||||
        if (y > parentHeight)
 | 
			
		||||
            dy = -std::abs (dy);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void componentMovedOrResized (Component& comp, bool, bool) override
 | 
			
		||||
    {
 | 
			
		||||
        const ScopedLock lock (drawing);
 | 
			
		||||
 | 
			
		||||
        parentWidth  = (float) comp.getWidth()  - size;
 | 
			
		||||
        parentHeight = (float) comp.getHeight() - size;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    float x = 0.0f, y = 0.0f,
 | 
			
		||||
          size = Random::getSystemRandom().nextFloat() * 30.0f + 30.0f,
 | 
			
		||||
          dx = 0.0f, dy = 0.0f,
 | 
			
		||||
          parentWidth = 50.0f, parentHeight = 50.0f;
 | 
			
		||||
 | 
			
		||||
    Colour colour;
 | 
			
		||||
    Thread::ThreadID threadId = {};
 | 
			
		||||
    CriticalSection drawing;
 | 
			
		||||
 | 
			
		||||
    Component& containerComponent;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BouncingBall)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class DemoThread    : public BouncingBall,
 | 
			
		||||
                      public Thread
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    DemoThread (Component& containerComp)
 | 
			
		||||
        : BouncingBall (containerComp),
 | 
			
		||||
          Thread ("JUCE Demo Thread")
 | 
			
		||||
    {
 | 
			
		||||
        // give the threads a random priority, so some will move more
 | 
			
		||||
        // smoothly than others..
 | 
			
		||||
        startThread (Random::getSystemRandom().nextInt (3) + 3);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~DemoThread() override
 | 
			
		||||
    {
 | 
			
		||||
        // allow the thread 2 seconds to stop cleanly - should be plenty of time.
 | 
			
		||||
        stopThread (2000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void run() override
 | 
			
		||||
    {
 | 
			
		||||
        // this is the code that runs this thread - we'll loop continuously,
 | 
			
		||||
        // updating the coordinates of our blob.
 | 
			
		||||
 | 
			
		||||
        // threadShouldExit() returns true when the stopThread() method has been
 | 
			
		||||
        // called, so we should check it often, and exit as soon as it gets flagged.
 | 
			
		||||
        while (! threadShouldExit())
 | 
			
		||||
        {
 | 
			
		||||
            // sleep a bit so the threads don't all grind the CPU to a halt..
 | 
			
		||||
            wait (interval);
 | 
			
		||||
 | 
			
		||||
            // because this is a background thread, we mustn't do any UI work without
 | 
			
		||||
            // first grabbing a MessageManagerLock..
 | 
			
		||||
            const MessageManagerLock mml (Thread::getCurrentThread());
 | 
			
		||||
 | 
			
		||||
            if (! mml.lockWasGained())  // if something is trying to kill this job, the lock
 | 
			
		||||
                return;                 // will fail, in which case we'd better return..
 | 
			
		||||
 | 
			
		||||
            // now we've got the UI thread locked, we can mess about with the components
 | 
			
		||||
            moveBall();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    int interval = Random::getSystemRandom().nextInt (50) + 6;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoThread)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class DemoThreadPoolJob  : public BouncingBall,
 | 
			
		||||
                           public ThreadPoolJob
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    DemoThreadPoolJob (Component& containerComp)
 | 
			
		||||
        : BouncingBall (containerComp),
 | 
			
		||||
          ThreadPoolJob ("Demo Threadpool Job")
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    JobStatus runJob() override
 | 
			
		||||
    {
 | 
			
		||||
        // this is the code that runs this job. It'll be repeatedly called until we return
 | 
			
		||||
        // jobHasFinished instead of jobNeedsRunningAgain.
 | 
			
		||||
        Thread::sleep (30);
 | 
			
		||||
 | 
			
		||||
        // because this is a background thread, we mustn't do any UI work without
 | 
			
		||||
        // first grabbing a MessageManagerLock..
 | 
			
		||||
        const MessageManagerLock mml (this);
 | 
			
		||||
 | 
			
		||||
        // before moving the ball, we need to check whether the lock was actually gained, because
 | 
			
		||||
        // if something is trying to stop this job, it will have failed..
 | 
			
		||||
        if (mml.lockWasGained())
 | 
			
		||||
            moveBall();
 | 
			
		||||
 | 
			
		||||
        return jobNeedsRunningAgain;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void removedFromQueue()
 | 
			
		||||
    {
 | 
			
		||||
        // This is called to tell us that our job has been removed from the pool.
 | 
			
		||||
        // In this case there's no need to do anything here.
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DemoThreadPoolJob)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class MultithreadingDemo   : public Component,
 | 
			
		||||
                             private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    MultithreadingDemo()
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (controlButton);
 | 
			
		||||
        controlButton.changeWidthToFitText (24);
 | 
			
		||||
        controlButton.setTopLeftPosition (20, 20);
 | 
			
		||||
        controlButton.setTriggeredOnMouseDown (true);
 | 
			
		||||
        controlButton.setAlwaysOnTop (true);
 | 
			
		||||
        controlButton.onClick = [this] { showMenu(); };
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
 | 
			
		||||
        resetAllBalls();
 | 
			
		||||
 | 
			
		||||
        startTimerHz (60);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~MultithreadingDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        pool.removeAllJobs (true, 2000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resetAllBalls()
 | 
			
		||||
    {
 | 
			
		||||
        pool.removeAllJobs (true, 4000);
 | 
			
		||||
        balls.clear();
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < 5; ++i)
 | 
			
		||||
            addABall();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
 | 
			
		||||
        for (auto* ball : balls)
 | 
			
		||||
            ball->draw (g);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void setUsingPool (bool usePool)
 | 
			
		||||
    {
 | 
			
		||||
        isUsingPool = usePool;
 | 
			
		||||
        resetAllBalls();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void addABall()
 | 
			
		||||
    {
 | 
			
		||||
        if (isUsingPool)
 | 
			
		||||
        {
 | 
			
		||||
            auto newBall = std::make_unique<DemoThreadPoolJob> (*this);
 | 
			
		||||
            pool.addJob (newBall.get(), false);
 | 
			
		||||
            balls.add (newBall.release());
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            balls.add (new DemoThread (*this));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void showMenu()
 | 
			
		||||
    {
 | 
			
		||||
        PopupMenu m;
 | 
			
		||||
        m.addItem (1, "Use one thread per ball", true, ! isUsingPool);
 | 
			
		||||
        m.addItem (2, "Use a thread pool",       true,   isUsingPool);
 | 
			
		||||
 | 
			
		||||
        m.showMenuAsync (PopupMenu::Options().withTargetComponent (controlButton),
 | 
			
		||||
                         ModalCallbackFunction::forComponent (menuItemChosenCallback, this));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void menuItemChosenCallback (int result, MultithreadingDemo* demoComponent)
 | 
			
		||||
    {
 | 
			
		||||
        if (result != 0 && demoComponent != nullptr)
 | 
			
		||||
            demoComponent->setUsingPool (result == 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    ThreadPool pool           { 3 };
 | 
			
		||||
    TextButton controlButton  { "Thread type" };
 | 
			
		||||
    bool isUsingPool = false;
 | 
			
		||||
 | 
			
		||||
    OwnedArray<BouncingBall> balls;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultithreadingDemo)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										274
									
								
								deps/juce/examples/Utilities/NetworkingDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										274
									
								
								deps/juce/examples/Utilities/NetworkingDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,137 +1,137 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             NetworkingDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Showcases networking features.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics, juce_gui_extra
 | 
			
		||||
 exporters:        xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        NetworkingDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class NetworkingDemo   : public Component,
 | 
			
		||||
                         private Thread
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    NetworkingDemo()
 | 
			
		||||
        : Thread ("Network Demo")
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (urlBox);
 | 
			
		||||
        urlBox.setText ("https://www.google.com");
 | 
			
		||||
        urlBox.onReturnKey = [this] { fetchButton.triggerClick(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (fetchButton);
 | 
			
		||||
        fetchButton.onClick = [this] { startThread(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (resultsBox);
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto area = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            auto topArea = area.removeFromTop (40);
 | 
			
		||||
            fetchButton.setBounds (topArea.removeFromRight (180).reduced (8));
 | 
			
		||||
            urlBox     .setBounds (topArea.reduced (8));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        resultsBox.setBounds (area.reduced (8));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void run() override
 | 
			
		||||
    {
 | 
			
		||||
        auto result = getResultText (urlBox.getText());
 | 
			
		||||
 | 
			
		||||
        MessageManagerLock mml (this);
 | 
			
		||||
 | 
			
		||||
        if (mml.lockWasGained())
 | 
			
		||||
            resultsBox.loadContent (result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    String getResultText (const URL& url)
 | 
			
		||||
    {
 | 
			
		||||
        StringPairArray responseHeaders;
 | 
			
		||||
        int statusCode = 0;
 | 
			
		||||
 | 
			
		||||
        if (auto stream = url.createInputStream (URL::InputStreamOptions (URL::ParameterHandling::inAddress)
 | 
			
		||||
                                                                                 .withConnectionTimeoutMs(10000)
 | 
			
		||||
                                                                                 .withResponseHeaders (&responseHeaders)
 | 
			
		||||
                                                                                 .withStatusCode (&statusCode)))
 | 
			
		||||
        {
 | 
			
		||||
            return (statusCode != 0 ? "Status code: " + String (statusCode) + newLine : String())
 | 
			
		||||
                    + "Response headers: " + newLine
 | 
			
		||||
                    + responseHeaders.getDescription() + newLine
 | 
			
		||||
                    + "----------------------------------------------------" + newLine
 | 
			
		||||
                    + stream->readEntireStreamAsString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (statusCode != 0)
 | 
			
		||||
            return "Failed to connect, status code = " + String (statusCode);
 | 
			
		||||
 | 
			
		||||
        return "Failed to connect!";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    TextEditor urlBox;
 | 
			
		||||
    TextButton fetchButton { "Download URL Contents" };
 | 
			
		||||
 | 
			
		||||
    CodeDocument resultsDocument;
 | 
			
		||||
    CodeEditorComponent resultsBox  { resultsDocument, nullptr };
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        urlBox.applyFontToAllText (urlBox.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NetworkingDemo)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             NetworkingDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Showcases networking features.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics, juce_gui_extra
 | 
			
		||||
 exporters:        xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        NetworkingDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class NetworkingDemo   : public Component,
 | 
			
		||||
                         private Thread
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    NetworkingDemo()
 | 
			
		||||
        : Thread ("Network Demo")
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (urlBox);
 | 
			
		||||
        urlBox.setText ("https://www.google.com");
 | 
			
		||||
        urlBox.onReturnKey = [this] { fetchButton.triggerClick(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (fetchButton);
 | 
			
		||||
        fetchButton.onClick = [this] { startThread(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (resultsBox);
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto area = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            auto topArea = area.removeFromTop (40);
 | 
			
		||||
            fetchButton.setBounds (topArea.removeFromRight (180).reduced (8));
 | 
			
		||||
            urlBox     .setBounds (topArea.reduced (8));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        resultsBox.setBounds (area.reduced (8));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void run() override
 | 
			
		||||
    {
 | 
			
		||||
        auto result = getResultText (urlBox.getText());
 | 
			
		||||
 | 
			
		||||
        MessageManagerLock mml (this);
 | 
			
		||||
 | 
			
		||||
        if (mml.lockWasGained())
 | 
			
		||||
            resultsBox.loadContent (result);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    String getResultText (const URL& url)
 | 
			
		||||
    {
 | 
			
		||||
        StringPairArray responseHeaders;
 | 
			
		||||
        int statusCode = 0;
 | 
			
		||||
 | 
			
		||||
        if (auto stream = url.createInputStream (URL::InputStreamOptions (URL::ParameterHandling::inAddress)
 | 
			
		||||
                                                                                 .withConnectionTimeoutMs(10000)
 | 
			
		||||
                                                                                 .withResponseHeaders (&responseHeaders)
 | 
			
		||||
                                                                                 .withStatusCode (&statusCode)))
 | 
			
		||||
        {
 | 
			
		||||
            return (statusCode != 0 ? "Status code: " + String (statusCode) + newLine : String())
 | 
			
		||||
                    + "Response headers: " + newLine
 | 
			
		||||
                    + responseHeaders.getDescription() + newLine
 | 
			
		||||
                    + "----------------------------------------------------" + newLine
 | 
			
		||||
                    + stream->readEntireStreamAsString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (statusCode != 0)
 | 
			
		||||
            return "Failed to connect, status code = " + String (statusCode);
 | 
			
		||||
 | 
			
		||||
        return "Failed to connect!";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    TextEditor urlBox;
 | 
			
		||||
    TextButton fetchButton { "Download URL Contents" };
 | 
			
		||||
 | 
			
		||||
    CodeDocument resultsDocument;
 | 
			
		||||
    CodeEditorComponent resultsBox  { resultsDocument, nullptr };
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        urlBox.applyFontToAllText (urlBox.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NetworkingDemo)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										988
									
								
								deps/juce/examples/Utilities/OSCDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										988
									
								
								deps/juce/examples/Utilities/OSCDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,494 +1,494 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             OSCDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Application using the OSC protocol.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics, juce_osc
 | 
			
		||||
 exporters:        xcode_mac, vs2019, linux_make
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        OSCDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class OSCLogListBox    : public ListBox,
 | 
			
		||||
                         private ListBoxModel,
 | 
			
		||||
                         private AsyncUpdater
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    OSCLogListBox()
 | 
			
		||||
    {
 | 
			
		||||
        setModel (this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~OSCLogListBox() override = default;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    int getNumRows() override
 | 
			
		||||
    {
 | 
			
		||||
        return oscLogList.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) override
 | 
			
		||||
    {
 | 
			
		||||
        ignoreUnused (rowIsSelected);
 | 
			
		||||
 | 
			
		||||
        if (isPositiveAndBelow (row, oscLogList.size()))
 | 
			
		||||
        {
 | 
			
		||||
            g.setColour (Colours::white);
 | 
			
		||||
 | 
			
		||||
            g.drawText (oscLogList[row],
 | 
			
		||||
                        Rectangle<int> (width, height).reduced (4, 0),
 | 
			
		||||
                        Justification::centredLeft, true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void addOSCMessage (const OSCMessage& message, int level = 0)
 | 
			
		||||
    {
 | 
			
		||||
        oscLogList.add (getIndentationString (level)
 | 
			
		||||
                        + "- osc message, address = '"
 | 
			
		||||
                        + message.getAddressPattern().toString()
 | 
			
		||||
                        + "', "
 | 
			
		||||
                        + String (message.size())
 | 
			
		||||
                        + " argument(s)");
 | 
			
		||||
 | 
			
		||||
        if (! message.isEmpty())
 | 
			
		||||
        {
 | 
			
		||||
            for (auto& arg : message)
 | 
			
		||||
                addOSCMessageArgument (arg, level + 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        triggerAsyncUpdate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void addOSCBundle (const OSCBundle& bundle, int level = 0)
 | 
			
		||||
    {
 | 
			
		||||
        OSCTimeTag timeTag = bundle.getTimeTag();
 | 
			
		||||
 | 
			
		||||
        oscLogList.add (getIndentationString (level)
 | 
			
		||||
                        + "- osc bundle, time tag = "
 | 
			
		||||
                        + timeTag.toTime().toString (true, true, true, true));
 | 
			
		||||
 | 
			
		||||
        for (auto& element : bundle)
 | 
			
		||||
        {
 | 
			
		||||
            if (element.isMessage())
 | 
			
		||||
                addOSCMessage (element.getMessage(), level + 1);
 | 
			
		||||
            else if (element.isBundle())
 | 
			
		||||
                addOSCBundle (element.getBundle(), level + 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        triggerAsyncUpdate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void addOSCMessageArgument (const OSCArgument& arg, int level)
 | 
			
		||||
    {
 | 
			
		||||
        String typeAsString;
 | 
			
		||||
        String valueAsString;
 | 
			
		||||
 | 
			
		||||
        if (arg.isFloat32())
 | 
			
		||||
        {
 | 
			
		||||
            typeAsString = "float32";
 | 
			
		||||
            valueAsString = String (arg.getFloat32());
 | 
			
		||||
        }
 | 
			
		||||
        else if (arg.isInt32())
 | 
			
		||||
        {
 | 
			
		||||
            typeAsString = "int32";
 | 
			
		||||
            valueAsString = String (arg.getInt32());
 | 
			
		||||
        }
 | 
			
		||||
        else if (arg.isString())
 | 
			
		||||
        {
 | 
			
		||||
            typeAsString = "string";
 | 
			
		||||
            valueAsString = arg.getString();
 | 
			
		||||
        }
 | 
			
		||||
        else if (arg.isBlob())
 | 
			
		||||
        {
 | 
			
		||||
            typeAsString = "blob";
 | 
			
		||||
            auto& blob = arg.getBlob();
 | 
			
		||||
            valueAsString = String::fromUTF8 ((const char*) blob.getData(), (int) blob.getSize());
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            typeAsString = "(unknown)";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        oscLogList.add (getIndentationString (level + 1) + "- " + typeAsString.paddedRight(' ', 12) + valueAsString);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void addInvalidOSCPacket (const char* /* data */, int dataSize)
 | 
			
		||||
    {
 | 
			
		||||
        oscLogList.add ("- (" + String(dataSize) + "bytes with invalid format)");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void clear()
 | 
			
		||||
    {
 | 
			
		||||
        oscLogList.clear();
 | 
			
		||||
        triggerAsyncUpdate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void handleAsyncUpdate() override
 | 
			
		||||
    {
 | 
			
		||||
        updateContent();
 | 
			
		||||
        scrollToEnsureRowIsOnscreen (oscLogList.size() - 1);
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static String getIndentationString (int level)
 | 
			
		||||
    {
 | 
			
		||||
        return String().paddedRight (' ', 2 * level);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    StringArray oscLogList;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCLogListBox)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class OSCSenderDemo   : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    OSCSenderDemo()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (senderLabel);
 | 
			
		||||
        senderLabel.attachToComponent (&rotaryKnob, false);
 | 
			
		||||
 | 
			
		||||
        rotaryKnob.setRange (0.0, 1.0);
 | 
			
		||||
        rotaryKnob.setSliderStyle (Slider::RotaryVerticalDrag);
 | 
			
		||||
        rotaryKnob.setTextBoxStyle (Slider::TextBoxBelow, true, 150, 25);
 | 
			
		||||
        rotaryKnob.setBounds (50, 50, 180, 180);
 | 
			
		||||
        addAndMakeVisible (rotaryKnob);
 | 
			
		||||
        rotaryKnob.onValueChange = [this]
 | 
			
		||||
        {
 | 
			
		||||
            // create and send an OSC message with an address and a float value:
 | 
			
		||||
            if (! sender1.send ("/juce/rotaryknob", (float) rotaryKnob.getValue()))
 | 
			
		||||
                showConnectionErrorMessage ("Error: could not send OSC message.");
 | 
			
		||||
            if (! sender2.send ("/juce/rotaryknob", (float) rotaryKnob.getValue()))
 | 
			
		||||
                showConnectionErrorMessage ("Error: could not send OSC message.");
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // specify here where to send OSC messages to: host URL and UDP port number
 | 
			
		||||
        if (! sender1.connect ("127.0.0.1", 9001))
 | 
			
		||||
            showConnectionErrorMessage ("Error: could not connect to UDP port 9001.");
 | 
			
		||||
        if (! sender2.connect ("127.0.0.1", 9002))
 | 
			
		||||
            showConnectionErrorMessage ("Error: could not connect to UDP port 9002.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void showConnectionErrorMessage (const String& messageText)
 | 
			
		||||
    {
 | 
			
		||||
        AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
 | 
			
		||||
                                          "Connection error",
 | 
			
		||||
                                          messageText,
 | 
			
		||||
                                          "OK");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Slider rotaryKnob;
 | 
			
		||||
    OSCSender sender1, sender2;
 | 
			
		||||
    Label senderLabel { {}, "Sender" };
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCSenderDemo)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class OSCReceiverDemo   : public Component,
 | 
			
		||||
                          private OSCReceiver,
 | 
			
		||||
                          private OSCReceiver::ListenerWithOSCAddress<OSCReceiver::MessageLoopCallback>
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    OSCReceiverDemo()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (receiverLabel);
 | 
			
		||||
        receiverLabel.attachToComponent (&rotaryKnob, false);
 | 
			
		||||
 | 
			
		||||
        rotaryKnob.setRange (0.0, 1.0);
 | 
			
		||||
        rotaryKnob.setSliderStyle (Slider::RotaryVerticalDrag);
 | 
			
		||||
        rotaryKnob.setTextBoxStyle (Slider::TextBoxBelow, true, 150, 25);
 | 
			
		||||
        rotaryKnob.setBounds (50, 50, 180, 180);
 | 
			
		||||
        rotaryKnob.setInterceptsMouseClicks (false, false);
 | 
			
		||||
        addAndMakeVisible (rotaryKnob);
 | 
			
		||||
 | 
			
		||||
        // specify here on which UDP port number to receive incoming OSC messages
 | 
			
		||||
        if (! connect (9001))
 | 
			
		||||
            showConnectionErrorMessage ("Error: could not connect to UDP port 9001.");
 | 
			
		||||
 | 
			
		||||
        // tell the component to listen for OSC messages matching this address:
 | 
			
		||||
        addListener (this, "/juce/rotaryknob");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void oscMessageReceived (const OSCMessage& message) override
 | 
			
		||||
    {
 | 
			
		||||
        if (message.size() == 1 && message[0].isFloat32())
 | 
			
		||||
            rotaryKnob.setValue (jlimit (0.0f, 10.0f, message[0].getFloat32()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void showConnectionErrorMessage (const String& messageText)
 | 
			
		||||
    {
 | 
			
		||||
        AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
 | 
			
		||||
                                          "Connection error",
 | 
			
		||||
                                          messageText,
 | 
			
		||||
                                          "OK");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Slider rotaryKnob;
 | 
			
		||||
    Label receiverLabel { {}, "Receiver" };
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCReceiverDemo)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class OSCMonitorDemo   : public Component,
 | 
			
		||||
                         private OSCReceiver::Listener<OSCReceiver::MessageLoopCallback>
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    OSCMonitorDemo()
 | 
			
		||||
    {
 | 
			
		||||
        portNumberLabel.setBounds (10, 18, 130, 25);
 | 
			
		||||
        addAndMakeVisible (portNumberLabel);
 | 
			
		||||
 | 
			
		||||
        portNumberField.setEditable (true, true, true);
 | 
			
		||||
        portNumberField.setBounds (140, 18, 50, 25);
 | 
			
		||||
        addAndMakeVisible (portNumberField);
 | 
			
		||||
 | 
			
		||||
        connectButton.setBounds (210, 18, 100, 25);
 | 
			
		||||
        addAndMakeVisible (connectButton);
 | 
			
		||||
        connectButton.onClick = [this] { connectButtonClicked(); };
 | 
			
		||||
 | 
			
		||||
        clearButton.setBounds (320, 18, 60, 25);
 | 
			
		||||
        addAndMakeVisible (clearButton);
 | 
			
		||||
        clearButton.onClick = [this] { clearButtonClicked(); };
 | 
			
		||||
 | 
			
		||||
        connectionStatusLabel.setBounds (450, 18, 240, 25);
 | 
			
		||||
        updateConnectionStatusLabel();
 | 
			
		||||
        addAndMakeVisible (connectionStatusLabel);
 | 
			
		||||
 | 
			
		||||
        oscLogListBox.setBounds (0, 60, 700, 340);
 | 
			
		||||
        addAndMakeVisible (oscLogListBox);
 | 
			
		||||
 | 
			
		||||
        oscReceiver.addListener (this);
 | 
			
		||||
        oscReceiver.registerFormatErrorHandler ([this] (const char* data, int dataSize)
 | 
			
		||||
                                                {
 | 
			
		||||
                                                    oscLogListBox.addInvalidOSCPacket (data, dataSize);
 | 
			
		||||
                                                });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Label portNumberLabel    { {}, "UDP Port Number: " };
 | 
			
		||||
    Label portNumberField    { {}, "9002" };
 | 
			
		||||
    TextButton connectButton { "Connect" };
 | 
			
		||||
    TextButton clearButton   { "Clear" };
 | 
			
		||||
    Label connectionStatusLabel;
 | 
			
		||||
 | 
			
		||||
    OSCLogListBox oscLogListBox;
 | 
			
		||||
    OSCReceiver oscReceiver;
 | 
			
		||||
 | 
			
		||||
    int currentPortNumber = -1;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void connectButtonClicked()
 | 
			
		||||
    {
 | 
			
		||||
        if (! isConnected())
 | 
			
		||||
            connect();
 | 
			
		||||
        else
 | 
			
		||||
            disconnect();
 | 
			
		||||
 | 
			
		||||
        updateConnectionStatusLabel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void clearButtonClicked()
 | 
			
		||||
    {
 | 
			
		||||
        oscLogListBox.clear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void oscMessageReceived (const OSCMessage& message) override
 | 
			
		||||
    {
 | 
			
		||||
        oscLogListBox.addOSCMessage (message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void oscBundleReceived (const OSCBundle& bundle) override
 | 
			
		||||
    {
 | 
			
		||||
        oscLogListBox.addOSCBundle (bundle);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void connect()
 | 
			
		||||
    {
 | 
			
		||||
        auto portToConnect = portNumberField.getText().getIntValue();
 | 
			
		||||
 | 
			
		||||
        if (! isValidOscPort (portToConnect))
 | 
			
		||||
        {
 | 
			
		||||
            handleInvalidPortNumberEntered();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (oscReceiver.connect (portToConnect))
 | 
			
		||||
        {
 | 
			
		||||
            currentPortNumber = portToConnect;
 | 
			
		||||
            connectButton.setButtonText ("Disconnect");
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            handleConnectError (portToConnect);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void disconnect()
 | 
			
		||||
    {
 | 
			
		||||
        if (oscReceiver.disconnect())
 | 
			
		||||
        {
 | 
			
		||||
            currentPortNumber = -1;
 | 
			
		||||
            connectButton.setButtonText ("Connect");
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            handleDisconnectError();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void handleConnectError (int failedPort)
 | 
			
		||||
    {
 | 
			
		||||
        AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
 | 
			
		||||
                                          "OSC Connection error",
 | 
			
		||||
                                          "Error: could not connect to port " + String (failedPort),
 | 
			
		||||
                                          "OK");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void handleDisconnectError()
 | 
			
		||||
    {
 | 
			
		||||
        AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
 | 
			
		||||
                                          "Unknown error",
 | 
			
		||||
                                          "An unknown error occurred while trying to disconnect from UDP port.",
 | 
			
		||||
                                          "OK");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void handleInvalidPortNumberEntered()
 | 
			
		||||
    {
 | 
			
		||||
        AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
 | 
			
		||||
                                          "Invalid port number",
 | 
			
		||||
                                          "Error: you have entered an invalid UDP port number.",
 | 
			
		||||
                                          "OK");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool isConnected() const
 | 
			
		||||
    {
 | 
			
		||||
        return currentPortNumber != -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool isValidOscPort (int port) const
 | 
			
		||||
    {
 | 
			
		||||
        return port > 0 && port < 65536;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void updateConnectionStatusLabel()
 | 
			
		||||
    {
 | 
			
		||||
        String text = "Status: ";
 | 
			
		||||
 | 
			
		||||
        if (isConnected())
 | 
			
		||||
            text += "Connected to UDP port " + String (currentPortNumber);
 | 
			
		||||
        else
 | 
			
		||||
            text += "Disconnected";
 | 
			
		||||
 | 
			
		||||
        auto textColour = isConnected() ? Colours::green : Colours::red;
 | 
			
		||||
 | 
			
		||||
        connectionStatusLabel.setText (text, dontSendNotification);
 | 
			
		||||
        connectionStatusLabel.setFont (Font (15.00f, Font::bold));
 | 
			
		||||
        connectionStatusLabel.setColour (Label::textColourId, textColour);
 | 
			
		||||
        connectionStatusLabel.setJustificationType (Justification::centredRight);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCMonitorDemo)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class OSCDemo   : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    OSCDemo()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (monitor);
 | 
			
		||||
        addAndMakeVisible (receiver);
 | 
			
		||||
        addAndMakeVisible (sender);
 | 
			
		||||
 | 
			
		||||
        setSize (700, 400);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto bounds = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        auto lowerBounds = bounds.removeFromBottom (getHeight() / 2);
 | 
			
		||||
        auto halfBounds  = bounds.removeFromRight  (getWidth()  / 2);
 | 
			
		||||
 | 
			
		||||
        sender  .setBounds (bounds);
 | 
			
		||||
        receiver.setBounds (halfBounds);
 | 
			
		||||
        monitor .setBounds (lowerBounds.removeFromTop (getHeight() / 2));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    OSCMonitorDemo  monitor;
 | 
			
		||||
    OSCReceiverDemo receiver;
 | 
			
		||||
    OSCSenderDemo   sender;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCDemo)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             OSCDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Application using the OSC protocol.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics, juce_osc
 | 
			
		||||
 exporters:        xcode_mac, vs2022, linux_make
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        OSCDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class OSCLogListBox    : public ListBox,
 | 
			
		||||
                         private ListBoxModel,
 | 
			
		||||
                         private AsyncUpdater
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    OSCLogListBox()
 | 
			
		||||
    {
 | 
			
		||||
        setModel (this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~OSCLogListBox() override = default;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    int getNumRows() override
 | 
			
		||||
    {
 | 
			
		||||
        return oscLogList.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) override
 | 
			
		||||
    {
 | 
			
		||||
        ignoreUnused (rowIsSelected);
 | 
			
		||||
 | 
			
		||||
        if (isPositiveAndBelow (row, oscLogList.size()))
 | 
			
		||||
        {
 | 
			
		||||
            g.setColour (Colours::white);
 | 
			
		||||
 | 
			
		||||
            g.drawText (oscLogList[row],
 | 
			
		||||
                        Rectangle<int> (width, height).reduced (4, 0),
 | 
			
		||||
                        Justification::centredLeft, true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void addOSCMessage (const OSCMessage& message, int level = 0)
 | 
			
		||||
    {
 | 
			
		||||
        oscLogList.add (getIndentationString (level)
 | 
			
		||||
                        + "- osc message, address = '"
 | 
			
		||||
                        + message.getAddressPattern().toString()
 | 
			
		||||
                        + "', "
 | 
			
		||||
                        + String (message.size())
 | 
			
		||||
                        + " argument(s)");
 | 
			
		||||
 | 
			
		||||
        if (! message.isEmpty())
 | 
			
		||||
        {
 | 
			
		||||
            for (auto& arg : message)
 | 
			
		||||
                addOSCMessageArgument (arg, level + 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        triggerAsyncUpdate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void addOSCBundle (const OSCBundle& bundle, int level = 0)
 | 
			
		||||
    {
 | 
			
		||||
        OSCTimeTag timeTag = bundle.getTimeTag();
 | 
			
		||||
 | 
			
		||||
        oscLogList.add (getIndentationString (level)
 | 
			
		||||
                        + "- osc bundle, time tag = "
 | 
			
		||||
                        + timeTag.toTime().toString (true, true, true, true));
 | 
			
		||||
 | 
			
		||||
        for (auto& element : bundle)
 | 
			
		||||
        {
 | 
			
		||||
            if (element.isMessage())
 | 
			
		||||
                addOSCMessage (element.getMessage(), level + 1);
 | 
			
		||||
            else if (element.isBundle())
 | 
			
		||||
                addOSCBundle (element.getBundle(), level + 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        triggerAsyncUpdate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void addOSCMessageArgument (const OSCArgument& arg, int level)
 | 
			
		||||
    {
 | 
			
		||||
        String typeAsString;
 | 
			
		||||
        String valueAsString;
 | 
			
		||||
 | 
			
		||||
        if (arg.isFloat32())
 | 
			
		||||
        {
 | 
			
		||||
            typeAsString = "float32";
 | 
			
		||||
            valueAsString = String (arg.getFloat32());
 | 
			
		||||
        }
 | 
			
		||||
        else if (arg.isInt32())
 | 
			
		||||
        {
 | 
			
		||||
            typeAsString = "int32";
 | 
			
		||||
            valueAsString = String (arg.getInt32());
 | 
			
		||||
        }
 | 
			
		||||
        else if (arg.isString())
 | 
			
		||||
        {
 | 
			
		||||
            typeAsString = "string";
 | 
			
		||||
            valueAsString = arg.getString();
 | 
			
		||||
        }
 | 
			
		||||
        else if (arg.isBlob())
 | 
			
		||||
        {
 | 
			
		||||
            typeAsString = "blob";
 | 
			
		||||
            auto& blob = arg.getBlob();
 | 
			
		||||
            valueAsString = String::fromUTF8 ((const char*) blob.getData(), (int) blob.getSize());
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            typeAsString = "(unknown)";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        oscLogList.add (getIndentationString (level + 1) + "- " + typeAsString.paddedRight(' ', 12) + valueAsString);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void addInvalidOSCPacket (const char* /* data */, int dataSize)
 | 
			
		||||
    {
 | 
			
		||||
        oscLogList.add ("- (" + String(dataSize) + "bytes with invalid format)");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void clear()
 | 
			
		||||
    {
 | 
			
		||||
        oscLogList.clear();
 | 
			
		||||
        triggerAsyncUpdate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void handleAsyncUpdate() override
 | 
			
		||||
    {
 | 
			
		||||
        updateContent();
 | 
			
		||||
        scrollToEnsureRowIsOnscreen (oscLogList.size() - 1);
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    static String getIndentationString (int level)
 | 
			
		||||
    {
 | 
			
		||||
        return String().paddedRight (' ', 2 * level);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    StringArray oscLogList;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCLogListBox)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class OSCSenderDemo   : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    OSCSenderDemo()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (senderLabel);
 | 
			
		||||
        senderLabel.attachToComponent (&rotaryKnob, false);
 | 
			
		||||
 | 
			
		||||
        rotaryKnob.setRange (0.0, 1.0);
 | 
			
		||||
        rotaryKnob.setSliderStyle (Slider::RotaryVerticalDrag);
 | 
			
		||||
        rotaryKnob.setTextBoxStyle (Slider::TextBoxBelow, true, 150, 25);
 | 
			
		||||
        rotaryKnob.setBounds (50, 50, 180, 180);
 | 
			
		||||
        addAndMakeVisible (rotaryKnob);
 | 
			
		||||
        rotaryKnob.onValueChange = [this]
 | 
			
		||||
        {
 | 
			
		||||
            // create and send an OSC message with an address and a float value:
 | 
			
		||||
            if (! sender1.send ("/juce/rotaryknob", (float) rotaryKnob.getValue()))
 | 
			
		||||
                showConnectionErrorMessage ("Error: could not send OSC message.");
 | 
			
		||||
            if (! sender2.send ("/juce/rotaryknob", (float) rotaryKnob.getValue()))
 | 
			
		||||
                showConnectionErrorMessage ("Error: could not send OSC message.");
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // specify here where to send OSC messages to: host URL and UDP port number
 | 
			
		||||
        if (! sender1.connect ("127.0.0.1", 9001))
 | 
			
		||||
            showConnectionErrorMessage ("Error: could not connect to UDP port 9001.");
 | 
			
		||||
        if (! sender2.connect ("127.0.0.1", 9002))
 | 
			
		||||
            showConnectionErrorMessage ("Error: could not connect to UDP port 9002.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void showConnectionErrorMessage (const String& messageText)
 | 
			
		||||
    {
 | 
			
		||||
        AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
 | 
			
		||||
                                          "Connection error",
 | 
			
		||||
                                          messageText,
 | 
			
		||||
                                          "OK");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Slider rotaryKnob;
 | 
			
		||||
    OSCSender sender1, sender2;
 | 
			
		||||
    Label senderLabel { {}, "Sender" };
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCSenderDemo)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class OSCReceiverDemo   : public Component,
 | 
			
		||||
                          private OSCReceiver,
 | 
			
		||||
                          private OSCReceiver::ListenerWithOSCAddress<OSCReceiver::MessageLoopCallback>
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    OSCReceiverDemo()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (receiverLabel);
 | 
			
		||||
        receiverLabel.attachToComponent (&rotaryKnob, false);
 | 
			
		||||
 | 
			
		||||
        rotaryKnob.setRange (0.0, 1.0);
 | 
			
		||||
        rotaryKnob.setSliderStyle (Slider::RotaryVerticalDrag);
 | 
			
		||||
        rotaryKnob.setTextBoxStyle (Slider::TextBoxBelow, true, 150, 25);
 | 
			
		||||
        rotaryKnob.setBounds (50, 50, 180, 180);
 | 
			
		||||
        rotaryKnob.setInterceptsMouseClicks (false, false);
 | 
			
		||||
        addAndMakeVisible (rotaryKnob);
 | 
			
		||||
 | 
			
		||||
        // specify here on which UDP port number to receive incoming OSC messages
 | 
			
		||||
        if (! connect (9001))
 | 
			
		||||
            showConnectionErrorMessage ("Error: could not connect to UDP port 9001.");
 | 
			
		||||
 | 
			
		||||
        // tell the component to listen for OSC messages matching this address:
 | 
			
		||||
        addListener (this, "/juce/rotaryknob");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void oscMessageReceived (const OSCMessage& message) override
 | 
			
		||||
    {
 | 
			
		||||
        if (message.size() == 1 && message[0].isFloat32())
 | 
			
		||||
            rotaryKnob.setValue (jlimit (0.0f, 10.0f, message[0].getFloat32()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void showConnectionErrorMessage (const String& messageText)
 | 
			
		||||
    {
 | 
			
		||||
        AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
 | 
			
		||||
                                          "Connection error",
 | 
			
		||||
                                          messageText,
 | 
			
		||||
                                          "OK");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Slider rotaryKnob;
 | 
			
		||||
    Label receiverLabel { {}, "Receiver" };
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCReceiverDemo)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class OSCMonitorDemo   : public Component,
 | 
			
		||||
                         private OSCReceiver::Listener<OSCReceiver::MessageLoopCallback>
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    OSCMonitorDemo()
 | 
			
		||||
    {
 | 
			
		||||
        portNumberLabel.setBounds (10, 18, 130, 25);
 | 
			
		||||
        addAndMakeVisible (portNumberLabel);
 | 
			
		||||
 | 
			
		||||
        portNumberField.setEditable (true, true, true);
 | 
			
		||||
        portNumberField.setBounds (140, 18, 50, 25);
 | 
			
		||||
        addAndMakeVisible (portNumberField);
 | 
			
		||||
 | 
			
		||||
        connectButton.setBounds (210, 18, 100, 25);
 | 
			
		||||
        addAndMakeVisible (connectButton);
 | 
			
		||||
        connectButton.onClick = [this] { connectButtonClicked(); };
 | 
			
		||||
 | 
			
		||||
        clearButton.setBounds (320, 18, 60, 25);
 | 
			
		||||
        addAndMakeVisible (clearButton);
 | 
			
		||||
        clearButton.onClick = [this] { clearButtonClicked(); };
 | 
			
		||||
 | 
			
		||||
        connectionStatusLabel.setBounds (450, 18, 240, 25);
 | 
			
		||||
        updateConnectionStatusLabel();
 | 
			
		||||
        addAndMakeVisible (connectionStatusLabel);
 | 
			
		||||
 | 
			
		||||
        oscLogListBox.setBounds (0, 60, 700, 340);
 | 
			
		||||
        addAndMakeVisible (oscLogListBox);
 | 
			
		||||
 | 
			
		||||
        oscReceiver.addListener (this);
 | 
			
		||||
        oscReceiver.registerFormatErrorHandler ([this] (const char* data, int dataSize)
 | 
			
		||||
                                                {
 | 
			
		||||
                                                    oscLogListBox.addInvalidOSCPacket (data, dataSize);
 | 
			
		||||
                                                });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    Label portNumberLabel    { {}, "UDP Port Number: " };
 | 
			
		||||
    Label portNumberField    { {}, "9002" };
 | 
			
		||||
    TextButton connectButton { "Connect" };
 | 
			
		||||
    TextButton clearButton   { "Clear" };
 | 
			
		||||
    Label connectionStatusLabel;
 | 
			
		||||
 | 
			
		||||
    OSCLogListBox oscLogListBox;
 | 
			
		||||
    OSCReceiver oscReceiver;
 | 
			
		||||
 | 
			
		||||
    int currentPortNumber = -1;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void connectButtonClicked()
 | 
			
		||||
    {
 | 
			
		||||
        if (! isConnected())
 | 
			
		||||
            connect();
 | 
			
		||||
        else
 | 
			
		||||
            disconnect();
 | 
			
		||||
 | 
			
		||||
        updateConnectionStatusLabel();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void clearButtonClicked()
 | 
			
		||||
    {
 | 
			
		||||
        oscLogListBox.clear();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void oscMessageReceived (const OSCMessage& message) override
 | 
			
		||||
    {
 | 
			
		||||
        oscLogListBox.addOSCMessage (message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void oscBundleReceived (const OSCBundle& bundle) override
 | 
			
		||||
    {
 | 
			
		||||
        oscLogListBox.addOSCBundle (bundle);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void connect()
 | 
			
		||||
    {
 | 
			
		||||
        auto portToConnect = portNumberField.getText().getIntValue();
 | 
			
		||||
 | 
			
		||||
        if (! isValidOscPort (portToConnect))
 | 
			
		||||
        {
 | 
			
		||||
            handleInvalidPortNumberEntered();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (oscReceiver.connect (portToConnect))
 | 
			
		||||
        {
 | 
			
		||||
            currentPortNumber = portToConnect;
 | 
			
		||||
            connectButton.setButtonText ("Disconnect");
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            handleConnectError (portToConnect);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void disconnect()
 | 
			
		||||
    {
 | 
			
		||||
        if (oscReceiver.disconnect())
 | 
			
		||||
        {
 | 
			
		||||
            currentPortNumber = -1;
 | 
			
		||||
            connectButton.setButtonText ("Connect");
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            handleDisconnectError();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void handleConnectError (int failedPort)
 | 
			
		||||
    {
 | 
			
		||||
        AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
 | 
			
		||||
                                          "OSC Connection error",
 | 
			
		||||
                                          "Error: could not connect to port " + String (failedPort),
 | 
			
		||||
                                          "OK");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void handleDisconnectError()
 | 
			
		||||
    {
 | 
			
		||||
        AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
 | 
			
		||||
                                          "Unknown error",
 | 
			
		||||
                                          "An unknown error occurred while trying to disconnect from UDP port.",
 | 
			
		||||
                                          "OK");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void handleInvalidPortNumberEntered()
 | 
			
		||||
    {
 | 
			
		||||
        AlertWindow::showMessageBoxAsync (MessageBoxIconType::WarningIcon,
 | 
			
		||||
                                          "Invalid port number",
 | 
			
		||||
                                          "Error: you have entered an invalid UDP port number.",
 | 
			
		||||
                                          "OK");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool isConnected() const
 | 
			
		||||
    {
 | 
			
		||||
        return currentPortNumber != -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    bool isValidOscPort (int port) const
 | 
			
		||||
    {
 | 
			
		||||
        return port > 0 && port < 65536;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void updateConnectionStatusLabel()
 | 
			
		||||
    {
 | 
			
		||||
        String text = "Status: ";
 | 
			
		||||
 | 
			
		||||
        if (isConnected())
 | 
			
		||||
            text += "Connected to UDP port " + String (currentPortNumber);
 | 
			
		||||
        else
 | 
			
		||||
            text += "Disconnected";
 | 
			
		||||
 | 
			
		||||
        auto textColour = isConnected() ? Colours::green : Colours::red;
 | 
			
		||||
 | 
			
		||||
        connectionStatusLabel.setText (text, dontSendNotification);
 | 
			
		||||
        connectionStatusLabel.setFont (Font (15.00f, Font::bold));
 | 
			
		||||
        connectionStatusLabel.setColour (Label::textColourId, textColour);
 | 
			
		||||
        connectionStatusLabel.setJustificationType (Justification::centredRight);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCMonitorDemo)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class OSCDemo   : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    OSCDemo()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (monitor);
 | 
			
		||||
        addAndMakeVisible (receiver);
 | 
			
		||||
        addAndMakeVisible (sender);
 | 
			
		||||
 | 
			
		||||
        setSize (700, 400);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto bounds = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        auto lowerBounds = bounds.removeFromBottom (getHeight() / 2);
 | 
			
		||||
        auto halfBounds  = bounds.removeFromRight  (getWidth()  / 2);
 | 
			
		||||
 | 
			
		||||
        sender  .setBounds (bounds);
 | 
			
		||||
        receiver.setBounds (halfBounds);
 | 
			
		||||
        monitor .setBounds (lowerBounds.removeFromTop (getHeight() / 2));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    OSCMonitorDemo  monitor;
 | 
			
		||||
    OSCReceiverDemo receiver;
 | 
			
		||||
    OSCSenderDemo   sender;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSCDemo)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2500
									
								
								deps/juce/examples/Utilities/PushNotificationsDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2500
									
								
								deps/juce/examples/Utilities/PushNotificationsDemo.h
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										490
									
								
								deps/juce/examples/Utilities/SystemInfoDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										490
									
								
								deps/juce/examples/Utilities/SystemInfoDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,245 +1,245 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             SystemInfoDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Displays system information.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        SystemInfoDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
static String getMacAddressList()
 | 
			
		||||
{
 | 
			
		||||
    String addressList;
 | 
			
		||||
 | 
			
		||||
    for (auto& addr : MACAddress::getAllAddresses())
 | 
			
		||||
        addressList << addr.toString() << newLine;
 | 
			
		||||
 | 
			
		||||
    return addressList;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static String getFileSystemRoots()
 | 
			
		||||
{
 | 
			
		||||
    Array<File> roots;
 | 
			
		||||
    File::findFileSystemRoots (roots);
 | 
			
		||||
 | 
			
		||||
    StringArray rootList;
 | 
			
		||||
    for (auto& r : roots)
 | 
			
		||||
        rootList.add (r.getFullPathName());
 | 
			
		||||
 | 
			
		||||
    return rootList.joinIntoString (", ");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static String getIPAddressList()
 | 
			
		||||
{
 | 
			
		||||
    String addressList;
 | 
			
		||||
 | 
			
		||||
    for (auto& addr : IPAddress::getAllAddresses())
 | 
			
		||||
        addressList << "   " << addr.toString() << newLine;
 | 
			
		||||
 | 
			
		||||
    return addressList;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char* getDisplayOrientation()
 | 
			
		||||
{
 | 
			
		||||
    switch (Desktop::getInstance().getCurrentOrientation())
 | 
			
		||||
    {
 | 
			
		||||
        case Desktop::upright:              return "Upright";
 | 
			
		||||
        case Desktop::upsideDown:           return "Upside-down";
 | 
			
		||||
        case Desktop::rotatedClockwise:     return "Rotated Clockwise";
 | 
			
		||||
        case Desktop::rotatedAntiClockwise: return "Rotated Anti-clockwise";
 | 
			
		||||
        case Desktop::allOrientations:      return "All";
 | 
			
		||||
        default: jassertfalse; break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static String getDisplayInfo()
 | 
			
		||||
{
 | 
			
		||||
    auto& displays = Desktop::getInstance().getDisplays();
 | 
			
		||||
 | 
			
		||||
    String displayDesc;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < displays.displays.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        auto display = displays.displays.getReference (i);
 | 
			
		||||
 | 
			
		||||
        displayDesc << "Display " << (i + 1) << (display.isMain ? " (main)" : "") << ":" << newLine
 | 
			
		||||
                    << "  Total area: " << display.totalArea.toString() << newLine
 | 
			
		||||
                    << "  User area:  " << display.userArea .toString() << newLine
 | 
			
		||||
                    << "  DPI: "        << display.dpi   << newLine
 | 
			
		||||
                    << "  Scale: "      << display.scale << newLine
 | 
			
		||||
                    << newLine;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    displayDesc << "Orientation: " << getDisplayOrientation() << newLine;
 | 
			
		||||
 | 
			
		||||
    return displayDesc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static String getAllSystemInfo()
 | 
			
		||||
{
 | 
			
		||||
    String systemInfo;
 | 
			
		||||
 | 
			
		||||
    systemInfo
 | 
			
		||||
      << "Here are a few system statistics..." << newLine
 | 
			
		||||
      << newLine
 | 
			
		||||
      << "Time and date:    " << Time::getCurrentTime().toString (true, true) << newLine
 | 
			
		||||
      << "System up-time:   " << RelativeTime::milliseconds ((int64) Time::getMillisecondCounterHiRes()).getDescription() << newLine
 | 
			
		||||
      << "Compilation date: " << Time::getCompilationDate().toString (true, false) << newLine
 | 
			
		||||
      << newLine
 | 
			
		||||
      << "Operating system: " << SystemStats::getOperatingSystemName() << newLine
 | 
			
		||||
      << "Host name:        " << SystemStats::getComputerName()        << newLine
 | 
			
		||||
      << "Device type:      " << SystemStats::getDeviceDescription()   << newLine
 | 
			
		||||
      << "Manufacturer:     " << SystemStats::getDeviceManufacturer()  << newLine
 | 
			
		||||
      << "User logon name:  " << SystemStats::getLogonName()           << newLine
 | 
			
		||||
      << "Full user name:   " << SystemStats::getFullUserName()        << newLine
 | 
			
		||||
      << "User region:      " << SystemStats::getUserRegion()          << newLine
 | 
			
		||||
      << "User language:    " << SystemStats::getUserLanguage()        << newLine
 | 
			
		||||
      << "Display language: " << SystemStats::getDisplayLanguage()     << newLine
 | 
			
		||||
      << newLine;
 | 
			
		||||
 | 
			
		||||
    systemInfo
 | 
			
		||||
      << "Number of logical CPUs:  " << SystemStats::getNumCpus() << newLine
 | 
			
		||||
      << "Number of physical CPUs: " << SystemStats::getNumPhysicalCpus() << newLine
 | 
			
		||||
      << "Memory size:             " << SystemStats::getMemorySizeInMegabytes() << " MB" << newLine
 | 
			
		||||
      << "CPU vendor:              " << SystemStats::getCpuVendor() << newLine
 | 
			
		||||
      << "CPU model:               " << SystemStats::getCpuModel()  << newLine
 | 
			
		||||
      << "CPU speed:               " << SystemStats::getCpuSpeedInMegahertz() << " MHz" << newLine
 | 
			
		||||
      << "CPU has MMX:             " << (SystemStats::hasMMX()             ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has FMA3:            " << (SystemStats::hasFMA3()            ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has FMA4:            " << (SystemStats::hasFMA4()            ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has SSE:             " << (SystemStats::hasSSE()             ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has SSE2:            " << (SystemStats::hasSSE2()            ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has SSE3:            " << (SystemStats::hasSSE3()            ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has SSSE3:           " << (SystemStats::hasSSSE3()           ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has SSE4.1:          " << (SystemStats::hasSSE41()           ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has SSE4.2:          " << (SystemStats::hasSSE42()           ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has 3DNOW:           " << (SystemStats::has3DNow()           ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX:             " << (SystemStats::hasAVX()             ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX2:            " << (SystemStats::hasAVX2()            ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512F:         " << (SystemStats::hasAVX512F()         ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512BW:        " << (SystemStats::hasAVX512BW()        ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512CD:        " << (SystemStats::hasAVX512CD()        ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512DQ:        " << (SystemStats::hasAVX512DQ()        ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512ER:        " << (SystemStats::hasAVX512ER()        ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512IFMA:      " << (SystemStats::hasAVX512IFMA()      ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512PF:        " << (SystemStats::hasAVX512PF()        ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512VBMI:      " << (SystemStats::hasAVX512VBMI()      ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512VL:        " << (SystemStats::hasAVX512VL()        ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512VPOPCNTDQ: " << (SystemStats::hasAVX512VPOPCNTDQ() ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has Neon:            " << (SystemStats::hasNeon()            ? "yes" : "no") << newLine
 | 
			
		||||
      << newLine;
 | 
			
		||||
 | 
			
		||||
    systemInfo
 | 
			
		||||
      << "Current working directory:  " << File::getCurrentWorkingDirectory().getFullPathName() << newLine
 | 
			
		||||
      << "Current application file:   " << File::getSpecialLocation (File::currentApplicationFile).getFullPathName() << newLine
 | 
			
		||||
      << "Current executable file:    " << File::getSpecialLocation (File::currentExecutableFile) .getFullPathName() << newLine
 | 
			
		||||
      << "Invoked executable file:    " << File::getSpecialLocation (File::invokedExecutableFile) .getFullPathName() << newLine
 | 
			
		||||
      << newLine;
 | 
			
		||||
 | 
			
		||||
    systemInfo
 | 
			
		||||
      << "User home folder:               " << File::getSpecialLocation (File::userHomeDirectory)             .getFullPathName() << newLine
 | 
			
		||||
      << "User desktop folder:            " << File::getSpecialLocation (File::userDesktopDirectory)          .getFullPathName() << newLine
 | 
			
		||||
      << "User documents folder:          " << File::getSpecialLocation (File::userDocumentsDirectory)        .getFullPathName() << newLine
 | 
			
		||||
      << "User application data folder:   " << File::getSpecialLocation (File::userApplicationDataDirectory)  .getFullPathName() << newLine
 | 
			
		||||
      << "User music folder:              " << File::getSpecialLocation (File::userMusicDirectory)            .getFullPathName() << newLine
 | 
			
		||||
      << "User movies folder:             " << File::getSpecialLocation (File::userMoviesDirectory)           .getFullPathName() << newLine
 | 
			
		||||
      << "User pictures folder:           " << File::getSpecialLocation (File::userPicturesDirectory)         .getFullPathName() << newLine
 | 
			
		||||
      << "Common application data folder: " << File::getSpecialLocation (File::commonApplicationDataDirectory).getFullPathName() << newLine
 | 
			
		||||
      << "Common documents folder:        " << File::getSpecialLocation (File::commonDocumentsDirectory)      .getFullPathName() << newLine
 | 
			
		||||
      << "Local temp folder:              " << File::getSpecialLocation (File::tempDirectory)                 .getFullPathName() << newLine
 | 
			
		||||
      << newLine;
 | 
			
		||||
 | 
			
		||||
    systemInfo
 | 
			
		||||
      << "File System roots: "          << getFileSystemRoots() << newLine
 | 
			
		||||
      << "Free space in home folder: "  << File::descriptionOfSizeInBytes (File::getSpecialLocation (File::userHomeDirectory)
 | 
			
		||||
                                                                                .getBytesFreeOnVolume()) << newLine
 | 
			
		||||
      << newLine
 | 
			
		||||
      << getDisplayInfo() << newLine
 | 
			
		||||
      << "Network IP addresses: "       << newLine << getIPAddressList()  << newLine
 | 
			
		||||
      << "Network card MAC addresses: " << newLine << getMacAddressList() << newLine;
 | 
			
		||||
 | 
			
		||||
    DBG (systemInfo);
 | 
			
		||||
    return systemInfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class SystemInfoDemo  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    SystemInfoDemo()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (resultsBox);
 | 
			
		||||
        resultsBox.setReadOnly (true);
 | 
			
		||||
        resultsBox.setMultiLine (true);
 | 
			
		||||
        resultsBox.setColour (TextEditor::backgroundColourId, Colours::transparentBlack);
 | 
			
		||||
        resultsBox.setFont ({ Font::getDefaultMonospacedFontName(), 12.0f, Font::plain });
 | 
			
		||||
        resultsBox.setText (getAllSystemInfo());
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
 | 
			
		||||
                                           Colour::greyLevel (0.93f)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        resultsBox.setBounds (getLocalBounds().reduced (8));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    TextEditor resultsBox;
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        resultsBox.applyFontToAllText (resultsBox.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SystemInfoDemo)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             SystemInfoDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Displays system information.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        SystemInfoDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
static String getMacAddressList()
 | 
			
		||||
{
 | 
			
		||||
    String addressList;
 | 
			
		||||
 | 
			
		||||
    for (auto& addr : MACAddress::getAllAddresses())
 | 
			
		||||
        addressList << addr.toString() << newLine;
 | 
			
		||||
 | 
			
		||||
    return addressList;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static String getFileSystemRoots()
 | 
			
		||||
{
 | 
			
		||||
    Array<File> roots;
 | 
			
		||||
    File::findFileSystemRoots (roots);
 | 
			
		||||
 | 
			
		||||
    StringArray rootList;
 | 
			
		||||
    for (auto& r : roots)
 | 
			
		||||
        rootList.add (r.getFullPathName());
 | 
			
		||||
 | 
			
		||||
    return rootList.joinIntoString (", ");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static String getIPAddressList()
 | 
			
		||||
{
 | 
			
		||||
    String addressList;
 | 
			
		||||
 | 
			
		||||
    for (auto& addr : IPAddress::getAllAddresses())
 | 
			
		||||
        addressList << "   " << addr.toString() << newLine;
 | 
			
		||||
 | 
			
		||||
    return addressList;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char* getDisplayOrientation()
 | 
			
		||||
{
 | 
			
		||||
    switch (Desktop::getInstance().getCurrentOrientation())
 | 
			
		||||
    {
 | 
			
		||||
        case Desktop::upright:              return "Upright";
 | 
			
		||||
        case Desktop::upsideDown:           return "Upside-down";
 | 
			
		||||
        case Desktop::rotatedClockwise:     return "Rotated Clockwise";
 | 
			
		||||
        case Desktop::rotatedAntiClockwise: return "Rotated Anti-clockwise";
 | 
			
		||||
        case Desktop::allOrientations:      return "All";
 | 
			
		||||
        default: jassertfalse; break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static String getDisplayInfo()
 | 
			
		||||
{
 | 
			
		||||
    auto& displays = Desktop::getInstance().getDisplays();
 | 
			
		||||
 | 
			
		||||
    String displayDesc;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < displays.displays.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        auto display = displays.displays.getReference (i);
 | 
			
		||||
 | 
			
		||||
        displayDesc << "Display " << (i + 1) << (display.isMain ? " (main)" : "") << ":" << newLine
 | 
			
		||||
                    << "  Total area: " << display.totalArea.toString() << newLine
 | 
			
		||||
                    << "  User area:  " << display.userArea .toString() << newLine
 | 
			
		||||
                    << "  DPI: "        << display.dpi   << newLine
 | 
			
		||||
                    << "  Scale: "      << display.scale << newLine
 | 
			
		||||
                    << newLine;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    displayDesc << "Orientation: " << getDisplayOrientation() << newLine;
 | 
			
		||||
 | 
			
		||||
    return displayDesc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static String getAllSystemInfo()
 | 
			
		||||
{
 | 
			
		||||
    String systemInfo;
 | 
			
		||||
 | 
			
		||||
    systemInfo
 | 
			
		||||
      << "Here are a few system statistics..." << newLine
 | 
			
		||||
      << newLine
 | 
			
		||||
      << "Time and date:    " << Time::getCurrentTime().toString (true, true) << newLine
 | 
			
		||||
      << "System up-time:   " << RelativeTime::milliseconds ((int64) Time::getMillisecondCounterHiRes()).getDescription() << newLine
 | 
			
		||||
      << "Compilation date: " << Time::getCompilationDate().toString (true, false) << newLine
 | 
			
		||||
      << newLine
 | 
			
		||||
      << "Operating system: " << SystemStats::getOperatingSystemName() << newLine
 | 
			
		||||
      << "Host name:        " << SystemStats::getComputerName()        << newLine
 | 
			
		||||
      << "Device type:      " << SystemStats::getDeviceDescription()   << newLine
 | 
			
		||||
      << "Manufacturer:     " << SystemStats::getDeviceManufacturer()  << newLine
 | 
			
		||||
      << "User logon name:  " << SystemStats::getLogonName()           << newLine
 | 
			
		||||
      << "Full user name:   " << SystemStats::getFullUserName()        << newLine
 | 
			
		||||
      << "User region:      " << SystemStats::getUserRegion()          << newLine
 | 
			
		||||
      << "User language:    " << SystemStats::getUserLanguage()        << newLine
 | 
			
		||||
      << "Display language: " << SystemStats::getDisplayLanguage()     << newLine
 | 
			
		||||
      << newLine;
 | 
			
		||||
 | 
			
		||||
    systemInfo
 | 
			
		||||
      << "Number of logical CPUs:  " << SystemStats::getNumCpus() << newLine
 | 
			
		||||
      << "Number of physical CPUs: " << SystemStats::getNumPhysicalCpus() << newLine
 | 
			
		||||
      << "Memory size:             " << SystemStats::getMemorySizeInMegabytes() << " MB" << newLine
 | 
			
		||||
      << "CPU vendor:              " << SystemStats::getCpuVendor() << newLine
 | 
			
		||||
      << "CPU model:               " << SystemStats::getCpuModel()  << newLine
 | 
			
		||||
      << "CPU speed:               " << SystemStats::getCpuSpeedInMegahertz() << " MHz" << newLine
 | 
			
		||||
      << "CPU has MMX:             " << (SystemStats::hasMMX()             ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has FMA3:            " << (SystemStats::hasFMA3()            ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has FMA4:            " << (SystemStats::hasFMA4()            ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has SSE:             " << (SystemStats::hasSSE()             ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has SSE2:            " << (SystemStats::hasSSE2()            ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has SSE3:            " << (SystemStats::hasSSE3()            ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has SSSE3:           " << (SystemStats::hasSSSE3()           ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has SSE4.1:          " << (SystemStats::hasSSE41()           ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has SSE4.2:          " << (SystemStats::hasSSE42()           ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has 3DNOW:           " << (SystemStats::has3DNow()           ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX:             " << (SystemStats::hasAVX()             ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX2:            " << (SystemStats::hasAVX2()            ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512F:         " << (SystemStats::hasAVX512F()         ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512BW:        " << (SystemStats::hasAVX512BW()        ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512CD:        " << (SystemStats::hasAVX512CD()        ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512DQ:        " << (SystemStats::hasAVX512DQ()        ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512ER:        " << (SystemStats::hasAVX512ER()        ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512IFMA:      " << (SystemStats::hasAVX512IFMA()      ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512PF:        " << (SystemStats::hasAVX512PF()        ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512VBMI:      " << (SystemStats::hasAVX512VBMI()      ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512VL:        " << (SystemStats::hasAVX512VL()        ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has AVX512VPOPCNTDQ: " << (SystemStats::hasAVX512VPOPCNTDQ() ? "yes" : "no") << newLine
 | 
			
		||||
      << "CPU has Neon:            " << (SystemStats::hasNeon()            ? "yes" : "no") << newLine
 | 
			
		||||
      << newLine;
 | 
			
		||||
 | 
			
		||||
    systemInfo
 | 
			
		||||
      << "Current working directory:  " << File::getCurrentWorkingDirectory().getFullPathName() << newLine
 | 
			
		||||
      << "Current application file:   " << File::getSpecialLocation (File::currentApplicationFile).getFullPathName() << newLine
 | 
			
		||||
      << "Current executable file:    " << File::getSpecialLocation (File::currentExecutableFile) .getFullPathName() << newLine
 | 
			
		||||
      << "Invoked executable file:    " << File::getSpecialLocation (File::invokedExecutableFile) .getFullPathName() << newLine
 | 
			
		||||
      << newLine;
 | 
			
		||||
 | 
			
		||||
    systemInfo
 | 
			
		||||
      << "User home folder:               " << File::getSpecialLocation (File::userHomeDirectory)             .getFullPathName() << newLine
 | 
			
		||||
      << "User desktop folder:            " << File::getSpecialLocation (File::userDesktopDirectory)          .getFullPathName() << newLine
 | 
			
		||||
      << "User documents folder:          " << File::getSpecialLocation (File::userDocumentsDirectory)        .getFullPathName() << newLine
 | 
			
		||||
      << "User application data folder:   " << File::getSpecialLocation (File::userApplicationDataDirectory)  .getFullPathName() << newLine
 | 
			
		||||
      << "User music folder:              " << File::getSpecialLocation (File::userMusicDirectory)            .getFullPathName() << newLine
 | 
			
		||||
      << "User movies folder:             " << File::getSpecialLocation (File::userMoviesDirectory)           .getFullPathName() << newLine
 | 
			
		||||
      << "User pictures folder:           " << File::getSpecialLocation (File::userPicturesDirectory)         .getFullPathName() << newLine
 | 
			
		||||
      << "Common application data folder: " << File::getSpecialLocation (File::commonApplicationDataDirectory).getFullPathName() << newLine
 | 
			
		||||
      << "Common documents folder:        " << File::getSpecialLocation (File::commonDocumentsDirectory)      .getFullPathName() << newLine
 | 
			
		||||
      << "Local temp folder:              " << File::getSpecialLocation (File::tempDirectory)                 .getFullPathName() << newLine
 | 
			
		||||
      << newLine;
 | 
			
		||||
 | 
			
		||||
    systemInfo
 | 
			
		||||
      << "File System roots: "          << getFileSystemRoots() << newLine
 | 
			
		||||
      << "Free space in home folder: "  << File::descriptionOfSizeInBytes (File::getSpecialLocation (File::userHomeDirectory)
 | 
			
		||||
                                                                                .getBytesFreeOnVolume()) << newLine
 | 
			
		||||
      << newLine
 | 
			
		||||
      << getDisplayInfo() << newLine
 | 
			
		||||
      << "Network IP addresses: "       << newLine << getIPAddressList()  << newLine
 | 
			
		||||
      << "Network card MAC addresses: " << newLine << getMacAddressList() << newLine;
 | 
			
		||||
 | 
			
		||||
    DBG (systemInfo);
 | 
			
		||||
    return systemInfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class SystemInfoDemo  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    SystemInfoDemo()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (resultsBox);
 | 
			
		||||
        resultsBox.setReadOnly (true);
 | 
			
		||||
        resultsBox.setMultiLine (true);
 | 
			
		||||
        resultsBox.setColour (TextEditor::backgroundColourId, Colours::transparentBlack);
 | 
			
		||||
        resultsBox.setFont ({ Font::getDefaultMonospacedFontName(), 12.0f, Font::plain });
 | 
			
		||||
        resultsBox.setText (getAllSystemInfo());
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
 | 
			
		||||
                                           Colour::greyLevel (0.93f)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        resultsBox.setBounds (getLocalBounds().reduced (8));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    TextEditor resultsBox;
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        resultsBox.applyFontToAllText (resultsBox.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SystemInfoDemo)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										522
									
								
								deps/juce/examples/Utilities/TimersAndEventsDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										522
									
								
								deps/juce/examples/Utilities/TimersAndEventsDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,261 +1,261 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             TimersAndEventsDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Application using timers and events.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        TimersAndEventsDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** Simple message that holds a Colour. */
 | 
			
		||||
struct ColourMessage  : public Message
 | 
			
		||||
{
 | 
			
		||||
    ColourMessage (Colour col)  : colour (col) {}
 | 
			
		||||
 | 
			
		||||
    /** Returns the colour of a ColourMessage of white if the message is not a ColourMessage. */
 | 
			
		||||
    static Colour getColour (const Message& message)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* cm = dynamic_cast<const ColourMessage*> (&message))
 | 
			
		||||
            return cm->colour;
 | 
			
		||||
 | 
			
		||||
        return Colours::white;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Colour colour;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourMessage)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** Simple component that can be triggered to flash.
 | 
			
		||||
    The flash will then fade using a Timer to repaint itself and will send a change
 | 
			
		||||
    message once it is finished.
 | 
			
		||||
 */
 | 
			
		||||
class FlashingComponent   : public Component,
 | 
			
		||||
                            public MessageListener,
 | 
			
		||||
                            public ChangeBroadcaster,
 | 
			
		||||
                            private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    FlashingComponent() {}
 | 
			
		||||
 | 
			
		||||
    void startFlashing()
 | 
			
		||||
    {
 | 
			
		||||
        flashAlpha = 1.0f;
 | 
			
		||||
        startTimerHz (25);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Stops this component flashing without sending a change message. */
 | 
			
		||||
    void stopFlashing()
 | 
			
		||||
    {
 | 
			
		||||
        flashAlpha = 0.0f;
 | 
			
		||||
        stopTimer();
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Sets the colour of the component. */
 | 
			
		||||
    void setFlashColour (const Colour newColour)
 | 
			
		||||
    {
 | 
			
		||||
        colour = newColour;
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Draws our component. */
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.setColour (colour.overlaidWith (Colours::white.withAlpha (flashAlpha)));
 | 
			
		||||
        g.fillEllipse (getLocalBounds().toFloat());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Custom mouse handler to trigger a flash. */
 | 
			
		||||
    void mouseDown (const MouseEvent&) override
 | 
			
		||||
    {
 | 
			
		||||
        startFlashing();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Message listener callback used to change our colour */
 | 
			
		||||
    void handleMessage (const Message& message) override
 | 
			
		||||
    {
 | 
			
		||||
        setFlashColour (ColourMessage::getColour (message));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    float flashAlpha = 0.0f;
 | 
			
		||||
    Colour colour { Colours::red };
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        // Reduce the alpha level of the flash slightly so it fades out
 | 
			
		||||
        flashAlpha -= 0.075f;
 | 
			
		||||
 | 
			
		||||
        if (flashAlpha < 0.05f)
 | 
			
		||||
        {
 | 
			
		||||
            stopFlashing();
 | 
			
		||||
            sendChangeMessage();
 | 
			
		||||
            // Once we've finished flashing send a change message to trigger the next component to flash
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlashingComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class TimersAndEventsDemo   : public Component,
 | 
			
		||||
                              private ChangeListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    TimersAndEventsDemo()
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
 | 
			
		||||
        // Create and add our FlashingComponents with some random colours and sizes
 | 
			
		||||
        for (int i = 0; i < numFlashingComponents; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            auto* newFlasher = new FlashingComponent();
 | 
			
		||||
            flashingComponents.add (newFlasher);
 | 
			
		||||
 | 
			
		||||
            newFlasher->setFlashColour (getRandomBrightColour());
 | 
			
		||||
            newFlasher->addChangeListener (this);
 | 
			
		||||
 | 
			
		||||
            auto diameter = 25 + random.nextInt (75);
 | 
			
		||||
            newFlasher->setSize (diameter, diameter);
 | 
			
		||||
 | 
			
		||||
            addAndMakeVisible (newFlasher);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (stopButton);
 | 
			
		||||
        stopButton.onClick = [this] { stopButtonClicked(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (randomColourButton);
 | 
			
		||||
        randomColourButton.onClick = [this] { randomColourButtonClicked(); };
 | 
			
		||||
 | 
			
		||||
        // lay out our components in a pseudo random grid
 | 
			
		||||
        Rectangle<int> area (0, 100, 150, 150);
 | 
			
		||||
 | 
			
		||||
        for (auto* comp : flashingComponents)
 | 
			
		||||
        {
 | 
			
		||||
            auto buttonArea = area.withSize (comp->getWidth(), comp->getHeight());
 | 
			
		||||
            buttonArea.translate (random.nextInt (area.getWidth()  - comp->getWidth()),
 | 
			
		||||
                                  random.nextInt (area.getHeight() - comp->getHeight()));
 | 
			
		||||
            comp->setBounds (buttonArea);
 | 
			
		||||
 | 
			
		||||
            area.translate (area.getWidth(), 0);
 | 
			
		||||
 | 
			
		||||
            // if we go off the right start a new row
 | 
			
		||||
            if (area.getRight() > (800 - area.getWidth()))
 | 
			
		||||
            {
 | 
			
		||||
                area.translate (0, area.getWidth());
 | 
			
		||||
                area.setX (0);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setSize (600, 600);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~TimersAndEventsDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        for (auto* fc : flashingComponents)
 | 
			
		||||
            fc->removeChangeListener (this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
 | 
			
		||||
                                           Colours::darkgrey));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paintOverChildren (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        auto explanationArea = getLocalBounds().removeFromTop (100);
 | 
			
		||||
 | 
			
		||||
        AttributedString s;
 | 
			
		||||
        s.append ("Click on a circle to make it flash. When it has finished flashing it will send a message which causes the next circle to flash");
 | 
			
		||||
        s.append (newLine);
 | 
			
		||||
        s.append ("Click the \"Set Random Colour\" button to change the colour of one of the circles.");
 | 
			
		||||
        s.append (newLine);
 | 
			
		||||
        s.setFont (16.0f);
 | 
			
		||||
        s.setColour (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::defaultText, Colours::lightgrey));
 | 
			
		||||
        s.draw (g, explanationArea.reduced (10).toFloat());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto area = getLocalBounds().removeFromBottom (40);
 | 
			
		||||
        randomColourButton.setBounds (area.removeFromLeft (166) .reduced (8));
 | 
			
		||||
        stopButton        .setBounds (area.removeFromRight (166).reduced (8));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    enum { numFlashingComponents = 9 };
 | 
			
		||||
 | 
			
		||||
    OwnedArray<FlashingComponent> flashingComponents;
 | 
			
		||||
    TextButton randomColourButton  { "Set Random Colour" },
 | 
			
		||||
               stopButton          { "Stop" };
 | 
			
		||||
    Random random;
 | 
			
		||||
 | 
			
		||||
    void changeListenerCallback (ChangeBroadcaster* source) override
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = 0; i < flashingComponents.size(); ++i)
 | 
			
		||||
            if (source == flashingComponents.getUnchecked (i))
 | 
			
		||||
                flashingComponents.getUnchecked ((i + 1) % flashingComponents.size())->startFlashing();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void randomColourButtonClicked()
 | 
			
		||||
    {
 | 
			
		||||
        // Here we post a new ColourMessage with a random colour to a random flashing component.
 | 
			
		||||
        // This will send a message to the component asynchronously and trigger its handleMessage callback
 | 
			
		||||
        flashingComponents.getUnchecked (random.nextInt (flashingComponents.size()))->postMessage (new ColourMessage (getRandomBrightColour()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void stopButtonClicked()
 | 
			
		||||
    {
 | 
			
		||||
        for (auto* fc : flashingComponents)
 | 
			
		||||
            fc->stopFlashing();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimersAndEventsDemo)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             TimersAndEventsDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Application using timers and events.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        TimersAndEventsDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** Simple message that holds a Colour. */
 | 
			
		||||
struct ColourMessage  : public Message
 | 
			
		||||
{
 | 
			
		||||
    ColourMessage (Colour col)  : colour (col) {}
 | 
			
		||||
 | 
			
		||||
    /** Returns the colour of a ColourMessage of white if the message is not a ColourMessage. */
 | 
			
		||||
    static Colour getColour (const Message& message)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* cm = dynamic_cast<const ColourMessage*> (&message))
 | 
			
		||||
            return cm->colour;
 | 
			
		||||
 | 
			
		||||
        return Colours::white;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Colour colour;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourMessage)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** Simple component that can be triggered to flash.
 | 
			
		||||
    The flash will then fade using a Timer to repaint itself and will send a change
 | 
			
		||||
    message once it is finished.
 | 
			
		||||
 */
 | 
			
		||||
class FlashingComponent   : public Component,
 | 
			
		||||
                            public MessageListener,
 | 
			
		||||
                            public ChangeBroadcaster,
 | 
			
		||||
                            private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    FlashingComponent() {}
 | 
			
		||||
 | 
			
		||||
    void startFlashing()
 | 
			
		||||
    {
 | 
			
		||||
        flashAlpha = 1.0f;
 | 
			
		||||
        startTimerHz (25);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Stops this component flashing without sending a change message. */
 | 
			
		||||
    void stopFlashing()
 | 
			
		||||
    {
 | 
			
		||||
        flashAlpha = 0.0f;
 | 
			
		||||
        stopTimer();
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Sets the colour of the component. */
 | 
			
		||||
    void setFlashColour (const Colour newColour)
 | 
			
		||||
    {
 | 
			
		||||
        colour = newColour;
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Draws our component. */
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.setColour (colour.overlaidWith (Colours::white.withAlpha (flashAlpha)));
 | 
			
		||||
        g.fillEllipse (getLocalBounds().toFloat());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Custom mouse handler to trigger a flash. */
 | 
			
		||||
    void mouseDown (const MouseEvent&) override
 | 
			
		||||
    {
 | 
			
		||||
        startFlashing();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Message listener callback used to change our colour */
 | 
			
		||||
    void handleMessage (const Message& message) override
 | 
			
		||||
    {
 | 
			
		||||
        setFlashColour (ColourMessage::getColour (message));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    float flashAlpha = 0.0f;
 | 
			
		||||
    Colour colour { Colours::red };
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        // Reduce the alpha level of the flash slightly so it fades out
 | 
			
		||||
        flashAlpha -= 0.075f;
 | 
			
		||||
 | 
			
		||||
        if (flashAlpha < 0.05f)
 | 
			
		||||
        {
 | 
			
		||||
            stopFlashing();
 | 
			
		||||
            sendChangeMessage();
 | 
			
		||||
            // Once we've finished flashing send a change message to trigger the next component to flash
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        repaint();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlashingComponent)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class TimersAndEventsDemo   : public Component,
 | 
			
		||||
                              private ChangeListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    TimersAndEventsDemo()
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
 | 
			
		||||
        // Create and add our FlashingComponents with some random colours and sizes
 | 
			
		||||
        for (int i = 0; i < numFlashingComponents; ++i)
 | 
			
		||||
        {
 | 
			
		||||
            auto* newFlasher = new FlashingComponent();
 | 
			
		||||
            flashingComponents.add (newFlasher);
 | 
			
		||||
 | 
			
		||||
            newFlasher->setFlashColour (getRandomBrightColour());
 | 
			
		||||
            newFlasher->addChangeListener (this);
 | 
			
		||||
 | 
			
		||||
            auto diameter = 25 + random.nextInt (75);
 | 
			
		||||
            newFlasher->setSize (diameter, diameter);
 | 
			
		||||
 | 
			
		||||
            addAndMakeVisible (newFlasher);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (stopButton);
 | 
			
		||||
        stopButton.onClick = [this] { stopButtonClicked(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (randomColourButton);
 | 
			
		||||
        randomColourButton.onClick = [this] { randomColourButtonClicked(); };
 | 
			
		||||
 | 
			
		||||
        // lay out our components in a pseudo random grid
 | 
			
		||||
        Rectangle<int> area (0, 100, 150, 150);
 | 
			
		||||
 | 
			
		||||
        for (auto* comp : flashingComponents)
 | 
			
		||||
        {
 | 
			
		||||
            auto buttonArea = area.withSize (comp->getWidth(), comp->getHeight());
 | 
			
		||||
            buttonArea.translate (random.nextInt (area.getWidth()  - comp->getWidth()),
 | 
			
		||||
                                  random.nextInt (area.getHeight() - comp->getHeight()));
 | 
			
		||||
            comp->setBounds (buttonArea);
 | 
			
		||||
 | 
			
		||||
            area.translate (area.getWidth(), 0);
 | 
			
		||||
 | 
			
		||||
            // if we go off the right start a new row
 | 
			
		||||
            if (area.getRight() > (800 - area.getWidth()))
 | 
			
		||||
            {
 | 
			
		||||
                area.translate (0, area.getWidth());
 | 
			
		||||
                area.setX (0);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setSize (600, 600);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~TimersAndEventsDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        for (auto* fc : flashingComponents)
 | 
			
		||||
            fc->removeChangeListener (this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
 | 
			
		||||
                                           Colours::darkgrey));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paintOverChildren (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        auto explanationArea = getLocalBounds().removeFromTop (100);
 | 
			
		||||
 | 
			
		||||
        AttributedString s;
 | 
			
		||||
        s.append ("Click on a circle to make it flash. When it has finished flashing it will send a message which causes the next circle to flash");
 | 
			
		||||
        s.append (newLine);
 | 
			
		||||
        s.append ("Click the \"Set Random Colour\" button to change the colour of one of the circles.");
 | 
			
		||||
        s.append (newLine);
 | 
			
		||||
        s.setFont (16.0f);
 | 
			
		||||
        s.setColour (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::defaultText, Colours::lightgrey));
 | 
			
		||||
        s.draw (g, explanationArea.reduced (10).toFloat());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto area = getLocalBounds().removeFromBottom (40);
 | 
			
		||||
        randomColourButton.setBounds (area.removeFromLeft (166) .reduced (8));
 | 
			
		||||
        stopButton        .setBounds (area.removeFromRight (166).reduced (8));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    enum { numFlashingComponents = 9 };
 | 
			
		||||
 | 
			
		||||
    OwnedArray<FlashingComponent> flashingComponents;
 | 
			
		||||
    TextButton randomColourButton  { "Set Random Colour" },
 | 
			
		||||
               stopButton          { "Stop" };
 | 
			
		||||
    Random random;
 | 
			
		||||
 | 
			
		||||
    void changeListenerCallback (ChangeBroadcaster* source) override
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = 0; i < flashingComponents.size(); ++i)
 | 
			
		||||
            if (source == flashingComponents.getUnchecked (i))
 | 
			
		||||
                flashingComponents.getUnchecked ((i + 1) % flashingComponents.size())->startFlashing();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void randomColourButtonClicked()
 | 
			
		||||
    {
 | 
			
		||||
        // Here we post a new ColourMessage with a random colour to a random flashing component.
 | 
			
		||||
        // This will send a message to the component asynchronously and trigger its handleMessage callback
 | 
			
		||||
        flashingComponents.getUnchecked (random.nextInt (flashingComponents.size()))->postMessage (new ColourMessage (getRandomBrightColour()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void stopButtonClicked()
 | 
			
		||||
    {
 | 
			
		||||
        for (auto* fc : flashingComponents)
 | 
			
		||||
            fc->stopFlashing();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimersAndEventsDemo)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										464
									
								
								deps/juce/examples/Utilities/UnitTestsDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										464
									
								
								deps/juce/examples/Utilities/UnitTestsDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,232 +1,232 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             UnitTestsDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Performs unit tests.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_analytics, juce_audio_basics, juce_audio_devices,
 | 
			
		||||
                   juce_audio_formats, juce_audio_processors, juce_audio_utils,
 | 
			
		||||
                   juce_core, juce_cryptography, juce_data_structures, juce_dsp,
 | 
			
		||||
                   juce_events, juce_graphics, juce_gui_basics, juce_gui_extra,
 | 
			
		||||
                   juce_opengl, juce_osc, juce_product_unlocking, juce_video
 | 
			
		||||
 exporters:        xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 defines:          JUCE_UNIT_TESTS=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        UnitTestsDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class UnitTestsDemo  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    UnitTestsDemo()
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (startTestButton);
 | 
			
		||||
        startTestButton.onClick = [this] { start(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (testResultsBox);
 | 
			
		||||
        testResultsBox.setMultiLine (true);
 | 
			
		||||
        testResultsBox.setFont (Font (Font::getDefaultMonospacedFontName(), 12.0f, Font::plain));
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (categoriesBox);
 | 
			
		||||
        categoriesBox.addItem ("All Tests", 1);
 | 
			
		||||
 | 
			
		||||
        auto categories = UnitTest::getAllCategories();
 | 
			
		||||
        categories.sort (true);
 | 
			
		||||
 | 
			
		||||
        categoriesBox.addItemList (categories, 2);
 | 
			
		||||
        categoriesBox.setSelectedId (1);
 | 
			
		||||
 | 
			
		||||
        logMessage ("This panel runs the built-in JUCE unit-tests from the selected category.\n");
 | 
			
		||||
        logMessage ("To add your own unit-tests, see the JUCE_UNIT_TESTS macro.");
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~UnitTestsDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        stopTest();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
 | 
			
		||||
                                           Colours::grey));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto bounds = getLocalBounds().reduced (6);
 | 
			
		||||
 | 
			
		||||
        auto topSlice = bounds.removeFromTop (25);
 | 
			
		||||
        startTestButton.setBounds (topSlice.removeFromLeft (200));
 | 
			
		||||
        topSlice.removeFromLeft (10);
 | 
			
		||||
        categoriesBox  .setBounds (topSlice.removeFromLeft (250));
 | 
			
		||||
 | 
			
		||||
        bounds.removeFromTop (5);
 | 
			
		||||
        testResultsBox.setBounds (bounds);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void start()
 | 
			
		||||
    {
 | 
			
		||||
        startTest (categoriesBox.getText());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void startTest (const String& category)
 | 
			
		||||
    {
 | 
			
		||||
        testResultsBox.clear();
 | 
			
		||||
        startTestButton.setEnabled (false);
 | 
			
		||||
 | 
			
		||||
        currentTestThread.reset (new TestRunnerThread (*this, category));
 | 
			
		||||
        currentTestThread->startThread();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void stopTest()
 | 
			
		||||
    {
 | 
			
		||||
        if (currentTestThread.get() != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            currentTestThread->stopThread (15000);
 | 
			
		||||
            currentTestThread.reset();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void logMessage (const String& message)
 | 
			
		||||
    {
 | 
			
		||||
        testResultsBox.moveCaretToEnd();
 | 
			
		||||
        testResultsBox.insertTextAtCaret (message + newLine);
 | 
			
		||||
        testResultsBox.moveCaretToEnd();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void testFinished()
 | 
			
		||||
    {
 | 
			
		||||
        stopTest();
 | 
			
		||||
        startTestButton.setEnabled (true);
 | 
			
		||||
        logMessage (newLine + "*** Tests finished ***");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    class TestRunnerThread  : public Thread,
 | 
			
		||||
                              private Timer
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        TestRunnerThread (UnitTestsDemo& utd, const String& ctg)
 | 
			
		||||
            : Thread ("Unit Tests"),
 | 
			
		||||
              owner (utd),
 | 
			
		||||
              category (ctg)
 | 
			
		||||
        {}
 | 
			
		||||
 | 
			
		||||
        void run() override
 | 
			
		||||
        {
 | 
			
		||||
            CustomTestRunner runner (*this);
 | 
			
		||||
 | 
			
		||||
            if (category == "All Tests")
 | 
			
		||||
                runner.runAllTests();
 | 
			
		||||
            else
 | 
			
		||||
                runner.runTestsInCategory (category);
 | 
			
		||||
 | 
			
		||||
            startTimer (50); // when finished, start the timer which will
 | 
			
		||||
                             // wait for the thread to end, then tell our component.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void logMessage (const String& message)
 | 
			
		||||
        {
 | 
			
		||||
            WeakReference<UnitTestsDemo> safeOwner (&owner);
 | 
			
		||||
 | 
			
		||||
            MessageManager::callAsync ([=]
 | 
			
		||||
            {
 | 
			
		||||
                if (auto* o = safeOwner.get())
 | 
			
		||||
                    o->logMessage (message);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void timerCallback() override
 | 
			
		||||
        {
 | 
			
		||||
            if (! isThreadRunning())
 | 
			
		||||
                owner.testFinished(); // inform the demo page when done, so it can delete this thread.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        // This subclass of UnitTestRunner is used to redirect the test output to our
 | 
			
		||||
        // TextBox, and to interrupt the running tests when our thread is asked to stop..
 | 
			
		||||
        class CustomTestRunner  : public UnitTestRunner
 | 
			
		||||
        {
 | 
			
		||||
        public:
 | 
			
		||||
            CustomTestRunner (TestRunnerThread& trt)  : owner (trt) {}
 | 
			
		||||
 | 
			
		||||
            void logMessage (const String& message) override
 | 
			
		||||
            {
 | 
			
		||||
                owner.logMessage (message);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool shouldAbortTests() override
 | 
			
		||||
            {
 | 
			
		||||
                return owner.threadShouldExit();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
            TestRunnerThread& owner;
 | 
			
		||||
 | 
			
		||||
            JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomTestRunner)
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        UnitTestsDemo& owner;
 | 
			
		||||
        const String category;
 | 
			
		||||
 | 
			
		||||
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestRunnerThread)
 | 
			
		||||
    };
 | 
			
		||||
    std::unique_ptr<TestRunnerThread> currentTestThread;
 | 
			
		||||
 | 
			
		||||
    TextButton startTestButton { "Run Unit Tests..." };
 | 
			
		||||
    ComboBox categoriesBox;
 | 
			
		||||
    TextEditor testResultsBox;
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        testResultsBox.applyFontToAllText (testResultsBox.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_WEAK_REFERENCEABLE (UnitTestsDemo)
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnitTestsDemo)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             UnitTestsDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Performs unit tests.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_analytics, juce_audio_basics, juce_audio_devices,
 | 
			
		||||
                   juce_audio_formats, juce_audio_processors, juce_audio_utils,
 | 
			
		||||
                   juce_core, juce_cryptography, juce_data_structures, juce_dsp,
 | 
			
		||||
                   juce_events, juce_graphics, juce_gui_basics, juce_gui_extra,
 | 
			
		||||
                   juce_opengl, juce_osc, juce_product_unlocking, juce_video
 | 
			
		||||
 exporters:        xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1,JUCE_PLUGINHOST_VST3=1,JUCE_PLUGINHOST_LV2=1
 | 
			
		||||
 defines:          JUCE_UNIT_TESTS=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        UnitTestsDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class UnitTestsDemo  : public Component
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    UnitTestsDemo()
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (startTestButton);
 | 
			
		||||
        startTestButton.onClick = [this] { start(); };
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (testResultsBox);
 | 
			
		||||
        testResultsBox.setMultiLine (true);
 | 
			
		||||
        testResultsBox.setFont (Font (Font::getDefaultMonospacedFontName(), 12.0f, Font::plain));
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (categoriesBox);
 | 
			
		||||
        categoriesBox.addItem ("All Tests", 1);
 | 
			
		||||
 | 
			
		||||
        auto categories = UnitTest::getAllCategories();
 | 
			
		||||
        categories.sort (true);
 | 
			
		||||
 | 
			
		||||
        categoriesBox.addItemList (categories, 2);
 | 
			
		||||
        categoriesBox.setSelectedId (1);
 | 
			
		||||
 | 
			
		||||
        logMessage ("This panel runs the built-in JUCE unit-tests from the selected category.\n");
 | 
			
		||||
        logMessage ("To add your own unit-tests, see the JUCE_UNIT_TESTS macro.");
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~UnitTestsDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        stopTest();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
 | 
			
		||||
                                           Colours::grey));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto bounds = getLocalBounds().reduced (6);
 | 
			
		||||
 | 
			
		||||
        auto topSlice = bounds.removeFromTop (25);
 | 
			
		||||
        startTestButton.setBounds (topSlice.removeFromLeft (200));
 | 
			
		||||
        topSlice.removeFromLeft (10);
 | 
			
		||||
        categoriesBox  .setBounds (topSlice.removeFromLeft (250));
 | 
			
		||||
 | 
			
		||||
        bounds.removeFromTop (5);
 | 
			
		||||
        testResultsBox.setBounds (bounds);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void start()
 | 
			
		||||
    {
 | 
			
		||||
        startTest (categoriesBox.getText());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void startTest (const String& category)
 | 
			
		||||
    {
 | 
			
		||||
        testResultsBox.clear();
 | 
			
		||||
        startTestButton.setEnabled (false);
 | 
			
		||||
 | 
			
		||||
        currentTestThread.reset (new TestRunnerThread (*this, category));
 | 
			
		||||
        currentTestThread->startThread();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void stopTest()
 | 
			
		||||
    {
 | 
			
		||||
        if (currentTestThread.get() != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            currentTestThread->stopThread (15000);
 | 
			
		||||
            currentTestThread.reset();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void logMessage (const String& message)
 | 
			
		||||
    {
 | 
			
		||||
        testResultsBox.moveCaretToEnd();
 | 
			
		||||
        testResultsBox.insertTextAtCaret (message + newLine);
 | 
			
		||||
        testResultsBox.moveCaretToEnd();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void testFinished()
 | 
			
		||||
    {
 | 
			
		||||
        stopTest();
 | 
			
		||||
        startTestButton.setEnabled (true);
 | 
			
		||||
        logMessage (newLine + "*** Tests finished ***");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    class TestRunnerThread  : public Thread,
 | 
			
		||||
                              private Timer
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        TestRunnerThread (UnitTestsDemo& utd, const String& ctg)
 | 
			
		||||
            : Thread ("Unit Tests"),
 | 
			
		||||
              owner (utd),
 | 
			
		||||
              category (ctg)
 | 
			
		||||
        {}
 | 
			
		||||
 | 
			
		||||
        void run() override
 | 
			
		||||
        {
 | 
			
		||||
            CustomTestRunner runner (*this);
 | 
			
		||||
 | 
			
		||||
            if (category == "All Tests")
 | 
			
		||||
                runner.runAllTests();
 | 
			
		||||
            else
 | 
			
		||||
                runner.runTestsInCategory (category);
 | 
			
		||||
 | 
			
		||||
            startTimer (50); // when finished, start the timer which will
 | 
			
		||||
                             // wait for the thread to end, then tell our component.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void logMessage (const String& message)
 | 
			
		||||
        {
 | 
			
		||||
            WeakReference<UnitTestsDemo> safeOwner (&owner);
 | 
			
		||||
 | 
			
		||||
            MessageManager::callAsync ([=]
 | 
			
		||||
            {
 | 
			
		||||
                if (auto* o = safeOwner.get())
 | 
			
		||||
                    o->logMessage (message);
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void timerCallback() override
 | 
			
		||||
        {
 | 
			
		||||
            if (! isThreadRunning())
 | 
			
		||||
                owner.testFinished(); // inform the demo page when done, so it can delete this thread.
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        // This subclass of UnitTestRunner is used to redirect the test output to our
 | 
			
		||||
        // TextBox, and to interrupt the running tests when our thread is asked to stop..
 | 
			
		||||
        class CustomTestRunner  : public UnitTestRunner
 | 
			
		||||
        {
 | 
			
		||||
        public:
 | 
			
		||||
            CustomTestRunner (TestRunnerThread& trt)  : owner (trt) {}
 | 
			
		||||
 | 
			
		||||
            void logMessage (const String& message) override
 | 
			
		||||
            {
 | 
			
		||||
                owner.logMessage (message);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool shouldAbortTests() override
 | 
			
		||||
            {
 | 
			
		||||
                return owner.threadShouldExit();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        private:
 | 
			
		||||
            TestRunnerThread& owner;
 | 
			
		||||
 | 
			
		||||
            JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomTestRunner)
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        UnitTestsDemo& owner;
 | 
			
		||||
        const String category;
 | 
			
		||||
 | 
			
		||||
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestRunnerThread)
 | 
			
		||||
    };
 | 
			
		||||
    std::unique_ptr<TestRunnerThread> currentTestThread;
 | 
			
		||||
 | 
			
		||||
    TextButton startTestButton { "Run Unit Tests..." };
 | 
			
		||||
    ComboBox categoriesBox;
 | 
			
		||||
    TextEditor testResultsBox;
 | 
			
		||||
 | 
			
		||||
    void lookAndFeelChanged() override
 | 
			
		||||
    {
 | 
			
		||||
        testResultsBox.applyFontToAllText (testResultsBox.getFont());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_WEAK_REFERENCEABLE (UnitTestsDemo)
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UnitTestsDemo)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										620
									
								
								deps/juce/examples/Utilities/ValueTreesDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										620
									
								
								deps/juce/examples/Utilities/ValueTreesDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,310 +1,310 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             ValueTreesDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Showcases value tree features.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        ValueTreesDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class ValueTreeItem  : public TreeViewItem,
 | 
			
		||||
                       private ValueTree::Listener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    ValueTreeItem (const ValueTree& v, UndoManager& um)
 | 
			
		||||
        : tree (v), undoManager (um)
 | 
			
		||||
    {
 | 
			
		||||
        tree.addListener (this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    String getUniqueName() const override
 | 
			
		||||
    {
 | 
			
		||||
        return tree["name"].toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool mightContainSubItems() override
 | 
			
		||||
    {
 | 
			
		||||
        return tree.getNumChildren() > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paintItem (Graphics& g, int width, int height) override
 | 
			
		||||
    {
 | 
			
		||||
        if (isSelected())
 | 
			
		||||
            g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::highlightedFill,
 | 
			
		||||
                                               Colours::teal));
 | 
			
		||||
 | 
			
		||||
        g.setColour (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::defaultText,
 | 
			
		||||
                                             Colours::black));
 | 
			
		||||
        g.setFont (15.0f);
 | 
			
		||||
 | 
			
		||||
        g.drawText (tree["name"].toString(),
 | 
			
		||||
                    4, 0, width - 4, height,
 | 
			
		||||
                    Justification::centredLeft, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void itemOpennessChanged (bool isNowOpen) override
 | 
			
		||||
    {
 | 
			
		||||
        if (isNowOpen && getNumSubItems() == 0)
 | 
			
		||||
            refreshSubItems();
 | 
			
		||||
        else
 | 
			
		||||
            clearSubItems();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var getDragSourceDescription() override
 | 
			
		||||
    {
 | 
			
		||||
        return "Drag Demo";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
 | 
			
		||||
    {
 | 
			
		||||
        return dragSourceDetails.description == "Drag Demo";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void itemDropped (const DragAndDropTarget::SourceDetails&, int insertIndex) override
 | 
			
		||||
    {
 | 
			
		||||
        OwnedArray<ValueTree> selectedTrees;
 | 
			
		||||
        getSelectedTreeViewItems (*getOwnerView(), selectedTrees);
 | 
			
		||||
 | 
			
		||||
        moveItems (*getOwnerView(), selectedTrees, tree, insertIndex, undoManager);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void moveItems (TreeView& treeView, const OwnedArray<ValueTree>& items,
 | 
			
		||||
                           ValueTree newParent, int insertIndex, UndoManager& undoManager)
 | 
			
		||||
    {
 | 
			
		||||
        if (items.size() > 0)
 | 
			
		||||
        {
 | 
			
		||||
            std::unique_ptr<XmlElement> oldOpenness (treeView.getOpennessState (false));
 | 
			
		||||
 | 
			
		||||
            for (auto* v : items)
 | 
			
		||||
            {
 | 
			
		||||
                if (v->getParent().isValid() && newParent != *v && ! newParent.isAChildOf (*v))
 | 
			
		||||
                {
 | 
			
		||||
                    if (v->getParent() == newParent && newParent.indexOf (*v) < insertIndex)
 | 
			
		||||
                        --insertIndex;
 | 
			
		||||
 | 
			
		||||
                    v->getParent().removeChild (*v, &undoManager);
 | 
			
		||||
                    newParent.addChild (*v, insertIndex, &undoManager);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (oldOpenness.get() != nullptr)
 | 
			
		||||
                treeView.restoreOpennessState (*oldOpenness, false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void getSelectedTreeViewItems (TreeView& treeView, OwnedArray<ValueTree>& items)
 | 
			
		||||
    {
 | 
			
		||||
        auto numSelected = treeView.getNumSelectedItems();
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < numSelected; ++i)
 | 
			
		||||
            if (auto* vti = dynamic_cast<ValueTreeItem*> (treeView.getSelectedItem (i)))
 | 
			
		||||
                items.add (new ValueTree (vti->tree));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    ValueTree tree;
 | 
			
		||||
    UndoManager& undoManager;
 | 
			
		||||
 | 
			
		||||
    void refreshSubItems()
 | 
			
		||||
    {
 | 
			
		||||
        clearSubItems();
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < tree.getNumChildren(); ++i)
 | 
			
		||||
            addSubItem (new ValueTreeItem (tree.getChild (i), undoManager));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void valueTreePropertyChanged (ValueTree&, const Identifier&) override
 | 
			
		||||
    {
 | 
			
		||||
        repaintItem();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override         { treeChildrenChanged (parentTree); }
 | 
			
		||||
    void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override  { treeChildrenChanged (parentTree); }
 | 
			
		||||
    void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override    { treeChildrenChanged (parentTree); }
 | 
			
		||||
    void valueTreeParentChanged (ValueTree&) override {}
 | 
			
		||||
 | 
			
		||||
    void treeChildrenChanged (const ValueTree& parentTree)
 | 
			
		||||
    {
 | 
			
		||||
        if (parentTree == tree)
 | 
			
		||||
        {
 | 
			
		||||
            refreshSubItems();
 | 
			
		||||
            treeHasChanged();
 | 
			
		||||
            setOpen (true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreeItem)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class ValueTreesDemo   : public Component,
 | 
			
		||||
                         public DragAndDropContainer,
 | 
			
		||||
                         private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    ValueTreesDemo()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (tree);
 | 
			
		||||
 | 
			
		||||
        tree.setTitle ("ValueTree");
 | 
			
		||||
        tree.setDefaultOpenness (true);
 | 
			
		||||
        tree.setMultiSelectEnabled (true);
 | 
			
		||||
        rootItem.reset (new ValueTreeItem (createRootValueTree(), undoManager));
 | 
			
		||||
        tree.setRootItem (rootItem.get());
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (undoButton);
 | 
			
		||||
        addAndMakeVisible (redoButton);
 | 
			
		||||
        undoButton.onClick = [this] { undoManager.undo(); };
 | 
			
		||||
        redoButton.onClick = [this] { undoManager.redo(); };
 | 
			
		||||
 | 
			
		||||
        startTimer (500);
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~ValueTreesDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        tree.setRootItem (nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto r = getLocalBounds().reduced (8);
 | 
			
		||||
 | 
			
		||||
        auto buttons = r.removeFromBottom (22);
 | 
			
		||||
        undoButton.setBounds (buttons.removeFromLeft (100));
 | 
			
		||||
        buttons.removeFromLeft (6);
 | 
			
		||||
        redoButton.setBounds (buttons.removeFromLeft (100));
 | 
			
		||||
 | 
			
		||||
        r.removeFromBottom (4);
 | 
			
		||||
        tree.setBounds (r);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static ValueTree createTree (const String& desc)
 | 
			
		||||
    {
 | 
			
		||||
        ValueTree t ("Item");
 | 
			
		||||
        t.setProperty ("name", desc, nullptr);
 | 
			
		||||
        return t;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static ValueTree createRootValueTree()
 | 
			
		||||
    {
 | 
			
		||||
        auto vt = createTree ("This demo displays a ValueTree as a treeview.");
 | 
			
		||||
        vt.appendChild (createTree ("You can drag around the nodes to rearrange them"),               nullptr);
 | 
			
		||||
        vt.appendChild (createTree ("..and press 'delete' or 'backspace' to delete them"),            nullptr);
 | 
			
		||||
        vt.appendChild (createTree ("Then, you can use the undo/redo buttons to undo these changes"), nullptr);
 | 
			
		||||
 | 
			
		||||
        int n = 1;
 | 
			
		||||
        vt.appendChild (createRandomTree (n, 0), nullptr);
 | 
			
		||||
 | 
			
		||||
        return vt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static ValueTree createRandomTree (int& counter, int depth)
 | 
			
		||||
    {
 | 
			
		||||
        auto t = createTree ("Item " + String (counter++));
 | 
			
		||||
 | 
			
		||||
        if (depth < 3)
 | 
			
		||||
            for (int i = 1 + Random::getSystemRandom().nextInt (7); --i >= 0;)
 | 
			
		||||
                t.appendChild (createRandomTree (counter, depth + 1), nullptr);
 | 
			
		||||
 | 
			
		||||
        return t;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void deleteSelectedItems()
 | 
			
		||||
    {
 | 
			
		||||
        OwnedArray<ValueTree> selectedItems;
 | 
			
		||||
        ValueTreeItem::getSelectedTreeViewItems (tree, selectedItems);
 | 
			
		||||
 | 
			
		||||
        for (auto* v : selectedItems)
 | 
			
		||||
        {
 | 
			
		||||
            if (v->getParent().isValid())
 | 
			
		||||
                v->getParent().removeChild (*v, &undoManager);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool keyPressed (const KeyPress& key) override
 | 
			
		||||
    {
 | 
			
		||||
        if (key == KeyPress::deleteKey || key == KeyPress::backspaceKey)
 | 
			
		||||
        {
 | 
			
		||||
            deleteSelectedItems();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (key == KeyPress ('z', ModifierKeys::commandModifier, 0))
 | 
			
		||||
        {
 | 
			
		||||
            undoManager.undo();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (key == KeyPress ('z', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0))
 | 
			
		||||
        {
 | 
			
		||||
            undoManager.redo();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Component::keyPressed (key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    TreeView tree;
 | 
			
		||||
    TextButton undoButton  { "Undo" },
 | 
			
		||||
               redoButton  { "Redo" };
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<ValueTreeItem> rootItem;
 | 
			
		||||
    UndoManager undoManager;
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        undoManager.beginNewTransaction();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreesDemo)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             ValueTreesDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Showcases value tree features.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics
 | 
			
		||||
 exporters:        xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        ValueTreesDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class ValueTreeItem  : public TreeViewItem,
 | 
			
		||||
                       private ValueTree::Listener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    ValueTreeItem (const ValueTree& v, UndoManager& um)
 | 
			
		||||
        : tree (v), undoManager (um)
 | 
			
		||||
    {
 | 
			
		||||
        tree.addListener (this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    String getUniqueName() const override
 | 
			
		||||
    {
 | 
			
		||||
        return tree["name"].toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool mightContainSubItems() override
 | 
			
		||||
    {
 | 
			
		||||
        return tree.getNumChildren() > 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paintItem (Graphics& g, int width, int height) override
 | 
			
		||||
    {
 | 
			
		||||
        if (isSelected())
 | 
			
		||||
            g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::highlightedFill,
 | 
			
		||||
                                               Colours::teal));
 | 
			
		||||
 | 
			
		||||
        g.setColour (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::defaultText,
 | 
			
		||||
                                             Colours::black));
 | 
			
		||||
        g.setFont (15.0f);
 | 
			
		||||
 | 
			
		||||
        g.drawText (tree["name"].toString(),
 | 
			
		||||
                    4, 0, width - 4, height,
 | 
			
		||||
                    Justification::centredLeft, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void itemOpennessChanged (bool isNowOpen) override
 | 
			
		||||
    {
 | 
			
		||||
        if (isNowOpen && getNumSubItems() == 0)
 | 
			
		||||
            refreshSubItems();
 | 
			
		||||
        else
 | 
			
		||||
            clearSubItems();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var getDragSourceDescription() override
 | 
			
		||||
    {
 | 
			
		||||
        return "Drag Demo";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isInterestedInDragSource (const DragAndDropTarget::SourceDetails& dragSourceDetails) override
 | 
			
		||||
    {
 | 
			
		||||
        return dragSourceDetails.description == "Drag Demo";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void itemDropped (const DragAndDropTarget::SourceDetails&, int insertIndex) override
 | 
			
		||||
    {
 | 
			
		||||
        OwnedArray<ValueTree> selectedTrees;
 | 
			
		||||
        getSelectedTreeViewItems (*getOwnerView(), selectedTrees);
 | 
			
		||||
 | 
			
		||||
        moveItems (*getOwnerView(), selectedTrees, tree, insertIndex, undoManager);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void moveItems (TreeView& treeView, const OwnedArray<ValueTree>& items,
 | 
			
		||||
                           ValueTree newParent, int insertIndex, UndoManager& undoManager)
 | 
			
		||||
    {
 | 
			
		||||
        if (items.size() > 0)
 | 
			
		||||
        {
 | 
			
		||||
            std::unique_ptr<XmlElement> oldOpenness (treeView.getOpennessState (false));
 | 
			
		||||
 | 
			
		||||
            for (auto* v : items)
 | 
			
		||||
            {
 | 
			
		||||
                if (v->getParent().isValid() && newParent != *v && ! newParent.isAChildOf (*v))
 | 
			
		||||
                {
 | 
			
		||||
                    if (v->getParent() == newParent && newParent.indexOf (*v) < insertIndex)
 | 
			
		||||
                        --insertIndex;
 | 
			
		||||
 | 
			
		||||
                    v->getParent().removeChild (*v, &undoManager);
 | 
			
		||||
                    newParent.addChild (*v, insertIndex, &undoManager);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (oldOpenness.get() != nullptr)
 | 
			
		||||
                treeView.restoreOpennessState (*oldOpenness, false);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void getSelectedTreeViewItems (TreeView& treeView, OwnedArray<ValueTree>& items)
 | 
			
		||||
    {
 | 
			
		||||
        auto numSelected = treeView.getNumSelectedItems();
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < numSelected; ++i)
 | 
			
		||||
            if (auto* vti = dynamic_cast<ValueTreeItem*> (treeView.getSelectedItem (i)))
 | 
			
		||||
                items.add (new ValueTree (vti->tree));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    ValueTree tree;
 | 
			
		||||
    UndoManager& undoManager;
 | 
			
		||||
 | 
			
		||||
    void refreshSubItems()
 | 
			
		||||
    {
 | 
			
		||||
        clearSubItems();
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < tree.getNumChildren(); ++i)
 | 
			
		||||
            addSubItem (new ValueTreeItem (tree.getChild (i), undoManager));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void valueTreePropertyChanged (ValueTree&, const Identifier&) override
 | 
			
		||||
    {
 | 
			
		||||
        repaintItem();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void valueTreeChildAdded (ValueTree& parentTree, ValueTree&) override         { treeChildrenChanged (parentTree); }
 | 
			
		||||
    void valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int) override  { treeChildrenChanged (parentTree); }
 | 
			
		||||
    void valueTreeChildOrderChanged (ValueTree& parentTree, int, int) override    { treeChildrenChanged (parentTree); }
 | 
			
		||||
    void valueTreeParentChanged (ValueTree&) override {}
 | 
			
		||||
 | 
			
		||||
    void treeChildrenChanged (const ValueTree& parentTree)
 | 
			
		||||
    {
 | 
			
		||||
        if (parentTree == tree)
 | 
			
		||||
        {
 | 
			
		||||
            refreshSubItems();
 | 
			
		||||
            treeHasChanged();
 | 
			
		||||
            setOpen (true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreeItem)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class ValueTreesDemo   : public Component,
 | 
			
		||||
                         public DragAndDropContainer,
 | 
			
		||||
                         private Timer
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    ValueTreesDemo()
 | 
			
		||||
    {
 | 
			
		||||
        addAndMakeVisible (tree);
 | 
			
		||||
 | 
			
		||||
        tree.setTitle ("ValueTree");
 | 
			
		||||
        tree.setDefaultOpenness (true);
 | 
			
		||||
        tree.setMultiSelectEnabled (true);
 | 
			
		||||
        rootItem.reset (new ValueTreeItem (createRootValueTree(), undoManager));
 | 
			
		||||
        tree.setRootItem (rootItem.get());
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (undoButton);
 | 
			
		||||
        addAndMakeVisible (redoButton);
 | 
			
		||||
        undoButton.onClick = [this] { undoManager.undo(); };
 | 
			
		||||
        redoButton.onClick = [this] { undoManager.redo(); };
 | 
			
		||||
 | 
			
		||||
        startTimer (500);
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~ValueTreesDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        tree.setRootItem (nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto r = getLocalBounds().reduced (8);
 | 
			
		||||
 | 
			
		||||
        auto buttons = r.removeFromBottom (22);
 | 
			
		||||
        undoButton.setBounds (buttons.removeFromLeft (100));
 | 
			
		||||
        buttons.removeFromLeft (6);
 | 
			
		||||
        redoButton.setBounds (buttons.removeFromLeft (100));
 | 
			
		||||
 | 
			
		||||
        r.removeFromBottom (4);
 | 
			
		||||
        tree.setBounds (r);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static ValueTree createTree (const String& desc)
 | 
			
		||||
    {
 | 
			
		||||
        ValueTree t ("Item");
 | 
			
		||||
        t.setProperty ("name", desc, nullptr);
 | 
			
		||||
        return t;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static ValueTree createRootValueTree()
 | 
			
		||||
    {
 | 
			
		||||
        auto vt = createTree ("This demo displays a ValueTree as a treeview.");
 | 
			
		||||
        vt.appendChild (createTree ("You can drag around the nodes to rearrange them"),               nullptr);
 | 
			
		||||
        vt.appendChild (createTree ("..and press 'delete' or 'backspace' to delete them"),            nullptr);
 | 
			
		||||
        vt.appendChild (createTree ("Then, you can use the undo/redo buttons to undo these changes"), nullptr);
 | 
			
		||||
 | 
			
		||||
        int n = 1;
 | 
			
		||||
        vt.appendChild (createRandomTree (n, 0), nullptr);
 | 
			
		||||
 | 
			
		||||
        return vt;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static ValueTree createRandomTree (int& counter, int depth)
 | 
			
		||||
    {
 | 
			
		||||
        auto t = createTree ("Item " + String (counter++));
 | 
			
		||||
 | 
			
		||||
        if (depth < 3)
 | 
			
		||||
            for (int i = 1 + Random::getSystemRandom().nextInt (7); --i >= 0;)
 | 
			
		||||
                t.appendChild (createRandomTree (counter, depth + 1), nullptr);
 | 
			
		||||
 | 
			
		||||
        return t;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void deleteSelectedItems()
 | 
			
		||||
    {
 | 
			
		||||
        OwnedArray<ValueTree> selectedItems;
 | 
			
		||||
        ValueTreeItem::getSelectedTreeViewItems (tree, selectedItems);
 | 
			
		||||
 | 
			
		||||
        for (auto* v : selectedItems)
 | 
			
		||||
        {
 | 
			
		||||
            if (v->getParent().isValid())
 | 
			
		||||
                v->getParent().removeChild (*v, &undoManager);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool keyPressed (const KeyPress& key) override
 | 
			
		||||
    {
 | 
			
		||||
        if (key == KeyPress::deleteKey || key == KeyPress::backspaceKey)
 | 
			
		||||
        {
 | 
			
		||||
            deleteSelectedItems();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (key == KeyPress ('z', ModifierKeys::commandModifier, 0))
 | 
			
		||||
        {
 | 
			
		||||
            undoManager.undo();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (key == KeyPress ('z', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0))
 | 
			
		||||
        {
 | 
			
		||||
            undoManager.redo();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Component::keyPressed (key);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    TreeView tree;
 | 
			
		||||
    TextButton undoButton  { "Undo" },
 | 
			
		||||
               redoButton  { "Redo" };
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<ValueTreeItem> rootItem;
 | 
			
		||||
    UndoManager undoManager;
 | 
			
		||||
 | 
			
		||||
    void timerCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        undoManager.beginNewTransaction();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreesDemo)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										800
									
								
								deps/juce/examples/Utilities/XMLandJSONDemo.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										800
									
								
								deps/juce/examples/Utilities/XMLandJSONDemo.h
									
									
									
									
										vendored
									
									
								
							@@ -1,400 +1,400 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             XMLandJSONDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Reads XML and JSON files.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics, juce_gui_extra
 | 
			
		||||
 exporters:        xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        XMLandJSONDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class XmlTreeItem  : public TreeViewItem
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    XmlTreeItem (XmlElement& x)  : xml (x)    {}
 | 
			
		||||
 | 
			
		||||
    String getUniqueName() const override
 | 
			
		||||
    {
 | 
			
		||||
        if (xml.getTagName().isEmpty())
 | 
			
		||||
            return "unknown";
 | 
			
		||||
 | 
			
		||||
        return xml.getTagName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool mightContainSubItems() override
 | 
			
		||||
    {
 | 
			
		||||
        return xml.getFirstChildElement() != nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paintItem (Graphics& g, int width, int height) override
 | 
			
		||||
    {
 | 
			
		||||
        // if this item is selected, fill it with a background colour..
 | 
			
		||||
        if (isSelected())
 | 
			
		||||
            g.fillAll (Colours::blue.withAlpha (0.3f));
 | 
			
		||||
 | 
			
		||||
        // use a "colour" attribute in the xml tag for this node to set the text colour..
 | 
			
		||||
        g.setColour (Colour::fromString (xml.getStringAttribute ("colour", "ff000000")));
 | 
			
		||||
        g.setFont ((float) height * 0.7f);
 | 
			
		||||
 | 
			
		||||
        // draw the xml element's tag name..
 | 
			
		||||
        g.drawText (xml.getTagName(),
 | 
			
		||||
                    4, 0, width - 4, height,
 | 
			
		||||
                    Justification::centredLeft, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void itemOpennessChanged (bool isNowOpen) override
 | 
			
		||||
    {
 | 
			
		||||
        if (isNowOpen)
 | 
			
		||||
        {
 | 
			
		||||
            // if we've not already done so, we'll now add the tree's sub-items. You could
 | 
			
		||||
            // also choose to delete the existing ones and refresh them if that's more suitable
 | 
			
		||||
            // in your app.
 | 
			
		||||
            if (getNumSubItems() == 0)
 | 
			
		||||
            {
 | 
			
		||||
                // create and add sub-items to this node of the tree, corresponding to
 | 
			
		||||
                // each sub-element in the XML..
 | 
			
		||||
 | 
			
		||||
                for (auto* child : xml.getChildIterator())
 | 
			
		||||
                    if (child != nullptr)
 | 
			
		||||
                        addSubItem (new XmlTreeItem (*child));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            // in this case, we'll leave any sub-items in the tree when the node gets closed,
 | 
			
		||||
            // though you could choose to delete them if that's more appropriate for
 | 
			
		||||
            // your application.
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    XmlElement& xml;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XmlTreeItem)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class JsonTreeItem  : public TreeViewItem
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    JsonTreeItem (Identifier i, var value)
 | 
			
		||||
        : identifier (i),
 | 
			
		||||
          json (value)
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    String getUniqueName() const override
 | 
			
		||||
    {
 | 
			
		||||
        return identifier.toString() + "_id";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool mightContainSubItems() override
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* obj = json.getDynamicObject())
 | 
			
		||||
            return obj->getProperties().size() > 0;
 | 
			
		||||
 | 
			
		||||
        return json.isArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paintItem (Graphics& g, int width, int height) override
 | 
			
		||||
    {
 | 
			
		||||
        // if this item is selected, fill it with a background colour..
 | 
			
		||||
        if (isSelected())
 | 
			
		||||
            g.fillAll (Colours::blue.withAlpha (0.3f));
 | 
			
		||||
 | 
			
		||||
        g.setColour (Colours::black);
 | 
			
		||||
        g.setFont ((float) height * 0.7f);
 | 
			
		||||
 | 
			
		||||
        // draw the element's tag name..
 | 
			
		||||
        g.drawText (getText(),
 | 
			
		||||
                    4, 0, width - 4, height,
 | 
			
		||||
                    Justification::centredLeft, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void itemOpennessChanged (bool isNowOpen) override
 | 
			
		||||
    {
 | 
			
		||||
        if (isNowOpen)
 | 
			
		||||
        {
 | 
			
		||||
            // if we've not already done so, we'll now add the tree's sub-items. You could
 | 
			
		||||
            // also choose to delete the existing ones and refresh them if that's more suitable
 | 
			
		||||
            // in your app.
 | 
			
		||||
            if (getNumSubItems() == 0)
 | 
			
		||||
            {
 | 
			
		||||
                // create and add sub-items to this node of the tree, corresponding to
 | 
			
		||||
                // the type of object this var represents
 | 
			
		||||
 | 
			
		||||
                if (json.isArray())
 | 
			
		||||
                {
 | 
			
		||||
                    for (int i = 0; i < json.size(); ++i)
 | 
			
		||||
                    {
 | 
			
		||||
                        auto& child = json[i];
 | 
			
		||||
                        jassert (! child.isVoid());
 | 
			
		||||
                        addSubItem (new JsonTreeItem ({}, child));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (auto* obj = json.getDynamicObject())
 | 
			
		||||
                {
 | 
			
		||||
                    auto& props = obj->getProperties();
 | 
			
		||||
 | 
			
		||||
                    for (int i = 0; i < props.size(); ++i)
 | 
			
		||||
                    {
 | 
			
		||||
                        auto id = props.getName (i);
 | 
			
		||||
 | 
			
		||||
                        auto child = props[id];
 | 
			
		||||
                        jassert (! child.isVoid());
 | 
			
		||||
 | 
			
		||||
                        addSubItem (new JsonTreeItem (id, child));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            // in this case, we'll leave any sub-items in the tree when the node gets closed,
 | 
			
		||||
            // though you could choose to delete them if that's more appropriate for
 | 
			
		||||
            // your application.
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    Identifier identifier;
 | 
			
		||||
    var json;
 | 
			
		||||
 | 
			
		||||
    /** Returns the text to display in the tree.
 | 
			
		||||
        This is a little more complex for JSON than XML as nodes can be strings, objects or arrays.
 | 
			
		||||
     */
 | 
			
		||||
    String getText() const
 | 
			
		||||
    {
 | 
			
		||||
        String text;
 | 
			
		||||
 | 
			
		||||
        if (identifier.isValid())
 | 
			
		||||
            text << identifier.toString();
 | 
			
		||||
 | 
			
		||||
        if (! json.isVoid())
 | 
			
		||||
        {
 | 
			
		||||
            if (text.isNotEmpty() && (! json.isArray()))
 | 
			
		||||
                text << ": ";
 | 
			
		||||
 | 
			
		||||
            if (json.isObject() && (! identifier.isValid()))
 | 
			
		||||
                text << "[Array]";
 | 
			
		||||
            else if (! json.isArray())
 | 
			
		||||
                text << json.toString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return text;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JsonTreeItem)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class XMLandJSONDemo   : public Component,
 | 
			
		||||
                         private CodeDocument::Listener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** The type of database to parse. */
 | 
			
		||||
    enum Type
 | 
			
		||||
    {
 | 
			
		||||
        xml,
 | 
			
		||||
        json
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    XMLandJSONDemo()
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (typeBox);
 | 
			
		||||
        typeBox.addItem ("XML",  1);
 | 
			
		||||
        typeBox.addItem ("JSON", 2);
 | 
			
		||||
 | 
			
		||||
        typeBox.onChange = [this]
 | 
			
		||||
        {
 | 
			
		||||
            if (typeBox.getSelectedId() == 1)
 | 
			
		||||
                reset (xml);
 | 
			
		||||
            else
 | 
			
		||||
                reset (json);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        comboBoxLabel.attachToComponent (&typeBox, true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (codeDocumentComponent);
 | 
			
		||||
        codeDocument.addListener (this);
 | 
			
		||||
 | 
			
		||||
        resultsTree.setTitle ("Results");
 | 
			
		||||
        addAndMakeVisible (resultsTree);
 | 
			
		||||
        resultsTree.setColour (TreeView::backgroundColourId, Colours::white);
 | 
			
		||||
        resultsTree.setDefaultOpenness (true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (errorMessage);
 | 
			
		||||
        errorMessage.setReadOnly (true);
 | 
			
		||||
        errorMessage.setMultiLine (true);
 | 
			
		||||
        errorMessage.setCaretVisible (false);
 | 
			
		||||
        errorMessage.setColour (TextEditor::outlineColourId, Colours::transparentWhite);
 | 
			
		||||
        errorMessage.setColour (TextEditor::shadowColourId,  Colours::transparentWhite);
 | 
			
		||||
 | 
			
		||||
        typeBox.setSelectedId (1);
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~XMLandJSONDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        resultsTree.setRootItem (nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto area = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        typeBox.setBounds (area.removeFromTop (36).removeFromRight (150).reduced (8));
 | 
			
		||||
        codeDocumentComponent.setBounds (area.removeFromTop(area.getHeight() / 2).reduced (8));
 | 
			
		||||
        resultsTree          .setBounds (area.reduced (8));
 | 
			
		||||
        errorMessage         .setBounds (resultsTree.getBounds());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    ComboBox typeBox;
 | 
			
		||||
    Label comboBoxLabel { {}, "Database Type:" };
 | 
			
		||||
    CodeDocument codeDocument;
 | 
			
		||||
    CodeEditorComponent codeDocumentComponent  { codeDocument, nullptr };
 | 
			
		||||
    TreeView resultsTree;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<TreeViewItem> rootItem;
 | 
			
		||||
    std::unique_ptr<XmlElement> parsedXml;
 | 
			
		||||
    TextEditor errorMessage;
 | 
			
		||||
 | 
			
		||||
    void rebuildTree()
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_ptr<XmlElement> openness;
 | 
			
		||||
 | 
			
		||||
        if (rootItem.get() != nullptr)
 | 
			
		||||
            openness = rootItem->getOpennessState();
 | 
			
		||||
 | 
			
		||||
        createNewRootNode();
 | 
			
		||||
 | 
			
		||||
        if (openness.get() != nullptr && rootItem.get() != nullptr)
 | 
			
		||||
            rootItem->restoreOpennessState (*openness);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void createNewRootNode()
 | 
			
		||||
    {
 | 
			
		||||
        // clear the current tree
 | 
			
		||||
        resultsTree.setRootItem (nullptr);
 | 
			
		||||
        rootItem.reset();
 | 
			
		||||
 | 
			
		||||
        // try and parse the editor's contents
 | 
			
		||||
        switch (typeBox.getSelectedItemIndex())
 | 
			
		||||
        {
 | 
			
		||||
            case xml:           rootItem.reset (rebuildXml());        break;
 | 
			
		||||
            case json:          rootItem.reset (rebuildJson());       break;
 | 
			
		||||
            default:            rootItem.reset();                     break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // if we have a valid TreeViewItem hide any old error messages and set our TreeView to use it
 | 
			
		||||
        if (rootItem.get() != nullptr)
 | 
			
		||||
            errorMessage.clear();
 | 
			
		||||
 | 
			
		||||
        errorMessage.setVisible (! errorMessage.isEmpty());
 | 
			
		||||
 | 
			
		||||
        resultsTree.setRootItem (rootItem.get());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Parses the editor's contents as XML. */
 | 
			
		||||
    TreeViewItem* rebuildXml()
 | 
			
		||||
    {
 | 
			
		||||
        parsedXml.reset();
 | 
			
		||||
 | 
			
		||||
        XmlDocument doc (codeDocument.getAllContent());
 | 
			
		||||
        parsedXml = doc.getDocumentElement();
 | 
			
		||||
 | 
			
		||||
        if (parsedXml.get() == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            auto error = doc.getLastParseError();
 | 
			
		||||
 | 
			
		||||
            if (error.isEmpty())
 | 
			
		||||
                error = "Unknown error";
 | 
			
		||||
 | 
			
		||||
            errorMessage.setText ("Error parsing XML: " + error, dontSendNotification);
 | 
			
		||||
 | 
			
		||||
            return nullptr;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new XmlTreeItem (*parsedXml);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Parses the editor's contents as JSON. */
 | 
			
		||||
    TreeViewItem* rebuildJson()
 | 
			
		||||
    {
 | 
			
		||||
        var parsedJson;
 | 
			
		||||
        auto result = JSON::parse (codeDocument.getAllContent(), parsedJson);
 | 
			
		||||
 | 
			
		||||
        if (! result.wasOk())
 | 
			
		||||
        {
 | 
			
		||||
            errorMessage.setText ("Error parsing JSON: " + result.getErrorMessage());
 | 
			
		||||
            return nullptr;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new JsonTreeItem (Identifier(), parsedJson);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Clears the editor and loads some default text. */
 | 
			
		||||
    void reset (Type type)
 | 
			
		||||
    {
 | 
			
		||||
        switch (type)
 | 
			
		||||
        {
 | 
			
		||||
            case xml:   codeDocument.replaceAllContent (loadEntireAssetIntoString ("treedemo.xml")); break;
 | 
			
		||||
            case json:  codeDocument.replaceAllContent (loadEntireAssetIntoString ("juce_module_info")); break;
 | 
			
		||||
            default:    codeDocument.replaceAllContent ({}); break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void codeDocumentTextInserted (const String&, int) override     { rebuildTree(); }
 | 
			
		||||
    void codeDocumentTextDeleted (int, int) override                { rebuildTree(); }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XMLandJSONDemo)
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE examples.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   The code included in this file is provided under the terms of the ISC license
 | 
			
		||||
   http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 | 
			
		||||
   To use, copy, modify, and/or distribute this software for any purpose with or
 | 
			
		||||
   without fee is hereby granted provided that the above copyright notice and
 | 
			
		||||
   this permission notice appear in all copies.
 | 
			
		||||
 | 
			
		||||
   THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
 | 
			
		||||
   WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
 | 
			
		||||
   PURPOSE, ARE DISCLAIMED.
 | 
			
		||||
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 The block below describes the properties of this PIP. A PIP is a short snippet
 | 
			
		||||
 of code that can be read by the Projucer and used to generate a JUCE project.
 | 
			
		||||
 | 
			
		||||
 BEGIN_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
 name:             XMLandJSONDemo
 | 
			
		||||
 version:          1.0.0
 | 
			
		||||
 vendor:           JUCE
 | 
			
		||||
 website:          http://juce.com
 | 
			
		||||
 description:      Reads XML and JSON files.
 | 
			
		||||
 | 
			
		||||
 dependencies:     juce_core, juce_data_structures, juce_events, juce_graphics,
 | 
			
		||||
                   juce_gui_basics, juce_gui_extra
 | 
			
		||||
 exporters:        xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
 | 
			
		||||
 | 
			
		||||
 moduleFlags:      JUCE_STRICT_REFCOUNTEDPOINTER=1
 | 
			
		||||
 | 
			
		||||
 type:             Component
 | 
			
		||||
 mainClass:        XMLandJSONDemo
 | 
			
		||||
 | 
			
		||||
 useLocalCopy:     1
 | 
			
		||||
 | 
			
		||||
 END_JUCE_PIP_METADATA
 | 
			
		||||
 | 
			
		||||
*******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "../Assets/DemoUtilities.h"
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class XmlTreeItem  : public TreeViewItem
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    XmlTreeItem (XmlElement& x)  : xml (x)    {}
 | 
			
		||||
 | 
			
		||||
    String getUniqueName() const override
 | 
			
		||||
    {
 | 
			
		||||
        if (xml.getTagName().isEmpty())
 | 
			
		||||
            return "unknown";
 | 
			
		||||
 | 
			
		||||
        return xml.getTagName();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool mightContainSubItems() override
 | 
			
		||||
    {
 | 
			
		||||
        return xml.getFirstChildElement() != nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paintItem (Graphics& g, int width, int height) override
 | 
			
		||||
    {
 | 
			
		||||
        // if this item is selected, fill it with a background colour..
 | 
			
		||||
        if (isSelected())
 | 
			
		||||
            g.fillAll (Colours::blue.withAlpha (0.3f));
 | 
			
		||||
 | 
			
		||||
        // use a "colour" attribute in the xml tag for this node to set the text colour..
 | 
			
		||||
        g.setColour (Colour::fromString (xml.getStringAttribute ("colour", "ff000000")));
 | 
			
		||||
        g.setFont ((float) height * 0.7f);
 | 
			
		||||
 | 
			
		||||
        // draw the xml element's tag name..
 | 
			
		||||
        g.drawText (xml.getTagName(),
 | 
			
		||||
                    4, 0, width - 4, height,
 | 
			
		||||
                    Justification::centredLeft, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void itemOpennessChanged (bool isNowOpen) override
 | 
			
		||||
    {
 | 
			
		||||
        if (isNowOpen)
 | 
			
		||||
        {
 | 
			
		||||
            // if we've not already done so, we'll now add the tree's sub-items. You could
 | 
			
		||||
            // also choose to delete the existing ones and refresh them if that's more suitable
 | 
			
		||||
            // in your app.
 | 
			
		||||
            if (getNumSubItems() == 0)
 | 
			
		||||
            {
 | 
			
		||||
                // create and add sub-items to this node of the tree, corresponding to
 | 
			
		||||
                // each sub-element in the XML..
 | 
			
		||||
 | 
			
		||||
                for (auto* child : xml.getChildIterator())
 | 
			
		||||
                    if (child != nullptr)
 | 
			
		||||
                        addSubItem (new XmlTreeItem (*child));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            // in this case, we'll leave any sub-items in the tree when the node gets closed,
 | 
			
		||||
            // though you could choose to delete them if that's more appropriate for
 | 
			
		||||
            // your application.
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    XmlElement& xml;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XmlTreeItem)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class JsonTreeItem  : public TreeViewItem
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    JsonTreeItem (Identifier i, var value)
 | 
			
		||||
        : identifier (i),
 | 
			
		||||
          json (value)
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    String getUniqueName() const override
 | 
			
		||||
    {
 | 
			
		||||
        return identifier.toString() + "_id";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool mightContainSubItems() override
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* obj = json.getDynamicObject())
 | 
			
		||||
            return obj->getProperties().size() > 0;
 | 
			
		||||
 | 
			
		||||
        return json.isArray();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paintItem (Graphics& g, int width, int height) override
 | 
			
		||||
    {
 | 
			
		||||
        // if this item is selected, fill it with a background colour..
 | 
			
		||||
        if (isSelected())
 | 
			
		||||
            g.fillAll (Colours::blue.withAlpha (0.3f));
 | 
			
		||||
 | 
			
		||||
        g.setColour (Colours::black);
 | 
			
		||||
        g.setFont ((float) height * 0.7f);
 | 
			
		||||
 | 
			
		||||
        // draw the element's tag name..
 | 
			
		||||
        g.drawText (getText(),
 | 
			
		||||
                    4, 0, width - 4, height,
 | 
			
		||||
                    Justification::centredLeft, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void itemOpennessChanged (bool isNowOpen) override
 | 
			
		||||
    {
 | 
			
		||||
        if (isNowOpen)
 | 
			
		||||
        {
 | 
			
		||||
            // if we've not already done so, we'll now add the tree's sub-items. You could
 | 
			
		||||
            // also choose to delete the existing ones and refresh them if that's more suitable
 | 
			
		||||
            // in your app.
 | 
			
		||||
            if (getNumSubItems() == 0)
 | 
			
		||||
            {
 | 
			
		||||
                // create and add sub-items to this node of the tree, corresponding to
 | 
			
		||||
                // the type of object this var represents
 | 
			
		||||
 | 
			
		||||
                if (json.isArray())
 | 
			
		||||
                {
 | 
			
		||||
                    for (int i = 0; i < json.size(); ++i)
 | 
			
		||||
                    {
 | 
			
		||||
                        auto& child = json[i];
 | 
			
		||||
                        jassert (! child.isVoid());
 | 
			
		||||
                        addSubItem (new JsonTreeItem ({}, child));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else if (auto* obj = json.getDynamicObject())
 | 
			
		||||
                {
 | 
			
		||||
                    auto& props = obj->getProperties();
 | 
			
		||||
 | 
			
		||||
                    for (int i = 0; i < props.size(); ++i)
 | 
			
		||||
                    {
 | 
			
		||||
                        auto id = props.getName (i);
 | 
			
		||||
 | 
			
		||||
                        auto child = props[id];
 | 
			
		||||
                        jassert (! child.isVoid());
 | 
			
		||||
 | 
			
		||||
                        addSubItem (new JsonTreeItem (id, child));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            // in this case, we'll leave any sub-items in the tree when the node gets closed,
 | 
			
		||||
            // though you could choose to delete them if that's more appropriate for
 | 
			
		||||
            // your application.
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    Identifier identifier;
 | 
			
		||||
    var json;
 | 
			
		||||
 | 
			
		||||
    /** Returns the text to display in the tree.
 | 
			
		||||
        This is a little more complex for JSON than XML as nodes can be strings, objects or arrays.
 | 
			
		||||
     */
 | 
			
		||||
    String getText() const
 | 
			
		||||
    {
 | 
			
		||||
        String text;
 | 
			
		||||
 | 
			
		||||
        if (identifier.isValid())
 | 
			
		||||
            text << identifier.toString();
 | 
			
		||||
 | 
			
		||||
        if (! json.isVoid())
 | 
			
		||||
        {
 | 
			
		||||
            if (text.isNotEmpty() && (! json.isArray()))
 | 
			
		||||
                text << ": ";
 | 
			
		||||
 | 
			
		||||
            if (json.isObject() && (! identifier.isValid()))
 | 
			
		||||
                text << "[Array]";
 | 
			
		||||
            else if (! json.isArray())
 | 
			
		||||
                text << json.toString();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return text;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JsonTreeItem)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
class XMLandJSONDemo   : public Component,
 | 
			
		||||
                         private CodeDocument::Listener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    /** The type of database to parse. */
 | 
			
		||||
    enum Type
 | 
			
		||||
    {
 | 
			
		||||
        xml,
 | 
			
		||||
        json
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    XMLandJSONDemo()
 | 
			
		||||
    {
 | 
			
		||||
        setOpaque (true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (typeBox);
 | 
			
		||||
        typeBox.addItem ("XML",  1);
 | 
			
		||||
        typeBox.addItem ("JSON", 2);
 | 
			
		||||
 | 
			
		||||
        typeBox.onChange = [this]
 | 
			
		||||
        {
 | 
			
		||||
            if (typeBox.getSelectedId() == 1)
 | 
			
		||||
                reset (xml);
 | 
			
		||||
            else
 | 
			
		||||
                reset (json);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        comboBoxLabel.attachToComponent (&typeBox, true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (codeDocumentComponent);
 | 
			
		||||
        codeDocument.addListener (this);
 | 
			
		||||
 | 
			
		||||
        resultsTree.setTitle ("Results");
 | 
			
		||||
        addAndMakeVisible (resultsTree);
 | 
			
		||||
        resultsTree.setColour (TreeView::backgroundColourId, Colours::white);
 | 
			
		||||
        resultsTree.setDefaultOpenness (true);
 | 
			
		||||
 | 
			
		||||
        addAndMakeVisible (errorMessage);
 | 
			
		||||
        errorMessage.setReadOnly (true);
 | 
			
		||||
        errorMessage.setMultiLine (true);
 | 
			
		||||
        errorMessage.setCaretVisible (false);
 | 
			
		||||
        errorMessage.setColour (TextEditor::outlineColourId, Colours::transparentWhite);
 | 
			
		||||
        errorMessage.setColour (TextEditor::shadowColourId,  Colours::transparentWhite);
 | 
			
		||||
 | 
			
		||||
        typeBox.setSelectedId (1);
 | 
			
		||||
 | 
			
		||||
        setSize (500, 500);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~XMLandJSONDemo() override
 | 
			
		||||
    {
 | 
			
		||||
        resultsTree.setRootItem (nullptr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void paint (Graphics& g) override
 | 
			
		||||
    {
 | 
			
		||||
        g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void resized() override
 | 
			
		||||
    {
 | 
			
		||||
        auto area = getLocalBounds();
 | 
			
		||||
 | 
			
		||||
        typeBox.setBounds (area.removeFromTop (36).removeFromRight (150).reduced (8));
 | 
			
		||||
        codeDocumentComponent.setBounds (area.removeFromTop(area.getHeight() / 2).reduced (8));
 | 
			
		||||
        resultsTree          .setBounds (area.reduced (8));
 | 
			
		||||
        errorMessage         .setBounds (resultsTree.getBounds());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    ComboBox typeBox;
 | 
			
		||||
    Label comboBoxLabel { {}, "Database Type:" };
 | 
			
		||||
    CodeDocument codeDocument;
 | 
			
		||||
    CodeEditorComponent codeDocumentComponent  { codeDocument, nullptr };
 | 
			
		||||
    TreeView resultsTree;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<TreeViewItem> rootItem;
 | 
			
		||||
    std::unique_ptr<XmlElement> parsedXml;
 | 
			
		||||
    TextEditor errorMessage;
 | 
			
		||||
 | 
			
		||||
    void rebuildTree()
 | 
			
		||||
    {
 | 
			
		||||
        std::unique_ptr<XmlElement> openness;
 | 
			
		||||
 | 
			
		||||
        if (rootItem.get() != nullptr)
 | 
			
		||||
            openness = rootItem->getOpennessState();
 | 
			
		||||
 | 
			
		||||
        createNewRootNode();
 | 
			
		||||
 | 
			
		||||
        if (openness.get() != nullptr && rootItem.get() != nullptr)
 | 
			
		||||
            rootItem->restoreOpennessState (*openness);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void createNewRootNode()
 | 
			
		||||
    {
 | 
			
		||||
        // clear the current tree
 | 
			
		||||
        resultsTree.setRootItem (nullptr);
 | 
			
		||||
        rootItem.reset();
 | 
			
		||||
 | 
			
		||||
        // try and parse the editor's contents
 | 
			
		||||
        switch (typeBox.getSelectedItemIndex())
 | 
			
		||||
        {
 | 
			
		||||
            case xml:           rootItem.reset (rebuildXml());        break;
 | 
			
		||||
            case json:          rootItem.reset (rebuildJson());       break;
 | 
			
		||||
            default:            rootItem.reset();                     break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // if we have a valid TreeViewItem hide any old error messages and set our TreeView to use it
 | 
			
		||||
        if (rootItem.get() != nullptr)
 | 
			
		||||
            errorMessage.clear();
 | 
			
		||||
 | 
			
		||||
        errorMessage.setVisible (! errorMessage.isEmpty());
 | 
			
		||||
 | 
			
		||||
        resultsTree.setRootItem (rootItem.get());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Parses the editor's contents as XML. */
 | 
			
		||||
    TreeViewItem* rebuildXml()
 | 
			
		||||
    {
 | 
			
		||||
        parsedXml.reset();
 | 
			
		||||
 | 
			
		||||
        XmlDocument doc (codeDocument.getAllContent());
 | 
			
		||||
        parsedXml = doc.getDocumentElement();
 | 
			
		||||
 | 
			
		||||
        if (parsedXml.get() == nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            auto error = doc.getLastParseError();
 | 
			
		||||
 | 
			
		||||
            if (error.isEmpty())
 | 
			
		||||
                error = "Unknown error";
 | 
			
		||||
 | 
			
		||||
            errorMessage.setText ("Error parsing XML: " + error, dontSendNotification);
 | 
			
		||||
 | 
			
		||||
            return nullptr;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new XmlTreeItem (*parsedXml);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Parses the editor's contents as JSON. */
 | 
			
		||||
    TreeViewItem* rebuildJson()
 | 
			
		||||
    {
 | 
			
		||||
        var parsedJson;
 | 
			
		||||
        auto result = JSON::parse (codeDocument.getAllContent(), parsedJson);
 | 
			
		||||
 | 
			
		||||
        if (! result.wasOk())
 | 
			
		||||
        {
 | 
			
		||||
            errorMessage.setText ("Error parsing JSON: " + result.getErrorMessage());
 | 
			
		||||
            return nullptr;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new JsonTreeItem (Identifier(), parsedJson);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** Clears the editor and loads some default text. */
 | 
			
		||||
    void reset (Type type)
 | 
			
		||||
    {
 | 
			
		||||
        switch (type)
 | 
			
		||||
        {
 | 
			
		||||
            case xml:   codeDocument.replaceAllContent (loadEntireAssetIntoString ("treedemo.xml")); break;
 | 
			
		||||
            case json:  codeDocument.replaceAllContent (loadEntireAssetIntoString ("juce_module_info")); break;
 | 
			
		||||
            default:    codeDocument.replaceAllContent ({}); break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void codeDocumentTextInserted (const String&, int) override     { rebuildTree(); }
 | 
			
		||||
    void codeDocumentTextDeleted (int, int) override                { rebuildTree(); }
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XMLandJSONDemo)
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user