text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    Creates a dialog box to choose a file or directory to load or save.

    To use a FileChooser:
    - create one (as a local stack variable is the neatest way)
    - call one of its browseFor.. methods
    - if this returns true, the user has selected a file, so you can retrieve it
      with the getResult() method.

    e.g. @code
    void loadMooseFile()
    {
        FileChooser myChooser ("Please select the moose you want to load...",
                               File::getSpecialLocation (File::userHomeDirectory),
                               "*.moose");

        if (myChooser.browseForFileToOpen())
        {
            File mooseFile (myChooser.getResult());

            loadMoose (mooseFile);
        }
    }
    @endcode
*/
class JUCE_API  FileChooser
{
public:
    //==============================================================================
    /** Creates a FileChooser.

        After creating one of these, use one of the browseFor... methods to display it.

        @param dialogBoxTitle                 a text string to display in the dialog box to
                                              tell the user what's going on
        @param initialFileOrDirectory         the file or directory that should be selected
                                              when the dialog box opens. If this parameter is
                                              set to File(), a sensible default directory will
                                              be used instead.
        @param filePatternsAllowed            a set of file patterns to specify which files
                                              can be selected - each pattern should be
                                              separated by a comma or semi-colon, e.g. "*" or
                                              "*.jpg;*.gif". An empty string means that all
                                              files are allowed
        @param useOSNativeDialogBox           if true, then a native dialog box will be used
                                              if possible; if false, then a Juce-based
                                              browser dialog box will always be used
        @param treatFilePackagesAsDirectories if true, then the file chooser will allow the
                                              selection of files inside packages when
                                              invoked on OS X and when using native dialog
                                              boxes.

        @see browseForFileToOpen, browseForFileToSave, browseForDirectory
    */
    FileChooser (const String& dialogBoxTitle,
                 const File& initialFileOrDirectory = File(),
                 const String& filePatternsAllowed = String(),
                 bool useOSNativeDialogBox = true,
                 bool treatFilePackagesAsDirectories = false);

    /** Destructor. */
    ~FileChooser();

    //==============================================================================
    /** Shows a dialog box to choose a file to open.

        This will display the dialog box modally, using an "open file" mode, so that
        it won't allow non-existent files or directories to be chosen.

        @param previewComponent   an optional component to display inside the dialog
                                  box to show special info about the files that the user
                                  is browsing. The component will not be deleted by this
                                  object, so the caller must take care of it.
        @returns    true if the user selected a file, in which case, use the getResult()
                    method to find out what it was. Returns false if they cancelled instead.
        @see browseForFileToSave, browseForDirectory
    */
    bool browseForFileToOpen (FilePreviewComponent* previewComponent = nullptr);

    /** Same as browseForFileToOpen, but allows the user to select multiple files.

        The files that are returned can be obtained by calling getResults(). See
        browseForFileToOpen() for more info about the behaviour of this method.
    */
    bool browseForMultipleFilesToOpen (FilePreviewComponent* previewComponent = nullptr);

    /** Shows a dialog box to choose a file to save.

        This will display the dialog box modally, using an "save file" mode, so it
        will allow non-existent files to be chosen, but not directories.

        @param warnAboutOverwritingExistingFiles     if true, the dialog box will ask
                    the user if they're sure they want to overwrite a file that already
                    exists
        @returns    true if the user chose a file and pressed 'ok', in which case, use
                    the getResult() method to find out what the file was. Returns false
                    if they cancelled instead.
        @see browseForFileToOpen, browseForDirectory
    */
    bool browseForFileToSave (bool warnAboutOverwritingExistingFiles);

    /** Shows a dialog box to choose a directory.

        This will display the dialog box modally, using an "open directory" mode, so it
        will only allow directories to be returned, not files.

        @returns    true if the user chose a directory and pressed 'ok', in which case, use
                    the getResult() method to find out what they chose. Returns false
                    if they cancelled instead.
        @see browseForFileToOpen, browseForFileToSave
    */
    bool browseForDirectory();

    /** Same as browseForFileToOpen, but allows the user to select multiple files and directories.

        The files that are returned can be obtained by calling getResults(). See
        browseForFileToOpen() for more info about the behaviour of this method.
    */
    bool browseForMultipleFilesOrDirectories (FilePreviewComponent* previewComponent = nullptr);

    //==============================================================================
    /** Runs a dialog box for the given set of option flags.
        The flag values used are those in FileBrowserComponent::FileChooserFlags.

        @returns    true if the user chose a directory and pressed 'ok', in which case, use
                    the getResult() method to find out what they chose. Returns false
                    if they cancelled instead.
        @see FileBrowserComponent::FileChooserFlags
    */
    bool showDialog (int flags, FilePreviewComponent* previewComponent);

    //==============================================================================
    /** Returns the last file that was chosen by one of the browseFor methods.

        After calling the appropriate browseFor... method, this method lets you
        find out what file or directory they chose.

        Note that the file returned is only valid if the browse method returned true (i.e.
        if the user pressed 'ok' rather than cancelling).

        If you're using a multiple-file select, then use the getResults() method instead,
        to obtain the list of all files chosen.

        @see getResults
    */
    File getResult() const;

    /** Returns a list of all the files that were chosen during the last call to a
        browse method.

        This array may be empty if no files were chosen, or can contain multiple entries
        if multiple files were chosen.

        @see getResult
    */
    const Array<File>& getResults() const noexcept      { return results; }

private:
    //==============================================================================
    String title, filters;
    const File startingFile;
    Array<File> results;
    const bool useNativeDialogBox;
    const bool treatFilePackagesAsDirs;

    static void showPlatformDialog (Array<File>& results, const String& title, const File& file,
                                    const String& filters, bool selectsDirectories, bool selectsFiles,
                                    bool isSave, bool warnAboutOverwritingExistingFiles, bool selectMultipleFiles,
                                    bool treatFilePackagesAsDirs, FilePreviewComponent* previewComponent);
    static bool isPlatformDialogAvailable();

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooser)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    The Component class uses a ComponentPeer internally to create and manage a real
    operating-system window.

    This is an abstract base class - the platform specific code contains implementations of
    it for the various platforms.

    User-code should very rarely need to have any involvement with this class.

    @see Component::createNewPeer
*/
class JUCE_API  ComponentPeer
{
public:
    //==============================================================================
    /** A combination of these flags is passed to the ComponentPeer constructor. */
    enum StyleFlags
    {
        windowAppearsOnTaskbar      = (1 << 0),    /**< Indicates that the window should have a corresponding
                                                        entry on the taskbar (ignored on MacOSX) */
        windowIsTemporary           = (1 << 1),    /**< Indicates that the window is a temporary popup, like a menu,
                                                        tooltip, etc. */
        windowIgnoresMouseClicks    = (1 << 2),    /**< Indicates that the window should let mouse clicks pass
                                                        through it (may not be possible on some platforms). */
        windowHasTitleBar           = (1 << 3),    /**< Indicates that the window should have a normal OS-specific
                                                        title bar and frame. if not specified, the window will be
                                                        borderless. */
        windowIsResizable           = (1 << 4),    /**< Indicates that the window should have a resizable border. */
        windowHasMinimiseButton     = (1 << 5),    /**< Indicates that if the window has a title bar, it should have a
                                                        minimise button on it. */
        windowHasMaximiseButton     = (1 << 6),    /**< Indicates that if the window has a title bar, it should have a
                                                        maximise button on it. */
        windowHasCloseButton        = (1 << 7),    /**< Indicates that if the window has a title bar, it should have a
                                                        close button on it. */
        windowHasDropShadow         = (1 << 8),    /**< Indicates that the window should have a drop-shadow (this may
                                                        not be possible on all platforms). */
        windowRepaintedExplictly    = (1 << 9),    /**< Not intended for public use - this tells a window not to
                                                        do its own repainting, but only to repaint when the
                                                        performAnyPendingRepaintsNow() method is called. */
        windowIgnoresKeyPresses     = (1 << 10),   /**< Tells the window not to catch any keypresses. This can
                                                        be used for things like plugin windows, to stop them interfering
                                                        with the host's shortcut keys */
        windowIsSemiTransparent     = (1 << 31)    /**< Not intended for public use - makes a window transparent. */

    };

    //==============================================================================
    /** Creates a peer.

        The component is the one that we intend to represent, and the style flags are
        a combination of the values in the StyleFlags enum
    */
    ComponentPeer (Component& component, int styleFlags);

    /** Destructor. */
    virtual ~ComponentPeer();

    //==============================================================================
    /** Returns the component being represented by this peer. */
    Component& getComponent() noexcept                      { return component; }

    /** Returns the set of style flags that were set when the window was created.
        @see Component::addToDesktop
    */
    int getStyleFlags() const noexcept                      { return styleFlags; }

    /** Returns a unique ID for this peer.
        Each peer that is created is given a different ID.
    */
    uint32 getUniqueID() const noexcept                     { return uniqueID; }

    //==============================================================================
    /** Returns the raw handle to whatever kind of window is being used.

        On windows, this is probably a HWND, on the mac, it's likely to be a WindowRef,
        but remember there's no guarantees what you'll get back.
    */
    virtual void* getNativeHandle() const = 0;

    /** Shows or hides the window. */
    virtual void setVisible (bool shouldBeVisible) = 0;

    /** Changes the title of the window. */
    virtual void setTitle (const String& title) = 0;

