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 and displays a popup-menu.

    To show a popup-menu, you create one of these, add some items to it, then
    call its show() method, which returns the id of the item the user selects.

    E.g. @code
    void MyWidget::mouseDown (const MouseEvent& e)
    {
        PopupMenu m;
        m.addItem (1, "item 1");
        m.addItem (2, "item 2");

        const int result = m.show();

        if (result == 0)
        {
            // user dismissed the menu without picking anything
        }
        else if (result == 1)
        {
            // user picked item 1
        }
        else if (result == 2)
        {
            // user picked item 2
        }
    }
    @endcode

    Submenus are easy too: @code

    void MyWidget::mouseDown (const MouseEvent& e)
    {
        PopupMenu subMenu;
        subMenu.addItem (1, "item 1");
        subMenu.addItem (2, "item 2");

        PopupMenu mainMenu;
        mainMenu.addItem (3, "item 3");
        mainMenu.addSubMenu ("other choices", subMenu);

        const int result = m.show();

        ...etc
    }
    @endcode
*/
class JUCE_API  PopupMenu
{
private:
    class Window;

public:
    class CustomComponent;
    class CustomCallback;

    //==============================================================================
    /** Creates an empty popup menu. */
    PopupMenu();

    /** Creates a copy of another menu. */
    PopupMenu (const PopupMenu&);

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

    /** Copies this menu from another one. */
    PopupMenu& operator= (const PopupMenu&);

    /** Move constructor */
    PopupMenu (PopupMenu&&) noexcept;

    /** Move assignment operator */
    PopupMenu& operator= (PopupMenu&&) noexcept;

    //==============================================================================
    /** Resets the menu, removing all its items. */
    void clear();

    /** Describes a popup menu item. */
    struct JUCE_API  Item
    {
        /** Creates a null item.
            You'll need to set some fields after creating an Item before you
            can add it to a PopupMenu
        */
        Item() noexcept;

        /** Creates a copy of an item. */
        Item (const Item&);

        /** Creates a copy of an item. */
        Item& operator= (const Item&);

        /** The menu item's name. */
        String text;

        /** The menu item's ID. This must not be 0 if you want the item to be triggerable! */
        int itemID = 0;

        /** A sub-menu, or nullptr if there isn't one. */
        ScopedPointer<PopupMenu> subMenu;

        /** A drawable to use as an icon, or nullptr if there isn't one. */
        ScopedPointer<Drawable> image;

        /** A custom component for the item to display, or nullptr if there isn't one. */
        ReferenceCountedObjectPtr<CustomComponent> customComponent;

        /** A custom callback for the item to use, or nullptr if there isn't one. */
        ReferenceCountedObjectPtr<CustomCallback> customCallback;

        /** A command manager to use to automatically invoke the command, or nullptr if none is specified. */
        ApplicationCommandManager* commandManager = nullptr;

        /** An optional string describing the shortcut key for this item.
            This is only used for displaying at the right-hand edge of a menu item - the
            menu won't attempt to actually catch or process the key. If you supply a
            commandManager parameter then the menu will attempt to fill-in this field
            automatically.
        */
        String shortcutKeyDescription;

        /** A colour to use to draw the menu text.
            By default this is transparent black, which means that the LookAndFeel should choose the colour.
        */
        Colour colour;

        /** True if this menu item is enabled. */
        bool isEnabled = true;

        /** True if this menu item should have a tick mark next to it. */
        bool isTicked = false;

        /** True if this menu item is a separator line. */
        bool isSeparator = false;

        /** True if this menu item is a section header. */
        bool isSectionHeader = false;
    };

    /** Adds an item to the menu.
        You can call this method for full control over the item that is added, or use the other
        addItem helper methods if you want to pass arguments rather than creating an Item object.
    */
    void addItem (const Item& newItem);

    /** Appends a new text item for this menu to show.

        @param itemResultID     the number that will be returned from the show() method
                                if the user picks this item. The value should never be
                                zero, because that's used to indicate that the user didn't
                                select anything.
        @param itemText         the text to show.
        @param isEnabled        if false, the item will be shown 'greyed-out' and can't be picked
        @param isTicked         if true, the item will be shown with a tick next to it

        @see addSeparator, addColouredItem, addCustomItem, addSubMenu
    */
    void addItem (int itemResultID,
                  const String& itemText,
                  bool isEnabled = true,
                  bool isTicked = false);

    /** Appends a new item with an icon.

        @param itemResultID     the number that will be returned from the show() method
                                if the user picks this item. The value should never be
                                zero, because that's used to indicate that the user didn't
                                select anything.
        @param itemText         the text to show.
        @param isEnabled        if false, the item will be shown 'greyed-out' and can't be picked
        @param isTicked         if true, the item will be shown with a tick next to it
        @param iconToUse        if this is a valid image, it will be displayed to the left of the item.

        @see addSeparator, addColouredItem, addCustomItem, addSubMenu
    */
    void addItem (int itemResultID,
                  const String& itemText,
                  bool isEnabled,
                  bool isTicked,
                  const Image& iconToUse);

    /** Appends a new item with an icon.

        @param itemResultID     the number that will be returned from the show() method
                                if the user picks this item. The value should never be
                                zero, because that's used to indicate that the user didn't
                                select anything.
        @param itemText         the text to show.
        @param isEnabled        if false, the item will be shown 'greyed-out' and can't be picked
        @param isTicked         if true, the item will be shown with a tick next to it
        @param iconToUse        a Drawable object to use as the icon to the left of the item.
                                The menu will take ownership of this drawable object and will
                                delete it later when no longer needed
        @see addSeparator, addColouredItem, addCustomItem, addSubMenu
    */
    void addItem (int itemResultID,
                  const String& itemText,
                  bool isEnabled,
                  bool isTicked,
                  Drawable* iconToUse);

    /** Adds an item that represents one of the commands in a command manager object.

        @param commandManager       the manager to use to trigger the command and get information
                                    about it
        @param commandID            the ID of the command
        @param displayName          if this is non-empty, then this string will be used instead of
                                    the command's registered name
        @param iconToUse            an optional Drawable object to use as the icon to the left of the item.
                                    The menu will take ownership of this drawable object and will
                                    delete it later when no longer needed
    */
    void addCommandItem (ApplicationCommandManager* commandManager,
                         CommandID commandID,
                         const String& displayName = String(),
                         Drawable* iconToUse = nullptr);

    /** Appends a text item with a special colour.

        This is the same as addItem(), but specifies a colour to use for the
        text, which will override the default colours that are used by the
        current look-and-feel. See addItem() for a description of the parameters.
    */
    void addColouredItem (int itemResultID,
                          const String& itemText,
                          Colour itemTextColour,
                          bool isEnabled = true,
                          bool isTicked = false,
                          const Image& iconToUse = Image());

    /** Appends a text item with a special colour.

        This is the same as addItem(), but specifies a colour to use for the
        text, which will override the default colours that are used by the
        current look-and-feel. See addItem() for a description of the parameters.
    */
    void addColouredItem (int itemResultID,
                          const String& itemText,
                          Colour itemTextColour,
                          bool isEnabled,
                          bool isTicked,
                          Drawable* iconToUse);

    /** Appends a custom menu item.

        This will add a user-defined component to use as a menu item. The component
        passed in will be deleted by this menu when it's no longer needed.

        Note that native macOS menus do not support custom components.

        @see CustomComponent
    */
    void addCustomItem (int itemResultID,
                        CustomComponent* customComponent,
                        const PopupMenu* optionalSubMenu = nullptr);

    /** Appends a custom menu item that can't be used to trigger a result.

        This will add a user-defined component to use as a menu item.
        It's the caller's responsibility to delete the component that is passed-in
        when it's no longer needed after the menu has been hidden.

        If triggerMenuItemAutomaticallyWhenClicked is true, the menu itself will handle
        detection of a mouse-click on your component, and use that to trigger the
        menu ID specified in itemResultID. If this is false, the menu item can't
        be triggered, so itemResultID is not used.

        Note that native macOS menus do support custom components.
    */
    void addCustomItem (int itemResultID,
                        Component* customComponent,
                        int idealWidth,
                        int idealHeight,
                        bool triggerMenuItemAutomaticallyWhenClicked,
                        const PopupMenu* optionalSubMenu = nullptr);

    /** Appends a sub-menu.

        If the menu that's passed in is empty, it will appear as an inactive item.
        If the itemResultID argument is non-zero, then the sub-menu item itself can be
        clicked to trigger it as a command.
    */
    void addSubMenu (const String& subMenuName,
                     const PopupMenu& subMenu,
                     bool isEnabled = true);

    /** Appends a sub-menu with an icon.

        If the menu that's passed in is empty, it will appear as an inactive item.
        If the itemResultID argument is non-zero, then the sub-menu item itself can be
        clicked to trigger it as a command.
    */
    void addSubMenu (const String& subMenuName,
                     const PopupMenu& subMenu,
                     bool isEnabled,
                     const Image& iconToUse,
                     bool isTicked = false,
                     int itemResultID = 0);

    /** Appends a sub-menu with an icon.

        If the menu that's passed in is empty, it will appear as an inactive item.
        If the itemResultID argument is non-zero, then the sub-menu item itself can be
        clicked to trigger it as a command.

        The iconToUse parameter is a Drawable object to use as the icon to the left of
        the item. The menu will take ownership of this drawable object and will delete it
        later when no longer needed
    */
    void addSubMenu (const String& subMenuName,
                     const PopupMenu& subMenu,
                     bool isEnabled,
                     Drawable* iconToUse,
                     bool isTicked = false,
                     int itemResultID = 0);

    /** Appends a separator to the menu, to help break it up into sections.
        The menu class is smart enough not to display separators at the top or bottom
        of the menu, and it will replace mutliple adjacent separators with a single
        one, so your code can be quite free and easy about adding these, and it'll
        always look ok.
    */
    void addSeparator();

    /** Adds a non-clickable text item to the menu.
        This is a bold-font items which can be used as a header to separate the items
        into named groups.
    */
    void addSectionHeader (const String& title);

    /** Returns the number of items that the menu currently contains.
        (This doesn't count separators).
    */
    int getNumItems() const noexcept;

    /** Returns true if the menu contains a command item that triggers the given command. */
    bool containsCommandItem (int commandID) const;

    /** Returns true if the menu contains any items that can be used. */
    bool containsAnyActiveItems() const noexcept;

    //==============================================================================
    /** Class used to create a set of options to pass to the show() method.
        You can chain together a series of calls to this class's methods to create
        a set of whatever options you want to specify.
        E.g. @code
        PopupMenu menu;
        ...
        menu.showMenu (PopupMenu::Options().withMinimumWidth (100)
                                           .withMaximumNumColumns (3)
                                           .withTargetComponent (myComp));
        @endcode
    */
    class JUCE_API  Options
    {
    public:
        Options();
        Options (const Options&) = default;
        Options& operator= (const Options&) = default;

        //==============================================================================
        Options withTargetComponent (Component* targetComponent) const noexcept;
        Options withTargetScreenArea (Rectangle<int> targetArea) const noexcept;
        Options withMinimumWidth (int minWidth) const noexcept;
        Options withMinimumNumColumns (int minNumColumns) const noexcept;
        Options withMaximumNumColumns (int maxNumColumns) const noexcept;
        Options withStandardItemHeight (int standardHeight) const noexcept;
        Options withItemThatMustBeVisible (int idOfItemToBeVisible) const noexcept;
        Options withParentComponent (Component* parentComponent) const noexcept;

        //==============================================================================
        Component* getParentComponent() const noexcept          { return parentComponent; }
        Component* getTargetComponent() const noexcept          { return targetComponent; }
        Rectangle<int> getTargetScreenArea() const noexcept     { return targetArea; }
        int getMinimumWidth() const noexcept                    { return minWidth; }
        int getMaximumNumColumns() const noexcept               { return maxColumns; }
        int getMinimumNumColumns() const noexcept               { return minColumns; }
        int getStandardItemHeight() const noexcept              { return standardHeight; }
        int getItemThatMustBeVisible() const noexcept           { return visibleItemID; }

    private:
        //==============================================================================
        Rectangle<int> targetArea;
        Component* targetComponent = nullptr;
        Component* parentComponent = nullptr;
        int visibleItemID = 0, minWidth = 0, minColumns = 1, maxColumns = 0, standardHeight = 0;
    };

    //==============================================================================
   #if JUCE_MODAL_LOOPS_PERMITTED
    /** Displays the menu and waits for the user to pick something.

        This will display the menu modally, and return the ID of the item that the
        user picks. If they click somewhere off the menu to get rid of it without
        choosing anything, this will return 0.

        The current location of the mouse will be used as the position to show the
        menu - to explicitly set the menu's position, use showAt() instead. Depending
        on where this point is on the screen, the menu will appear above, below or
        to the side of the point.

        @param itemIDThatMustBeVisible  if you set this to the ID of one of the menu items,
                                        then when the menu first appears, it will make sure
                                        that this item is visible. So if the menu has too many
                                        items to fit on the screen, it will be scrolled to a
                                        position where this item is visible.
        @param minimumWidth             a minimum width for the menu, in pixels. It may be wider
                                        than this if some items are too long to fit.
        @param maximumNumColumns        if there are too many items to fit on-screen in a single
                                        vertical column, the menu may be laid out as a series of
                                        columns - this is the maximum number allowed. To use the
                                        default value for this (probably about 7), you can pass
                                        in zero.
        @param standardItemHeight       if this is non-zero, it will be used as the standard
                                        height for menu items (apart from custom items)
        @param callback                 if this is not a nullptr, the menu will be launched
                                        asynchronously, returning immediately, and the callback
                                        will receive a call when the menu is either dismissed or
                                        has an item selected. This object will be owned and
                                        deleted by the system, so make sure that it works safely
                                        and that any pointers that it uses are safely within scope.
        @see showAt
    */
    int show (int itemIDThatMustBeVisible = 0,
              int minimumWidth = 0,
              int maximumNumColumns = 0,
              int standardItemHeight = 0,
              ModalComponentManager::Callback* callback = nullptr);

    /** Displays the menu at a specific location.

        This is the same as show(), but uses a specific location (in global screen
        coordinates) rather than the current mouse position.

        The screenAreaToAttachTo parameter indicates a screen area to which the menu
        will be adjacent. Depending on where this is, the menu will decide which edge to
        attach itself to, in order to fit itself fully on-screen. If you just want to
        trigger a menu at a specific point, you can pass in a rectangle of size (0, 0)
        with the position that you want.

        @see show()
    */
    int showAt (Rectangle<int> screenAreaToAttachTo,
                int itemIDThatMustBeVisible = 0,
                int minimumWidth = 0,
                int maximumNumColumns = 0,
                int standardItemHeight = 0,
                ModalComponentManager::Callback* callback = nullptr);

    /** Displays the menu as if it's attached to a component such as a button.

        This is similar to showAt(), but will position it next to the given component, e.g.
        so that the menu's edge is aligned with that of the component. This is intended for
        things like buttons that trigger a pop-up menu.
    */
    int showAt (Component* componentToAttachTo,
                int itemIDThatMustBeVisible = 0,
                int minimumWidth = 0,
                int maximumNumColumns = 0,
                int standardItemHeight = 0,
                ModalComponentManager::Callback* callback = nullptr);

    /** Displays and runs the menu modally, with a set of options.
    */
    int showMenu (const Options& options);
   #endif

    /** Runs the menu asynchronously, with a user-provided callback that will receive the result. */
    void showMenuAsync (const Options& options,
                        ModalComponentManager::Callback* callback);

    //==============================================================================
    /** Closes any menus that are currently open.

        This might be useful if you have a situation where your window is being closed
        by some means other than a user action, and you'd like to make sure that menus
        aren't left hanging around.
    */
    static bool JUCE_CALLTYPE dismissAllActiveMenus();

    //==============================================================================
    /** Specifies a look-and-feel for the menu and any sub-menus that it has.

        This can be called before show() if you need a customised menu. Be careful
        not to delete the LookAndFeel object before the menu has been deleted.
    */
    void setLookAndFeel (LookAndFeel* newLookAndFeel);

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

        These constants can be used either via the LookAndFeel::setColour()
        method for the look and feel that is set for this menu with setLookAndFeel()

        @see setLookAndFeel, LookAndFeel::setColour, LookAndFeel::findColour
    */
    enum ColourIds
    {
        backgroundColourId             = 0x1000700,  /**< The colour to fill the menu's background with. */
        textColourId                   = 0x1000600,  /**< The colour for normal menu item text, (unless the
                                                          colour is specified when the item is added). */
        headerTextColourId             = 0x1000601,  /**< The colour for section header item text (see the
                                                          addSectionHeader() method). */
        highlightedBackgroundColourId  = 0x1000900,  /**< The colour to fill the background of the currently
                                                          highlighted menu item. */
        highlightedTextColourId        = 0x1000800,  /**< The colour to use for the text of the currently
                                                          highlighted item. */
    };