    /** If this type of window is capable of indicating that the document in it has been
        edited, then this changes its status.

        For example in OSX, this changes the appearance of the close button.
        @returns true if the window has a mechanism for showing this, or false if not.
    */
    virtual bool setDocumentEditedStatus (bool edited);

    /** If this type of window is capable of indicating that it represents a file, then
        this lets you set the file.

        E.g. in OSX it'll show an icon for the file in the title bar.
    */
    virtual void setRepresentedFile (const File&);

    //==============================================================================
    /** Moves and resizes the window.

        If the native window is contained in another window, then the coordinates are
        relative to the parent window's origin, not the screen origin.

        This should result in a callback to handleMovedOrResized().
    */
    virtual void setBounds (const Rectangle<int>& newBounds, bool isNowFullScreen) = 0;

    /** Updates the peer's bounds to match its component. */
    void updateBounds();

    /** Returns the current position and size of the window.

        If the native window is contained in another window, then the coordinates are
        relative to the parent window's origin, not the screen origin.
    */
    virtual Rectangle<int> getBounds() const = 0;

    /** Converts a position relative to the top-left of this component to screen coordinates. */
    virtual Point<float> localToGlobal (Point<float> relativePosition) = 0;

    /** Converts a screen coordinate to a position relative to the top-left of this component. */
    virtual Point<float> globalToLocal (Point<float> screenPosition) = 0;

    /** Converts a position relative to the top-left of this component to screen coordinates. */
    Point<int> localToGlobal (Point<int> relativePosition);

    /** Converts a screen coordinate to a position relative to the top-left of this component. */
    Point<int> globalToLocal (Point<int> screenPosition);

    /** Converts a rectangle relative to the top-left of this component to screen coordinates. */
    virtual Rectangle<int> localToGlobal (const Rectangle<int>& relativePosition);

    /** Converts a screen area to a position relative to the top-left of this component. */
    virtual Rectangle<int> globalToLocal (const Rectangle<int>& screenPosition);

    /** Returns the area in peer coordinates that is covered by the given sub-comp (which
        may be at any depth)
    */
    Rectangle<int> getAreaCoveredBy (Component& subComponent) const;

    /** Minimises the window. */
    virtual void setMinimised (bool shouldBeMinimised) = 0;

    /** True if the window is currently minimised. */
    virtual bool isMinimised() const = 0;

    /** Enable/disable fullscreen mode for the window. */
    virtual void setFullScreen (bool shouldBeFullScreen) = 0;

    /** True if the window is currently full-screen. */
    virtual bool isFullScreen() const = 0;

    /** True if the window is in kiosk-mode. */
    virtual bool isKioskMode() const;

    /** Sets the size to restore to if fullscreen mode is turned off. */
    void setNonFullScreenBounds (const Rectangle<int>& newBounds) noexcept;

    /** Returns the size to restore to if fullscreen mode is turned off. */
    const Rectangle<int>& getNonFullScreenBounds() const noexcept;

    /** Attempts to change the icon associated with this window. */
    virtual void setIcon (const Image& newIcon) = 0;

    /** Sets a constrainer to use if the peer can resize itself.
        The constrainer won't be deleted by this object, so the caller must manage its lifetime.
    */
    void setConstrainer (ComponentBoundsConstrainer* newConstrainer) noexcept;

    /** Returns the current constrainer, if one has been set. */
    ComponentBoundsConstrainer* getConstrainer() const noexcept             { return constrainer; }

    /** Checks if a point is in the window.

        The position is relative to the top-left of this window, in unscaled peer coordinates.
        If trueIfInAChildWindow is false, then this returns false if the point is actually
        inside a child of this window.
    */
    virtual bool contains (Point<int> localPos, bool trueIfInAChildWindow) const = 0;

    /** Returns the size of the window frame that's around this window.
        Whether or not the window has a normal window frame depends on the flags
        that were set when the window was created by Component::addToDesktop()
    */
    virtual BorderSize<int> getFrameSize() const = 0;

    /** This is called when the window's bounds change.
        A peer implementation must call this when the window is moved and resized, so that
        this method can pass the message on to the component.
    */
    void handleMovedOrResized();

    /** This is called if the screen resolution changes.
        A peer implementation must call this if the monitor arrangement changes or the available
        screen size changes.
    */
    virtual void handleScreenSizeChange();

    //==============================================================================
    /** This is called to repaint the component into the given context. */
    void handlePaint (LowLevelGraphicsContext& contextToPaintTo);

    //==============================================================================
    /** Sets this window to either be always-on-top or normal.
        Some kinds of window might not be able to do this, so should return false.
    */
    virtual bool setAlwaysOnTop (bool alwaysOnTop) = 0;

    /** Brings the window to the top, optionally also giving it focus. */
    virtual void toFront (bool makeActive) = 0;

    /** Moves the window to be just behind another one. */
    virtual void toBehind (ComponentPeer* other) = 0;

    /** Called when the window is brought to the front, either by the OS or by a call
        to toFront().
    */
    void handleBroughtToFront();

    //==============================================================================
    /** True if the window has the keyboard focus. */
    virtual bool isFocused() const = 0;

    /** Tries to give the window keyboard focus. */
    virtual void grabFocus() = 0;

    /** Called when the window gains keyboard focus. */
    void handleFocusGain();
    /** Called when the window loses keyboard focus. */
    void handleFocusLoss();

    Component* getLastFocusedSubcomponent() const noexcept;

    /** Called when a key is pressed.
        For keycode info, see the KeyPress class.
        Returns true if the keystroke was used.
    */
    bool handleKeyPress (int keyCode, juce_wchar textCharacter);

    /** Called when a key is pressed.
        Returns true if the keystroke was used.
    */
    bool handleKeyPress (const KeyPress& key);

    /** Called whenever a key is pressed or released.
        Returns true if the keystroke was used.
    */
    bool handleKeyUpOrDown (bool isKeyDown);

    /** Called whenever a modifier key is pressed or released. */
    void handleModifierKeysChange();

    //==============================================================================
    /** Tells the window that text input may be required at the given position.
        This may cause things like a virtual on-screen keyboard to appear, depending
        on the OS.
    */
    virtual void textInputRequired (Point<int> position, TextInputTarget&) = 0;

    /** If there's some kind of OS input-method in progress, this should dismiss it. */
    virtual void dismissPendingTextInput();

    /** Returns the currently focused TextInputTarget, or null if none is found. */
    TextInputTarget* findCurrentTextInputTarget();

    //==============================================================================
    /** Invalidates a region of the window to be repainted asynchronously. */
    virtual void repaint (const Rectangle<int>& area) = 0;

    /** This can be called (from the message thread) to cause the immediate redrawing
        of any areas of this window that need repainting.

        You shouldn't ever really need to use this, it's mainly for special purposes
        like supporting audio plugins where the host's event loop is out of our control.
    */
    virtual void performAnyPendingRepaintsNow() = 0;

    /** Changes the window's transparency. */
    virtual void setAlpha (float newAlpha) = 0;

    //==============================================================================
    void handleMouseEvent (MouseInputSource::InputSourceType type, Point<float> positionWithinPeer, ModifierKeys newMods, float pressure,
                           float orientation, int64 time, PenDetails pen = {}, int touchIndex = 0);

    void handleMouseWheel (MouseInputSource::InputSourceType type, Point<float> positionWithinPeer,
                           int64 time, const MouseWheelDetails&, int touchIndex = 0);

    void handleMagnifyGesture (MouseInputSource::InputSourceType type, Point<float> positionWithinPeer,
                               int64 time, float scaleFactor, int touchIndex = 0);

    void handleUserClosingWindow();

    struct DragInfo
    {
        StringArray files;
        String text;
        Point<int> position;

        bool isEmpty() const noexcept       { return files.size() == 0 && text.isEmpty(); }
        void clear() noexcept               { files.clear(); text.clear(); }
    };

    bool handleDragMove (const DragInfo&);
    bool handleDragExit (const DragInfo&);
    bool handleDragDrop (const DragInfo&);

    //==============================================================================
    /** Returns the number of currently-active peers.
        @see getPeer
    */
    static int getNumPeers() noexcept;

    /** Returns one of the currently-active peers.
        @see getNumPeers
    */
    static ComponentPeer* getPeer (int index) noexcept;

    /** Returns the peer that's attached to the given component, or nullptr if there isn't one. */
    static ComponentPeer* getPeerFor (const Component*) noexcept;

    /** Checks if this peer object is valid.
        @see getNumPeers
    */
    static bool isValidPeer (const ComponentPeer* peer) noexcept;

    //==============================================================================
    virtual StringArray getAvailableRenderingEngines() = 0;
    virtual int getCurrentRenderingEngine() const;
    virtual void setCurrentRenderingEngine (int index);

protected:
    //==============================================================================
    Component& component;
    const int styleFlags;
    Rectangle<int> lastNonFullscreenBounds;
    ComponentBoundsConstrainer* constrainer = nullptr;

private:
    //==============================================================================
    WeakReference<Component> lastFocusedComponent, dragAndDropTargetComponent;
    Component* lastDragAndDropCompUnderMouse = nullptr;
    const uint32 uniqueID;
    bool isWindowMinimised = false;
    Component* getTargetForKeyPress();

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentPeer)
};

} // namespace juce

repo_name: fedden/RenderMan

mime_type: text/x-c++


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

DialogWindow::DialogWindow (const String& name, Colour colour,
                            const bool escapeCloses, const bool onDesktop)
    : DocumentWindow (name, colour, DocumentWindow::closeButton, onDesktop),
      escapeKeyTriggersCloseButton (escapeCloses)
{
}

DialogWindow::~DialogWindow()
{
}

bool DialogWindow::escapeKeyPressed()
{
    if (escapeKeyTriggersCloseButton)
    {
        setVisible (false);
        return true;
    }

    return false;
}

bool DialogWindow::keyPressed (const KeyPress& key)
{
    if (key == KeyPress::escapeKey && escapeKeyPressed())
        return true;

    return DocumentWindow::keyPressed (key);
}

void DialogWindow::resized()
{
    DocumentWindow::resized();

    if (escapeKeyTriggersCloseButton)
    {
        if (auto* close = getCloseButton())
        {
            const KeyPress esc (KeyPress::escapeKey, 0, 0);

            if (! close->isRegisteredForShortcut (esc))
                close->addShortcut (esc);
        }
    }
}

//==============================================================================
class DefaultDialogWindow   : public DialogWindow
{
public:
    DefaultDialogWindow (LaunchOptions& options)
        : DialogWindow (options.dialogTitle, options.dialogBackgroundColour,
                        options.escapeKeyTriggersCloseButton, true)
    {
        setUsingNativeTitleBar (options.useNativeTitleBar);
        setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());

        if (options.content.willDeleteObject())
            setContentOwned (options.content.release(), true);
        else
            setContentNonOwned (options.content.release(), true);

        centreAroundComponent (options.componentToCentreAround, getWidth(), getHeight());
        setResizable (options.resizable, options.useBottomRightCornerResizer);
    }

    void closeButtonPressed() override
    {
        setVisible (false);
    }

private:
    JUCE_DECLARE_NON_COPYABLE (DefaultDialogWindow)
};

DialogWindow::LaunchOptions::LaunchOptions() noexcept {}

DialogWindow* DialogWindow::LaunchOptions::create()
{
    jassert (content != nullptr); // You need to provide some kind of content for the dialog!

    return new DefaultDialogWindow (*this);
}

DialogWindow* DialogWindow::LaunchOptions::launchAsync()
{
    auto* d = create();
    d->enterModalState (true, nullptr, true);
    return d;
}

#if JUCE_MODAL_LOOPS_PERMITTED || DOXYGEN
int DialogWindow::LaunchOptions::runModal()
{
    return launchAsync()->runModalLoop();
}
#endif

//==============================================================================
void DialogWindow::showDialog (const String& dialogTitle,
                               Component* const contentComponent,
                               Component* const componentToCentreAround,
                               Colour backgroundColour,
                               const bool escapeKeyTriggersCloseButton,
                               const bool resizable,
                               const bool useBottomRightCornerResizer)
{
    LaunchOptions o;
    o.dialogTitle = dialogTitle;
    o.content.setNonOwned (contentComponent);
    o.componentToCentreAround = componentToCentreAround;
    o.dialogBackgroundColour = backgroundColour;
    o.escapeKeyTriggersCloseButton = escapeKeyTriggersCloseButton;
    o.useNativeTitleBar = false;
    o.resizable = resizable;
    o.useBottomRightCornerResizer = useBottomRightCornerResizer;

    o.launchAsync();
}

#if JUCE_MODAL_LOOPS_PERMITTED
int DialogWindow::showModalDialog (const String& dialogTitle,
                                   Component* const contentComponent,
                                   Component* const componentToCentreAround,
                                   Colour backgroundColour,
                                   const bool escapeKeyTriggersCloseButton,
                                   const bool resizable,
                                   const bool useBottomRightCornerResizer)
{
    LaunchOptions o;
    o.dialogTitle = dialogTitle;
    o.content.setNonOwned (contentComponent);
    o.componentToCentreAround = componentToCentreAround;
    o.dialogBackgroundColour = backgroundColour;
    o.escapeKeyTriggersCloseButton = escapeKeyTriggersCloseButton;
    o.useNativeTitleBar = false;
    o.resizable = resizable;
    o.useBottomRightCornerResizer = useBottomRightCornerResizer;

    return o.runModal();
}
#endif

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

ResizableWindow::ResizableWindow (const String& name, bool shouldAddToDesktop)
    : TopLevelWindow (name, shouldAddToDesktop)
{
    initialise (shouldAddToDesktop);
}

ResizableWindow::ResizableWindow (const String& name, Colour bkgnd, bool shouldAddToDesktop)
    : TopLevelWindow (name, shouldAddToDesktop)
{
    setBackgroundColour (bkgnd);
    initialise (shouldAddToDesktop);
}

ResizableWindow::~ResizableWindow()
{
    splashScreen.deleteAndZero();

    // Don't delete or remove the resizer components yourself! They're managed by the
    // ResizableWindow, and you should leave them alone! You may have deleted them
    // accidentally by careless use of deleteAllChildren()..?
    jassert (resizableCorner == nullptr || getIndexOfChildComponent (resizableCorner) >= 0);
    jassert (resizableBorder == nullptr || getIndexOfChildComponent (resizableBorder) >= 0);

    resizableCorner = nullptr;
    resizableBorder = nullptr;
    clearContentComponent();

    // have you been adding your own components directly to this window..? tut tut tut.
    // Read the instructions for using a ResizableWindow!
    jassert (getNumChildComponents() == 0);
}

void ResizableWindow::initialise (const bool shouldAddToDesktop)
{
    /*
      ==========================================================================
       In accordance with the terms of the JUCE 5 End-Use License Agreement, the
       JUCE Code in SECTION A cannot be removed, changed or otherwise rendered
       ineffective unless you have a JUCE Indie or Pro license, or are using
       JUCE under the GPL v3 license.

       End User License Agreement: www.juce.com/juce-5-licence
      ==========================================================================
    */

    // BEGIN SECTION A

   #if ! JucePlugin_Build_Standalone
    splashScreen = new JUCESplashScreen (*this);
   #endif

    // END SECTION A

    defaultConstrainer.setMinimumOnscreenAmounts (0x10000, 16, 24, 16);

    lastNonFullScreenPos.setBounds (50, 50, 256, 256);

    if (shouldAddToDesktop)
        addToDesktop();
}

int ResizableWindow::getDesktopWindowStyleFlags() const
{
    int styleFlags = TopLevelWindow::getDesktopWindowStyleFlags();

    if (isResizable() && (styleFlags & ComponentPeer::windowHasTitleBar) != 0)
        styleFlags |= ComponentPeer::windowIsResizable;

    return styleFlags;
}

//==============================================================================
void ResizableWindow::clearContentComponent()
{
    if (ownsContentComponent)
    {
        contentComponent.deleteAndZero();
    }
    else
    {
        removeChildComponent (contentComponent);
        contentComponent = nullptr;
    }
}

void ResizableWindow::setContent (Component* newContentComponent,
                                  const bool takeOwnership,
                                  const bool resizeToFitWhenContentChangesSize)
{
    if (newContentComponent != contentComponent)
    {
        clearContentComponent();

        contentComponent = newContentComponent;
        Component::addAndMakeVisible (contentComponent);
    }

    ownsContentComponent = takeOwnership;
    resizeToFitContent = resizeToFitWhenContentChangesSize;

    if (resizeToFitWhenContentChangesSize)
        childBoundsChanged (contentComponent);

    resized(); // must always be called to position the new content comp
}

void ResizableWindow::setContentOwned (Component* newContentComponent, const bool resizeToFitWhenContentChangesSize)
{
    setContent (newContentComponent, true, resizeToFitWhenContentChangesSize);
}

void ResizableWindow::setContentNonOwned (Component* newContentComponent, const bool resizeToFitWhenContentChangesSize)
{
    setContent (newContentComponent, false, resizeToFitWhenContentChangesSize);
}

void ResizableWindow::setContentComponent (Component* const newContentComponent,
                                           const bool deleteOldOne,
                                           const bool resizeToFitWhenContentChangesSize)
{
    if (newContentComponent != contentComponent)
    {
        if (deleteOldOne)
        {
            contentComponent.deleteAndZero();
        }
        else
        {
            removeChildComponent (contentComponent);
            contentComponent = nullptr;
        }
    }

    setContent (newContentComponent, true, resizeToFitWhenContentChangesSize);
}

void ResizableWindow::setContentComponentSize (int width, int height)
{
    jassert (width > 0 && height > 0); // not a great idea to give it a zero size..

    auto border = getContentComponentBorder();

    setSize (width + border.getLeftAndRight(),
             height + border.getTopAndBottom());
}

BorderSize<int> ResizableWindow::getBorderThickness()
{
    if (isUsingNativeTitleBar() || isKioskMode())
        return {};

    return BorderSize<int> ((resizableBorder != nullptr && ! isFullScreen()) ? 4 : 1);
}

BorderSize<int> ResizableWindow::getContentComponentBorder()
{
    return getBorderThickness();
}

void ResizableWindow::moved()
{
    updateLastPosIfShowing();
}

void ResizableWindow::visibilityChanged()
{
    TopLevelWindow::visibilityChanged();
    updateLastPosIfShowing();
}