    //==============================================================================
    /**
        Allows you to iterate through the items in a pop-up menu, and examine
        their properties.

        To use this, just create one and repeatedly call its next() method. When this
        returns true, all the member variables of the iterator are filled-out with
        information describing the menu item. When it returns false, the end of the
        list has been reached.
    */
    class JUCE_API  MenuItemIterator
    {
    public:
        //==============================================================================
        /** Creates an iterator that will scan through the items in the specified
            menu.

            Be careful not to add any items to a menu while it is being iterated,
            or things could get out of step.

            @param menu                 the menu that needs to be scanned

            @param searchRecursively    if true, all submenus will be recursed into to
                                        do an exhaustive search
        */
        MenuItemIterator (const PopupMenu& menu, bool searchRecursively = false);

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

        /** Returns true if there is another item, and sets up all this object's
            member variables to reflect that item's properties.
        */
        bool next();

        /** Returns a reference to the description of the current item.
            It is only valid to call this after next() has returned true!
        */
        Item& getItem() const noexcept;

    private:
        //==============================================================================
        bool searchRecursively;

        Array<int> index;
        Array<const PopupMenu*> menus;
        PopupMenu::Item* currentItem = nullptr;

        MenuItemIterator& operator= (const MenuItemIterator&);
        JUCE_LEAK_DETECTOR (MenuItemIterator)
    };

    //==============================================================================
    /** A user-defined component that can be used as an item in a popup menu.
        @see PopupMenu::addCustomItem
    */
    class JUCE_API  CustomComponent  : public Component,
                                       public SingleThreadedReferenceCountedObject
    {
    public:
        /** Creates a custom item.
            If isTriggeredAutomatically is true, then the menu will automatically detect
            a mouse-click on this component and use that to invoke the menu item. If it's
            false, then it's up to your class to manually trigger the item when it wants to.
        */
        CustomComponent (bool isTriggeredAutomatically = true);

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

        /** Returns a rectangle with the size that this component would like to have.

            Note that the size which this method returns isn't necessarily the one that
            the menu will give it, as the items will be stretched to have a uniform width.
        */
        virtual void getIdealSize (int& idealWidth, int& idealHeight) = 0;

        /** Dismisses the menu, indicating that this item has been chosen.

            This will cause the menu to exit from its modal state, returning
            this item's id as the result.
        */
        void triggerMenuItem();

        /** Returns true if this item should be highlighted because the mouse is over it.
            You can call this method in your paint() method to find out whether
            to draw a highlight.
        */
        bool isItemHighlighted() const noexcept                 { return isHighlighted; }

        /** @internal */
        bool isTriggeredAutomatically() const noexcept          { return triggeredAutomatically; }
        /** @internal */
        void setHighlighted (bool shouldBeHighlighted);

    private:
        //==============================================================================
        bool isHighlighted = false, triggeredAutomatically;

        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomComponent)
    };

    //==============================================================================
    /** A user-defined callback that can be used for specific items in a popup menu.
        @see PopupMenu::Item::customCallback
    */
    class JUCE_API  CustomCallback  : public SingleThreadedReferenceCountedObject
    {
    public:
        CustomCallback();
        ~CustomCallback();

        /** Callback to indicate this item has been triggered.
            @returns true if the itemID should be sent to the exitModalState method, or
                     false if it should send 0, indicating no further action should be taken
        */
        virtual bool menuItemTriggered() = 0;

    private:
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomCallback)
    };

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

        /** Fills the background of a popup menu component. */
        virtual void drawPopupMenuBackground (Graphics&, int width, int height) = 0;

        /** Draws one of the items in a popup menu. */
        virtual void drawPopupMenuItem (Graphics&, const Rectangle<int>& area,
                                        bool isSeparator, bool isActive, bool isHighlighted,
                                        bool isTicked, bool hasSubMenu,
                                        const String& text,
                                        const String& shortcutKeyText,
                                        const Drawable* icon,
                                        const Colour* textColour) = 0;

        virtual void drawPopupMenuSectionHeader (Graphics&, const Rectangle<int>& area,
                                                 const String& sectionName) = 0;

        /** Returns the size and style of font to use in popup menus. */
        virtual Font getPopupMenuFont() = 0;

        virtual void drawPopupMenuUpDownArrow (Graphics&,
                                               int width, int height,
                                               bool isScrollUpArrow) = 0;

        /** Finds the best size for an item in a popup menu. */
        virtual void getIdealPopupMenuItemSize (const String& text,
                                                bool isSeparator,
                                                int standardMenuItemHeight,
                                                int& idealWidth,
                                                int& idealHeight) = 0;

        virtual int getMenuWindowFlags() = 0;

        virtual void drawMenuBarBackground (Graphics&, int width, int height,
                                            bool isMouseOverBar,
                                            MenuBarComponent&) = 0;

        virtual int getDefaultMenuBarHeight() = 0;

        virtual int getMenuBarItemWidth (MenuBarComponent&, int itemIndex, const String& itemText) = 0;

        virtual Font getMenuBarFont (MenuBarComponent&, int itemIndex, const String& itemText) = 0;

        virtual void drawMenuBarItem (Graphics&, int width, int height,
                                      int itemIndex,
                                      const String& itemText,
                                      bool isMouseOverItem,
                                      bool isMenuOpen,
                                      bool isMouseOverBar,
                                      MenuBarComponent&) = 0;

        virtual Component* getParentComponentForMenuOptions (const PopupMenu::Options& options) = 0;

        virtual void preparePopupMenuWindow (Component& newWindow) = 0;

        /** Return true if you want your popup menus to scale with the target component's AffineTransform
            or scale factor */
        virtual bool shouldPopupMenuScaleWithTargetComponent (const PopupMenu::Options& options) = 0;

        virtual int getPopupMenuBorderSize() = 0;
    };

private:
    //==============================================================================
    JUCE_PUBLIC_IN_DLL_BUILD (struct HelperClasses)
    friend struct HelperClasses;
    friend class MenuBarComponent;

    OwnedArray<Item> items;
    WeakReference<LookAndFeel> lookAndFeel;

    Component* createWindow (const Options&, ApplicationCommandManager**) const;
    int showWithOptionalCallback (const Options&, ModalComponentManager::Callback*, bool);

   #if JUCE_CATCH_DEPRECATED_CODE_MISUSE
    // These methods have new implementations now - see its new definition
    int drawPopupMenuItem (Graphics&, int, int, bool, bool, bool, bool, bool, const String&, const String&, Image*, const Colour*) { return 0; }
   #endif

    JUCE_LEAK_DETECTOR (PopupMenu)
};

} // 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 menu bar component.

    @see MenuBarModel
*/
class JUCE_API  MenuBarComponent  : public Component,
                                    private MenuBarModel::Listener,
                                    private Timer
{
public:
    //==============================================================================
    /** Creates a menu bar.

        @param model    the model object to use to control this bar. You can
                        pass omit the parameter or pass nullptr into this if you like,
                        and set the model later using the setModel() method.
    */
    MenuBarComponent (MenuBarModel* model = nullptr);

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

    //==============================================================================
    /** Changes the model object to use to control the bar.

        This can be a null pointer, in which case the bar will be empty. Don't delete the object
        that is passed-in while it's still being used by this MenuBar.
    */
    void setModel (MenuBarModel* newModel);

    /** Returns the current menu bar model being used. */
    MenuBarModel* getModel() const noexcept;

    //==============================================================================
    /** Pops up one of the menu items.

        This lets you manually open one of the menus - it could be triggered by a
        key shortcut, for example.
    */
    void showMenu (int menuIndex);

    //==============================================================================
    /** @internal */
    void paint (Graphics&) override;
    /** @internal */
    void resized() override;
    /** @internal */
    void mouseEnter (const MouseEvent&) override;
    /** @internal */
    void mouseExit (const MouseEvent&) override;
    /** @internal */
    void mouseDown (const MouseEvent&) override;
    /** @internal */
    void mouseDrag (const MouseEvent&) override;
    /** @internal */
    void mouseUp (const MouseEvent&) override;
    /** @internal */
    void mouseMove (const MouseEvent&) override;
    /** @internal */
    void handleCommandMessage (int commandId) override;
    /** @internal */
    bool keyPressed (const KeyPress&) override;
    /** @internal */
    void menuBarItemsChanged (MenuBarModel*) override;
    /** @internal */
    void menuCommandInvoked (MenuBarModel*, const ApplicationCommandTarget::InvocationInfo&) override;

private:
    //==============================================================================
    MenuBarModel* model;

    StringArray menuNames;
    Array<int> xPositions;
    Point<int> lastMousePos;
    int itemUnderMouse, currentPopupIndex, topLevelIndexClicked;

    int getItemAt (Point<int>);
    void setItemUnderMouse (int index);
    void setOpenItem (int index);
    void updateItemUnderMouse (Point<int>);
    void timerCallback() override;
    void repaintMenuItem (int index);
    void menuDismissed (int topLevelIndex, int itemId);
    static void menuBarMenuDismissedCallback (int, MenuBarComponent*, int);

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MenuBarComponent)
};

} // 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
{

MenuBarModel::MenuBarModel() noexcept
    : manager (nullptr)
{
}

MenuBarModel::~MenuBarModel()
{
    setApplicationCommandManagerToWatch (nullptr);
}

//==============================================================================
void MenuBarModel::menuItemsChanged()
{
    triggerAsyncUpdate();
}

void MenuBarModel::setApplicationCommandManagerToWatch (ApplicationCommandManager* const newManager) noexcept
{
    if (manager != newManager)
    {
        if (manager != nullptr)
            manager->removeListener (this);

        manager = newManager;

        if (manager != nullptr)
            manager->addListener (this);
    }
}

void MenuBarModel::addListener (Listener* const newListener) noexcept
{
    listeners.add (newListener);
}

void MenuBarModel::removeListener (Listener* const listenerToRemove) noexcept
{
    // Trying to remove a listener that isn't on the list!
    // If this assertion happens because this object is a dangling pointer, make sure you've not
    // deleted this menu model while it's still being used by something (e.g. by a MenuBarComponent)
    jassert (listeners.contains (listenerToRemove));

    listeners.remove (listenerToRemove);
}

//==============================================================================
void MenuBarModel::handleAsyncUpdate()
{
    listeners.call (&MenuBarModel::Listener::menuBarItemsChanged, this);
}

void MenuBarModel::applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info)
{
    listeners.call (&MenuBarModel::Listener::menuCommandInvoked, this, info);
}