void ResizableWindow::resized()
{
    const bool resizerHidden = isFullScreen() || isKioskMode() || isUsingNativeTitleBar();

    if (resizableBorder != nullptr)
    {
        resizableBorder->setVisible (! resizerHidden);
        resizableBorder->setBorderThickness (getBorderThickness());
        resizableBorder->setSize (getWidth(), getHeight());
        resizableBorder->toBack();
    }

    if (resizableCorner != nullptr)
    {
        resizableCorner->setVisible (! resizerHidden);

        const int resizerSize = 18;
        resizableCorner->setBounds (getWidth() - resizerSize,
                                    getHeight() - resizerSize,
                                    resizerSize, resizerSize);
    }

    if (contentComponent != nullptr)
    {
        // The window expects to be able to be able to manage the size and position
        // of its content component, so you can't arbitrarily add a transform to it!
        jassert (! contentComponent->isTransformed());

        contentComponent->setBoundsInset (getContentComponentBorder());
    }

    updateLastPosIfShowing();

   #if JUCE_DEBUG
    hasBeenResized = true;
   #endif
}

void ResizableWindow::childBoundsChanged (Component* child)
{
    if ((child == contentComponent) && (child != nullptr) && resizeToFitContent)
    {
        // not going to look very good if this component has a zero size..
        jassert (child->getWidth() > 0);
        jassert (child->getHeight() > 0);

        auto borders = getContentComponentBorder();

        setSize (child->getWidth() + borders.getLeftAndRight(),
                 child->getHeight() + borders.getTopAndBottom());
    }
}

//==============================================================================
void ResizableWindow::activeWindowStatusChanged()
{
    auto border = getContentComponentBorder();
    auto area = getLocalBounds();

    repaint (area.removeFromTop (border.getTop()));
    repaint (area.removeFromLeft (border.getLeft()));
    repaint (area.removeFromRight (border.getRight()));
    repaint (area.removeFromBottom (border.getBottom()));
}

//==============================================================================
void ResizableWindow::setResizable (const bool shouldBeResizable,
                                    const bool useBottomRightCornerResizer)
{
    if (shouldBeResizable)
    {
        if (useBottomRightCornerResizer)
        {
            resizableBorder = nullptr;

            if (resizableCorner == nullptr)
            {
                Component::addChildComponent (resizableCorner = new ResizableCornerComponent (this, constrainer));
                resizableCorner->setAlwaysOnTop (true);
            }
        }
        else
        {
            resizableCorner = nullptr;

            if (resizableBorder == nullptr)
                Component::addChildComponent (resizableBorder = new ResizableBorderComponent (this, constrainer));
        }
    }
    else
    {
        resizableCorner = nullptr;
        resizableBorder = nullptr;
    }

    if (isUsingNativeTitleBar())
        recreateDesktopWindow();

    childBoundsChanged (contentComponent);
    resized();
}

bool ResizableWindow::isResizable() const noexcept
{
    return resizableCorner != nullptr
        || resizableBorder != nullptr;
}

void ResizableWindow::setResizeLimits (int newMinimumWidth,
                                       int newMinimumHeight,
                                       int newMaximumWidth,
                                       int newMaximumHeight) noexcept
{
    // if you've set up a custom constrainer then these settings won't have any effect..
    jassert (constrainer == &defaultConstrainer || constrainer == nullptr);

    if (constrainer == nullptr)
        setConstrainer (&defaultConstrainer);

    defaultConstrainer.setSizeLimits (newMinimumWidth, newMinimumHeight,
                                      newMaximumWidth, newMaximumHeight);

    setBoundsConstrained (getBounds());
}

void ResizableWindow::setDraggable (bool shouldBeDraggable) noexcept
{
    canDrag = shouldBeDraggable;
}

void ResizableWindow::setConstrainer (ComponentBoundsConstrainer* newConstrainer)
{
    if (constrainer != newConstrainer)
    {
        constrainer = newConstrainer;

        bool useBottomRightCornerResizer = resizableCorner != nullptr;
        bool shouldBeResizable = useBottomRightCornerResizer || resizableBorder != nullptr;

        resizableCorner = nullptr;
        resizableBorder = nullptr;

        setResizable (shouldBeResizable, useBottomRightCornerResizer);
        updatePeerConstrainer();
    }
}

void ResizableWindow::setBoundsConstrained (const Rectangle<int>& newBounds)
{
    if (constrainer != nullptr)
        constrainer->setBoundsForComponent (this, newBounds, false, false, false, false);
    else
        setBounds (newBounds);
}

//==============================================================================
void ResizableWindow::paint (Graphics& g)
{
    auto& lf = getLookAndFeel();

    lf.fillResizableWindowBackground (g, getWidth(), getHeight(),
                                      getBorderThickness(), *this);

    if (! isFullScreen())
        lf.drawResizableWindowBorder (g, getWidth(), getHeight(),
                                      getBorderThickness(), *this);

   #if JUCE_DEBUG
    /* If this fails, then you've probably written a subclass with a resized()
       callback but forgotten to make it call its parent class's resized() method.

       It's important when you override methods like resized(), moved(),
       etc., that you make sure the base class methods also get called.

       Of course you shouldn't really be overriding ResizableWindow::resized() anyway,
       because your content should all be inside the content component - and it's the
       content component's resized() method that you should be using to do your
       layout.
    */
    jassert (hasBeenResized || (getWidth() == 0 && getHeight() == 0));
   #endif
}

void ResizableWindow::lookAndFeelChanged()
{
    resized();

    if (isOnDesktop())
    {
        Component::addToDesktop (getDesktopWindowStyleFlags());
        updatePeerConstrainer();
    }
}

Colour ResizableWindow::getBackgroundColour() const noexcept
{
    return findColour (backgroundColourId, false);
}

void ResizableWindow::setBackgroundColour (Colour newColour)
{
    auto backgroundColour = newColour;

    if (! Desktop::canUseSemiTransparentWindows())
        backgroundColour = newColour.withAlpha (1.0f);

    setColour (backgroundColourId, backgroundColour);
    setOpaque (backgroundColour.isOpaque());
    repaint();
}

//==============================================================================
bool ResizableWindow::isFullScreen() const
{
    if (isOnDesktop())
    {
        auto* peer = getPeer();
        return peer != nullptr && peer->isFullScreen();
    }

    return fullscreen;
}

void ResizableWindow::setFullScreen (const bool shouldBeFullScreen)
{
    if (shouldBeFullScreen != isFullScreen())
    {
        updateLastPosIfShowing();
        fullscreen = shouldBeFullScreen;

        if (isOnDesktop())
        {
            if (auto* peer = getPeer())
            {
                // keep a copy of this intact in case the real one gets messed-up while we're un-maximising
                auto lastPos = lastNonFullScreenPos;

                peer->setFullScreen (shouldBeFullScreen);

                if ((! shouldBeFullScreen) && ! lastPos.isEmpty())
                    setBounds (lastPos);
            }
            else
            {
                jassertfalse;
            }
        }
        else
        {
            if (shouldBeFullScreen)
                setBounds (0, 0, getParentWidth(), getParentHeight());
            else
                setBounds (lastNonFullScreenPos);
        }

        resized();
    }
}

bool ResizableWindow::isMinimised() const
{
    if (auto* peer = getPeer())
        return peer->isMinimised();

    return false;
}

void ResizableWindow::setMinimised (const bool shouldMinimise)
{
    if (shouldMinimise != isMinimised())
    {
        if (auto* peer = getPeer())
        {
            updateLastPosIfShowing();
            peer->setMinimised (shouldMinimise);
        }
        else
        {
            jassertfalse;
        }
    }
}

bool ResizableWindow::isKioskMode() const
{
    if (isOnDesktop())
        if (auto* peer = getPeer())
            return peer->isKioskMode();

    return Desktop::getInstance().getKioskModeComponent() == this;
}

void ResizableWindow::updateLastPosIfShowing()
{
    if (isShowing())
    {
        updateLastPosIfNotFullScreen();
        updatePeerConstrainer();
    }
}

void ResizableWindow::updateLastPosIfNotFullScreen()
{
    if (! (isFullScreen() || isMinimised() || isKioskMode()))
        lastNonFullScreenPos = getBounds();
}

void ResizableWindow::updatePeerConstrainer()
{
    if (isOnDesktop())
        if (auto* peer = getPeer())
            peer->setConstrainer (constrainer);
}

void ResizableWindow::parentSizeChanged()
{
    if (isFullScreen() && getParentComponent() != nullptr)
        setBounds (getParentComponent()->getLocalBounds());
}

//==============================================================================
String ResizableWindow::getWindowStateAsString()
{
    updateLastPosIfShowing();
    return (isFullScreen() && ! isKioskMode() ? "fs " : "") + lastNonFullScreenPos.toString();
}

bool ResizableWindow::restoreWindowStateFromString (const String& s)
{
    StringArray tokens;
    tokens.addTokens (s, false);
    tokens.removeEmptyStrings();
    tokens.trim();

    const bool fs = tokens[0].startsWithIgnoreCase ("fs");
    const int firstCoord = fs ? 1 : 0;

    if (tokens.size() != firstCoord + 4)
        return false;

    Rectangle<int> newPos (tokens[firstCoord].getIntValue(),
                           tokens[firstCoord + 1].getIntValue(),
                           tokens[firstCoord + 2].getIntValue(),
                           tokens[firstCoord + 3].getIntValue());

    if (newPos.isEmpty())
        return false;

    auto* peer = isOnDesktop() ? getPeer() : nullptr;

    if (peer != nullptr)
        peer->getFrameSize().addTo (newPos);

    {
        auto& desktop = Desktop::getInstance();
        auto allMonitors = desktop.getDisplays().getRectangleList (true);
        allMonitors.clipTo (newPos);
        auto onScreenArea = allMonitors.getBounds();

        if (onScreenArea.getWidth() * onScreenArea.getHeight() < 32 * 32)
        {
            auto screen = desktop.getDisplays().getDisplayContaining (newPos.getCentre()).userArea;

            newPos.setSize (jmin (newPos.getWidth(),  screen.getWidth()),
                            jmin (newPos.getHeight(), screen.getHeight()));

            newPos.setPosition (jlimit (screen.getX(), screen.getRight()  - newPos.getWidth(),  newPos.getX()),
                                jlimit (screen.getY(), screen.getBottom() - newPos.getHeight(), newPos.getY()));
        }
    }

    if (peer != nullptr)
    {
        peer->getFrameSize().subtractFrom (newPos);
        peer->setNonFullScreenBounds (newPos);
    }

    updateLastPosIfNotFullScreen();

    if (fs)
        setBoundsConstrained (newPos);

    setFullScreen (fs);

    if (! fs)
        setBoundsConstrained (newPos);

    return true;
}

//==============================================================================
void ResizableWindow::mouseDown (const MouseEvent& e)
{
    if (canDrag && ! isFullScreen())
    {
        dragStarted = true;
        dragger.startDraggingComponent (this, e);
    }
}

void ResizableWindow::mouseDrag (const MouseEvent& e)
{
    if (dragStarted)
        dragger.dragComponent (this, e, constrainer);
}

void ResizableWindow::mouseUp (const MouseEvent&)
{
    dragStarted = false;
}

//==============================================================================
#if JUCE_DEBUG
void ResizableWindow::addChildComponent (Component* const child, int zOrder)
{
    /* Agh! You shouldn't add components directly to a ResizableWindow - this class
       manages its child components automatically, and if you add your own it'll cause
       trouble. Instead, use setContentComponent() to give it a component which
       will be automatically resized and kept in the right place - then you can add
       subcomponents to the content comp. See the notes for the ResizableWindow class
       for more info.

       If you really know what you're doing and want to avoid this assertion, just call
       Component::addChildComponent directly.
    */
    jassertfalse;

    Component::addChildComponent (child, zOrder);
}

void ResizableWindow::addAndMakeVisible (Component* const child, int zOrder)
{
    /* Agh! You shouldn't add components directly to a ResizableWindow - this class
       manages its child components automatically, and if you add your own it'll cause
       trouble. Instead, use setContentComponent() to give it a component which
       will be automatically resized and kept in the right place - then you can add
       subcomponents to the content comp. See the notes for the ResizableWindow class
       for more info.

       If you really know what you're doing and want to avoid this assertion, just call
       Component::addAndMakeVisible directly.
    */
    jassertfalse;

    Component::addAndMakeVisible (child, zOrder);
}
#endif

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

static uint32 lastUniquePeerID = 1;

//==============================================================================
ComponentPeer::ComponentPeer (Component& comp, int flags)
    : component (comp),
      styleFlags (flags),
      uniqueID (lastUniquePeerID += 2) // increment by 2 so that this can never hit 0
{
    Desktop::getInstance().peers.add (this);
}

ComponentPeer::~ComponentPeer()
{
    auto& desktop = Desktop::getInstance();
    desktop.peers.removeFirstMatchingValue (this);
    desktop.triggerFocusCallback();
}

//==============================================================================
int ComponentPeer::getNumPeers() noexcept
{
    return Desktop::getInstance().peers.size();
}

ComponentPeer* ComponentPeer::getPeer (const int index) noexcept
{
    return Desktop::getInstance().peers [index];
}

ComponentPeer* ComponentPeer::getPeerFor (const Component* const component) noexcept
{
    for (auto* peer : Desktop::getInstance().peers)
        if (&(peer->getComponent()) == component)
            return peer;

    return nullptr;
}

bool ComponentPeer::isValidPeer (const ComponentPeer* const peer) noexcept
{
    return Desktop::getInstance().peers.contains (const_cast<ComponentPeer*> (peer));
}

void ComponentPeer::updateBounds()
{
    setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, component.getBoundsInParent()), false);
}

bool ComponentPeer::isKioskMode() const
{
    return Desktop::getInstance().getKioskModeComponent() == &component;
}

//==============================================================================
void ComponentPeer::handleMouseEvent (MouseInputSource::InputSourceType type, Point<float> pos, ModifierKeys newMods,
                                      float newPressure, float newOrientation, int64 time, PenDetails pen, int touchIndex)
{
    if (auto* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (type, touchIndex))
        MouseInputSource (*mouse).handleEvent (*this, pos, time, newMods, newPressure, newOrientation, pen);
}

void ComponentPeer::handleMouseWheel (MouseInputSource::InputSourceType type, Point<float> pos, int64 time, const MouseWheelDetails& wheel, int touchIndex)
{
    if (auto* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (type, touchIndex))
        MouseInputSource (*mouse).handleWheel (*this, pos, time, wheel);
}

void ComponentPeer::handleMagnifyGesture (MouseInputSource::InputSourceType type, Point<float> pos, int64 time, float scaleFactor, int touchIndex)
{
    if (auto* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (type, touchIndex))
        MouseInputSource (*mouse).handleMagnifyGesture (*this, pos, time, scaleFactor);
}

//==============================================================================
void ComponentPeer::handlePaint (LowLevelGraphicsContext& contextToPaintTo)
{
    ModifierKeys::updateCurrentModifiers();

    Graphics g (contextToPaintTo);

    if (component.isTransformed())
        g.addTransform (component.getTransform());

    auto peerBounds = getBounds();

    if (peerBounds.getWidth() != component.getWidth() || peerBounds.getHeight() != component.getHeight())
        // Tweak the scaling so that the component's integer size exactly aligns with the peer's scaled size
        g.addTransform (AffineTransform::scale (peerBounds.getWidth()  / (float) component.getWidth(),
                                                peerBounds.getHeight() / (float) component.getHeight()));

  #if JUCE_ENABLE_REPAINT_DEBUGGING
   #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
    if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
   #endif
    {
        g.saveState();
    }
  #endif

    JUCE_TRY
    {
        component.paintEntireComponent (g, true);
    }
    JUCE_CATCH_EXCEPTION

  #if JUCE_ENABLE_REPAINT_DEBUGGING
   #ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
    if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
   #endif
    {
        // enabling this code will fill all areas that get repainted with a colour overlay, to show
        // clearly when things are being repainted.
        g.restoreState();

        static Random rng;

        g.fillAll (Colour ((uint8) rng.nextInt (255),
                           (uint8) rng.nextInt (255),
                           (uint8) rng.nextInt (255),
                           (uint8) 0x50));
    }
  #endif

    /** If this fails, it's probably be because your CPU floating-point precision mode has
        been set to low.. This setting is sometimes changed by things like Direct3D, and can
        mess up a lot of the calculations that the library needs to do.
    */
    jassert (roundToInt (10.1f) == 10);
}

Component* ComponentPeer::getTargetForKeyPress()
{
    auto* c = Component::getCurrentlyFocusedComponent();

    if (c == nullptr)
        c = &component;

    if (c->isCurrentlyBlockedByAnotherModalComponent())
        if (auto* currentModalComp = Component::getCurrentlyModalComponent())
            c = currentModalComp;

    return c;
}

bool ComponentPeer::handleKeyPress (const int keyCode, const juce_wchar textCharacter)
{
    ModifierKeys::updateCurrentModifiers();

    return handleKeyPress (KeyPress (keyCode,
                                     ModifierKeys::getCurrentModifiers().withoutMouseButtons(),
                                     textCharacter));
}