void MenuBarModel::applicationCommandListChanged()
{
    menuItemsChanged();
}

void MenuBarModel::handleMenuBarActivate (bool isActive)
{
    menuBarActivated (isActive);
    listeners.call (&MenuBarModel::Listener::menuBarActivated, this, isActive);
}

void MenuBarModel::menuBarActivated (bool) {}
void MenuBarModel::Listener::menuBarActivated (MenuBarModel*, bool) {}

} // 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
{

//==============================================================================
/**
    Represents a key press, including any modifier keys that are needed.

    E.g. a KeyPress might represent CTRL+C, SHIFT+ALT+H, Spacebar, Escape, etc.

    @see Component, KeyListener, KeyPressMappingSet, Button::addShortcut
*/
class JUCE_API  KeyPress
{
public:
    //==============================================================================
    /** Creates an (invalid) KeyPress.

        @see isValid
    */
    KeyPress() noexcept;

    /** Creates a KeyPress for a key and some modifiers.

        e.g.
        CTRL+C would be: KeyPress ('c', ModifierKeys::ctrlModifier)
        SHIFT+Escape would be: KeyPress (KeyPress::escapeKey, ModifierKeys::shiftModifier)

        @param keyCode      a code that represents the key - this value must be
                            one of special constants listed in this class, or an
                            8-bit character code such as a letter (case is ignored),
                            digit or a simple key like "," or ".". Note that this
                            isn't the same as the textCharacter parameter, so for example
                            a keyCode of 'a' and a shift-key modifier should have a
                            textCharacter value of 'A'.
        @param modifiers    the modifiers to associate with the keystroke
        @param textCharacter    the character that would be printed if someone typed
                            this keypress into a text editor. This value may be
                            null if the keypress is a non-printing character
        @see getKeyCode, isKeyCode, getModifiers
    */
    KeyPress (int keyCode,
              ModifierKeys modifiers,
              juce_wchar textCharacter) noexcept;

    /** Creates a keypress with a keyCode but no modifiers or text character. */
    explicit KeyPress (int keyCode) noexcept;

    /** Creates a copy of another KeyPress. */
    KeyPress (const KeyPress& other) noexcept;

    /** Copies this KeyPress from another one. */
    KeyPress& operator= (const KeyPress& other) noexcept;

    /** Compares two KeyPress objects. */
    bool operator== (const KeyPress& other) const noexcept;

    /** Compares two KeyPress objects. */
    bool operator!= (const KeyPress& other) const noexcept;

    /** Returns true if this keypress is for the given keycode without any modifiers. */
    bool operator== (int keyCode) const noexcept;

    /** Returns true if this keypress is not for the given keycode without any modifiers. */
    bool operator!= (int keyCode) const noexcept;

    //==============================================================================
    /** Returns true if this is a valid KeyPress.

        A null keypress can be created by the default constructor, in case it's
        needed.
    */
    bool isValid() const noexcept                               { return keyCode != 0; }

    /** Returns the key code itself.

        This will either be one of the special constants defined in this class,
        or an 8-bit character code.
    */
    int getKeyCode() const noexcept                             { return keyCode; }

    /** Returns the key modifiers.

        @see ModifierKeys
    */
    ModifierKeys getModifiers() const noexcept                  { return mods; }

    /** Returns the character that is associated with this keypress.

        This is the character that you'd expect to see printed if you press this
        keypress in a text editor or similar component.
    */
    juce_wchar getTextCharacter() const noexcept                { return textCharacter; }

    /** Checks whether the KeyPress's key is the same as the one provided, without checking
        the modifiers.

        The values for key codes can either be one of the special constants defined in
        this class, or an 8-bit character code.

        @see getKeyCode
    */
    bool isKeyCode (int keyCodeToCompare) const noexcept        { return keyCode == keyCodeToCompare; }

    //==============================================================================
    /** Converts a textual key description to a KeyPress.

        This attempts to decode a textual version of a keypress, e.g. "CTRL + C" or "SPACE".

        This isn't designed to cope with any kind of input, but should be given the
        strings that are created by the getTextDescription() method.

        If the string can't be parsed, the object returned will be invalid.

        @see getTextDescription
    */
    static KeyPress createFromDescription (const String& textVersion);

    /** Creates a textual description of the key combination.

        e.g. "CTRL + C" or "DELETE".

        To store a keypress in a file, use this method, along with createFromDescription()
        to retrieve it later.
    */
    String getTextDescription() const;

    /** Creates a textual description of the key combination, using unicode icon symbols if possible.

        On OSX, this uses the Apple symbols for command, option, shift, etc, instead of the textual
        modifier key descriptions that are returned by getTextDescription()
    */
    String getTextDescriptionWithIcons() const;

    //==============================================================================
    /** Checks whether the user is currently holding down the keys that make up this
        KeyPress.

        Note that this will return false if any extra modifier keys are
        down - e.g. if the keypress is CTRL+X and the user is actually holding CTRL+ALT+x
        then it will be false.
    */
    bool isCurrentlyDown() const;

    /** Checks whether a particular key is held down, irrespective of modifiers.

        The values for key codes can either be one of the special constants defined in
        this class, or an 8-bit character code.
    */
    static bool isKeyCurrentlyDown (int keyCode);

    //==============================================================================
    // Key codes
    //
    // Note that the actual values of these are platform-specific and may change
    // without warning, so don't store them anywhere as constants. For persisting/retrieving
    // KeyPress objects, use getTextDescription() and createFromDescription() instead.
    //

    static const int spaceKey;      /**< key-code for the space bar */
    static const int escapeKey;     /**< key-code for the escape key */
    static const int returnKey;     /**< key-code for the return key*/
    static const int tabKey;        /**< key-code for the tab key*/

    static const int deleteKey;     /**< key-code for the delete key (not backspace) */
    static const int backspaceKey;  /**< key-code for the backspace key */
    static const int insertKey;     /**< key-code for the insert key */

    static const int upKey;         /**< key-code for the cursor-up key */
    static const int downKey;       /**< key-code for the cursor-down key */
    static const int leftKey;       /**< key-code for the cursor-left key */
    static const int rightKey;      /**< key-code for the cursor-right key */
    static const int pageUpKey;     /**< key-code for the page-up key */
    static const int pageDownKey;   /**< key-code for the page-down key */
    static const int homeKey;       /**< key-code for the home key */
    static const int endKey;        /**< key-code for the end key */

    static const int F1Key;         /**< key-code for the F1 key */
    static const int F2Key;         /**< key-code for the F2 key */
    static const int F3Key;         /**< key-code for the F3 key */
    static const int F4Key;         /**< key-code for the F4 key */
    static const int F5Key;         /**< key-code for the F5 key */
    static const int F6Key;         /**< key-code for the F6 key */
    static const int F7Key;         /**< key-code for the F7 key */
    static const int F8Key;         /**< key-code for the F8 key */
    static const int F9Key;         /**< key-code for the F9 key */
    static const int F10Key;        /**< key-code for the F10 key */
    static const int F11Key;        /**< key-code for the F11 key */
    static const int F12Key;        /**< key-code for the F12 key */
    static const int F13Key;        /**< key-code for the F13 key */
    static const int F14Key;        /**< key-code for the F14 key */
    static const int F15Key;        /**< key-code for the F15 key */
    static const int F16Key;        /**< key-code for the F16 key */
    static const int F17Key;        /**< key-code for the F17 key */
    static const int F18Key;        /**< key-code for the F18 key */
    static const int F19Key;        /**< key-code for the F19 key */
    static const int F20Key;        /**< key-code for the F20 key */
    static const int F21Key;        /**< key-code for the F21 key */
    static const int F22Key;        /**< key-code for the F22 key */
    static const int F23Key;        /**< key-code for the F23 key */
    static const int F24Key;        /**< key-code for the F24 key */
    static const int F25Key;        /**< key-code for the F25 key */
    static const int F26Key;        /**< key-code for the F26 key */
    static const int F27Key;        /**< key-code for the F27 key */
    static const int F28Key;        /**< key-code for the F28 key */
    static const int F29Key;        /**< key-code for the F29 key */
    static const int F30Key;        /**< key-code for the F30 key */
    static const int F31Key;        /**< key-code for the F31 key */
    static const int F32Key;        /**< key-code for the F32 key */
    static const int F33Key;        /**< key-code for the F33 key */
    static const int F34Key;        /**< key-code for the F34 key */
    static const int F35Key;        /**< key-code for the F35 key */

    static const int numberPad0;     /**< key-code for the 0 on the numeric keypad. */
    static const int numberPad1;     /**< key-code for the 1 on the numeric keypad. */
    static const int numberPad2;     /**< key-code for the 2 on the numeric keypad. */
    static const int numberPad3;     /**< key-code for the 3 on the numeric keypad. */
    static const int numberPad4;     /**< key-code for the 4 on the numeric keypad. */
    static const int numberPad5;     /**< key-code for the 5 on the numeric keypad. */
    static const int numberPad6;     /**< key-code for the 6 on the numeric keypad. */
    static const int numberPad7;     /**< key-code for the 7 on the numeric keypad. */
    static const int numberPad8;     /**< key-code for the 8 on the numeric keypad. */
    static const int numberPad9;     /**< key-code for the 9 on the numeric keypad. */

    static const int numberPadAdd;            /**< key-code for the add sign on the numeric keypad. */
    static const int numberPadSubtract;       /**< key-code for the subtract sign on the numeric keypad. */
    static const int numberPadMultiply;       /**< key-code for the multiply sign on the numeric keypad. */
    static const int numberPadDivide;         /**< key-code for the divide sign on the numeric keypad. */
    static const int numberPadSeparator;      /**< key-code for the comma on the numeric keypad. */
    static const int numberPadDecimalPoint;   /**< key-code for the decimal point sign on the numeric keypad. */
    static const int numberPadEquals;         /**< key-code for the equals key on the numeric keypad. */
    static const int numberPadDelete;         /**< key-code for the delete key on the numeric keypad. */

    static const int playKey;        /**< key-code for a multimedia 'play' key, (not all keyboards will have one) */
    static const int stopKey;        /**< key-code for a multimedia 'stop' key, (not all keyboards will have one) */
    static const int fastForwardKey; /**< key-code for a multimedia 'fast-forward' key, (not all keyboards will have one) */
    static const int rewindKey;      /**< key-code for a multimedia 'rewind' key, (not all keyboards will have one) */

private:
    //==============================================================================
    int keyCode = 0;
    ModifierKeys mods;
    juce_wchar textCharacter = 0;

    JUCE_LEAK_DETECTOR (KeyPress)
};

} // 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
{

KeyPress::KeyPress() noexcept
{
}

KeyPress::KeyPress (int code, ModifierKeys m, juce_wchar textChar) noexcept
    : keyCode (code), mods (m), textCharacter (textChar)
{
}

KeyPress::KeyPress (const int code) noexcept  : keyCode (code)
{
}

KeyPress::KeyPress (const KeyPress& other) noexcept
    : keyCode (other.keyCode), mods (other.mods), textCharacter (other.textCharacter)
{
}

KeyPress& KeyPress::operator= (const KeyPress& other) noexcept
{
    keyCode = other.keyCode;
    mods = other.mods;
    textCharacter = other.textCharacter;
    return *this;
}

bool KeyPress::operator== (int otherKeyCode) const noexcept
{
    return keyCode == otherKeyCode && ! mods.isAnyModifierKeyDown();
}

bool KeyPress::operator== (const KeyPress& other) const noexcept
{
    return mods.getRawFlags() == other.mods.getRawFlags()
            && (textCharacter == other.textCharacter
                 || textCharacter == 0
                 || other.textCharacter == 0)
            && (keyCode == other.keyCode
                 || (keyCode < 256
                      && other.keyCode < 256
                      && CharacterFunctions::toLowerCase ((juce_wchar) keyCode)
                           == CharacterFunctions::toLowerCase ((juce_wchar) other.keyCode)));
}

bool KeyPress::operator!= (const KeyPress& other) const noexcept    { return ! operator== (other); }
bool KeyPress::operator!= (int otherKeyCode) const noexcept         { return ! operator== (otherKeyCode); }

bool KeyPress::isCurrentlyDown() const
{
    return isKeyCurrentlyDown (keyCode)
            && (ModifierKeys::getCurrentModifiers().getRawFlags() & ModifierKeys::allKeyboardModifiers)
                  == (mods.getRawFlags() & ModifierKeys::allKeyboardModifiers);
}

//==============================================================================
namespace KeyPressHelpers
{
    struct KeyNameAndCode
    {
        const char* name;
        int code;
    };

    const KeyNameAndCode translations[] =
    {
        { "spacebar",       KeyPress::spaceKey },
        { "return",         KeyPress::returnKey },
        { "escape",         KeyPress::escapeKey },
        { "backspace",      KeyPress::backspaceKey },
        { "cursor left",    KeyPress::leftKey },
        { "cursor right",   KeyPress::rightKey },
        { "cursor up",      KeyPress::upKey },
        { "cursor down",    KeyPress::downKey },
        { "page up",        KeyPress::pageUpKey },
        { "page down",      KeyPress::pageDownKey },
        { "home",           KeyPress::homeKey },
        { "end",            KeyPress::endKey },
        { "delete",         KeyPress::deleteKey },
        { "insert",         KeyPress::insertKey },
        { "tab",            KeyPress::tabKey },
        { "play",           KeyPress::playKey },
        { "stop",           KeyPress::stopKey },
        { "fast forward",   KeyPress::fastForwardKey },
        { "rewind",         KeyPress::rewindKey }
    };

    struct ModifierDescription
    {
        const char* name;
        int flag;
    };

    static const ModifierDescription modifierNames[] =
    {
        { "ctrl",      ModifierKeys::ctrlModifier },
        { "control",   ModifierKeys::ctrlModifier },
        { "ctl",       ModifierKeys::ctrlModifier },
        { "shift",     ModifierKeys::shiftModifier },
        { "shft",      ModifierKeys::shiftModifier },
        { "alt",       ModifierKeys::altModifier },
        { "option",    ModifierKeys::altModifier },
        { "command",   ModifierKeys::commandModifier },
        { "cmd",       ModifierKeys::commandModifier }
    };

    static const char* numberPadPrefix() noexcept      { return "numpad "; }

    static int getNumpadKeyCode (const String& desc)
    {
        if (desc.containsIgnoreCase (numberPadPrefix()))
        {
            auto lastChar = desc.trimEnd().getLastCharacter();

            switch (lastChar)
            {
                case '0': case '1': case '2': case '3': case '4':
                case '5': case '6': case '7': case '8': case '9':
                    return (int) (KeyPress::numberPad0 + (int) lastChar - '0');

                case '+':   return KeyPress::numberPadAdd;
                case '-':   return KeyPress::numberPadSubtract;
                case '*':   return KeyPress::numberPadMultiply;
                case '/':   return KeyPress::numberPadDivide;
                case '.':   return KeyPress::numberPadDecimalPoint;
                case '=':   return KeyPress::numberPadEquals;

                default:    break;
            }

            if (desc.endsWith ("separator"))  return KeyPress::numberPadSeparator;
            if (desc.endsWith ("delete"))     return KeyPress::numberPadDelete;
        }

        return 0;
    }

   #if JUCE_MAC
    struct OSXSymbolReplacement
    {
        const char* text;
        juce_wchar symbol;
    };

    const OSXSymbolReplacement osxSymbols[] =
    {
        { "shift + ",     0x21e7 },
        { "command + ",   0x2318 },
        { "option + ",    0x2325 },
        { "ctrl + ",      0x2303 },
        { "return",       0x21b5 },
        { "cursor left",  0x2190 },
        { "cursor right", 0x2192 },
        { "cursor up",    0x2191 },
        { "cursor down",  0x2193 },
        { "backspace",    0x232b },
        { "delete",       0x2326 },
        { "spacebar",     0x2423 }
    };
   #endif
}

//==============================================================================
KeyPress KeyPress::createFromDescription (const String& desc)
{
    int modifiers = 0;

    for (int i = 0; i < numElementsInArray (KeyPressHelpers::modifierNames); ++i)
        if (desc.containsWholeWordIgnoreCase (KeyPressHelpers::modifierNames[i].name))
            modifiers |= KeyPressHelpers::modifierNames[i].flag;

    int key = 0;

    for (int i = 0; i < numElementsInArray (KeyPressHelpers::translations); ++i)
    {
        if (desc.containsWholeWordIgnoreCase (String (KeyPressHelpers::translations[i].name)))
        {
            key = KeyPressHelpers::translations[i].code;
            break;
        }
    }

    if (key == 0)
        key = KeyPressHelpers::getNumpadKeyCode (desc);

    if (key == 0)
    {
        // see if it's a function key..
        if (! desc.containsChar ('#')) // avoid mistaking hex-codes like "#f1"
        {
            for (int i = 1; i <= 35; ++i)
            {
                if (desc.containsWholeWordIgnoreCase ("f" + String (i)))
                {
                    if (i <= 16)        key = F1Key + i - 1;
                    else if (i <= 24)   key = F17Key + i - 17;
                    else if (i <= 35)   key = F25Key + i - 25;
                }
            }
        }

        if (key == 0)
        {
            // give up and use the hex code..
            auto hexCode = desc.fromFirstOccurrenceOf ("#", false, false)
                               .retainCharacters ("0123456789abcdefABCDEF")
                               .getHexValue32();

            if (hexCode > 0)
                key = hexCode;
            else
                key = (int) CharacterFunctions::toUpperCase (desc.getLastCharacter());
        }
    }

    return KeyPress (key, ModifierKeys (modifiers), 0);
}

String KeyPress::getTextDescription() const
{
    String desc;

    if (keyCode > 0)
    {
        // some keyboard layouts use a shift-key to get the slash, but in those cases, we
        // want to store it as being a slash, not shift+whatever.
        if (textCharacter == '/' && keyCode != numberPadDivide)
            return "/";

        if (mods.isCtrlDown())      desc << "ctrl + ";
        if (mods.isShiftDown())     desc << "shift + ";

       #if JUCE_MAC
        if (mods.isAltDown())       desc << "option + ";
        if (mods.isCommandDown())   desc << "command + ";
       #else
        if (mods.isAltDown())       desc << "alt + ";
       #endif

        for (int i = 0; i < numElementsInArray (KeyPressHelpers::translations); ++i)
            if (keyCode == KeyPressHelpers::translations[i].code)
                return desc + KeyPressHelpers::translations[i].name;

        // not all F keys have consecutive key codes on all platforms
        if      (keyCode >= F1Key  && keyCode <= F16Key)                  desc << 'F' << (1 + keyCode - F1Key);
        else if (keyCode >= F17Key && keyCode <= F24Key)                  desc << 'F' << (17 + keyCode - F17Key);
        else if (keyCode >= F25Key && keyCode <= F35Key)                  desc << 'F' << (25 + keyCode - F25Key);
        else if (keyCode >= numberPad0 && keyCode <= numberPad9)    desc << KeyPressHelpers::numberPadPrefix() << (keyCode - numberPad0);
        else if (keyCode >= 33 && keyCode < 176)        desc += CharacterFunctions::toUpperCase ((juce_wchar) keyCode);
        else if (keyCode == numberPadAdd)               desc << KeyPressHelpers::numberPadPrefix() << '+';
        else if (keyCode == numberPadSubtract)          desc << KeyPressHelpers::numberPadPrefix() << '-';
        else if (keyCode == numberPadMultiply)          desc << KeyPressHelpers::numberPadPrefix() << '*';
        else if (keyCode == numberPadDivide)            desc << KeyPressHelpers::numberPadPrefix() << '/';
        else if (keyCode == numberPadSeparator)         desc << KeyPressHelpers::numberPadPrefix() << "separator";
        else if (keyCode == numberPadDecimalPoint)      desc << KeyPressHelpers::numberPadPrefix() << '.';
        else if (keyCode == numberPadDelete)            desc << KeyPressHelpers::numberPadPrefix() << "delete";
        else                                            desc << '#' << String::toHexString (keyCode);
    }

    return desc;
}

String KeyPress::getTextDescriptionWithIcons() const
{
   #if JUCE_MAC
    auto s = getTextDescription();

    for (int i = 0; i < numElementsInArray (KeyPressHelpers::osxSymbols); ++i)
        s = s.replace (KeyPressHelpers::osxSymbols[i].text,
                       String::charToString (KeyPressHelpers::osxSymbols[i].symbol));

    return s;
   #else
    return getTextDescription();
   #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
{

//==============================================================================
/**
    Represents the state of the mouse buttons and modifier keys.

    This is used both by mouse events and by KeyPress objects to describe
    the state of keys such as shift, control, alt, etc.

    @see KeyPress, MouseEvent::mods
*/
class JUCE_API  ModifierKeys
{
public:
    //==============================================================================
    /** Creates a ModifierKeys object with no flags set. */
    ModifierKeys() noexcept;

    /** Creates a ModifierKeys object from a raw set of flags.

        @param flags to represent the keys that are down
        @see    shiftModifier, ctrlModifier, altModifier, leftButtonModifier,
                rightButtonModifier, commandModifier, popupMenuClickModifier
    */
    ModifierKeys (int flags) noexcept;

    /** Creates a copy of another object. */
    ModifierKeys (const ModifierKeys& other) noexcept;

    /** Copies this object from another one. */
    ModifierKeys& operator= (const ModifierKeys other) noexcept;

    //==============================================================================
    /** Checks whether the 'command' key flag is set (or 'ctrl' on Windows/Linux).

        This is a platform-agnostic way of checking for the operating system's
        preferred command-key modifier - so on the Mac it tests for the Apple key, on
        Windows/Linux, it's actually checking for the CTRL key.
    */
    inline bool isCommandDown() const noexcept          { return testFlags (commandModifier); }

    /** Checks whether the user is trying to launch a pop-up menu.

        This checks for platform-specific modifiers that might indicate that the user
        is following the operating system's normal method of showing a pop-up menu.

        So on Windows/Linux, this method is really testing for a right-click.
        On the Mac, it tests for either the CTRL key being down, or a right-click.
    */
    inline bool isPopupMenu() const noexcept            { return testFlags (popupMenuClickModifier); }

    /** Checks whether the flag is set for the left mouse-button. */
    inline bool isLeftButtonDown() const noexcept       { return testFlags (leftButtonModifier); }

    /** Checks whether the flag is set for the right mouse-button.

        Note that for detecting popup-menu clicks, you should be using isPopupMenu() instead, as
        this is platform-independent (and makes your code more explanatory too).
    */
    inline bool isRightButtonDown() const noexcept      { return testFlags (rightButtonModifier); }

    inline bool isMiddleButtonDown() const noexcept     { return testFlags (middleButtonModifier); }

    /** Tests for any of the mouse-button flags. */
    inline bool isAnyMouseButtonDown() const noexcept   { return testFlags (allMouseButtonModifiers); }

    /** Tests for any of the modifier key flags. */
    inline bool isAnyModifierKeyDown() const noexcept   { return testFlags ((shiftModifier | ctrlModifier | altModifier | commandModifier)); }

    /** Checks whether the shift key's flag is set. */
    inline bool isShiftDown() const noexcept            { return testFlags (shiftModifier); }

    /** Checks whether the CTRL key's flag is set.

        Remember that it's better to use the platform-agnostic routines to test for command-key and
        popup-menu modifiers.

        @see isCommandDown, isPopupMenu
    */
    inline bool isCtrlDown() const noexcept             { return testFlags (ctrlModifier); }

    /** Checks whether the ALT key's flag is set. */
    inline bool isAltDown() const noexcept              { return testFlags (altModifier); }

    //==============================================================================
    /** Flags that represent the different keys. */
    enum Flags
    {
        /** Indicates no modifier keys. */
        noModifiers                             = 0,

        /** Shift key flag. */
        shiftModifier                           = 1,

        /** CTRL key flag. */
        ctrlModifier                            = 2,

        /** ALT key flag. */
        altModifier                             = 4,

        /** Left mouse button flag. */
        leftButtonModifier                      = 16,

        /** Right mouse button flag. */
        rightButtonModifier                     = 32,

        /** Middle mouse button flag. */
        middleButtonModifier                    = 64,

       #if JUCE_MAC
        /** Command key flag - on windows this is the same as the CTRL key flag. */
        commandModifier                         = 8,

        /** Popup menu flag - on windows this is the same as rightButtonModifier, on the
            Mac it's the same as (rightButtonModifier | ctrlModifier). */
        popupMenuClickModifier                  = rightButtonModifier | ctrlModifier,
       #else
        /** Command key flag - on windows this is the same as the CTRL key flag. */
        commandModifier                         = ctrlModifier,

        /** Popup menu flag - on windows this is the same as rightButtonModifier, on the
            Mac it's the same as (rightButtonModifier | ctrlModifier). */
        popupMenuClickModifier                  = rightButtonModifier,
       #endif

        /** Represents a combination of all the shift, alt, ctrl and command key modifiers. */
        allKeyboardModifiers                    = shiftModifier | ctrlModifier | altModifier | commandModifier,

        /** Represents a combination of all the mouse buttons at once. */
        allMouseButtonModifiers                 = leftButtonModifier | rightButtonModifier | middleButtonModifier,

        /** Represents a combination of all the alt, ctrl and command key modifiers. */
        ctrlAltCommandModifiers                 = ctrlModifier | altModifier | commandModifier
    };

    //==============================================================================
    /** Returns a copy of only the mouse-button flags */
    ModifierKeys withOnlyMouseButtons() const noexcept                  { return ModifierKeys (flags & allMouseButtonModifiers); }

    /** Returns a copy of only the non-mouse flags */
    ModifierKeys withoutMouseButtons() const noexcept                   { return ModifierKeys (flags & ~allMouseButtonModifiers); }

    bool operator== (const ModifierKeys other) const noexcept           { return flags == other.flags; }
    bool operator!= (const ModifierKeys other) const noexcept           { return flags != other.flags; }

    //==============================================================================
    /** Returns the raw flags for direct testing. */
    inline int getRawFlags() const noexcept                             { return flags; }

    ModifierKeys withoutFlags (int rawFlagsToClear) const noexcept      { return ModifierKeys (flags & ~rawFlagsToClear); }
    ModifierKeys withFlags (int rawFlagsToSet) const noexcept           { return ModifierKeys (flags | rawFlagsToSet); }

    /** Tests a combination of flags and returns true if any of them are set. */
    bool testFlags (int flagsToTest) const noexcept                     { return (flags & flagsToTest) != 0; }

    /** Returns the total number of mouse buttons that are down. */
    int getNumMouseButtonsDown() const noexcept;

    //==============================================================================
    /** Creates a ModifierKeys object to represent the last-known state of the
        keyboard and mouse buttons.

        @see getCurrentModifiersRealtime
    */
    static ModifierKeys getCurrentModifiers() noexcept;

    /** Creates a ModifierKeys object to represent the current state of the
        keyboard and mouse buttons.

        This isn't often needed and isn't recommended, but will actively check all the
        mouse and key states rather than just returning their last-known state like
        getCurrentModifiers() does.

        This is only needed in special circumstances for up-to-date modifier information
        at times when the app's event loop isn't running normally.

        Another reason to avoid this method is that it's not stateless, and calling it may
        update the value returned by getCurrentModifiers(), which could cause subtle changes
        in the behaviour of some components.
    */
    static ModifierKeys getCurrentModifiersRealtime() noexcept;

private:
    //==============================================================================
    int flags;

    friend class ComponentPeer;
    friend class MouseInputSource;
    friend class MouseInputSourceInternal;

    static ModifierKeys currentModifiers;
    static void updateCurrentModifiers() noexcept;
};

} // 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
{

//==============================================================================
/**
    Handles reading/writing to the system's clipboard.
*/
class JUCE_API  SystemClipboard
{
public:
    /** Copies a string of text onto the clipboard */
    static void copyTextToClipboard (const String& text);

    /** Gets the current clipboard's contents.

        Obviously this might have come from another app, so could contain
        anything..
    */
    static String getTextFromClipboard();
};

} // 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
{

//==============================================================================
/** This class is used to invoke a range of text-editor navigation methods on
    an object, based upon a keypress event.

    It's currently used internally by the TextEditor and CodeEditorComponent.
*/
template <class CallbackClass>
struct TextEditorKeyMapper
{
    /** Checks the keypress and invokes one of a range of navigation functions that
        the target class must implement, based on the key event.
    */
    static bool invokeKeyFunction (CallbackClass& target, const KeyPress& key)
    {
        const ModifierKeys& mods = key.getModifiers();

        const bool isShiftDown   = mods.isShiftDown();
        const bool ctrlOrAltDown = mods.isCtrlDown() || mods.isAltDown();

        int numCtrlAltCommandKeys = 0;
        if (mods.isCtrlDown())    ++numCtrlAltCommandKeys;
        if (mods.isAltDown())     ++numCtrlAltCommandKeys;

        if (key == KeyPress (KeyPress::downKey, ModifierKeys::ctrlModifier, 0) && target.scrollUp())   return true;
        if (key == KeyPress (KeyPress::upKey,   ModifierKeys::ctrlModifier, 0) && target.scrollDown()) return true;

       #if JUCE_MAC
        if (mods.isCommandDown() && ! ctrlOrAltDown)
        {
            if (key.isKeyCode (KeyPress::upKey))        return target.moveCaretToTop (isShiftDown);
            if (key.isKeyCode (KeyPress::downKey))      return target.moveCaretToEnd (isShiftDown);
            if (key.isKeyCode (KeyPress::leftKey))      return target.moveCaretToStartOfLine (isShiftDown);
            if (key.isKeyCode (KeyPress::rightKey))     return target.moveCaretToEndOfLine   (isShiftDown);
        }

        if (mods.isCommandDown())
            ++numCtrlAltCommandKeys;
       #endif

        if (numCtrlAltCommandKeys < 2)
        {
            if (key.isKeyCode (KeyPress::leftKey))  return target.moveCaretLeft  (ctrlOrAltDown, isShiftDown);
            if (key.isKeyCode (KeyPress::rightKey)) return target.moveCaretRight (ctrlOrAltDown, isShiftDown);

            if (key.isKeyCode (KeyPress::homeKey))  return ctrlOrAltDown ? target.moveCaretToTop         (isShiftDown)
                                                                         : target.moveCaretToStartOfLine (isShiftDown);
            if (key.isKeyCode (KeyPress::endKey))   return ctrlOrAltDown ? target.moveCaretToEnd         (isShiftDown)
                                                                         : target.moveCaretToEndOfLine   (isShiftDown);
        }

        if (numCtrlAltCommandKeys == 0)
        {
            if (key.isKeyCode (KeyPress::upKey))        return target.moveCaretUp   (isShiftDown);
            if (key.isKeyCode (KeyPress::downKey))      return target.moveCaretDown (isShiftDown);

            if (key.isKeyCode (KeyPress::pageUpKey))    return target.pageUp   (isShiftDown);
            if (key.isKeyCode (KeyPress::pageDownKey))  return target.pageDown (isShiftDown);
        }

        if (key == KeyPress ('c', ModifierKeys::commandModifier, 0)
              || key == KeyPress (KeyPress::insertKey, ModifierKeys::ctrlModifier, 0))
            return target.copyToClipboard();

        if (key == KeyPress ('x', ModifierKeys::commandModifier, 0)
              || key == KeyPress (KeyPress::deleteKey, ModifierKeys::shiftModifier, 0))
            return target.cutToClipboard();

        if (key == KeyPress ('v', ModifierKeys::commandModifier, 0)
              || key == KeyPress (KeyPress::insertKey, ModifierKeys::shiftModifier, 0))
            return target.pasteFromClipboard();

        // NB: checking for delete must happen after the earlier check for shift + delete
        if (numCtrlAltCommandKeys < 2)
        {
            if (key.isKeyCode (KeyPress::backspaceKey)) return target.deleteBackwards (ctrlOrAltDown);
            if (key.isKeyCode (KeyPress::deleteKey))    return target.deleteForwards  (ctrlOrAltDown);
        }

        if (key == KeyPress ('a', ModifierKeys::commandModifier, 0))
            return target.selectAll();

        if (key == KeyPress ('z', ModifierKeys::commandModifier, 0))
            return target.undo();

        if (key == KeyPress ('y', ModifierKeys::commandModifier, 0)
             || key == KeyPress ('z', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0))
            return target.redo();

        return false;
    }
};

} // 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
{

ModifierKeys::ModifierKeys() noexcept  : flags (0) {}
ModifierKeys::ModifierKeys (int rawFlags) noexcept  : flags (rawFlags) {}
ModifierKeys::ModifierKeys (const ModifierKeys& other) noexcept  : flags (other.flags) {}

ModifierKeys& ModifierKeys::operator= (const ModifierKeys other) noexcept
{
    flags = other.flags;
    return *this;
}

ModifierKeys ModifierKeys::currentModifiers;

ModifierKeys ModifierKeys::getCurrentModifiers() noexcept
{
    return currentModifiers;
}

int ModifierKeys::getNumMouseButtonsDown() const noexcept
{
    int num = 0;

    if (isLeftButtonDown())     ++num;
    if (isRightButtonDown())    ++num;
    if (isMiddleButtonDown())   ++num;

    return num;
}

} // 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
{

//==============================================================================
/**
    Controls the order in which focus moves between components.

    The default algorithm used by this class to work out the order of traversal
    is as follows:
    - if two components both have an explicit focus order specified, then the
      one with the lowest number comes first (see the Component::setExplicitFocusOrder()
      method).
    - any component with an explicit focus order greater than 0 comes before ones
      that don't have an order specified.
    - any unspecified components are traversed in a left-to-right, then top-to-bottom
      order.

    If you need traversal in a more customised way, you can create a subclass
    of KeyboardFocusTraverser that uses your own algorithm, and use
    Component::createFocusTraverser() to create it.

    @see Component::setExplicitFocusOrder, Component::createFocusTraverser
*/
class JUCE_API  KeyboardFocusTraverser
{
public:
    KeyboardFocusTraverser();

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

    /** Returns the component that should be given focus after the specified one
        when moving "forwards".

        The default implementation will return the next component which is to the
        right of or below this one.

        This may return nullptr if there's no suitable candidate.
    */
    virtual Component* getNextComponent (Component* current);

    /** Returns the component that should be given focus after the specified one
        when moving "backwards".

        The default implementation will return the next component which is to the
        left of or above this one.

        This may return nullptr if there's no suitable candidate.
    */
    virtual Component* getPreviousComponent (Component* current);

    /** Returns the component that should receive focus be default within the given
        parent component.

        The default implementation will just return the foremost child component that
        wants focus.

        This may return nullptr if there's no suitable candidate.
    */
    virtual Component* getDefaultComponent (Component* parentComponent);
};

} // 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
{

namespace KeyboardFocusHelpers
{
    // This will sort a set of components, so that they are ordered in terms of
    // left-to-right and then top-to-bottom.
    struct ScreenPositionComparator
    {
        static int compareElements (const Component* first, const Component* second)
        {
            auto explicitOrder1 = getOrder (first);
            auto explicitOrder2 = getOrder (second);

            if (explicitOrder1 != explicitOrder2)
                return explicitOrder1 - explicitOrder2;

            auto yDiff = first->getY() - second->getY();

            return yDiff == 0 ? first->getX() - second->getX()
                              : yDiff;
        }

        static int getOrder (const Component* c)
        {
            auto order = c->getExplicitFocusOrder();
            return order > 0 ? order : (std::numeric_limits<int>::max() / 2);
        }
    };

    static void findAllFocusableComponents (Component* parent, Array<Component*>& comps)
    {
        if (parent->getNumChildComponents() != 0)
        {
            Array<Component*> localComps;
            ScreenPositionComparator comparator;

            for (auto* c : parent->getChildren())
                if (c->isVisible() && c->isEnabled())
                    localComps.addSorted (comparator, c);

            for (auto* c : localComps)
            {
                if (c->getWantsKeyboardFocus())
                    comps.add (c);

                if (! c->isFocusContainer())
                    findAllFocusableComponents (c, comps);
            }
        }
    }

    static Component* findFocusContainer (Component* c)
    {
        c = c->getParentComponent();

        if (c != nullptr)
            while (c->getParentComponent() != nullptr && ! c->isFocusContainer())
                c = c->getParentComponent();

        return c;
    }

    static Component* getIncrementedComponent (Component* current, int delta)
    {
        if (auto* focusContainer = findFocusContainer (current))
        {
            Array<Component*> comps;
            KeyboardFocusHelpers::findAllFocusableComponents (focusContainer, comps);

            if (! comps.isEmpty())
            {
                auto index = comps.indexOf (current);
                return comps [(index + comps.size() + delta) % comps.size()];
            }
        }

        return nullptr;
    }
}

//==============================================================================
KeyboardFocusTraverser::KeyboardFocusTraverser() {}
KeyboardFocusTraverser::~KeyboardFocusTraverser() {}

Component* KeyboardFocusTraverser::getNextComponent (Component* current)
{
    jassert (current != nullptr);
    return KeyboardFocusHelpers::getIncrementedComponent (current, 1);
}

Component* KeyboardFocusTraverser::getPreviousComponent (Component* current)
{
    jassert (current != nullptr);
    return KeyboardFocusHelpers::getIncrementedComponent (current, -1);
}

Component* KeyboardFocusTraverser::getDefaultComponent (Component* parentComponent)
{
    Array<Component*> comps;

    if (parentComponent != nullptr)
        KeyboardFocusHelpers::findAllFocusableComponents (parentComponent, comps);

    return comps.getFirst();
}

} // 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
{

CaretComponent::CaretComponent (Component* const keyFocusOwner)
    : owner (keyFocusOwner)
{
    setPaintingIsUnclipped (true);
    setInterceptsMouseClicks (false, false);
}

CaretComponent::~CaretComponent()
{
}

void CaretComponent::paint (Graphics& g)
{
    g.setColour (findColour (caretColourId, true));
    g.fillRect (getLocalBounds());
}

void CaretComponent::timerCallback()
{
    setVisible (shouldBeShown() && ! isVisible());
}

void CaretComponent::setCaretPosition (const Rectangle<int>& characterArea)
{
    startTimer (380);
    setVisible (shouldBeShown());
    setBounds (characterArea.withWidth (2));
}

bool CaretComponent::shouldBeShown() const
{
    return owner == nullptr || (owner->hasKeyboardFocus (false)
                                 && ! owner->isCurrentlyBlockedByAnotherModalComponent());
}

} // 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
{

bool KeyListener::keyStateChanged (const bool, Component*)
{
    return false;
}

} // 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
{

//==============================================================================
/**
*/
class JUCE_API  CaretComponent   : public Component,
                                   private Timer
{
public:
    //==============================================================================
    /** Creates the caret component.
        The keyFocusOwner is an optional component which the caret will check, making
        itself visible only when the keyFocusOwner has keyboard focus.
    */
    CaretComponent (Component* keyFocusOwner);

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

    //==============================================================================
    /** Sets the caret's position to place it next to the given character.
        The area is the rectangle containing the entire character that the caret is
        positioned on, so by default a vertical-line caret may choose to just show itself
        at the left of this area. You can override this method to customise its size.
        This method will also force the caret to reset its timer and become visible (if
        appropriate), so that as it moves, you can see where it is.
    */
    virtual void setCaretPosition (const Rectangle<int>& characterArea);

    /** A set of colour IDs to use to change the colour of various aspects of the caret.
        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
    {
        caretColourId    = 0x1000204, /**< The colour with which to draw the caret. */
    };

    //==============================================================================
    /** @internal */
    void paint (Graphics&) override;

private:
    Component* owner;

    bool shouldBeShown() const;
    void timerCallback() override;

    JUCE_DECLARE_NON_COPYABLE (CaretComponent)
};

} // 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
{

//==============================================================================
/**
    Receives callbacks when keys are pressed.

    You can add a key listener to a component to be informed when that component
    gets key events. See the Component::addListener method for more details.

    @see KeyPress, Component::addKeyListener, KeyPressMappingSet
*/
class JUCE_API  KeyListener
{
public:
    /** Destructor. */
    virtual ~KeyListener()  {}

    //==============================================================================
    /** Called to indicate that a key has been pressed.

        If your implementation returns true, then the key event is considered to have
        been consumed, and will not be passed on to any other components. If it returns
        false, then the key will be passed to other components that might want to use it.

        @param key                      the keystroke, including modifier keys
        @param originatingComponent     the component that received the key event
        @see keyStateChanged, Component::keyPressed
    */
    virtual bool keyPressed (const KeyPress& key,
                             Component* originatingComponent) = 0;

    /** Called when any key is pressed or released.

        When this is called, classes that might be interested in
        the state of one or more keys can use KeyPress::isKeyCurrentlyDown() to
        check whether their key has changed.

        If your implementation returns true, then the key event is considered to have
        been consumed, and will not be passed on to any other components. If it returns
        false, then the key will be passed to other components that might want to use it.

        @param originatingComponent     the component that received the key event
        @param isKeyDown                true if a key is being pressed, false if one is being released
        @see KeyPress, Component::keyStateChanged
    */
    virtual bool keyStateChanged (bool isKeyDown, Component* originatingComponent);
};

} // namespace juce