bool ComponentPeer::handleKeyPress (const KeyPress& keyInfo)
{
    bool keyWasUsed = false;

    for (auto* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
    {
        const WeakReference<Component> deletionChecker (target);

        if (auto* keyListeners = target->keyListeners.get())
        {
            for (int i = keyListeners->size(); --i >= 0;)
            {
                keyWasUsed = keyListeners->getUnchecked(i)->keyPressed (keyInfo, target);

                if (keyWasUsed || deletionChecker == nullptr)
                    return keyWasUsed;

                i = jmin (i, keyListeners->size());
            }
        }

        keyWasUsed = target->keyPressed (keyInfo);

        if (keyWasUsed || deletionChecker == nullptr)
            break;

        if (auto* currentlyFocused = Component::getCurrentlyFocusedComponent())
        {
            const bool isTab      = (keyInfo == KeyPress::tabKey);
            const bool isShiftTab = (keyInfo == KeyPress (KeyPress::tabKey, ModifierKeys::shiftModifier, 0));

            if (isTab || isShiftTab)
            {
                currentlyFocused->moveKeyboardFocusToSibling (isTab);
                keyWasUsed = (currentlyFocused != Component::getCurrentlyFocusedComponent());

                if (keyWasUsed || deletionChecker == nullptr)
                    break;
            }
        }
    }

    return keyWasUsed;
}

bool ComponentPeer::handleKeyUpOrDown (const bool isKeyDown)
{
    ModifierKeys::updateCurrentModifiers();
    bool keyWasUsed = false;

    for (auto* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
    {
        const WeakReference<Component> deletionChecker (target);

        keyWasUsed = target->keyStateChanged (isKeyDown);

        if (keyWasUsed || deletionChecker == nullptr)
            break;

        if (auto* keyListeners = target->keyListeners.get())
        {
            for (int i = keyListeners->size(); --i >= 0;)
            {
                keyWasUsed = keyListeners->getUnchecked(i)->keyStateChanged (isKeyDown, target);

                if (keyWasUsed || deletionChecker == nullptr)
                    return keyWasUsed;

                i = jmin (i, keyListeners->size());
            }
        }
    }

    return keyWasUsed;
}

void ComponentPeer::handleModifierKeysChange()
{
    ModifierKeys::updateCurrentModifiers();

    auto* target = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();

    if (target == nullptr)
        target = Component::getCurrentlyFocusedComponent();

    if (target == nullptr)
        target = &component;

    target->internalModifierKeysChanged();
}

TextInputTarget* ComponentPeer::findCurrentTextInputTarget()
{
    auto* c = Component::getCurrentlyFocusedComponent();

    if (c == &component || component.isParentOf (c))
        if (auto* ti = dynamic_cast<TextInputTarget*> (c))
            if (ti->isTextInputActive())
                return ti;

    return nullptr;
}

void ComponentPeer::dismissPendingTextInput() {}

//==============================================================================
void ComponentPeer::handleBroughtToFront()
{
    ModifierKeys::updateCurrentModifiers();
    component.internalBroughtToFront();
}

void ComponentPeer::setConstrainer (ComponentBoundsConstrainer* const newConstrainer) noexcept
{
    constrainer = newConstrainer;
}

void ComponentPeer::handleMovedOrResized()
{
    ModifierKeys::updateCurrentModifiers();

    const bool nowMinimised = isMinimised();

    if (component.flags.hasHeavyweightPeerFlag && ! nowMinimised)
    {
        const WeakReference<Component> deletionChecker (&component);

        auto newBounds = Component::ComponentHelpers::rawPeerPositionToLocal (component, getBounds());
        auto oldBounds = component.getBounds();

        const bool wasMoved   = (oldBounds.getPosition() != newBounds.getPosition());
        const bool wasResized = (oldBounds.getWidth() != newBounds.getWidth() || oldBounds.getHeight() != newBounds.getHeight());

        if (wasMoved || wasResized)
        {
            component.boundsRelativeToParent = newBounds;

            if (wasResized)
                component.repaint();

            component.sendMovedResizedMessages (wasMoved, wasResized);

            if (deletionChecker == nullptr)
                return;
        }
    }

    if (isWindowMinimised != nowMinimised)
    {
        isWindowMinimised = nowMinimised;
        component.minimisationStateChanged (nowMinimised);
        component.sendVisibilityChangeMessage();
    }

    if (! isFullScreen())
        lastNonFullscreenBounds = component.getBounds();
}

void ComponentPeer::handleFocusGain()
{
    ModifierKeys::updateCurrentModifiers();

    if (component.isParentOf (lastFocusedComponent))
    {
        Component::currentlyFocusedComponent = lastFocusedComponent;
        Desktop::getInstance().triggerFocusCallback();
        lastFocusedComponent->internalFocusGain (Component::focusChangedDirectly);
    }
    else
    {
        if (! component.isCurrentlyBlockedByAnotherModalComponent())
            component.grabKeyboardFocus();
        else
            ModalComponentManager::getInstance()->bringModalComponentsToFront();
    }
}

void ComponentPeer::handleFocusLoss()
{
    ModifierKeys::updateCurrentModifiers();

    if (component.hasKeyboardFocus (true))
    {
        lastFocusedComponent = Component::currentlyFocusedComponent;

        if (lastFocusedComponent != nullptr)
        {
            Component::currentlyFocusedComponent = nullptr;
            Desktop::getInstance().triggerFocusCallback();
            lastFocusedComponent->internalFocusLoss (Component::focusChangedByMouseClick);
        }
    }
}

Component* ComponentPeer::getLastFocusedSubcomponent() const noexcept
{
    return (component.isParentOf (lastFocusedComponent) && lastFocusedComponent->isShowing())
                ? static_cast<Component*> (lastFocusedComponent)
                : &component;
}

void ComponentPeer::handleScreenSizeChange()
{
    ModifierKeys::updateCurrentModifiers();

    component.parentSizeChanged();
    handleMovedOrResized();
}

void ComponentPeer::setNonFullScreenBounds (const Rectangle<int>& newBounds) noexcept
{
    lastNonFullscreenBounds = newBounds;
}

const Rectangle<int>& ComponentPeer::getNonFullScreenBounds() const noexcept
{
    return lastNonFullscreenBounds;
}

Point<int> ComponentPeer::localToGlobal (Point<int> p)   { return localToGlobal (p.toFloat()).roundToInt(); }
Point<int> ComponentPeer::globalToLocal (Point<int> p)   { return globalToLocal (p.toFloat()).roundToInt(); }

Rectangle<int> ComponentPeer::localToGlobal (const Rectangle<int>& relativePosition)
{
    return relativePosition.withPosition (localToGlobal (relativePosition.getPosition()));
}

Rectangle<int> ComponentPeer::globalToLocal (const Rectangle<int>& screenPosition)
{
    return screenPosition.withPosition (globalToLocal (screenPosition.getPosition()));
}

Rectangle<int> ComponentPeer::getAreaCoveredBy (Component& subComponent) const
{
    return ScalingHelpers::scaledScreenPosToUnscaled
            (component, component.getLocalArea (&subComponent, subComponent.getLocalBounds()));
}

//==============================================================================
namespace DragHelpers
{
    static bool isFileDrag (const ComponentPeer::DragInfo& info)
    {
        return info.files.size() > 0;
    }

    static bool isSuitableTarget (const ComponentPeer::DragInfo& info, Component* target)
    {
        return isFileDrag (info) ? dynamic_cast<FileDragAndDropTarget*> (target) != nullptr
                                 : dynamic_cast<TextDragAndDropTarget*> (target) != nullptr;
    }

    static bool isInterested (const ComponentPeer::DragInfo& info, Component* target)
    {
        return isFileDrag (info) ? dynamic_cast<FileDragAndDropTarget*> (target)->isInterestedInFileDrag (info.files)
                                 : dynamic_cast<TextDragAndDropTarget*> (target)->isInterestedInTextDrag (info.text);
    }

    static Component* findDragAndDropTarget (Component* c, const ComponentPeer::DragInfo& info, Component* const lastOne)
    {
        for (; c != nullptr; c = c->getParentComponent())
            if (isSuitableTarget (info, c) && (c == lastOne || isInterested (info, c)))
                return c;

        return nullptr;
    }
}

bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info)
{
    ModifierKeys::updateCurrentModifiers();

    Component* const compUnderMouse = component.getComponentAt (info.position);

    Component* const lastTarget = dragAndDropTargetComponent;
    Component* newTarget = nullptr;

    if (compUnderMouse != lastDragAndDropCompUnderMouse)
    {
        lastDragAndDropCompUnderMouse = compUnderMouse;
        newTarget = DragHelpers::findDragAndDropTarget (compUnderMouse, info, lastTarget);

        if (newTarget != lastTarget)
        {
            if (lastTarget != nullptr)
            {
                if (DragHelpers::isFileDrag (info))
                    dynamic_cast<FileDragAndDropTarget*> (lastTarget)->fileDragExit (info.files);
                else
                    dynamic_cast<TextDragAndDropTarget*> (lastTarget)->textDragExit (info.text);
            }

            dragAndDropTargetComponent = nullptr;

            if (DragHelpers::isSuitableTarget (info, newTarget))
            {
                dragAndDropTargetComponent = newTarget;
                auto pos = newTarget->getLocalPoint (&component, info.position);

                if (DragHelpers::isFileDrag (info))
                    dynamic_cast<FileDragAndDropTarget*> (newTarget)->fileDragEnter (info.files, pos.x, pos.y);
                else
                    dynamic_cast<TextDragAndDropTarget*> (newTarget)->textDragEnter (info.text, pos.x, pos.y);
            }
        }
    }
    else
    {
        newTarget = lastTarget;
    }

    if (! DragHelpers::isSuitableTarget (info, newTarget))
        return false;

    auto pos = newTarget->getLocalPoint (&component, info.position);

    if (DragHelpers::isFileDrag (info))
        dynamic_cast<FileDragAndDropTarget*> (newTarget)->fileDragMove (info.files, pos.x, pos.y);
    else
        dynamic_cast<TextDragAndDropTarget*> (newTarget)->textDragMove (info.text, pos.x, pos.y);

    return true;
}

bool ComponentPeer::handleDragExit (const ComponentPeer::DragInfo& info)
{
    DragInfo info2 (info);
    info2.position.setXY (-1, -1);
    const bool used = handleDragMove (info2);

    jassert (dragAndDropTargetComponent == nullptr);
    lastDragAndDropCompUnderMouse = nullptr;
    return used;
}

bool ComponentPeer::handleDragDrop (const ComponentPeer::DragInfo& info)
{
    handleDragMove (info);

    if (WeakReference<Component> targetComp = dragAndDropTargetComponent)
    {
        dragAndDropTargetComponent = nullptr;
        lastDragAndDropCompUnderMouse = nullptr;

        if (DragHelpers::isSuitableTarget (info, targetComp))
        {
            if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
            {
                targetComp->internalModalInputAttempt();

                if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
                    return true;
            }

            ComponentPeer::DragInfo infoCopy (info);
            infoCopy.position = targetComp->getLocalPoint (&component, info.position);

            // We'll use an async message to deliver the drop, because if the target decides
            // to run a modal loop, it can gum-up the operating system..
            MessageManager::callAsync ([=]()
            {
                if (auto* c = targetComp.get())
                {
                    if (DragHelpers::isFileDrag (info))
                        dynamic_cast<FileDragAndDropTarget*> (c)->filesDropped (infoCopy.files, infoCopy.position.x, infoCopy.position.y);
                    else
                        dynamic_cast<TextDragAndDropTarget*> (c)->textDropped (infoCopy.text, infoCopy.position.x, infoCopy.position.y);
                }
            });

            return true;
        }
    }

    return false;
}

//==============================================================================
void ComponentPeer::handleUserClosingWindow()
{
    ModifierKeys::updateCurrentModifiers();
    component.userTriedToCloseWindow();
}

bool ComponentPeer::setDocumentEditedStatus (bool)
{
    return false;
}

void ComponentPeer::setRepresentedFile (const File&)
{
}

//==============================================================================
int ComponentPeer::getCurrentRenderingEngine() const            { return 0; }
void ComponentPeer::setCurrentRenderingEngine (int index)       { jassert (index == 0); ignoreUnused (index); }

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    A resizable window with a title bar and maximise, minimise and close buttons.

    This subclass of ResizableWindow creates a fairly standard type of window with
    a title bar and various buttons. The name of the component is shown in the
    title bar, and an icon can optionally be specified with setIcon().

    All the methods available to a ResizableWindow are also available to this,
    so it can easily be made resizable, minimised, maximised, etc.

    It's not advisable to add child components directly to a DocumentWindow: put them
    inside your content component instead. And overriding methods like resized(), moved(), etc
    is also not recommended - instead override these methods for your content component.
    (If for some obscure reason you do need to override these methods, always remember to
    call the super-class's resized() method too, otherwise it'll fail to lay out the window
    decorations correctly).

    You can also automatically add a menu bar to the window, using the setMenuBar()
    method.

    @see ResizableWindow, DialogWindow
*/
class JUCE_API  DocumentWindow   : public ResizableWindow
{
public:
    //==============================================================================
    /** The set of available button-types that can be put on the title bar.

        @see setTitleBarButtonsRequired
    */
    enum TitleBarButtons
    {
        minimiseButton = 1,
        maximiseButton = 2,
        closeButton = 4,

        /** A combination of all the buttons above. */
        allButtons = 7
    };

    //==============================================================================
    /** Creates a DocumentWindow.

        @param name             the name to give the component - this is also
                                the title shown at the top of the window. To change
                                this later, use setName()
        @param backgroundColour the colour to use for filling the window's background.
        @param requiredButtons  specifies which of the buttons (close, minimise, maximise)
                                should be shown on the title bar. This value is a bitwise
                                combination of values from the TitleBarButtons enum. Note
                                that it can be "allButtons" to get them all. You
                                can change this later with the setTitleBarButtonsRequired()
                                method, which can also specify where they are positioned.
        @param addToDesktop     if true, the window will be automatically added to the
                                desktop; if false, you can use it as a child component
        @see TitleBarButtons
    */
    DocumentWindow (const String& name,
                    Colour backgroundColour,
                    int requiredButtons,
                    bool addToDesktop = true);

    /** Destructor.
        If a content component has been set with setContentOwned(), it will be deleted.
    */
    ~DocumentWindow();

    //==============================================================================
    /** Changes the component's name.

        (This is overridden from Component::setName() to cause a repaint, as
        the name is what gets drawn across the window's title bar).
    */
    void setName (const String& newName) override;

    /** Sets an icon to show in the title bar, next to the title.

        A copy is made internally of the image, so the caller can delete the
        image after calling this. If an empty Image is passed-in, any existing icon
        will be removed.
    */
    void setIcon (const Image& imageToUse);

    /** Changes the height of the title-bar. */
    void setTitleBarHeight (int newHeight);

    /** Returns the current title bar height. */
    int getTitleBarHeight() const;

    /** Changes the set of title-bar buttons being shown.

        @param requiredButtons  specifies which of the buttons (close, minimise, maximise)
                                should be shown on the title bar. This value is a bitwise
                                combination of values from the TitleBarButtons enum. Note
                                that it can be "allButtons" to get them all.
        @param positionTitleBarButtonsOnLeft    if true, the buttons should go at the
                                left side of the bar; if false, they'll be placed at the right
    */
    void setTitleBarButtonsRequired (int requiredButtons,
                                     bool positionTitleBarButtonsOnLeft);

    /** Sets whether the title should be centred within the window.

        If true, the title text is shown in the middle of the title-bar; if false,
        it'll be shown at the left of the bar.
    */
    void setTitleBarTextCentred (bool textShouldBeCentred);

    //==============================================================================
    /** Creates a menu inside this window.

        @param menuBarModel     this specifies a MenuBarModel that should be used to
                                generate the contents of a menu bar that will be placed
                                just below the title bar, and just above any content
                                component. If this value is a nullptr, any existing menu bar
                                will be removed from the component; if it is not a nullptr,
                                one will be added if it's required.
        @param menuBarHeight    the height of the menu bar component, if one is needed. Pass a value of zero
                                or less to use the look-and-feel's default size.
    */
    void setMenuBar (MenuBarModel* menuBarModel,
                     int menuBarHeight = 0);

    /** Returns the current menu bar component, or null if there isn't one.
        This is probably a MenuBarComponent, unless a custom one has been set using
        setMenuBarComponent().
    */
    Component* getMenuBarComponent() const noexcept;

    /** Replaces the current menu bar with a custom component.
        The component will be owned and deleted by the document window.
    */
    void setMenuBarComponent (Component* newMenuBarComponent);

    //==============================================================================
    /** This method is called when the user tries to close the window.

        This is triggered by the user clicking the close button, or using some other
        OS-specific key shortcut or OS menu for getting rid of a window.

        If the window is just a pop-up, you should override this closeButtonPressed()
        method and make it delete the window in whatever way is appropriate for your
        app. E.g. you might just want to call "delete this".

        If your app is centred around this window such that the whole app should quit when
        the window is closed, then you will probably want to use this method as an opportunity
        to call JUCEApplicationBase::quit(), and leave the window to be deleted later by your
        JUCEApplicationBase::shutdown() method. (Doing it this way means that your window will
        still get cleaned-up if the app is quit by some other means (e.g. a cmd-Q on the mac
        or closing it via the taskbar icon on Windows).

        (Note that the DocumentWindow class overrides Component::userTriedToCloseWindow() and
        redirects it to call this method, so any methods of closing the window that are
        caught by userTriedToCloseWindow() will also end up here).
    */
    virtual void closeButtonPressed();

    /** Callback that is triggered when the minimise button is pressed.

        The default implementation of this calls ResizableWindow::setMinimised(), but
        you can override it to do more customised behaviour.
    */
    virtual void minimiseButtonPressed();

    /** Callback that is triggered when the maximise button is pressed, or when the
        title-bar is double-clicked.

        The default implementation of this calls ResizableWindow::setFullScreen(), but
        you can override it to do more customised behaviour.
    */
    virtual void maximiseButtonPressed();

    //==============================================================================
    /** Returns the close button, (or nullptr if there isn't one). */
    Button* getCloseButton() const noexcept;

    /** Returns the minimise button, (or nullptr if there isn't one). */
    Button* getMinimiseButton() const noexcept;

    /** Returns the maximise button, (or nullptr if there isn't one). */
    Button* getMaximiseButton() const noexcept;

    //==============================================================================
    /** A set of colour IDs to use to change the colour of various aspects of the window.

        These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
        methods.

        @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
    */
    enum ColourIds
    {
        textColourId                = 0x1005701,  /**< The colour to draw any text with. It's up to the look
                                                       and feel class how this is used. */
    };

    //==============================================================================
    /** This abstract base class is implemented by LookAndFeel classes to provide
        window drawing functionality.
    */
    struct JUCE_API  LookAndFeelMethods
    {
        virtual ~LookAndFeelMethods() {}

        virtual void drawDocumentWindowTitleBar (DocumentWindow&,
                                                 Graphics&, int w, int h,
                                                 int titleSpaceX, int titleSpaceW,
                                                 const Image* icon,
                                                 bool drawTitleTextOnLeft) = 0;

        virtual Button* createDocumentWindowButton (int buttonType) = 0;

        virtual void positionDocumentWindowButtons (DocumentWindow&,
                                                    int titleBarX, int titleBarY, int titleBarW, int titleBarH,
                                                    Button* minimiseButton,
                                                    Button* maximiseButton,
                                                    Button* closeButton,
                                                    bool positionTitleBarButtonsOnLeft) = 0;
    };

    //==============================================================================
   #ifndef DOXYGEN
    /** @internal */
    void paint (Graphics&) override;
    /** @internal */
    void resized() override;
    /** @internal */
    void lookAndFeelChanged() override;
    /** @internal */
    BorderSize<int> getBorderThickness() override;
    /** @internal */
    BorderSize<int> getContentComponentBorder() override;
    /** @internal */
    void mouseDoubleClick (const MouseEvent&) override;
    /** @internal */
    void userTriedToCloseWindow() override;
    /** @internal */
    void activeWindowStatusChanged() override;
    /** @internal */
    int getDesktopWindowStyleFlags() const override;
    /** @internal */
    void parentHierarchyChanged() override;
    /** @internal */
    Rectangle<int> getTitleBarArea();
   #endif

private:
    //==============================================================================
    int titleBarHeight = 26, menuBarHeight = 24, requiredButtons;
    bool positionTitleBarButtonsOnLeft, drawTitleTextCentred = true;
    ScopedPointer<Button> titleBarButtons [3];
    Image titleBarIcon;
    ScopedPointer<Component> menuBar;
    MenuBarModel* menuBarModel = nullptr;

    class ButtonListenerProxy;
    friend struct ContainerDeletePolicy<ButtonListenerProxy>;
    ScopedPointer<ButtonListenerProxy> buttonListener;

    void repaintTitleBar();

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DocumentWindow)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    A thread that automatically pops up a modal dialog box with a progress bar
    and cancel button while it's busy running.

    These are handy for performing some sort of task while giving the user feedback
    about how long there is to go, etc.

    E.g. @code
    class MyTask  : public ThreadWithProgressWindow
    {
    public:
        MyTask()    : ThreadWithProgressWindow ("busy...", true, true)
        {
        }

        void run()
        {
            for (int i = 0; i < thingsToDo; ++i)
            {
                // must check this as often as possible, because this is
                // how we know if the user's pressed 'cancel'
                if (threadShouldExit())
                    break;

                // this will update the progress bar on the dialog box
                setProgress (i / (double) thingsToDo);

                //   ... do the business here...
            }
        }
    };

    void doTheTask()
    {
        MyTask m;

        if (m.runThread())
        {
            // thread finished normally..
        }
        else
        {
            // user pressed the cancel button..
        }
    }

    @endcode

    @see Thread, AlertWindow
*/
class JUCE_API  ThreadWithProgressWindow  : public Thread,
                                            private Timer
{
public:
    //==============================================================================
    /** Creates the thread.

        Initially, the dialog box won't be visible, it'll only appear when the
        runThread() method is called.

        @param windowTitle              the title to go at the top of the dialog box
        @param hasProgressBar           whether the dialog box should have a progress bar (see
                                        setProgress() )
        @param hasCancelButton          whether the dialog box should have a cancel button
        @param timeOutMsWhenCancelling  when 'cancel' is pressed, this is how long to wait for
                                        the thread to stop before killing it forcibly (see
                                        Thread::stopThread() )
        @param cancelButtonText         the text that should be shown in the cancel button
                                        (if it has one). Leave this empty for the default "Cancel"
        @param componentToCentreAround  if this is non-null, the window will be positioned
                                        so that it's centred around this component.
    */
    ThreadWithProgressWindow (const String& windowTitle,
                              bool hasProgressBar,
                              bool hasCancelButton,
                              int timeOutMsWhenCancelling = 10000,
                              const String& cancelButtonText = String(),
                              Component* componentToCentreAround = nullptr);

    /** Destructor. */
    ~ThreadWithProgressWindow();

    //==============================================================================
   #if JUCE_MODAL_LOOPS_PERMITTED
    /** Starts the thread and waits for it to finish.

        This will start the thread, make the dialog box appear, and wait until either
        the thread finishes normally, or until the cancel button is pressed.

        Before returning, the dialog box will be hidden.

        @param priority   the priority to use when starting the thread - see
                          Thread::startThread() for values
        @returns true if the thread finished normally; false if the user pressed cancel
    */
    bool runThread (int priority = 5);
   #endif

    /** Starts the thread and returns.

        This will start the thread and make the dialog box appear in a modal state. When
        the thread finishes normally, or the cancel button is pressed, the window will be
        hidden and the threadComplete() method will be called.

        @param priority   the priority to use when starting the thread - see
                          Thread::startThread() for values
    */
    void launchThread (int priority = 5);

    /** The thread should call this periodically to update the position of the progress bar.

        @param newProgress  the progress, from 0.0 to 1.0
        @see setStatusMessage
    */
    void setProgress (double newProgress);

    /** The thread can call this to change the message that's displayed in the dialog box. */
    void setStatusMessage (const String& newStatusMessage);

    /** Returns the AlertWindow that is being used. */
    AlertWindow* getAlertWindow() const noexcept        { return alertWindow; }

    //==============================================================================
    /** This method is called (on the message thread) when the operation has finished.
        You may choose to use this callback to delete the ThreadWithProgressWindow object.
    */
    virtual void threadComplete (bool userPressedCancel);

private:
    //==============================================================================
    void timerCallback() override;

    double progress;
    ScopedPointer<AlertWindow> alertWindow;
    String message;
    CriticalSection messageLock;
    const int timeOutMsWhenCancelling;
    bool wasCancelledByUser;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadWithProgressWindow)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   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 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    A box with a small arrow that can be used as a temporary pop-up window to show
    extra controls when a button or other component is clicked.

    Using one of these is similar to having a popup menu attached to a button or
    other component - but it looks fancier, and has an arrow that can indicate the
    object that it applies to.

    The class works best when shown modally, but obviously running modal loops is
    evil and must never be done, so the launchAsynchronously method is provided as
    a handy way of launching an instance of a CallOutBox and automatically managing
    its lifetime, e.g.

    @code
    void mouseUp (const MouseEvent&)
    {
        FoobarContentComp* content = new FoobarContentComp();
        content->setSize (300, 300);

        CallOutBox& myBox
            = CallOutBox::launchAsynchronously (content, getScreenBounds(), nullptr);
    }
    @endcode

    The call-out will resize and position itself when the content changes size.
*/
class JUCE_API  CallOutBox    : public Component,
                                private Timer
{
public:
    //==============================================================================
    /** Creates a CallOutBox.

        @param contentComponent     the component to display inside the call-out. This should
                                    already have a size set (although the call-out will also
                                    update itself when the component's size is changed later).
                                    Obviously this component must not be deleted until the
                                    call-out box has been deleted.
        @param areaToPointTo        the area that the call-out's arrow should point towards. If
                                    a parentComponent is supplied, then this is relative to that
                                    parent; otherwise, it's a global screen coord.
        @param parentComponent      if not a nullptr, this is the component to add the call-out to.
                                    If this is a nullptr, the call-out will be added to the desktop.
    */
    CallOutBox (Component& contentComponent,
                Rectangle<int> areaToPointTo,
                Component* parentComponent);

    /** Destructor. */
    ~CallOutBox();

    //==============================================================================
    /** Changes the length of the arrow. */
    void setArrowSize (float newSize);

    /** Updates the position and size of the box.

        You shouldn't normally need to call this, unless you need more precise control over the
        layout.

        @param newAreaToPointTo     the rectangle to make the box's arrow point to
        @param newAreaToFitIn       the area within which the box's position should be constrained
    */
    void updatePosition (const Rectangle<int>& newAreaToPointTo,
                         const Rectangle<int>& newAreaToFitIn);

    /** This will launch a callout box containing the given content, pointing to the
        specified target component.

        This method will create and display a callout, returning immediately, after which
        the box will continue to run modally until the user clicks on some other component, at
        which point it will be dismissed and deleted automatically.

        It returns a reference to the newly-created box so that you can customise it, but don't
        keep a pointer to it, as it'll be deleted at some point when it gets closed.

        @param contentComponent     the component to display inside the call-out. This should
                                    already have a size set (although the call-out will also
                                    update itself when the component's size is changed later).
                                    This component will be owned by the callout box and deleted
                                    later when the box is dismissed.
        @param areaToPointTo        the area that the call-out's arrow should point towards. If
                                    a parentComponent is supplied, then this is relative to that
                                    parent; otherwise, it's a global screen coord.
        @param parentComponent      if not a nullptr, this is the component to add the call-out to.
                                    If this is a nullptr, the call-out will be added to the desktop.
    */
    static CallOutBox& launchAsynchronously (Component* contentComponent,
                                             Rectangle<int> areaToPointTo,
                                             Component* parentComponent);

    /** Posts a message which will dismiss the callout box asynchronously.
        NB: it's safe to call this method from any thread.
    */
    void dismiss();

    /** Determines whether the mouse events for clicks outside the calloutbox are
        consumed, or allowed to arrive at the other component that they were aimed at.

        By default this is false, so that when you click on something outside the calloutbox,
        that event will also be sent to the component that was clicked on. If you set it to
        true, then the first click will always just dismiss the box and not be sent to
        anything else.
    */
    void setDismissalMouseClicksAreAlwaysConsumed (bool shouldAlwaysBeConsumed) noexcept;

    //==============================================================================
    /** This abstract base class is implemented by LookAndFeel classes. */
    struct JUCE_API  LookAndFeelMethods
    {
        virtual ~LookAndFeelMethods() {}

        virtual void drawCallOutBoxBackground (CallOutBox&, Graphics&, const Path&, Image&) = 0;
        virtual int getCallOutBoxBorderSize (const CallOutBox&) = 0;
    };

    //==============================================================================
    /** @internal */
    void paint (Graphics&) override;
    /** @internal */
    void resized() override;
    /** @internal */
    void moved() override;
    /** @internal */
    void childBoundsChanged (Component*) override;
    /** @internal */
    bool hitTest (int x, int y) override;
    /** @internal */
    void inputAttemptWhenModal() override;
    /** @internal */
    bool keyPressed (const KeyPress&) override;
    /** @internal */
    void handleCommandMessage (int) override;
    /** @internal */
    int getBorderSize() const noexcept;

private:
    //==============================================================================
    Component& content;
    Path outline;
    Point<float> targetPoint;
    Rectangle<int> availableArea, targetArea;
    Image background;
    float arrowSize = 16.0f;
    bool dismissalMouseClicksAreAlwaysConsumed = false;

    Time creationTime;

    void refreshPath();
    void timerCallback() override;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallOutBox)
};

} // namespace juce