migrating to the latest JUCE version
This commit is contained in:
		@@ -1,90 +1,90 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-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 type used to hold the unique ID for an application command.
 | 
			
		||||
 | 
			
		||||
    This is a numeric type, so it can be stored as an integer.
 | 
			
		||||
 | 
			
		||||
    @see ApplicationCommandInfo, ApplicationCommandManager,
 | 
			
		||||
         ApplicationCommandTarget, KeyPressMappingSet
 | 
			
		||||
*/
 | 
			
		||||
using CommandID = int;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** A set of general-purpose application command IDs.
 | 
			
		||||
 | 
			
		||||
    Because these commands are likely to be used in most apps, they're defined
 | 
			
		||||
    here to help different apps to use the same numeric values for them.
 | 
			
		||||
 | 
			
		||||
    Of course you don't have to use these, but some of them are used internally by
 | 
			
		||||
    JUCE - e.g. the quit ID is recognised as a command by the JUCEApplication class.
 | 
			
		||||
 | 
			
		||||
    @see ApplicationCommandInfo, ApplicationCommandManager,
 | 
			
		||||
         ApplicationCommandTarget, KeyPressMappingSet
 | 
			
		||||
*/
 | 
			
		||||
namespace StandardApplicationCommandIDs
 | 
			
		||||
{
 | 
			
		||||
    enum
 | 
			
		||||
    {
 | 
			
		||||
        /** This command ID should be used to send a "Quit the App" command.
 | 
			
		||||
 | 
			
		||||
            This command is recognised by the JUCEApplication class, so if it is invoked
 | 
			
		||||
            and no other ApplicationCommandTarget handles the event first, the JUCEApplication
 | 
			
		||||
            object will catch it and call JUCEApplicationBase::systemRequestedQuit().
 | 
			
		||||
        */
 | 
			
		||||
        quit           = 0x1001,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "Delete" command. */
 | 
			
		||||
        del            = 0x1002,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "Cut" command. */
 | 
			
		||||
        cut            = 0x1003,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "Copy to clipboard" command. */
 | 
			
		||||
        copy           = 0x1004,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "Paste from clipboard" command. */
 | 
			
		||||
        paste          = 0x1005,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "Select all" command. */
 | 
			
		||||
        selectAll      = 0x1006,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "Deselect all" command. */
 | 
			
		||||
        deselectAll    = 0x1007,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "undo" command. */
 | 
			
		||||
        undo           = 0x1008,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "redo" command. */
 | 
			
		||||
        redo           = 0x1009
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   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 type used to hold the unique ID for an application command.
 | 
			
		||||
 | 
			
		||||
    This is a numeric type, so it can be stored as an integer.
 | 
			
		||||
 | 
			
		||||
    @see ApplicationCommandInfo, ApplicationCommandManager,
 | 
			
		||||
         ApplicationCommandTarget, KeyPressMappingSet
 | 
			
		||||
*/
 | 
			
		||||
using CommandID = int;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/** A set of general-purpose application command IDs.
 | 
			
		||||
 | 
			
		||||
    Because these commands are likely to be used in most apps, they're defined
 | 
			
		||||
    here to help different apps to use the same numeric values for them.
 | 
			
		||||
 | 
			
		||||
    Of course you don't have to use these, but some of them are used internally by
 | 
			
		||||
    JUCE - e.g. the quit ID is recognised as a command by the JUCEApplication class.
 | 
			
		||||
 | 
			
		||||
    @see ApplicationCommandInfo, ApplicationCommandManager,
 | 
			
		||||
         ApplicationCommandTarget, KeyPressMappingSet
 | 
			
		||||
*/
 | 
			
		||||
namespace StandardApplicationCommandIDs
 | 
			
		||||
{
 | 
			
		||||
    enum
 | 
			
		||||
    {
 | 
			
		||||
        /** This command ID should be used to send a "Quit the App" command.
 | 
			
		||||
 | 
			
		||||
            This command is recognised by the JUCEApplication class, so if it is invoked
 | 
			
		||||
            and no other ApplicationCommandTarget handles the event first, the JUCEApplication
 | 
			
		||||
            object will catch it and call JUCEApplicationBase::systemRequestedQuit().
 | 
			
		||||
        */
 | 
			
		||||
        quit           = 0x1001,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "Delete" command. */
 | 
			
		||||
        del            = 0x1002,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "Cut" command. */
 | 
			
		||||
        cut            = 0x1003,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "Copy to clipboard" command. */
 | 
			
		||||
        copy           = 0x1004,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "Paste from clipboard" command. */
 | 
			
		||||
        paste          = 0x1005,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "Select all" command. */
 | 
			
		||||
        selectAll      = 0x1006,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "Deselect all" command. */
 | 
			
		||||
        deselectAll    = 0x1007,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "undo" command. */
 | 
			
		||||
        undo           = 0x1008,
 | 
			
		||||
 | 
			
		||||
        /** The command ID that should be used to send a "redo" command. */
 | 
			
		||||
        redo           = 0x1009
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,66 +1,66 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ApplicationCommandInfo::ApplicationCommandInfo (const CommandID cid) noexcept
 | 
			
		||||
    : commandID (cid), flags (0)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandInfo::setInfo (const String& shortName_,
 | 
			
		||||
                                      const String& description_,
 | 
			
		||||
                                      const String& categoryName_,
 | 
			
		||||
                                      const int flags_) noexcept
 | 
			
		||||
{
 | 
			
		||||
    shortName = shortName_;
 | 
			
		||||
    description = description_;
 | 
			
		||||
    categoryName = categoryName_;
 | 
			
		||||
    flags = flags_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandInfo::setActive (const bool b) noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (b)
 | 
			
		||||
        flags &= ~isDisabled;
 | 
			
		||||
    else
 | 
			
		||||
        flags |= isDisabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandInfo::setTicked (const bool b) noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (b)
 | 
			
		||||
        flags |= isTicked;
 | 
			
		||||
    else
 | 
			
		||||
        flags &= ~isTicked;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandInfo::addDefaultKeypress (const int keyCode, ModifierKeys modifiers) noexcept
 | 
			
		||||
{
 | 
			
		||||
    defaultKeypresses.add (KeyPress (keyCode, modifiers, 0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ApplicationCommandInfo::ApplicationCommandInfo (const CommandID cid) noexcept
 | 
			
		||||
    : commandID (cid), flags (0)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandInfo::setInfo (const String& shortName_,
 | 
			
		||||
                                      const String& description_,
 | 
			
		||||
                                      const String& categoryName_,
 | 
			
		||||
                                      const int flags_) noexcept
 | 
			
		||||
{
 | 
			
		||||
    shortName = shortName_;
 | 
			
		||||
    description = description_;
 | 
			
		||||
    categoryName = categoryName_;
 | 
			
		||||
    flags = flags_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandInfo::setActive (const bool b) noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (b)
 | 
			
		||||
        flags &= ~isDisabled;
 | 
			
		||||
    else
 | 
			
		||||
        flags |= isDisabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandInfo::setTicked (const bool b) noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (b)
 | 
			
		||||
        flags |= isTicked;
 | 
			
		||||
    else
 | 
			
		||||
        flags &= ~isTicked;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandInfo::addDefaultKeypress (const int keyCode, ModifierKeys modifiers) noexcept
 | 
			
		||||
{
 | 
			
		||||
    defaultKeypresses.add (KeyPress (keyCode, modifiers, 0));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,190 +1,190 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Holds information describing an application command.
 | 
			
		||||
 | 
			
		||||
    This object is used to pass information about a particular command, such as its
 | 
			
		||||
    name, description and other usage flags.
 | 
			
		||||
 | 
			
		||||
    When an ApplicationCommandTarget is asked to provide information about the commands
 | 
			
		||||
    it can perform, this is the structure gets filled-in to describe each one.
 | 
			
		||||
 | 
			
		||||
    @see ApplicationCommandTarget, ApplicationCommandTarget::getCommandInfo(),
 | 
			
		||||
         ApplicationCommandManager
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
struct JUCE_API  ApplicationCommandInfo
 | 
			
		||||
{
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    explicit ApplicationCommandInfo (CommandID commandID) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets a number of the structures values at once.
 | 
			
		||||
 | 
			
		||||
        The meanings of each of the parameters is described below, in the appropriate
 | 
			
		||||
        member variable's description.
 | 
			
		||||
    */
 | 
			
		||||
    void setInfo (const String& shortName,
 | 
			
		||||
                  const String& description,
 | 
			
		||||
                  const String& categoryName,
 | 
			
		||||
                  int flags) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** An easy way to set or remove the isDisabled bit in the structure's flags field.
 | 
			
		||||
 | 
			
		||||
        If isActive is true, the flags member has the isDisabled bit cleared; if isActive
 | 
			
		||||
        is false, the bit is set.
 | 
			
		||||
    */
 | 
			
		||||
    void setActive (bool isActive) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** An easy way to set or remove the isTicked bit in the structure's flags field.
 | 
			
		||||
    */
 | 
			
		||||
    void setTicked (bool isTicked) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Handy method for adding a keypress to the defaultKeypresses array.
 | 
			
		||||
 | 
			
		||||
        This is just so you can write things like:
 | 
			
		||||
        @code
 | 
			
		||||
        myinfo.addDefaultKeypress ('s', ModifierKeys::commandModifier);
 | 
			
		||||
        @endcode
 | 
			
		||||
        instead of
 | 
			
		||||
        @code
 | 
			
		||||
        myinfo.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier));
 | 
			
		||||
        @endcode
 | 
			
		||||
    */
 | 
			
		||||
    void addDefaultKeypress (int keyCode, ModifierKeys modifiers) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The command's unique ID number.
 | 
			
		||||
    */
 | 
			
		||||
    CommandID commandID;
 | 
			
		||||
 | 
			
		||||
    /** A short name to describe the command.
 | 
			
		||||
 | 
			
		||||
        This should be suitable for use in menus, on buttons that trigger the command, etc.
 | 
			
		||||
 | 
			
		||||
        You can use the setInfo() method to quickly set this and some of the command's
 | 
			
		||||
        other properties.
 | 
			
		||||
    */
 | 
			
		||||
    String shortName;
 | 
			
		||||
 | 
			
		||||
    /** A longer description of the command.
 | 
			
		||||
 | 
			
		||||
        This should be suitable for use in contexts such as a KeyMappingEditorComponent or
 | 
			
		||||
        pop-up tooltip describing what the command does.
 | 
			
		||||
 | 
			
		||||
        You can use the setInfo() method to quickly set this and some of the command's
 | 
			
		||||
        other properties.
 | 
			
		||||
    */
 | 
			
		||||
    String description;
 | 
			
		||||
 | 
			
		||||
    /** A named category that the command fits into.
 | 
			
		||||
 | 
			
		||||
        You can give your commands any category you like, and these will be displayed in
 | 
			
		||||
        contexts such as the KeyMappingEditorComponent, where the category is used to group
 | 
			
		||||
        commands together.
 | 
			
		||||
 | 
			
		||||
        You can use the setInfo() method to quickly set this and some of the command's
 | 
			
		||||
        other properties.
 | 
			
		||||
    */
 | 
			
		||||
    String categoryName;
 | 
			
		||||
 | 
			
		||||
    /** A list of zero or more keypresses that should be used as the default keys for
 | 
			
		||||
        this command.
 | 
			
		||||
 | 
			
		||||
        Methods such as KeyPressMappingSet::resetToDefaultMappings() will use the keypresses in
 | 
			
		||||
        this list to initialise the default set of key-to-command mappings.
 | 
			
		||||
 | 
			
		||||
        @see addDefaultKeypress
 | 
			
		||||
    */
 | 
			
		||||
    Array<KeyPress> defaultKeypresses;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Flags describing the ways in which this command should be used.
 | 
			
		||||
 | 
			
		||||
        A bitwise-OR of these values is stored in the ApplicationCommandInfo::flags
 | 
			
		||||
        variable.
 | 
			
		||||
    */
 | 
			
		||||
    enum CommandFlags
 | 
			
		||||
    {
 | 
			
		||||
        /** Indicates that the command can't currently be performed.
 | 
			
		||||
 | 
			
		||||
            The ApplicationCommandTarget::getCommandInfo() method must set this flag if it's
 | 
			
		||||
            not currently permissible to perform the command. If the flag is set, then
 | 
			
		||||
            components that trigger the command, e.g. PopupMenu, may choose to grey-out the
 | 
			
		||||
            command or show themselves as not being enabled.
 | 
			
		||||
 | 
			
		||||
            @see ApplicationCommandInfo::setActive
 | 
			
		||||
        */
 | 
			
		||||
        isDisabled                  = 1 << 0,
 | 
			
		||||
 | 
			
		||||
        /** Indicates that the command should have a tick next to it on a menu.
 | 
			
		||||
 | 
			
		||||
            If your command is shown on a menu and this is set, it'll show a tick next to
 | 
			
		||||
            it. Other components such as buttons may also use this flag to indicate that it
 | 
			
		||||
            is a value that can be toggled, and is currently in the 'on' state.
 | 
			
		||||
 | 
			
		||||
            @see ApplicationCommandInfo::setTicked
 | 
			
		||||
        */
 | 
			
		||||
        isTicked                    = 1 << 1,
 | 
			
		||||
 | 
			
		||||
        /** If this flag is present, then when a KeyPressMappingSet invokes the command,
 | 
			
		||||
            it will call the command twice, once on key-down and again on key-up.
 | 
			
		||||
 | 
			
		||||
            @see ApplicationCommandTarget::InvocationInfo
 | 
			
		||||
        */
 | 
			
		||||
        wantsKeyUpDownCallbacks     = 1 << 2,
 | 
			
		||||
 | 
			
		||||
        /** If this flag is present, then a KeyMappingEditorComponent will not display the
 | 
			
		||||
            command in its list.
 | 
			
		||||
        */
 | 
			
		||||
        hiddenFromKeyEditor         = 1 << 3,
 | 
			
		||||
 | 
			
		||||
        /** If this flag is present, then a KeyMappingEditorComponent will display the
 | 
			
		||||
            command in its list, but won't allow the assigned keypress to be changed.
 | 
			
		||||
        */
 | 
			
		||||
        readOnlyInKeyEditor         = 1 << 4,
 | 
			
		||||
 | 
			
		||||
        /** If this flag is present and the command is invoked from a keypress, then any
 | 
			
		||||
            buttons or menus that are also connected to the command will not flash to
 | 
			
		||||
            indicate that they've been triggered.
 | 
			
		||||
        */
 | 
			
		||||
        dontTriggerVisualFeedback   = 1 << 5
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** A bitwise-OR of the values specified in the CommandFlags enum.
 | 
			
		||||
 | 
			
		||||
        You can use the setInfo() method to quickly set this and some of the command's
 | 
			
		||||
        other properties.
 | 
			
		||||
    */
 | 
			
		||||
    int flags;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Holds information describing an application command.
 | 
			
		||||
 | 
			
		||||
    This object is used to pass information about a particular command, such as its
 | 
			
		||||
    name, description and other usage flags.
 | 
			
		||||
 | 
			
		||||
    When an ApplicationCommandTarget is asked to provide information about the commands
 | 
			
		||||
    it can perform, this is the structure gets filled-in to describe each one.
 | 
			
		||||
 | 
			
		||||
    @see ApplicationCommandTarget, ApplicationCommandTarget::getCommandInfo(),
 | 
			
		||||
         ApplicationCommandManager
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
struct JUCE_API  ApplicationCommandInfo
 | 
			
		||||
{
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    explicit ApplicationCommandInfo (CommandID commandID) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Sets a number of the structures values at once.
 | 
			
		||||
 | 
			
		||||
        The meanings of each of the parameters is described below, in the appropriate
 | 
			
		||||
        member variable's description.
 | 
			
		||||
    */
 | 
			
		||||
    void setInfo (const String& shortName,
 | 
			
		||||
                  const String& description,
 | 
			
		||||
                  const String& categoryName,
 | 
			
		||||
                  int flags) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** An easy way to set or remove the isDisabled bit in the structure's flags field.
 | 
			
		||||
 | 
			
		||||
        If isActive is true, the flags member has the isDisabled bit cleared; if isActive
 | 
			
		||||
        is false, the bit is set.
 | 
			
		||||
    */
 | 
			
		||||
    void setActive (bool isActive) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** An easy way to set or remove the isTicked bit in the structure's flags field.
 | 
			
		||||
    */
 | 
			
		||||
    void setTicked (bool isTicked) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Handy method for adding a keypress to the defaultKeypresses array.
 | 
			
		||||
 | 
			
		||||
        This is just so you can write things like:
 | 
			
		||||
        @code
 | 
			
		||||
        myinfo.addDefaultKeypress ('s', ModifierKeys::commandModifier);
 | 
			
		||||
        @endcode
 | 
			
		||||
        instead of
 | 
			
		||||
        @code
 | 
			
		||||
        myinfo.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier));
 | 
			
		||||
        @endcode
 | 
			
		||||
    */
 | 
			
		||||
    void addDefaultKeypress (int keyCode, ModifierKeys modifiers) noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** The command's unique ID number.
 | 
			
		||||
    */
 | 
			
		||||
    CommandID commandID;
 | 
			
		||||
 | 
			
		||||
    /** A short name to describe the command.
 | 
			
		||||
 | 
			
		||||
        This should be suitable for use in menus, on buttons that trigger the command, etc.
 | 
			
		||||
 | 
			
		||||
        You can use the setInfo() method to quickly set this and some of the command's
 | 
			
		||||
        other properties.
 | 
			
		||||
    */
 | 
			
		||||
    String shortName;
 | 
			
		||||
 | 
			
		||||
    /** A longer description of the command.
 | 
			
		||||
 | 
			
		||||
        This should be suitable for use in contexts such as a KeyMappingEditorComponent or
 | 
			
		||||
        pop-up tooltip describing what the command does.
 | 
			
		||||
 | 
			
		||||
        You can use the setInfo() method to quickly set this and some of the command's
 | 
			
		||||
        other properties.
 | 
			
		||||
    */
 | 
			
		||||
    String description;
 | 
			
		||||
 | 
			
		||||
    /** A named category that the command fits into.
 | 
			
		||||
 | 
			
		||||
        You can give your commands any category you like, and these will be displayed in
 | 
			
		||||
        contexts such as the KeyMappingEditorComponent, where the category is used to group
 | 
			
		||||
        commands together.
 | 
			
		||||
 | 
			
		||||
        You can use the setInfo() method to quickly set this and some of the command's
 | 
			
		||||
        other properties.
 | 
			
		||||
    */
 | 
			
		||||
    String categoryName;
 | 
			
		||||
 | 
			
		||||
    /** A list of zero or more keypresses that should be used as the default keys for
 | 
			
		||||
        this command.
 | 
			
		||||
 | 
			
		||||
        Methods such as KeyPressMappingSet::resetToDefaultMappings() will use the keypresses in
 | 
			
		||||
        this list to initialise the default set of key-to-command mappings.
 | 
			
		||||
 | 
			
		||||
        @see addDefaultKeypress
 | 
			
		||||
    */
 | 
			
		||||
    Array<KeyPress> defaultKeypresses;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Flags describing the ways in which this command should be used.
 | 
			
		||||
 | 
			
		||||
        A bitwise-OR of these values is stored in the ApplicationCommandInfo::flags
 | 
			
		||||
        variable.
 | 
			
		||||
    */
 | 
			
		||||
    enum CommandFlags
 | 
			
		||||
    {
 | 
			
		||||
        /** Indicates that the command can't currently be performed.
 | 
			
		||||
 | 
			
		||||
            The ApplicationCommandTarget::getCommandInfo() method must set this flag if it's
 | 
			
		||||
            not currently permissible to perform the command. If the flag is set, then
 | 
			
		||||
            components that trigger the command, e.g. PopupMenu, may choose to grey-out the
 | 
			
		||||
            command or show themselves as not being enabled.
 | 
			
		||||
 | 
			
		||||
            @see ApplicationCommandInfo::setActive
 | 
			
		||||
        */
 | 
			
		||||
        isDisabled                  = 1 << 0,
 | 
			
		||||
 | 
			
		||||
        /** Indicates that the command should have a tick next to it on a menu.
 | 
			
		||||
 | 
			
		||||
            If your command is shown on a menu and this is set, it'll show a tick next to
 | 
			
		||||
            it. Other components such as buttons may also use this flag to indicate that it
 | 
			
		||||
            is a value that can be toggled, and is currently in the 'on' state.
 | 
			
		||||
 | 
			
		||||
            @see ApplicationCommandInfo::setTicked
 | 
			
		||||
        */
 | 
			
		||||
        isTicked                    = 1 << 1,
 | 
			
		||||
 | 
			
		||||
        /** If this flag is present, then when a KeyPressMappingSet invokes the command,
 | 
			
		||||
            it will call the command twice, once on key-down and again on key-up.
 | 
			
		||||
 | 
			
		||||
            @see ApplicationCommandTarget::InvocationInfo
 | 
			
		||||
        */
 | 
			
		||||
        wantsKeyUpDownCallbacks     = 1 << 2,
 | 
			
		||||
 | 
			
		||||
        /** If this flag is present, then a KeyMappingEditorComponent will not display the
 | 
			
		||||
            command in its list.
 | 
			
		||||
        */
 | 
			
		||||
        hiddenFromKeyEditor         = 1 << 3,
 | 
			
		||||
 | 
			
		||||
        /** If this flag is present, then a KeyMappingEditorComponent will display the
 | 
			
		||||
            command in its list, but won't allow the assigned keypress to be changed.
 | 
			
		||||
        */
 | 
			
		||||
        readOnlyInKeyEditor         = 1 << 4,
 | 
			
		||||
 | 
			
		||||
        /** If this flag is present and the command is invoked from a keypress, then any
 | 
			
		||||
            buttons or menus that are also connected to the command will not flash to
 | 
			
		||||
            indicate that they've been triggered.
 | 
			
		||||
        */
 | 
			
		||||
        dontTriggerVisualFeedback   = 1 << 5
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    /** A bitwise-OR of the values specified in the CommandFlags enum.
 | 
			
		||||
 | 
			
		||||
        You can use the setInfo() method to quickly set this and some of the command's
 | 
			
		||||
        other properties.
 | 
			
		||||
    */
 | 
			
		||||
    int flags;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,322 +1,322 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ApplicationCommandManager::ApplicationCommandManager()
 | 
			
		||||
{
 | 
			
		||||
    keyMappings.reset (new KeyPressMappingSet (*this));
 | 
			
		||||
    Desktop::getInstance().addFocusChangeListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ApplicationCommandManager::~ApplicationCommandManager()
 | 
			
		||||
{
 | 
			
		||||
    Desktop::getInstance().removeFocusChangeListener (this);
 | 
			
		||||
    keyMappings.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ApplicationCommandManager::clearCommands()
 | 
			
		||||
{
 | 
			
		||||
    commands.clear();
 | 
			
		||||
    keyMappings->clearAllKeyPresses();
 | 
			
		||||
    triggerAsyncUpdate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::registerCommand (const ApplicationCommandInfo& newCommand)
 | 
			
		||||
{
 | 
			
		||||
    // zero isn't a valid command ID!
 | 
			
		||||
    jassert (newCommand.commandID != 0);
 | 
			
		||||
 | 
			
		||||
    // the name isn't optional!
 | 
			
		||||
    jassert (newCommand.shortName.isNotEmpty());
 | 
			
		||||
 | 
			
		||||
    if (auto* command = getMutableCommandForID (newCommand.commandID))
 | 
			
		||||
    {
 | 
			
		||||
        // Trying to re-register the same command ID with different parameters can often indicate a typo.
 | 
			
		||||
        // This assertion is here because I've found it useful catching some mistakes, but it may also cause
 | 
			
		||||
        // false alarms if you're deliberately updating some flags for a command.
 | 
			
		||||
        jassert (newCommand.shortName == getCommandForID (newCommand.commandID)->shortName
 | 
			
		||||
                  && newCommand.categoryName == getCommandForID (newCommand.commandID)->categoryName
 | 
			
		||||
                  && newCommand.defaultKeypresses == getCommandForID (newCommand.commandID)->defaultKeypresses
 | 
			
		||||
                  && (newCommand.flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor))
 | 
			
		||||
                       == (getCommandForID (newCommand.commandID)->flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor)));
 | 
			
		||||
 | 
			
		||||
        *command = newCommand;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        auto* newInfo = new ApplicationCommandInfo (newCommand);
 | 
			
		||||
        newInfo->flags &= ~ApplicationCommandInfo::isTicked;
 | 
			
		||||
        commands.add (newInfo);
 | 
			
		||||
 | 
			
		||||
        keyMappings->resetToDefaultMapping (newCommand.commandID);
 | 
			
		||||
 | 
			
		||||
        triggerAsyncUpdate();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::registerAllCommandsForTarget (ApplicationCommandTarget* target)
 | 
			
		||||
{
 | 
			
		||||
    if (target != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        Array<CommandID> commandIDs;
 | 
			
		||||
        target->getAllCommands (commandIDs);
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < commandIDs.size(); ++i)
 | 
			
		||||
        {
 | 
			
		||||
            ApplicationCommandInfo info (commandIDs.getUnchecked(i));
 | 
			
		||||
            target->getCommandInfo (info.commandID, info);
 | 
			
		||||
 | 
			
		||||
            registerCommand (info);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::removeCommand (const CommandID commandID)
 | 
			
		||||
{
 | 
			
		||||
    for (int i = commands.size(); --i >= 0;)
 | 
			
		||||
    {
 | 
			
		||||
        if (commands.getUnchecked (i)->commandID == commandID)
 | 
			
		||||
        {
 | 
			
		||||
            commands.remove (i);
 | 
			
		||||
            triggerAsyncUpdate();
 | 
			
		||||
 | 
			
		||||
            const Array<KeyPress> keys (keyMappings->getKeyPressesAssignedToCommand (commandID));
 | 
			
		||||
 | 
			
		||||
            for (int j = keys.size(); --j >= 0;)
 | 
			
		||||
                keyMappings->removeKeyPress (keys.getReference (j));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::commandStatusChanged()
 | 
			
		||||
{
 | 
			
		||||
    triggerAsyncUpdate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ApplicationCommandInfo* ApplicationCommandManager::getMutableCommandForID (CommandID commandID) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    for (int i = commands.size(); --i >= 0;)
 | 
			
		||||
        if (commands.getUnchecked(i)->commandID == commandID)
 | 
			
		||||
            return commands.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ApplicationCommandInfo* ApplicationCommandManager::getCommandForID (CommandID commandID) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return getMutableCommandForID (commandID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String ApplicationCommandManager::getNameOfCommand (CommandID commandID) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (auto* ci = getCommandForID (commandID))
 | 
			
		||||
        return ci->shortName;
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String ApplicationCommandManager::getDescriptionOfCommand (CommandID commandID) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (auto* ci = getCommandForID (commandID))
 | 
			
		||||
        return ci->description.isNotEmpty() ? ci->description
 | 
			
		||||
                                            : ci->shortName;
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StringArray ApplicationCommandManager::getCommandCategories() const
 | 
			
		||||
{
 | 
			
		||||
    StringArray s;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < commands.size(); ++i)
 | 
			
		||||
        s.addIfNotAlreadyThere (commands.getUnchecked(i)->categoryName, false);
 | 
			
		||||
 | 
			
		||||
    return s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Array<CommandID> ApplicationCommandManager::getCommandsInCategory (const String& categoryName) const
 | 
			
		||||
{
 | 
			
		||||
    Array<CommandID> results;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < commands.size(); ++i)
 | 
			
		||||
        if (commands.getUnchecked(i)->categoryName == categoryName)
 | 
			
		||||
            results.add (commands.getUnchecked(i)->commandID);
 | 
			
		||||
 | 
			
		||||
    return results;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
bool ApplicationCommandManager::invokeDirectly (CommandID commandID, bool asynchronously)
 | 
			
		||||
{
 | 
			
		||||
    ApplicationCommandTarget::InvocationInfo info (commandID);
 | 
			
		||||
    info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct;
 | 
			
		||||
 | 
			
		||||
    return invoke (info, asynchronously);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ApplicationCommandManager::invoke (const ApplicationCommandTarget::InvocationInfo& inf, bool asynchronously)
 | 
			
		||||
{
 | 
			
		||||
    // This call isn't thread-safe for use from a non-UI thread without locking the message
 | 
			
		||||
    // manager first..
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
 | 
			
		||||
    bool ok = false;
 | 
			
		||||
    ApplicationCommandInfo commandInfo (0);
 | 
			
		||||
 | 
			
		||||
    if (auto* target = getTargetForCommand (inf.commandID, commandInfo))
 | 
			
		||||
    {
 | 
			
		||||
        ApplicationCommandTarget::InvocationInfo info (inf);
 | 
			
		||||
        info.commandFlags = commandInfo.flags;
 | 
			
		||||
 | 
			
		||||
        sendListenerInvokeCallback (info);
 | 
			
		||||
        ok = target->invoke (info, asynchronously);
 | 
			
		||||
        commandStatusChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ApplicationCommandTarget* ApplicationCommandManager::getFirstCommandTarget (CommandID)
 | 
			
		||||
{
 | 
			
		||||
    return firstTarget != nullptr ? firstTarget
 | 
			
		||||
                                  : findDefaultComponentTarget();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::setFirstCommandTarget (ApplicationCommandTarget* newTarget) noexcept
 | 
			
		||||
{
 | 
			
		||||
    firstTarget = newTarget;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ApplicationCommandTarget* ApplicationCommandManager::getTargetForCommand (CommandID commandID,
 | 
			
		||||
                                                                          ApplicationCommandInfo& upToDateInfo)
 | 
			
		||||
{
 | 
			
		||||
    auto* target = getFirstCommandTarget (commandID);
 | 
			
		||||
 | 
			
		||||
    if (target == nullptr)
 | 
			
		||||
        target = JUCEApplication::getInstance();
 | 
			
		||||
 | 
			
		||||
    if (target != nullptr)
 | 
			
		||||
        target = target->getTargetForCommand (commandID);
 | 
			
		||||
 | 
			
		||||
    if (target != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        upToDateInfo.commandID = commandID;
 | 
			
		||||
        target->getCommandInfo (commandID, upToDateInfo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return target;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ApplicationCommandTarget* ApplicationCommandManager::findTargetForComponent (Component* c)
 | 
			
		||||
{
 | 
			
		||||
    auto* target = dynamic_cast<ApplicationCommandTarget*> (c);
 | 
			
		||||
 | 
			
		||||
    if (target == nullptr && c != nullptr)
 | 
			
		||||
        target = c->findParentComponentOfClass<ApplicationCommandTarget>();
 | 
			
		||||
 | 
			
		||||
    return target;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ApplicationCommandTarget* ApplicationCommandManager::findDefaultComponentTarget()
 | 
			
		||||
{
 | 
			
		||||
    auto* c = Component::getCurrentlyFocusedComponent();
 | 
			
		||||
 | 
			
		||||
    if (c == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* activeWindow = TopLevelWindow::getActiveTopLevelWindow())
 | 
			
		||||
        {
 | 
			
		||||
            if (auto* peer = activeWindow->getPeer())
 | 
			
		||||
            {
 | 
			
		||||
                c = peer->getLastFocusedSubcomponent();
 | 
			
		||||
 | 
			
		||||
                if (c == nullptr)
 | 
			
		||||
                    c = activeWindow;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (c == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        auto& desktop = Desktop::getInstance();
 | 
			
		||||
 | 
			
		||||
        // getting a bit desperate now: try all desktop comps..
 | 
			
		||||
        for (int i = desktop.getNumComponents(); --i >= 0;)
 | 
			
		||||
            if (auto* component = desktop.getComponent (i))
 | 
			
		||||
                if (isForegroundOrEmbeddedProcess (component))
 | 
			
		||||
                    if (auto* peer = component->getPeer())
 | 
			
		||||
                        if (auto* target = findTargetForComponent (peer->getLastFocusedSubcomponent()))
 | 
			
		||||
                            return target;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (c != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        // if we're focused on a ResizableWindow, chances are that it's the content
 | 
			
		||||
        // component that really should get the event. And if not, the event will
 | 
			
		||||
        // still be passed up to the top level window anyway, so let's send it to the
 | 
			
		||||
        // content comp.
 | 
			
		||||
        if (auto* resizableWindow = dynamic_cast<ResizableWindow*> (c))
 | 
			
		||||
            if (auto* content = resizableWindow->getContentComponent())
 | 
			
		||||
                c = content;
 | 
			
		||||
 | 
			
		||||
        if (auto* target = findTargetForComponent (c))
 | 
			
		||||
            return target;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return JUCEApplication::getInstance();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ApplicationCommandManager::addListener (ApplicationCommandManagerListener* listener)
 | 
			
		||||
{
 | 
			
		||||
    listeners.add (listener);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::removeListener (ApplicationCommandManagerListener* listener)
 | 
			
		||||
{
 | 
			
		||||
    listeners.remove (listener);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo& info)
 | 
			
		||||
{
 | 
			
		||||
    listeners.call ([&] (ApplicationCommandManagerListener& l) { l.applicationCommandInvoked (info); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::handleAsyncUpdate()
 | 
			
		||||
{
 | 
			
		||||
    listeners.call ([] (ApplicationCommandManagerListener& l) { l.applicationCommandListChanged(); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::globalFocusChanged (Component*)
 | 
			
		||||
{
 | 
			
		||||
    commandStatusChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
ApplicationCommandManager::ApplicationCommandManager()
 | 
			
		||||
{
 | 
			
		||||
    keyMappings.reset (new KeyPressMappingSet (*this));
 | 
			
		||||
    Desktop::getInstance().addFocusChangeListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ApplicationCommandManager::~ApplicationCommandManager()
 | 
			
		||||
{
 | 
			
		||||
    Desktop::getInstance().removeFocusChangeListener (this);
 | 
			
		||||
    keyMappings.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ApplicationCommandManager::clearCommands()
 | 
			
		||||
{
 | 
			
		||||
    commands.clear();
 | 
			
		||||
    keyMappings->clearAllKeyPresses();
 | 
			
		||||
    triggerAsyncUpdate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::registerCommand (const ApplicationCommandInfo& newCommand)
 | 
			
		||||
{
 | 
			
		||||
    // zero isn't a valid command ID!
 | 
			
		||||
    jassert (newCommand.commandID != 0);
 | 
			
		||||
 | 
			
		||||
    // the name isn't optional!
 | 
			
		||||
    jassert (newCommand.shortName.isNotEmpty());
 | 
			
		||||
 | 
			
		||||
    if (auto* command = getMutableCommandForID (newCommand.commandID))
 | 
			
		||||
    {
 | 
			
		||||
        // Trying to re-register the same command ID with different parameters can often indicate a typo.
 | 
			
		||||
        // This assertion is here because I've found it useful catching some mistakes, but it may also cause
 | 
			
		||||
        // false alarms if you're deliberately updating some flags for a command.
 | 
			
		||||
        jassert (newCommand.shortName == getCommandForID (newCommand.commandID)->shortName
 | 
			
		||||
                  && newCommand.categoryName == getCommandForID (newCommand.commandID)->categoryName
 | 
			
		||||
                  && newCommand.defaultKeypresses == getCommandForID (newCommand.commandID)->defaultKeypresses
 | 
			
		||||
                  && (newCommand.flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor))
 | 
			
		||||
                       == (getCommandForID (newCommand.commandID)->flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor)));
 | 
			
		||||
 | 
			
		||||
        *command = newCommand;
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
        auto* newInfo = new ApplicationCommandInfo (newCommand);
 | 
			
		||||
        newInfo->flags &= ~ApplicationCommandInfo::isTicked;
 | 
			
		||||
        commands.add (newInfo);
 | 
			
		||||
 | 
			
		||||
        keyMappings->resetToDefaultMapping (newCommand.commandID);
 | 
			
		||||
 | 
			
		||||
        triggerAsyncUpdate();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::registerAllCommandsForTarget (ApplicationCommandTarget* target)
 | 
			
		||||
{
 | 
			
		||||
    if (target != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        Array<CommandID> commandIDs;
 | 
			
		||||
        target->getAllCommands (commandIDs);
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < commandIDs.size(); ++i)
 | 
			
		||||
        {
 | 
			
		||||
            ApplicationCommandInfo info (commandIDs.getUnchecked(i));
 | 
			
		||||
            target->getCommandInfo (info.commandID, info);
 | 
			
		||||
 | 
			
		||||
            registerCommand (info);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::removeCommand (const CommandID commandID)
 | 
			
		||||
{
 | 
			
		||||
    for (int i = commands.size(); --i >= 0;)
 | 
			
		||||
    {
 | 
			
		||||
        if (commands.getUnchecked (i)->commandID == commandID)
 | 
			
		||||
        {
 | 
			
		||||
            commands.remove (i);
 | 
			
		||||
            triggerAsyncUpdate();
 | 
			
		||||
 | 
			
		||||
            const Array<KeyPress> keys (keyMappings->getKeyPressesAssignedToCommand (commandID));
 | 
			
		||||
 | 
			
		||||
            for (int j = keys.size(); --j >= 0;)
 | 
			
		||||
                keyMappings->removeKeyPress (keys.getReference (j));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::commandStatusChanged()
 | 
			
		||||
{
 | 
			
		||||
    triggerAsyncUpdate();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ApplicationCommandInfo* ApplicationCommandManager::getMutableCommandForID (CommandID commandID) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    for (int i = commands.size(); --i >= 0;)
 | 
			
		||||
        if (commands.getUnchecked(i)->commandID == commandID)
 | 
			
		||||
            return commands.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ApplicationCommandInfo* ApplicationCommandManager::getCommandForID (CommandID commandID) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    return getMutableCommandForID (commandID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String ApplicationCommandManager::getNameOfCommand (CommandID commandID) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (auto* ci = getCommandForID (commandID))
 | 
			
		||||
        return ci->shortName;
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String ApplicationCommandManager::getDescriptionOfCommand (CommandID commandID) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    if (auto* ci = getCommandForID (commandID))
 | 
			
		||||
        return ci->description.isNotEmpty() ? ci->description
 | 
			
		||||
                                            : ci->shortName;
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StringArray ApplicationCommandManager::getCommandCategories() const
 | 
			
		||||
{
 | 
			
		||||
    StringArray s;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < commands.size(); ++i)
 | 
			
		||||
        s.addIfNotAlreadyThere (commands.getUnchecked(i)->categoryName, false);
 | 
			
		||||
 | 
			
		||||
    return s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Array<CommandID> ApplicationCommandManager::getCommandsInCategory (const String& categoryName) const
 | 
			
		||||
{
 | 
			
		||||
    Array<CommandID> results;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < commands.size(); ++i)
 | 
			
		||||
        if (commands.getUnchecked(i)->categoryName == categoryName)
 | 
			
		||||
            results.add (commands.getUnchecked(i)->commandID);
 | 
			
		||||
 | 
			
		||||
    return results;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
bool ApplicationCommandManager::invokeDirectly (CommandID commandID, bool asynchronously)
 | 
			
		||||
{
 | 
			
		||||
    ApplicationCommandTarget::InvocationInfo info (commandID);
 | 
			
		||||
    info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct;
 | 
			
		||||
 | 
			
		||||
    return invoke (info, asynchronously);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ApplicationCommandManager::invoke (const ApplicationCommandTarget::InvocationInfo& inf, bool asynchronously)
 | 
			
		||||
{
 | 
			
		||||
    // This call isn't thread-safe for use from a non-UI thread without locking the message
 | 
			
		||||
    // manager first..
 | 
			
		||||
    JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
 | 
			
		||||
 | 
			
		||||
    bool ok = false;
 | 
			
		||||
    ApplicationCommandInfo commandInfo (0);
 | 
			
		||||
 | 
			
		||||
    if (auto* target = getTargetForCommand (inf.commandID, commandInfo))
 | 
			
		||||
    {
 | 
			
		||||
        ApplicationCommandTarget::InvocationInfo info (inf);
 | 
			
		||||
        info.commandFlags = commandInfo.flags;
 | 
			
		||||
 | 
			
		||||
        sendListenerInvokeCallback (info);
 | 
			
		||||
        ok = target->invoke (info, asynchronously);
 | 
			
		||||
        commandStatusChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ApplicationCommandTarget* ApplicationCommandManager::getFirstCommandTarget (CommandID)
 | 
			
		||||
{
 | 
			
		||||
    return firstTarget != nullptr ? firstTarget
 | 
			
		||||
                                  : findDefaultComponentTarget();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::setFirstCommandTarget (ApplicationCommandTarget* newTarget) noexcept
 | 
			
		||||
{
 | 
			
		||||
    firstTarget = newTarget;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ApplicationCommandTarget* ApplicationCommandManager::getTargetForCommand (CommandID commandID,
 | 
			
		||||
                                                                          ApplicationCommandInfo& upToDateInfo)
 | 
			
		||||
{
 | 
			
		||||
    auto* target = getFirstCommandTarget (commandID);
 | 
			
		||||
 | 
			
		||||
    if (target == nullptr)
 | 
			
		||||
        target = JUCEApplication::getInstance();
 | 
			
		||||
 | 
			
		||||
    if (target != nullptr)
 | 
			
		||||
        target = target->getTargetForCommand (commandID);
 | 
			
		||||
 | 
			
		||||
    if (target != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        upToDateInfo.commandID = commandID;
 | 
			
		||||
        target->getCommandInfo (commandID, upToDateInfo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return target;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ApplicationCommandTarget* ApplicationCommandManager::findTargetForComponent (Component* c)
 | 
			
		||||
{
 | 
			
		||||
    auto* target = dynamic_cast<ApplicationCommandTarget*> (c);
 | 
			
		||||
 | 
			
		||||
    if (target == nullptr && c != nullptr)
 | 
			
		||||
        target = c->findParentComponentOfClass<ApplicationCommandTarget>();
 | 
			
		||||
 | 
			
		||||
    return target;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ApplicationCommandTarget* ApplicationCommandManager::findDefaultComponentTarget()
 | 
			
		||||
{
 | 
			
		||||
    auto* c = Component::getCurrentlyFocusedComponent();
 | 
			
		||||
 | 
			
		||||
    if (c == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        if (auto* activeWindow = TopLevelWindow::getActiveTopLevelWindow())
 | 
			
		||||
        {
 | 
			
		||||
            if (auto* peer = activeWindow->getPeer())
 | 
			
		||||
            {
 | 
			
		||||
                c = peer->getLastFocusedSubcomponent();
 | 
			
		||||
 | 
			
		||||
                if (c == nullptr)
 | 
			
		||||
                    c = activeWindow;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (c == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        auto& desktop = Desktop::getInstance();
 | 
			
		||||
 | 
			
		||||
        // getting a bit desperate now: try all desktop comps..
 | 
			
		||||
        for (int i = desktop.getNumComponents(); --i >= 0;)
 | 
			
		||||
            if (auto* component = desktop.getComponent (i))
 | 
			
		||||
                if (isForegroundOrEmbeddedProcess (component))
 | 
			
		||||
                    if (auto* peer = component->getPeer())
 | 
			
		||||
                        if (auto* target = findTargetForComponent (peer->getLastFocusedSubcomponent()))
 | 
			
		||||
                            return target;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (c != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        // if we're focused on a ResizableWindow, chances are that it's the content
 | 
			
		||||
        // component that really should get the event. And if not, the event will
 | 
			
		||||
        // still be passed up to the top level window anyway, so let's send it to the
 | 
			
		||||
        // content comp.
 | 
			
		||||
        if (auto* resizableWindow = dynamic_cast<ResizableWindow*> (c))
 | 
			
		||||
            if (auto* content = resizableWindow->getContentComponent())
 | 
			
		||||
                c = content;
 | 
			
		||||
 | 
			
		||||
        if (auto* target = findTargetForComponent (c))
 | 
			
		||||
            return target;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return JUCEApplication::getInstance();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
void ApplicationCommandManager::addListener (ApplicationCommandManagerListener* listener)
 | 
			
		||||
{
 | 
			
		||||
    listeners.add (listener);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::removeListener (ApplicationCommandManagerListener* listener)
 | 
			
		||||
{
 | 
			
		||||
    listeners.remove (listener);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo& info)
 | 
			
		||||
{
 | 
			
		||||
    listeners.call ([&] (ApplicationCommandManagerListener& l) { l.applicationCommandInvoked (info); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::handleAsyncUpdate()
 | 
			
		||||
{
 | 
			
		||||
    listeners.call ([] (ApplicationCommandManagerListener& l) { l.applicationCommandListChanged(); });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ApplicationCommandManager::globalFocusChanged (Component*)
 | 
			
		||||
{
 | 
			
		||||
    commandStatusChanged();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,349 +1,349 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    One of these objects holds a list of all the commands your app can perform,
 | 
			
		||||
    and despatches these commands when needed.
 | 
			
		||||
 | 
			
		||||
    Application commands are a good way to trigger actions in your app, e.g. "Quit",
 | 
			
		||||
    "Copy", "Paste", etc. Menus, buttons and keypresses can all be given commands
 | 
			
		||||
    to invoke automatically, which means you don't have to handle the result of a menu
 | 
			
		||||
    or button click manually. Commands are despatched to ApplicationCommandTarget objects
 | 
			
		||||
    which can choose which events they want to handle.
 | 
			
		||||
 | 
			
		||||
    This architecture also allows for nested ApplicationCommandTargets, so that for example
 | 
			
		||||
    you could have two different objects, one inside the other, both of which can respond to
 | 
			
		||||
    a "delete" command. Depending on which one has focus, the command will be sent to the
 | 
			
		||||
    appropriate place, regardless of whether it was triggered by a menu, keypress or some other
 | 
			
		||||
    method.
 | 
			
		||||
 | 
			
		||||
    To set up your app to use commands, you'll need to do the following:
 | 
			
		||||
 | 
			
		||||
    - Create a global ApplicationCommandManager to hold the list of all possible
 | 
			
		||||
      commands. (This will also manage a set of key-mappings for them).
 | 
			
		||||
 | 
			
		||||
    - Make some of your UI components (or other objects) inherit from ApplicationCommandTarget.
 | 
			
		||||
      This allows the object to provide a list of commands that it can perform, and
 | 
			
		||||
      to handle them.
 | 
			
		||||
 | 
			
		||||
    - Register each type of command using ApplicationCommandManager::registerAllCommandsForTarget(),
 | 
			
		||||
      or ApplicationCommandManager::registerCommand().
 | 
			
		||||
 | 
			
		||||
    - If you want key-presses to trigger your commands, use the ApplicationCommandManager::getKeyMappings()
 | 
			
		||||
      method to access the key-mapper object, which you will need to register as a key-listener
 | 
			
		||||
      in whatever top-level component you're using. See the KeyPressMappingSet class for more help
 | 
			
		||||
      about setting this up.
 | 
			
		||||
 | 
			
		||||
    - Use methods such as PopupMenu::addCommandItem() or Button::setCommandToTrigger() to
 | 
			
		||||
      cause these commands to be invoked automatically.
 | 
			
		||||
 | 
			
		||||
    - Commands can be invoked directly by your code using ApplicationCommandManager::invokeDirectly().
 | 
			
		||||
 | 
			
		||||
    When a command is invoked, the ApplicationCommandManager will try to choose the best
 | 
			
		||||
    ApplicationCommandTarget to receive the specified command. To do this it will use the
 | 
			
		||||
    current keyboard focus to see which component might be interested, and will search the
 | 
			
		||||
    component hierarchy for those that also implement the ApplicationCommandTarget interface.
 | 
			
		||||
    If an ApplicationCommandTarget isn't interested in the command that is being invoked, then
 | 
			
		||||
    the next one in line will be tried (see the ApplicationCommandTarget::getNextCommandTarget()
 | 
			
		||||
    method), and so on until ApplicationCommandTarget::getNextCommandTarget() returns nullptr.
 | 
			
		||||
    At this point if the command still hasn't been performed, it will be passed to the current
 | 
			
		||||
    JUCEApplication object (which is itself an ApplicationCommandTarget).
 | 
			
		||||
 | 
			
		||||
    To exert some custom control over which ApplicationCommandTarget is chosen to invoke a command,
 | 
			
		||||
    you can override the ApplicationCommandManager::getFirstCommandTarget() method and choose
 | 
			
		||||
    the object yourself.
 | 
			
		||||
 | 
			
		||||
    @see ApplicationCommandTarget, ApplicationCommandInfo
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ApplicationCommandManager   : private AsyncUpdater,
 | 
			
		||||
                                              private FocusChangeListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an ApplicationCommandManager.
 | 
			
		||||
 | 
			
		||||
        Once created, you'll need to register all your app's commands with it, using
 | 
			
		||||
        ApplicationCommandManager::registerAllCommandsForTarget() or
 | 
			
		||||
        ApplicationCommandManager::registerCommand().
 | 
			
		||||
    */
 | 
			
		||||
    ApplicationCommandManager();
 | 
			
		||||
 | 
			
		||||
    /** Destructor.
 | 
			
		||||
 | 
			
		||||
        Make sure that you don't delete this if pointers to it are still being used by
 | 
			
		||||
        objects such as PopupMenus or Buttons.
 | 
			
		||||
    */
 | 
			
		||||
    ~ApplicationCommandManager() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Clears the current list of all commands.
 | 
			
		||||
        Note that this will also clear the contents of the KeyPressMappingSet.
 | 
			
		||||
    */
 | 
			
		||||
    void clearCommands();
 | 
			
		||||
 | 
			
		||||
    /** Adds a command to the list of registered commands.
 | 
			
		||||
        @see registerAllCommandsForTarget
 | 
			
		||||
    */
 | 
			
		||||
    void registerCommand (const ApplicationCommandInfo& newCommand);
 | 
			
		||||
 | 
			
		||||
    /** Adds all the commands that this target publishes to the manager's list.
 | 
			
		||||
 | 
			
		||||
        This will use ApplicationCommandTarget::getAllCommands() and ApplicationCommandTarget::getCommandInfo()
 | 
			
		||||
        to get details about all the commands that this target can do, and will call
 | 
			
		||||
        registerCommand() to add each one to the manger's list.
 | 
			
		||||
 | 
			
		||||
        @see registerCommand
 | 
			
		||||
    */
 | 
			
		||||
    void registerAllCommandsForTarget (ApplicationCommandTarget* target);
 | 
			
		||||
 | 
			
		||||
    /** Removes the command with a specified ID.
 | 
			
		||||
        Note that this will also remove any key mappings that are mapped to the command.
 | 
			
		||||
    */
 | 
			
		||||
    void removeCommand (CommandID commandID);
 | 
			
		||||
 | 
			
		||||
    /** This should be called to tell the manager that one of its registered commands may have changed
 | 
			
		||||
        its active status.
 | 
			
		||||
 | 
			
		||||
        Because the command manager only finds out whether a command is active or inactive by querying
 | 
			
		||||
        the current ApplicationCommandTarget, this is used to tell it that things may have changed. It
 | 
			
		||||
        allows things like buttons to update their enablement, etc.
 | 
			
		||||
 | 
			
		||||
        This method will cause an asynchronous call to ApplicationCommandManagerListener::applicationCommandListChanged()
 | 
			
		||||
        for any registered listeners.
 | 
			
		||||
    */
 | 
			
		||||
    void commandStatusChanged();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the number of commands that have been registered.
 | 
			
		||||
        @see registerCommand
 | 
			
		||||
    */
 | 
			
		||||
    int getNumCommands() const noexcept                                             { return commands.size(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the details about one of the registered commands.
 | 
			
		||||
        The index is between 0 and (getNumCommands() - 1).
 | 
			
		||||
    */
 | 
			
		||||
    const ApplicationCommandInfo* getCommandForIndex (int index) const noexcept     { return commands [index]; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the details about a given command ID.
 | 
			
		||||
 | 
			
		||||
        This will search the list of registered commands for one with the given command
 | 
			
		||||
        ID number, and return its associated info. If no matching command is found, this
 | 
			
		||||
        will return nullptr.
 | 
			
		||||
    */
 | 
			
		||||
    const ApplicationCommandInfo* getCommandForID (CommandID commandID) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the name field for a command.
 | 
			
		||||
 | 
			
		||||
        An empty string is returned if no command with this ID has been registered.
 | 
			
		||||
        @see getDescriptionOfCommand
 | 
			
		||||
    */
 | 
			
		||||
    String getNameOfCommand (CommandID commandID) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the description field for a command.
 | 
			
		||||
 | 
			
		||||
        An empty string is returned if no command with this ID has been registered. If the
 | 
			
		||||
        command has no description, this will return its short name field instead.
 | 
			
		||||
 | 
			
		||||
        @see getNameOfCommand
 | 
			
		||||
    */
 | 
			
		||||
    String getDescriptionOfCommand (CommandID commandID) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the list of categories.
 | 
			
		||||
 | 
			
		||||
        This will go through all registered commands, and return a list of all the distinct
 | 
			
		||||
        categoryName values from their ApplicationCommandInfo structure.
 | 
			
		||||
 | 
			
		||||
        @see getCommandsInCategory()
 | 
			
		||||
    */
 | 
			
		||||
    StringArray getCommandCategories() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns a list of all the command UIDs in a particular category.
 | 
			
		||||
        @see getCommandCategories()
 | 
			
		||||
    */
 | 
			
		||||
    Array<CommandID> getCommandsInCategory (const String& categoryName) const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the manager's internal set of key mappings.
 | 
			
		||||
 | 
			
		||||
        This object can be used to edit the keypresses. To actually link this object up
 | 
			
		||||
        to invoke commands when a key is pressed, see the comments for the KeyPressMappingSet
 | 
			
		||||
        class.
 | 
			
		||||
 | 
			
		||||
        @see KeyPressMappingSet
 | 
			
		||||
    */
 | 
			
		||||
    KeyPressMappingSet* getKeyMappings() const noexcept         { return keyMappings.get(); }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Invokes the given command directly, sending it to the default target.
 | 
			
		||||
        This is just an easy way to call invoke() without having to fill out the InvocationInfo
 | 
			
		||||
        structure.
 | 
			
		||||
    */
 | 
			
		||||
    bool invokeDirectly (CommandID commandID, bool asynchronously);
 | 
			
		||||
 | 
			
		||||
    /** Sends a command to the default target.
 | 
			
		||||
 | 
			
		||||
        This will choose a target using getFirstCommandTarget(), and send the specified command
 | 
			
		||||
        to it using the ApplicationCommandTarget::invoke() method. This means that if the
 | 
			
		||||
        first target can't handle the command, it will be passed on to targets further down the
 | 
			
		||||
        chain (see ApplicationCommandTarget::invoke() for more info).
 | 
			
		||||
 | 
			
		||||
        @param invocationInfo       this must be correctly filled-in, describing the context for
 | 
			
		||||
                                    the invocation.
 | 
			
		||||
        @param asynchronously       if false, the command will be performed before this method returns.
 | 
			
		||||
                                    If true, a message will be posted so that the command will be performed
 | 
			
		||||
                                    later on the message thread, and this method will return immediately.
 | 
			
		||||
 | 
			
		||||
        @see ApplicationCommandTarget::invoke
 | 
			
		||||
    */
 | 
			
		||||
    bool invoke (const ApplicationCommandTarget::InvocationInfo& invocationInfo,
 | 
			
		||||
                 bool asynchronously);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Chooses the ApplicationCommandTarget to which a command should be sent.
 | 
			
		||||
 | 
			
		||||
        Whenever the manager needs to know which target a command should be sent to, it calls
 | 
			
		||||
        this method to determine the first one to try.
 | 
			
		||||
 | 
			
		||||
        By default, this method will return the target that was set by calling setFirstCommandTarget().
 | 
			
		||||
        If no target is set, it will return the result of findDefaultComponentTarget().
 | 
			
		||||
 | 
			
		||||
        If you need to make sure all commands go via your own custom target, then you can
 | 
			
		||||
        either use setFirstCommandTarget() to specify a single target, or override this method
 | 
			
		||||
        if you need more complex logic to choose one.
 | 
			
		||||
 | 
			
		||||
        It may return nullptr if no targets are available.
 | 
			
		||||
 | 
			
		||||
        @see getTargetForCommand, invoke, invokeDirectly
 | 
			
		||||
    */
 | 
			
		||||
    virtual ApplicationCommandTarget* getFirstCommandTarget (CommandID commandID);
 | 
			
		||||
 | 
			
		||||
    /** Sets a target to be returned by getFirstCommandTarget().
 | 
			
		||||
 | 
			
		||||
        If this is set to nullptr, then getFirstCommandTarget() will by default return the
 | 
			
		||||
        result of findDefaultComponentTarget().
 | 
			
		||||
 | 
			
		||||
        If you use this to set a target, make sure you call setFirstCommandTarget(nullptr)
 | 
			
		||||
        before deleting the target object.
 | 
			
		||||
    */
 | 
			
		||||
    void setFirstCommandTarget (ApplicationCommandTarget* newTarget) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Tries to find the best target to use to perform a given command.
 | 
			
		||||
 | 
			
		||||
        This will call getFirstCommandTarget() to find the preferred target, and will
 | 
			
		||||
        check whether that target can handle the given command. If it can't, then it'll use
 | 
			
		||||
        ApplicationCommandTarget::getNextCommandTarget() to find the next one to try, and
 | 
			
		||||
        so on until no more are available.
 | 
			
		||||
 | 
			
		||||
        If no targets are found that can perform the command, this method will return nullptr.
 | 
			
		||||
 | 
			
		||||
        If a target is found, then it will get the target to fill-in the upToDateInfo
 | 
			
		||||
        structure with the latest info about that command, so that the caller can see
 | 
			
		||||
        whether the command is disabled, ticked, etc.
 | 
			
		||||
    */
 | 
			
		||||
    ApplicationCommandTarget* getTargetForCommand (CommandID commandID,
 | 
			
		||||
                                                   ApplicationCommandInfo& upToDateInfo);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Registers a listener that will be called when various events occur. */
 | 
			
		||||
    void addListener (ApplicationCommandManagerListener* listener);
 | 
			
		||||
 | 
			
		||||
    /** Deregisters a previously-added listener. */
 | 
			
		||||
    void removeListener (ApplicationCommandManagerListener* listener);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Looks for a suitable command target based on which Components have the keyboard focus.
 | 
			
		||||
 | 
			
		||||
        This is used by the default implementation of ApplicationCommandTarget::getFirstCommandTarget(),
 | 
			
		||||
        but is exposed here in case it's useful.
 | 
			
		||||
 | 
			
		||||
        It tries to pick the best ApplicationCommandTarget by looking at focused components, top level
 | 
			
		||||
        windows, etc., and using the findTargetForComponent() method.
 | 
			
		||||
    */
 | 
			
		||||
    static ApplicationCommandTarget* findDefaultComponentTarget();
 | 
			
		||||
 | 
			
		||||
    /** Examines this component and all its parents in turn, looking for the first one
 | 
			
		||||
        which is an ApplicationCommandTarget.
 | 
			
		||||
 | 
			
		||||
        Returns the first ApplicationCommandTarget that it finds, or nullptr if none of them
 | 
			
		||||
        implement that class.
 | 
			
		||||
    */
 | 
			
		||||
    static ApplicationCommandTarget* findTargetForComponent (Component*);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    OwnedArray<ApplicationCommandInfo> commands;
 | 
			
		||||
    ListenerList<ApplicationCommandManagerListener> listeners;
 | 
			
		||||
    std::unique_ptr<KeyPressMappingSet> keyMappings;
 | 
			
		||||
    ApplicationCommandTarget* firstTarget = nullptr;
 | 
			
		||||
 | 
			
		||||
    void sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo&);
 | 
			
		||||
    void handleAsyncUpdate() override;
 | 
			
		||||
    void globalFocusChanged (Component*) override;
 | 
			
		||||
    ApplicationCommandInfo* getMutableCommandForID (CommandID) const noexcept;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandManager)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A listener that receives callbacks from an ApplicationCommandManager when
 | 
			
		||||
    commands are invoked or the command list is changed.
 | 
			
		||||
 | 
			
		||||
    @see ApplicationCommandManager::addListener, ApplicationCommandManager::removeListener
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ApplicationCommandManagerListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~ApplicationCommandManagerListener() = default;
 | 
			
		||||
 | 
			
		||||
    /** Called when an app command is about to be invoked. */
 | 
			
		||||
    virtual void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo&) = 0;
 | 
			
		||||
 | 
			
		||||
    /** Called when commands are registered or deregistered from the
 | 
			
		||||
        command manager, or when commands are made active or inactive.
 | 
			
		||||
 | 
			
		||||
        Note that if you're using this to watch for changes to whether a command is disabled,
 | 
			
		||||
        you'll need to make sure that ApplicationCommandManager::commandStatusChanged() is called
 | 
			
		||||
        whenever the status of your command might have changed.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void applicationCommandListChanged() = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    One of these objects holds a list of all the commands your app can perform,
 | 
			
		||||
    and despatches these commands when needed.
 | 
			
		||||
 | 
			
		||||
    Application commands are a good way to trigger actions in your app, e.g. "Quit",
 | 
			
		||||
    "Copy", "Paste", etc. Menus, buttons and keypresses can all be given commands
 | 
			
		||||
    to invoke automatically, which means you don't have to handle the result of a menu
 | 
			
		||||
    or button click manually. Commands are despatched to ApplicationCommandTarget objects
 | 
			
		||||
    which can choose which events they want to handle.
 | 
			
		||||
 | 
			
		||||
    This architecture also allows for nested ApplicationCommandTargets, so that for example
 | 
			
		||||
    you could have two different objects, one inside the other, both of which can respond to
 | 
			
		||||
    a "delete" command. Depending on which one has focus, the command will be sent to the
 | 
			
		||||
    appropriate place, regardless of whether it was triggered by a menu, keypress or some other
 | 
			
		||||
    method.
 | 
			
		||||
 | 
			
		||||
    To set up your app to use commands, you'll need to do the following:
 | 
			
		||||
 | 
			
		||||
    - Create a global ApplicationCommandManager to hold the list of all possible
 | 
			
		||||
      commands. (This will also manage a set of key-mappings for them).
 | 
			
		||||
 | 
			
		||||
    - Make some of your UI components (or other objects) inherit from ApplicationCommandTarget.
 | 
			
		||||
      This allows the object to provide a list of commands that it can perform, and
 | 
			
		||||
      to handle them.
 | 
			
		||||
 | 
			
		||||
    - Register each type of command using ApplicationCommandManager::registerAllCommandsForTarget(),
 | 
			
		||||
      or ApplicationCommandManager::registerCommand().
 | 
			
		||||
 | 
			
		||||
    - If you want key-presses to trigger your commands, use the ApplicationCommandManager::getKeyMappings()
 | 
			
		||||
      method to access the key-mapper object, which you will need to register as a key-listener
 | 
			
		||||
      in whatever top-level component you're using. See the KeyPressMappingSet class for more help
 | 
			
		||||
      about setting this up.
 | 
			
		||||
 | 
			
		||||
    - Use methods such as PopupMenu::addCommandItem() or Button::setCommandToTrigger() to
 | 
			
		||||
      cause these commands to be invoked automatically.
 | 
			
		||||
 | 
			
		||||
    - Commands can be invoked directly by your code using ApplicationCommandManager::invokeDirectly().
 | 
			
		||||
 | 
			
		||||
    When a command is invoked, the ApplicationCommandManager will try to choose the best
 | 
			
		||||
    ApplicationCommandTarget to receive the specified command. To do this it will use the
 | 
			
		||||
    current keyboard focus to see which component might be interested, and will search the
 | 
			
		||||
    component hierarchy for those that also implement the ApplicationCommandTarget interface.
 | 
			
		||||
    If an ApplicationCommandTarget isn't interested in the command that is being invoked, then
 | 
			
		||||
    the next one in line will be tried (see the ApplicationCommandTarget::getNextCommandTarget()
 | 
			
		||||
    method), and so on until ApplicationCommandTarget::getNextCommandTarget() returns nullptr.
 | 
			
		||||
    At this point if the command still hasn't been performed, it will be passed to the current
 | 
			
		||||
    JUCEApplication object (which is itself an ApplicationCommandTarget).
 | 
			
		||||
 | 
			
		||||
    To exert some custom control over which ApplicationCommandTarget is chosen to invoke a command,
 | 
			
		||||
    you can override the ApplicationCommandManager::getFirstCommandTarget() method and choose
 | 
			
		||||
    the object yourself.
 | 
			
		||||
 | 
			
		||||
    @see ApplicationCommandTarget, ApplicationCommandInfo
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ApplicationCommandManager   : private AsyncUpdater,
 | 
			
		||||
                                              private FocusChangeListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates an ApplicationCommandManager.
 | 
			
		||||
 | 
			
		||||
        Once created, you'll need to register all your app's commands with it, using
 | 
			
		||||
        ApplicationCommandManager::registerAllCommandsForTarget() or
 | 
			
		||||
        ApplicationCommandManager::registerCommand().
 | 
			
		||||
    */
 | 
			
		||||
    ApplicationCommandManager();
 | 
			
		||||
 | 
			
		||||
    /** Destructor.
 | 
			
		||||
 | 
			
		||||
        Make sure that you don't delete this if pointers to it are still being used by
 | 
			
		||||
        objects such as PopupMenus or Buttons.
 | 
			
		||||
    */
 | 
			
		||||
    ~ApplicationCommandManager() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Clears the current list of all commands.
 | 
			
		||||
        Note that this will also clear the contents of the KeyPressMappingSet.
 | 
			
		||||
    */
 | 
			
		||||
    void clearCommands();
 | 
			
		||||
 | 
			
		||||
    /** Adds a command to the list of registered commands.
 | 
			
		||||
        @see registerAllCommandsForTarget
 | 
			
		||||
    */
 | 
			
		||||
    void registerCommand (const ApplicationCommandInfo& newCommand);
 | 
			
		||||
 | 
			
		||||
    /** Adds all the commands that this target publishes to the manager's list.
 | 
			
		||||
 | 
			
		||||
        This will use ApplicationCommandTarget::getAllCommands() and ApplicationCommandTarget::getCommandInfo()
 | 
			
		||||
        to get details about all the commands that this target can do, and will call
 | 
			
		||||
        registerCommand() to add each one to the manger's list.
 | 
			
		||||
 | 
			
		||||
        @see registerCommand
 | 
			
		||||
    */
 | 
			
		||||
    void registerAllCommandsForTarget (ApplicationCommandTarget* target);
 | 
			
		||||
 | 
			
		||||
    /** Removes the command with a specified ID.
 | 
			
		||||
        Note that this will also remove any key mappings that are mapped to the command.
 | 
			
		||||
    */
 | 
			
		||||
    void removeCommand (CommandID commandID);
 | 
			
		||||
 | 
			
		||||
    /** This should be called to tell the manager that one of its registered commands may have changed
 | 
			
		||||
        its active status.
 | 
			
		||||
 | 
			
		||||
        Because the command manager only finds out whether a command is active or inactive by querying
 | 
			
		||||
        the current ApplicationCommandTarget, this is used to tell it that things may have changed. It
 | 
			
		||||
        allows things like buttons to update their enablement, etc.
 | 
			
		||||
 | 
			
		||||
        This method will cause an asynchronous call to ApplicationCommandManagerListener::applicationCommandListChanged()
 | 
			
		||||
        for any registered listeners.
 | 
			
		||||
    */
 | 
			
		||||
    void commandStatusChanged();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the number of commands that have been registered.
 | 
			
		||||
        @see registerCommand
 | 
			
		||||
    */
 | 
			
		||||
    int getNumCommands() const noexcept                                             { return commands.size(); }
 | 
			
		||||
 | 
			
		||||
    /** Returns the details about one of the registered commands.
 | 
			
		||||
        The index is between 0 and (getNumCommands() - 1).
 | 
			
		||||
    */
 | 
			
		||||
    const ApplicationCommandInfo* getCommandForIndex (int index) const noexcept     { return commands [index]; }
 | 
			
		||||
 | 
			
		||||
    /** Returns the details about a given command ID.
 | 
			
		||||
 | 
			
		||||
        This will search the list of registered commands for one with the given command
 | 
			
		||||
        ID number, and return its associated info. If no matching command is found, this
 | 
			
		||||
        will return nullptr.
 | 
			
		||||
    */
 | 
			
		||||
    const ApplicationCommandInfo* getCommandForID (CommandID commandID) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the name field for a command.
 | 
			
		||||
 | 
			
		||||
        An empty string is returned if no command with this ID has been registered.
 | 
			
		||||
        @see getDescriptionOfCommand
 | 
			
		||||
    */
 | 
			
		||||
    String getNameOfCommand (CommandID commandID) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the description field for a command.
 | 
			
		||||
 | 
			
		||||
        An empty string is returned if no command with this ID has been registered. If the
 | 
			
		||||
        command has no description, this will return its short name field instead.
 | 
			
		||||
 | 
			
		||||
        @see getNameOfCommand
 | 
			
		||||
    */
 | 
			
		||||
    String getDescriptionOfCommand (CommandID commandID) const noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Returns the list of categories.
 | 
			
		||||
 | 
			
		||||
        This will go through all registered commands, and return a list of all the distinct
 | 
			
		||||
        categoryName values from their ApplicationCommandInfo structure.
 | 
			
		||||
 | 
			
		||||
        @see getCommandsInCategory()
 | 
			
		||||
    */
 | 
			
		||||
    StringArray getCommandCategories() const;
 | 
			
		||||
 | 
			
		||||
    /** Returns a list of all the command UIDs in a particular category.
 | 
			
		||||
        @see getCommandCategories()
 | 
			
		||||
    */
 | 
			
		||||
    Array<CommandID> getCommandsInCategory (const String& categoryName) const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns the manager's internal set of key mappings.
 | 
			
		||||
 | 
			
		||||
        This object can be used to edit the keypresses. To actually link this object up
 | 
			
		||||
        to invoke commands when a key is pressed, see the comments for the KeyPressMappingSet
 | 
			
		||||
        class.
 | 
			
		||||
 | 
			
		||||
        @see KeyPressMappingSet
 | 
			
		||||
    */
 | 
			
		||||
    KeyPressMappingSet* getKeyMappings() const noexcept         { return keyMappings.get(); }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Invokes the given command directly, sending it to the default target.
 | 
			
		||||
        This is just an easy way to call invoke() without having to fill out the InvocationInfo
 | 
			
		||||
        structure.
 | 
			
		||||
    */
 | 
			
		||||
    bool invokeDirectly (CommandID commandID, bool asynchronously);
 | 
			
		||||
 | 
			
		||||
    /** Sends a command to the default target.
 | 
			
		||||
 | 
			
		||||
        This will choose a target using getFirstCommandTarget(), and send the specified command
 | 
			
		||||
        to it using the ApplicationCommandTarget::invoke() method. This means that if the
 | 
			
		||||
        first target can't handle the command, it will be passed on to targets further down the
 | 
			
		||||
        chain (see ApplicationCommandTarget::invoke() for more info).
 | 
			
		||||
 | 
			
		||||
        @param invocationInfo       this must be correctly filled-in, describing the context for
 | 
			
		||||
                                    the invocation.
 | 
			
		||||
        @param asynchronously       if false, the command will be performed before this method returns.
 | 
			
		||||
                                    If true, a message will be posted so that the command will be performed
 | 
			
		||||
                                    later on the message thread, and this method will return immediately.
 | 
			
		||||
 | 
			
		||||
        @see ApplicationCommandTarget::invoke
 | 
			
		||||
    */
 | 
			
		||||
    bool invoke (const ApplicationCommandTarget::InvocationInfo& invocationInfo,
 | 
			
		||||
                 bool asynchronously);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Chooses the ApplicationCommandTarget to which a command should be sent.
 | 
			
		||||
 | 
			
		||||
        Whenever the manager needs to know which target a command should be sent to, it calls
 | 
			
		||||
        this method to determine the first one to try.
 | 
			
		||||
 | 
			
		||||
        By default, this method will return the target that was set by calling setFirstCommandTarget().
 | 
			
		||||
        If no target is set, it will return the result of findDefaultComponentTarget().
 | 
			
		||||
 | 
			
		||||
        If you need to make sure all commands go via your own custom target, then you can
 | 
			
		||||
        either use setFirstCommandTarget() to specify a single target, or override this method
 | 
			
		||||
        if you need more complex logic to choose one.
 | 
			
		||||
 | 
			
		||||
        It may return nullptr if no targets are available.
 | 
			
		||||
 | 
			
		||||
        @see getTargetForCommand, invoke, invokeDirectly
 | 
			
		||||
    */
 | 
			
		||||
    virtual ApplicationCommandTarget* getFirstCommandTarget (CommandID commandID);
 | 
			
		||||
 | 
			
		||||
    /** Sets a target to be returned by getFirstCommandTarget().
 | 
			
		||||
 | 
			
		||||
        If this is set to nullptr, then getFirstCommandTarget() will by default return the
 | 
			
		||||
        result of findDefaultComponentTarget().
 | 
			
		||||
 | 
			
		||||
        If you use this to set a target, make sure you call setFirstCommandTarget(nullptr)
 | 
			
		||||
        before deleting the target object.
 | 
			
		||||
    */
 | 
			
		||||
    void setFirstCommandTarget (ApplicationCommandTarget* newTarget) noexcept;
 | 
			
		||||
 | 
			
		||||
    /** Tries to find the best target to use to perform a given command.
 | 
			
		||||
 | 
			
		||||
        This will call getFirstCommandTarget() to find the preferred target, and will
 | 
			
		||||
        check whether that target can handle the given command. If it can't, then it'll use
 | 
			
		||||
        ApplicationCommandTarget::getNextCommandTarget() to find the next one to try, and
 | 
			
		||||
        so on until no more are available.
 | 
			
		||||
 | 
			
		||||
        If no targets are found that can perform the command, this method will return nullptr.
 | 
			
		||||
 | 
			
		||||
        If a target is found, then it will get the target to fill-in the upToDateInfo
 | 
			
		||||
        structure with the latest info about that command, so that the caller can see
 | 
			
		||||
        whether the command is disabled, ticked, etc.
 | 
			
		||||
    */
 | 
			
		||||
    ApplicationCommandTarget* getTargetForCommand (CommandID commandID,
 | 
			
		||||
                                                   ApplicationCommandInfo& upToDateInfo);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Registers a listener that will be called when various events occur. */
 | 
			
		||||
    void addListener (ApplicationCommandManagerListener* listener);
 | 
			
		||||
 | 
			
		||||
    /** Deregisters a previously-added listener. */
 | 
			
		||||
    void removeListener (ApplicationCommandManagerListener* listener);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Looks for a suitable command target based on which Components have the keyboard focus.
 | 
			
		||||
 | 
			
		||||
        This is used by the default implementation of ApplicationCommandTarget::getFirstCommandTarget(),
 | 
			
		||||
        but is exposed here in case it's useful.
 | 
			
		||||
 | 
			
		||||
        It tries to pick the best ApplicationCommandTarget by looking at focused components, top level
 | 
			
		||||
        windows, etc., and using the findTargetForComponent() method.
 | 
			
		||||
    */
 | 
			
		||||
    static ApplicationCommandTarget* findDefaultComponentTarget();
 | 
			
		||||
 | 
			
		||||
    /** Examines this component and all its parents in turn, looking for the first one
 | 
			
		||||
        which is an ApplicationCommandTarget.
 | 
			
		||||
 | 
			
		||||
        Returns the first ApplicationCommandTarget that it finds, or nullptr if none of them
 | 
			
		||||
        implement that class.
 | 
			
		||||
    */
 | 
			
		||||
    static ApplicationCommandTarget* findTargetForComponent (Component*);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    OwnedArray<ApplicationCommandInfo> commands;
 | 
			
		||||
    ListenerList<ApplicationCommandManagerListener> listeners;
 | 
			
		||||
    std::unique_ptr<KeyPressMappingSet> keyMappings;
 | 
			
		||||
    ApplicationCommandTarget* firstTarget = nullptr;
 | 
			
		||||
 | 
			
		||||
    void sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo&);
 | 
			
		||||
    void handleAsyncUpdate() override;
 | 
			
		||||
    void globalFocusChanged (Component*) override;
 | 
			
		||||
    ApplicationCommandInfo* getMutableCommandForID (CommandID) const noexcept;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandManager)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    A listener that receives callbacks from an ApplicationCommandManager when
 | 
			
		||||
    commands are invoked or the command list is changed.
 | 
			
		||||
 | 
			
		||||
    @see ApplicationCommandManager::addListener, ApplicationCommandManager::removeListener
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ApplicationCommandManagerListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~ApplicationCommandManagerListener() = default;
 | 
			
		||||
 | 
			
		||||
    /** Called when an app command is about to be invoked. */
 | 
			
		||||
    virtual void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo&) = 0;
 | 
			
		||||
 | 
			
		||||
    /** Called when commands are registered or deregistered from the
 | 
			
		||||
        command manager, or when commands are made active or inactive.
 | 
			
		||||
 | 
			
		||||
        Note that if you're using this to watch for changes to whether a command is disabled,
 | 
			
		||||
        you'll need to make sure that ApplicationCommandManager::commandStatusChanged() is called
 | 
			
		||||
        whenever the status of your command might have changed.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void applicationCommandListChanged() = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,186 +1,186 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-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 ApplicationCommandTarget::CommandMessage  : public MessageManager::MessageBase
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    CommandMessage (ApplicationCommandTarget* const target, const InvocationInfo& inf)
 | 
			
		||||
        : owner (target), info (inf)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void messageCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        if (ApplicationCommandTarget* const target = owner)
 | 
			
		||||
            target->tryToInvoke (info, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    WeakReference<ApplicationCommandTarget> owner;
 | 
			
		||||
    const InvocationInfo info;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE (CommandMessage)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ApplicationCommandTarget::ApplicationCommandTarget() {}
 | 
			
		||||
ApplicationCommandTarget::~ApplicationCommandTarget() {}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
bool ApplicationCommandTarget::tryToInvoke (const InvocationInfo& info, const bool async)
 | 
			
		||||
{
 | 
			
		||||
    if (isCommandActive (info.commandID))
 | 
			
		||||
    {
 | 
			
		||||
        if (async)
 | 
			
		||||
        {
 | 
			
		||||
            (new CommandMessage (this, info))->post();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (perform (info))
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        // Hmm.. your target claimed that it could perform this command, but failed to do so.
 | 
			
		||||
        // If it can't do it at the moment for some reason, it should clear the 'isActive' flag
 | 
			
		||||
        // when it returns the command's info.
 | 
			
		||||
        jassertfalse;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ApplicationCommandTarget* ApplicationCommandTarget::findFirstTargetParentComponent()
 | 
			
		||||
{
 | 
			
		||||
    if (Component* const c = dynamic_cast<Component*> (this))
 | 
			
		||||
        return c->findParentComponentOfClass<ApplicationCommandTarget>();
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ApplicationCommandTarget* ApplicationCommandTarget::getTargetForCommand (const CommandID commandID)
 | 
			
		||||
{
 | 
			
		||||
    ApplicationCommandTarget* target = this;
 | 
			
		||||
    int depth = 0;
 | 
			
		||||
 | 
			
		||||
    while (target != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        Array<CommandID> commandIDs;
 | 
			
		||||
        target->getAllCommands (commandIDs);
 | 
			
		||||
 | 
			
		||||
        if (commandIDs.contains (commandID))
 | 
			
		||||
            return target;
 | 
			
		||||
 | 
			
		||||
        target = target->getNextCommandTarget();
 | 
			
		||||
 | 
			
		||||
        ++depth;
 | 
			
		||||
        jassert (depth < 100); // could be a recursive command chain??
 | 
			
		||||
        jassert (target != this); // definitely a recursive command chain!
 | 
			
		||||
 | 
			
		||||
        if (depth > 100 || target == this)
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (target == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        target = JUCEApplication::getInstance();
 | 
			
		||||
 | 
			
		||||
        if (target != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            Array<CommandID> commandIDs;
 | 
			
		||||
            target->getAllCommands (commandIDs);
 | 
			
		||||
 | 
			
		||||
            if (commandIDs.contains (commandID))
 | 
			
		||||
                return target;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ApplicationCommandTarget::isCommandActive (const CommandID commandID)
 | 
			
		||||
{
 | 
			
		||||
    ApplicationCommandInfo info (commandID);
 | 
			
		||||
    info.flags = ApplicationCommandInfo::isDisabled;
 | 
			
		||||
 | 
			
		||||
    getCommandInfo (commandID, info);
 | 
			
		||||
 | 
			
		||||
    return (info.flags & ApplicationCommandInfo::isDisabled) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
bool ApplicationCommandTarget::invoke (const InvocationInfo& info, const bool async)
 | 
			
		||||
{
 | 
			
		||||
    ApplicationCommandTarget* target = this;
 | 
			
		||||
    int depth = 0;
 | 
			
		||||
 | 
			
		||||
    while (target != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        if (target->tryToInvoke (info, async))
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        target = target->getNextCommandTarget();
 | 
			
		||||
 | 
			
		||||
        ++depth;
 | 
			
		||||
        jassert (depth < 100); // could be a recursive command chain??
 | 
			
		||||
        jassert (target != this); // definitely a recursive command chain!
 | 
			
		||||
 | 
			
		||||
        if (depth > 100 || target == this)
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (target == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        target = JUCEApplication::getInstance();
 | 
			
		||||
 | 
			
		||||
        if (target != nullptr)
 | 
			
		||||
            return target->tryToInvoke (info, async);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ApplicationCommandTarget::invokeDirectly (const CommandID commandID, const bool asynchronously)
 | 
			
		||||
{
 | 
			
		||||
    ApplicationCommandTarget::InvocationInfo info (commandID);
 | 
			
		||||
    info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct;
 | 
			
		||||
 | 
			
		||||
    return invoke (info, asynchronously);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ApplicationCommandTarget::InvocationInfo::InvocationInfo (const CommandID command)
 | 
			
		||||
    : commandID (command),
 | 
			
		||||
      commandFlags (0),
 | 
			
		||||
      invocationMethod (direct),
 | 
			
		||||
      originatingComponent (nullptr),
 | 
			
		||||
      isKeyDown (false),
 | 
			
		||||
      millisecsSinceKeyPressed (0)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   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 ApplicationCommandTarget::CommandMessage  : public MessageManager::MessageBase
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    CommandMessage (ApplicationCommandTarget* const target, const InvocationInfo& inf)
 | 
			
		||||
        : owner (target), info (inf)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void messageCallback() override
 | 
			
		||||
    {
 | 
			
		||||
        if (ApplicationCommandTarget* const target = owner)
 | 
			
		||||
            target->tryToInvoke (info, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    WeakReference<ApplicationCommandTarget> owner;
 | 
			
		||||
    const InvocationInfo info;
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE (CommandMessage)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ApplicationCommandTarget::ApplicationCommandTarget() {}
 | 
			
		||||
ApplicationCommandTarget::~ApplicationCommandTarget() {}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
bool ApplicationCommandTarget::tryToInvoke (const InvocationInfo& info, const bool async)
 | 
			
		||||
{
 | 
			
		||||
    if (isCommandActive (info.commandID))
 | 
			
		||||
    {
 | 
			
		||||
        if (async)
 | 
			
		||||
        {
 | 
			
		||||
            (new CommandMessage (this, info))->post();
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (perform (info))
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        // Hmm.. your target claimed that it could perform this command, but failed to do so.
 | 
			
		||||
        // If it can't do it at the moment for some reason, it should clear the 'isActive' flag
 | 
			
		||||
        // when it returns the command's info.
 | 
			
		||||
        jassertfalse;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ApplicationCommandTarget* ApplicationCommandTarget::findFirstTargetParentComponent()
 | 
			
		||||
{
 | 
			
		||||
    if (Component* const c = dynamic_cast<Component*> (this))
 | 
			
		||||
        return c->findParentComponentOfClass<ApplicationCommandTarget>();
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ApplicationCommandTarget* ApplicationCommandTarget::getTargetForCommand (const CommandID commandID)
 | 
			
		||||
{
 | 
			
		||||
    ApplicationCommandTarget* target = this;
 | 
			
		||||
    int depth = 0;
 | 
			
		||||
 | 
			
		||||
    while (target != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        Array<CommandID> commandIDs;
 | 
			
		||||
        target->getAllCommands (commandIDs);
 | 
			
		||||
 | 
			
		||||
        if (commandIDs.contains (commandID))
 | 
			
		||||
            return target;
 | 
			
		||||
 | 
			
		||||
        target = target->getNextCommandTarget();
 | 
			
		||||
 | 
			
		||||
        ++depth;
 | 
			
		||||
        jassert (depth < 100); // could be a recursive command chain??
 | 
			
		||||
        jassert (target != this); // definitely a recursive command chain!
 | 
			
		||||
 | 
			
		||||
        if (depth > 100 || target == this)
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (target == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        target = JUCEApplication::getInstance();
 | 
			
		||||
 | 
			
		||||
        if (target != nullptr)
 | 
			
		||||
        {
 | 
			
		||||
            Array<CommandID> commandIDs;
 | 
			
		||||
            target->getAllCommands (commandIDs);
 | 
			
		||||
 | 
			
		||||
            if (commandIDs.contains (commandID))
 | 
			
		||||
                return target;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ApplicationCommandTarget::isCommandActive (const CommandID commandID)
 | 
			
		||||
{
 | 
			
		||||
    ApplicationCommandInfo info (commandID);
 | 
			
		||||
    info.flags = ApplicationCommandInfo::isDisabled;
 | 
			
		||||
 | 
			
		||||
    getCommandInfo (commandID, info);
 | 
			
		||||
 | 
			
		||||
    return (info.flags & ApplicationCommandInfo::isDisabled) == 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
bool ApplicationCommandTarget::invoke (const InvocationInfo& info, const bool async)
 | 
			
		||||
{
 | 
			
		||||
    ApplicationCommandTarget* target = this;
 | 
			
		||||
    int depth = 0;
 | 
			
		||||
 | 
			
		||||
    while (target != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        if (target->tryToInvoke (info, async))
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        target = target->getNextCommandTarget();
 | 
			
		||||
 | 
			
		||||
        ++depth;
 | 
			
		||||
        jassert (depth < 100); // could be a recursive command chain??
 | 
			
		||||
        jassert (target != this); // definitely a recursive command chain!
 | 
			
		||||
 | 
			
		||||
        if (depth > 100 || target == this)
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (target == nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        target = JUCEApplication::getInstance();
 | 
			
		||||
 | 
			
		||||
        if (target != nullptr)
 | 
			
		||||
            return target->tryToInvoke (info, async);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ApplicationCommandTarget::invokeDirectly (const CommandID commandID, const bool asynchronously)
 | 
			
		||||
{
 | 
			
		||||
    ApplicationCommandTarget::InvocationInfo info (commandID);
 | 
			
		||||
    info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct;
 | 
			
		||||
 | 
			
		||||
    return invoke (info, asynchronously);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
ApplicationCommandTarget::InvocationInfo::InvocationInfo (const CommandID command)
 | 
			
		||||
    : commandID (command),
 | 
			
		||||
      commandFlags (0),
 | 
			
		||||
      invocationMethod (direct),
 | 
			
		||||
      originatingComponent (nullptr),
 | 
			
		||||
      isKeyDown (false),
 | 
			
		||||
      millisecsSinceKeyPressed (0)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,244 +1,244 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-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 command target publishes a list of command IDs that it can perform.
 | 
			
		||||
 | 
			
		||||
    An ApplicationCommandManager despatches commands to targets, which must be
 | 
			
		||||
    able to provide information about what commands they can handle.
 | 
			
		||||
 | 
			
		||||
    To create a target, you'll need to inherit from this class, implementing all of
 | 
			
		||||
    its pure virtual methods.
 | 
			
		||||
 | 
			
		||||
    For info about how a target is chosen to receive a command, see
 | 
			
		||||
    ApplicationCommandManager::getFirstCommandTarget().
 | 
			
		||||
 | 
			
		||||
    @see ApplicationCommandManager, ApplicationCommandInfo
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ApplicationCommandTarget
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a command target. */
 | 
			
		||||
    ApplicationCommandTarget();
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~ApplicationCommandTarget();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /**
 | 
			
		||||
        Contains contextual details about the invocation of a command.
 | 
			
		||||
    */
 | 
			
		||||
    struct JUCE_API  InvocationInfo
 | 
			
		||||
    {
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        InvocationInfo (const CommandID commandID);
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** The UID of the command that should be performed. */
 | 
			
		||||
        CommandID commandID;
 | 
			
		||||
 | 
			
		||||
        /** The command's flags.
 | 
			
		||||
            See ApplicationCommandInfo for a description of these flag values.
 | 
			
		||||
        */
 | 
			
		||||
        int commandFlags;
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** The types of context in which the command might be called. */
 | 
			
		||||
        enum InvocationMethod
 | 
			
		||||
        {
 | 
			
		||||
            direct = 0,     /**< The command is being invoked directly by a piece of code. */
 | 
			
		||||
            fromKeyPress,   /**< The command is being invoked by a key-press. */
 | 
			
		||||
            fromMenu,       /**< The command is being invoked by a menu selection. */
 | 
			
		||||
            fromButton      /**< The command is being invoked by a button click. */
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /** The type of event that triggered this command. */
 | 
			
		||||
        InvocationMethod invocationMethod;
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** If triggered by a keypress or menu, this will be the component that had the
 | 
			
		||||
            keyboard focus at the time.
 | 
			
		||||
 | 
			
		||||
            If triggered by a button, it may be set to that component, or it may be null.
 | 
			
		||||
        */
 | 
			
		||||
        Component* originatingComponent;
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** The keypress that was used to invoke it.
 | 
			
		||||
 | 
			
		||||
            Note that this will be an invalid keypress if the command was invoked
 | 
			
		||||
            by some other means than a keyboard shortcut.
 | 
			
		||||
        */
 | 
			
		||||
        KeyPress keyPress;
 | 
			
		||||
 | 
			
		||||
        /** True if the callback is being invoked when the key is pressed,
 | 
			
		||||
            false if the key is being released.
 | 
			
		||||
 | 
			
		||||
            @see KeyPressMappingSet::addCommand()
 | 
			
		||||
        */
 | 
			
		||||
        bool isKeyDown;
 | 
			
		||||
 | 
			
		||||
        /** If the key is being released, this indicates how long it had been held
 | 
			
		||||
            down for.
 | 
			
		||||
 | 
			
		||||
            (Only relevant if isKeyDown is false.)
 | 
			
		||||
        */
 | 
			
		||||
        int millisecsSinceKeyPressed;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This must return the next target to try after this one.
 | 
			
		||||
 | 
			
		||||
        When a command is being sent, and the first target can't handle
 | 
			
		||||
        that command, this method is used to determine the next target that should
 | 
			
		||||
        be tried.
 | 
			
		||||
 | 
			
		||||
        It may return nullptr if it doesn't know of another target.
 | 
			
		||||
 | 
			
		||||
        If your target is a Component, you would usually use the findFirstTargetParentComponent()
 | 
			
		||||
        method to return a parent component that might want to handle it.
 | 
			
		||||
 | 
			
		||||
        @see invoke
 | 
			
		||||
    */
 | 
			
		||||
    virtual ApplicationCommandTarget* getNextCommandTarget() = 0;
 | 
			
		||||
 | 
			
		||||
    /** This must return a complete list of commands that this target can handle.
 | 
			
		||||
 | 
			
		||||
        Your target should add all the command IDs that it handles to the array that is
 | 
			
		||||
        passed-in.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void getAllCommands (Array<CommandID>& commands) = 0;
 | 
			
		||||
 | 
			
		||||
    /** This must provide details about one of the commands that this target can perform.
 | 
			
		||||
 | 
			
		||||
        This will be called with one of the command IDs that the target provided in its
 | 
			
		||||
        getAllCommands() methods.
 | 
			
		||||
 | 
			
		||||
        It should fill-in all appropriate fields of the ApplicationCommandInfo structure with
 | 
			
		||||
        suitable information about the command. (The commandID field will already have been filled-in
 | 
			
		||||
        by the caller).
 | 
			
		||||
 | 
			
		||||
        The easiest way to set the info is using the ApplicationCommandInfo::setInfo() method to
 | 
			
		||||
        set all the fields at once.
 | 
			
		||||
 | 
			
		||||
        If the command is currently inactive for some reason, this method must use
 | 
			
		||||
        ApplicationCommandInfo::setActive() to make that clear, (or it should set the isDisabled
 | 
			
		||||
        bit of the ApplicationCommandInfo::flags field).
 | 
			
		||||
 | 
			
		||||
        Any default key-presses for the command should be appended to the
 | 
			
		||||
        ApplicationCommandInfo::defaultKeypresses field.
 | 
			
		||||
 | 
			
		||||
        Note that if you change something that affects the status of the commands
 | 
			
		||||
        that would be returned by this method (e.g. something that makes some commands
 | 
			
		||||
        active or inactive), you should call ApplicationCommandManager::commandStatusChanged()
 | 
			
		||||
        to cause the manager to refresh its status.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) = 0;
 | 
			
		||||
 | 
			
		||||
    /** This must actually perform the specified command.
 | 
			
		||||
 | 
			
		||||
        If this target is able to perform the command specified by the commandID field of the
 | 
			
		||||
        InvocationInfo structure, then it should do so, and must return true.
 | 
			
		||||
 | 
			
		||||
        If it can't handle this command, it should return false, which tells the caller to pass
 | 
			
		||||
        the command on to the next target in line.
 | 
			
		||||
 | 
			
		||||
        @see invoke, ApplicationCommandManager::invoke
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool perform (const InvocationInfo& info) = 0;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Makes this target invoke a command.
 | 
			
		||||
 | 
			
		||||
        Your code can call this method to invoke a command on this target, but normally
 | 
			
		||||
        you'd call it indirectly via ApplicationCommandManager::invoke() or
 | 
			
		||||
        ApplicationCommandManager::invokeDirectly().
 | 
			
		||||
 | 
			
		||||
        If this target can perform the given command, it will call its perform() method to
 | 
			
		||||
        do so. If not, then getNextCommandTarget() will be used to determine the next target
 | 
			
		||||
        to try, and the command will be passed along to it.
 | 
			
		||||
 | 
			
		||||
        @param invocationInfo       this must be correctly filled-in, describing the context for
 | 
			
		||||
                                    the invocation.
 | 
			
		||||
        @param asynchronously       if false, the command will be performed before this method returns.
 | 
			
		||||
                                    If true, a message will be posted so that the command will be performed
 | 
			
		||||
                                    later on the message thread, and this method will return immediately.
 | 
			
		||||
        @see perform, ApplicationCommandManager::invoke
 | 
			
		||||
    */
 | 
			
		||||
    bool invoke (const InvocationInfo& invocationInfo,
 | 
			
		||||
                 const bool asynchronously);
 | 
			
		||||
 | 
			
		||||
    /** Invokes a given command directly on this target.
 | 
			
		||||
 | 
			
		||||
        This is just an easy way to call invoke() without having to fill out the InvocationInfo
 | 
			
		||||
        structure.
 | 
			
		||||
    */
 | 
			
		||||
    bool invokeDirectly (const CommandID commandID,
 | 
			
		||||
                         const bool asynchronously);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Searches this target and all subsequent ones for the first one that can handle
 | 
			
		||||
        the specified command.
 | 
			
		||||
 | 
			
		||||
        This will use getNextCommandTarget() to determine the chain of targets to try
 | 
			
		||||
        after this one.
 | 
			
		||||
    */
 | 
			
		||||
    ApplicationCommandTarget* getTargetForCommand (const CommandID commandID);
 | 
			
		||||
 | 
			
		||||
    /** Checks whether this command can currently be performed by this target.
 | 
			
		||||
 | 
			
		||||
        This will return true only if a call to getCommandInfo() doesn't set the
 | 
			
		||||
        isDisabled flag to indicate that the command is inactive.
 | 
			
		||||
    */
 | 
			
		||||
    bool isCommandActive (const CommandID commandID);
 | 
			
		||||
 | 
			
		||||
    /** If this object is a Component, this method will search upwards in its current
 | 
			
		||||
        UI hierarchy for the next parent component that implements the
 | 
			
		||||
        ApplicationCommandTarget class.
 | 
			
		||||
 | 
			
		||||
        If your target is a Component, this is a very handy method to use in your
 | 
			
		||||
        getNextCommandTarget() implementation.
 | 
			
		||||
    */
 | 
			
		||||
    ApplicationCommandTarget* findFirstTargetParentComponent();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    class CommandMessage;
 | 
			
		||||
    friend class CommandMessage;
 | 
			
		||||
 | 
			
		||||
    bool tryToInvoke (const InvocationInfo&, bool async);
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_WEAK_REFERENCEABLE (ApplicationCommandTarget)
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandTarget)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   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 command target publishes a list of command IDs that it can perform.
 | 
			
		||||
 | 
			
		||||
    An ApplicationCommandManager despatches commands to targets, which must be
 | 
			
		||||
    able to provide information about what commands they can handle.
 | 
			
		||||
 | 
			
		||||
    To create a target, you'll need to inherit from this class, implementing all of
 | 
			
		||||
    its pure virtual methods.
 | 
			
		||||
 | 
			
		||||
    For info about how a target is chosen to receive a command, see
 | 
			
		||||
    ApplicationCommandManager::getFirstCommandTarget().
 | 
			
		||||
 | 
			
		||||
    @see ApplicationCommandManager, ApplicationCommandInfo
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  ApplicationCommandTarget
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a command target. */
 | 
			
		||||
    ApplicationCommandTarget();
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    virtual ~ApplicationCommandTarget();
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /**
 | 
			
		||||
        Contains contextual details about the invocation of a command.
 | 
			
		||||
    */
 | 
			
		||||
    struct JUCE_API  InvocationInfo
 | 
			
		||||
    {
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        InvocationInfo (const CommandID commandID);
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** The UID of the command that should be performed. */
 | 
			
		||||
        CommandID commandID;
 | 
			
		||||
 | 
			
		||||
        /** The command's flags.
 | 
			
		||||
            See ApplicationCommandInfo for a description of these flag values.
 | 
			
		||||
        */
 | 
			
		||||
        int commandFlags;
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** The types of context in which the command might be called. */
 | 
			
		||||
        enum InvocationMethod
 | 
			
		||||
        {
 | 
			
		||||
            direct = 0,     /**< The command is being invoked directly by a piece of code. */
 | 
			
		||||
            fromKeyPress,   /**< The command is being invoked by a key-press. */
 | 
			
		||||
            fromMenu,       /**< The command is being invoked by a menu selection. */
 | 
			
		||||
            fromButton      /**< The command is being invoked by a button click. */
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /** The type of event that triggered this command. */
 | 
			
		||||
        InvocationMethod invocationMethod;
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** If triggered by a keypress or menu, this will be the component that had the
 | 
			
		||||
            keyboard focus at the time.
 | 
			
		||||
 | 
			
		||||
            If triggered by a button, it may be set to that component, or it may be null.
 | 
			
		||||
        */
 | 
			
		||||
        Component* originatingComponent;
 | 
			
		||||
 | 
			
		||||
        //==============================================================================
 | 
			
		||||
        /** The keypress that was used to invoke it.
 | 
			
		||||
 | 
			
		||||
            Note that this will be an invalid keypress if the command was invoked
 | 
			
		||||
            by some other means than a keyboard shortcut.
 | 
			
		||||
        */
 | 
			
		||||
        KeyPress keyPress;
 | 
			
		||||
 | 
			
		||||
        /** True if the callback is being invoked when the key is pressed,
 | 
			
		||||
            false if the key is being released.
 | 
			
		||||
 | 
			
		||||
            @see KeyPressMappingSet::addCommand()
 | 
			
		||||
        */
 | 
			
		||||
        bool isKeyDown;
 | 
			
		||||
 | 
			
		||||
        /** If the key is being released, this indicates how long it had been held
 | 
			
		||||
            down for.
 | 
			
		||||
 | 
			
		||||
            (Only relevant if isKeyDown is false.)
 | 
			
		||||
        */
 | 
			
		||||
        int millisecsSinceKeyPressed;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** This must return the next target to try after this one.
 | 
			
		||||
 | 
			
		||||
        When a command is being sent, and the first target can't handle
 | 
			
		||||
        that command, this method is used to determine the next target that should
 | 
			
		||||
        be tried.
 | 
			
		||||
 | 
			
		||||
        It may return nullptr if it doesn't know of another target.
 | 
			
		||||
 | 
			
		||||
        If your target is a Component, you would usually use the findFirstTargetParentComponent()
 | 
			
		||||
        method to return a parent component that might want to handle it.
 | 
			
		||||
 | 
			
		||||
        @see invoke
 | 
			
		||||
    */
 | 
			
		||||
    virtual ApplicationCommandTarget* getNextCommandTarget() = 0;
 | 
			
		||||
 | 
			
		||||
    /** This must return a complete list of commands that this target can handle.
 | 
			
		||||
 | 
			
		||||
        Your target should add all the command IDs that it handles to the array that is
 | 
			
		||||
        passed-in.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void getAllCommands (Array<CommandID>& commands) = 0;
 | 
			
		||||
 | 
			
		||||
    /** This must provide details about one of the commands that this target can perform.
 | 
			
		||||
 | 
			
		||||
        This will be called with one of the command IDs that the target provided in its
 | 
			
		||||
        getAllCommands() methods.
 | 
			
		||||
 | 
			
		||||
        It should fill-in all appropriate fields of the ApplicationCommandInfo structure with
 | 
			
		||||
        suitable information about the command. (The commandID field will already have been filled-in
 | 
			
		||||
        by the caller).
 | 
			
		||||
 | 
			
		||||
        The easiest way to set the info is using the ApplicationCommandInfo::setInfo() method to
 | 
			
		||||
        set all the fields at once.
 | 
			
		||||
 | 
			
		||||
        If the command is currently inactive for some reason, this method must use
 | 
			
		||||
        ApplicationCommandInfo::setActive() to make that clear, (or it should set the isDisabled
 | 
			
		||||
        bit of the ApplicationCommandInfo::flags field).
 | 
			
		||||
 | 
			
		||||
        Any default key-presses for the command should be appended to the
 | 
			
		||||
        ApplicationCommandInfo::defaultKeypresses field.
 | 
			
		||||
 | 
			
		||||
        Note that if you change something that affects the status of the commands
 | 
			
		||||
        that would be returned by this method (e.g. something that makes some commands
 | 
			
		||||
        active or inactive), you should call ApplicationCommandManager::commandStatusChanged()
 | 
			
		||||
        to cause the manager to refresh its status.
 | 
			
		||||
    */
 | 
			
		||||
    virtual void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) = 0;
 | 
			
		||||
 | 
			
		||||
    /** This must actually perform the specified command.
 | 
			
		||||
 | 
			
		||||
        If this target is able to perform the command specified by the commandID field of the
 | 
			
		||||
        InvocationInfo structure, then it should do so, and must return true.
 | 
			
		||||
 | 
			
		||||
        If it can't handle this command, it should return false, which tells the caller to pass
 | 
			
		||||
        the command on to the next target in line.
 | 
			
		||||
 | 
			
		||||
        @see invoke, ApplicationCommandManager::invoke
 | 
			
		||||
    */
 | 
			
		||||
    virtual bool perform (const InvocationInfo& info) = 0;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Makes this target invoke a command.
 | 
			
		||||
 | 
			
		||||
        Your code can call this method to invoke a command on this target, but normally
 | 
			
		||||
        you'd call it indirectly via ApplicationCommandManager::invoke() or
 | 
			
		||||
        ApplicationCommandManager::invokeDirectly().
 | 
			
		||||
 | 
			
		||||
        If this target can perform the given command, it will call its perform() method to
 | 
			
		||||
        do so. If not, then getNextCommandTarget() will be used to determine the next target
 | 
			
		||||
        to try, and the command will be passed along to it.
 | 
			
		||||
 | 
			
		||||
        @param invocationInfo       this must be correctly filled-in, describing the context for
 | 
			
		||||
                                    the invocation.
 | 
			
		||||
        @param asynchronously       if false, the command will be performed before this method returns.
 | 
			
		||||
                                    If true, a message will be posted so that the command will be performed
 | 
			
		||||
                                    later on the message thread, and this method will return immediately.
 | 
			
		||||
        @see perform, ApplicationCommandManager::invoke
 | 
			
		||||
    */
 | 
			
		||||
    bool invoke (const InvocationInfo& invocationInfo,
 | 
			
		||||
                 const bool asynchronously);
 | 
			
		||||
 | 
			
		||||
    /** Invokes a given command directly on this target.
 | 
			
		||||
 | 
			
		||||
        This is just an easy way to call invoke() without having to fill out the InvocationInfo
 | 
			
		||||
        structure.
 | 
			
		||||
    */
 | 
			
		||||
    bool invokeDirectly (const CommandID commandID,
 | 
			
		||||
                         const bool asynchronously);
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Searches this target and all subsequent ones for the first one that can handle
 | 
			
		||||
        the specified command.
 | 
			
		||||
 | 
			
		||||
        This will use getNextCommandTarget() to determine the chain of targets to try
 | 
			
		||||
        after this one.
 | 
			
		||||
    */
 | 
			
		||||
    ApplicationCommandTarget* getTargetForCommand (const CommandID commandID);
 | 
			
		||||
 | 
			
		||||
    /** Checks whether this command can currently be performed by this target.
 | 
			
		||||
 | 
			
		||||
        This will return true only if a call to getCommandInfo() doesn't set the
 | 
			
		||||
        isDisabled flag to indicate that the command is inactive.
 | 
			
		||||
    */
 | 
			
		||||
    bool isCommandActive (const CommandID commandID);
 | 
			
		||||
 | 
			
		||||
    /** If this object is a Component, this method will search upwards in its current
 | 
			
		||||
        UI hierarchy for the next parent component that implements the
 | 
			
		||||
        ApplicationCommandTarget class.
 | 
			
		||||
 | 
			
		||||
        If your target is a Component, this is a very handy method to use in your
 | 
			
		||||
        getNextCommandTarget() implementation.
 | 
			
		||||
    */
 | 
			
		||||
    ApplicationCommandTarget* findFirstTargetParentComponent();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    class CommandMessage;
 | 
			
		||||
    friend class CommandMessage;
 | 
			
		||||
 | 
			
		||||
    bool tryToInvoke (const InvocationInfo&, bool async);
 | 
			
		||||
 | 
			
		||||
    JUCE_DECLARE_WEAK_REFERENCEABLE (ApplicationCommandTarget)
 | 
			
		||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandTarget)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,418 +1,418 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
KeyPressMappingSet::KeyPressMappingSet (ApplicationCommandManager& cm)
 | 
			
		||||
    : commandManager (cm)
 | 
			
		||||
{
 | 
			
		||||
    Desktop::getInstance().addFocusChangeListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
KeyPressMappingSet::KeyPressMappingSet (const KeyPressMappingSet& other)
 | 
			
		||||
    : KeyListener(), ChangeBroadcaster(), FocusChangeListener(), commandManager (other.commandManager)
 | 
			
		||||
{
 | 
			
		||||
    Desktop::getInstance().addFocusChangeListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
KeyPressMappingSet::~KeyPressMappingSet()
 | 
			
		||||
{
 | 
			
		||||
    Desktop::getInstance().removeFocusChangeListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
Array<KeyPress> KeyPressMappingSet::getKeyPressesAssignedToCommand (const CommandID commandID) const
 | 
			
		||||
{
 | 
			
		||||
    for (int i = 0; i < mappings.size(); ++i)
 | 
			
		||||
        if (mappings.getUnchecked(i)->commandID == commandID)
 | 
			
		||||
            return mappings.getUnchecked (i)->keypresses;
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::addKeyPress (const CommandID commandID, const KeyPress& newKeyPress, int insertIndex)
 | 
			
		||||
{
 | 
			
		||||
    // If you specify an upper-case letter but no shift key, how is the user supposed to press it!?
 | 
			
		||||
    // Stick to lower-case letters when defining a keypress, to avoid ambiguity.
 | 
			
		||||
    jassert (! (CharacterFunctions::isUpperCase (newKeyPress.getTextCharacter())
 | 
			
		||||
                 && ! newKeyPress.getModifiers().isShiftDown()));
 | 
			
		||||
 | 
			
		||||
    if (findCommandForKeyPress (newKeyPress) != commandID)
 | 
			
		||||
    {
 | 
			
		||||
        if (newKeyPress.isValid())
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = mappings.size(); --i >= 0;)
 | 
			
		||||
            {
 | 
			
		||||
                if (mappings.getUnchecked(i)->commandID == commandID)
 | 
			
		||||
                {
 | 
			
		||||
                    mappings.getUnchecked(i)->keypresses.insert (insertIndex, newKeyPress);
 | 
			
		||||
 | 
			
		||||
                    sendChangeMessage();
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (commandID))
 | 
			
		||||
            {
 | 
			
		||||
                CommandMapping* const cm = new CommandMapping();
 | 
			
		||||
                cm->commandID = commandID;
 | 
			
		||||
                cm->keypresses.add (newKeyPress);
 | 
			
		||||
                cm->wantsKeyUpDownCallbacks = (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) != 0;
 | 
			
		||||
 | 
			
		||||
                mappings.add (cm);
 | 
			
		||||
                sendChangeMessage();
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                // If you hit this, you're trying to attach a keypress to a command ID that
 | 
			
		||||
                // doesn't exist, so the key is not being attached.
 | 
			
		||||
                jassertfalse;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void addKeyPresses (KeyPressMappingSet& set, const ApplicationCommandInfo* const ci)
 | 
			
		||||
{
 | 
			
		||||
    for (int j = 0; j < ci->defaultKeypresses.size(); ++j)
 | 
			
		||||
        set.addKeyPress (ci->commandID, ci->defaultKeypresses.getReference (j));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::resetToDefaultMappings()
 | 
			
		||||
{
 | 
			
		||||
    mappings.clear();
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < commandManager.getNumCommands(); ++i)
 | 
			
		||||
        addKeyPresses (*this, commandManager.getCommandForIndex (i));
 | 
			
		||||
 | 
			
		||||
    sendChangeMessage();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::resetToDefaultMapping (const CommandID commandID)
 | 
			
		||||
{
 | 
			
		||||
    clearAllKeyPresses (commandID);
 | 
			
		||||
 | 
			
		||||
    if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (commandID))
 | 
			
		||||
        addKeyPresses (*this, ci);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::clearAllKeyPresses()
 | 
			
		||||
{
 | 
			
		||||
    if (mappings.size() > 0)
 | 
			
		||||
    {
 | 
			
		||||
        sendChangeMessage();
 | 
			
		||||
        mappings.clear();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::clearAllKeyPresses (const CommandID commandID)
 | 
			
		||||
{
 | 
			
		||||
    for (int i = mappings.size(); --i >= 0;)
 | 
			
		||||
    {
 | 
			
		||||
        if (mappings.getUnchecked(i)->commandID == commandID)
 | 
			
		||||
        {
 | 
			
		||||
            mappings.remove (i);
 | 
			
		||||
            sendChangeMessage();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::removeKeyPress (const KeyPress& keypress)
 | 
			
		||||
{
 | 
			
		||||
    if (keypress.isValid())
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = mappings.size(); --i >= 0;)
 | 
			
		||||
        {
 | 
			
		||||
            CommandMapping& cm = *mappings.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
            for (int j = cm.keypresses.size(); --j >= 0;)
 | 
			
		||||
            {
 | 
			
		||||
                if (keypress == cm.keypresses [j])
 | 
			
		||||
                {
 | 
			
		||||
                    cm.keypresses.remove (j);
 | 
			
		||||
                    sendChangeMessage();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::removeKeyPress (const CommandID commandID, const int keyPressIndex)
 | 
			
		||||
{
 | 
			
		||||
    for (int i = mappings.size(); --i >= 0;)
 | 
			
		||||
    {
 | 
			
		||||
        if (mappings.getUnchecked(i)->commandID == commandID)
 | 
			
		||||
        {
 | 
			
		||||
            mappings.getUnchecked(i)->keypresses.remove (keyPressIndex);
 | 
			
		||||
            sendChangeMessage();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
CommandID KeyPressMappingSet::findCommandForKeyPress (const KeyPress& keyPress) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    for (int i = 0; i < mappings.size(); ++i)
 | 
			
		||||
        if (mappings.getUnchecked(i)->keypresses.contains (keyPress))
 | 
			
		||||
            return mappings.getUnchecked(i)->commandID;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool KeyPressMappingSet::containsMapping (const CommandID commandID, const KeyPress& keyPress) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    for (int i = mappings.size(); --i >= 0;)
 | 
			
		||||
        if (mappings.getUnchecked(i)->commandID == commandID)
 | 
			
		||||
            return mappings.getUnchecked(i)->keypresses.contains (keyPress);
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::invokeCommand (const CommandID commandID,
 | 
			
		||||
                                        const KeyPress& key,
 | 
			
		||||
                                        const bool isKeyDown,
 | 
			
		||||
                                        const int millisecsSinceKeyPressed,
 | 
			
		||||
                                        Component* const originatingComponent) const
 | 
			
		||||
{
 | 
			
		||||
    ApplicationCommandTarget::InvocationInfo info (commandID);
 | 
			
		||||
 | 
			
		||||
    info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromKeyPress;
 | 
			
		||||
    info.isKeyDown = isKeyDown;
 | 
			
		||||
    info.keyPress = key;
 | 
			
		||||
    info.millisecsSinceKeyPressed = millisecsSinceKeyPressed;
 | 
			
		||||
    info.originatingComponent = originatingComponent;
 | 
			
		||||
 | 
			
		||||
    commandManager.invoke (info, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
bool KeyPressMappingSet::restoreFromXml (const XmlElement& xmlVersion)
 | 
			
		||||
{
 | 
			
		||||
    if (xmlVersion.hasTagName ("KEYMAPPINGS"))
 | 
			
		||||
    {
 | 
			
		||||
        if (xmlVersion.getBoolAttribute ("basedOnDefaults", true))
 | 
			
		||||
        {
 | 
			
		||||
            // if the XML was created as a set of differences from the default mappings,
 | 
			
		||||
            // (i.e. by calling createXml (true)), then we need to first restore the defaults.
 | 
			
		||||
            resetToDefaultMappings();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            // if the XML was created calling createXml (false), then we need to clear all
 | 
			
		||||
            // the keys and treat the xml as describing the entire set of mappings.
 | 
			
		||||
            clearAllKeyPresses();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (auto* map : xmlVersion.getChildIterator())
 | 
			
		||||
        {
 | 
			
		||||
            const CommandID commandId = map->getStringAttribute ("commandId").getHexValue32();
 | 
			
		||||
 | 
			
		||||
            if (commandId != 0)
 | 
			
		||||
            {
 | 
			
		||||
                auto key = KeyPress::createFromDescription (map->getStringAttribute ("key"));
 | 
			
		||||
 | 
			
		||||
                if (map->hasTagName ("MAPPING"))
 | 
			
		||||
                {
 | 
			
		||||
                    addKeyPress (commandId, key);
 | 
			
		||||
                }
 | 
			
		||||
                else if (map->hasTagName ("UNMAPPING"))
 | 
			
		||||
                {
 | 
			
		||||
                    for (auto& m : mappings)
 | 
			
		||||
                        if (m->commandID == commandId)
 | 
			
		||||
                            m->keypresses.removeAllInstancesOf (key);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<XmlElement> KeyPressMappingSet::createXml (const bool saveDifferencesFromDefaultSet) const
 | 
			
		||||
{
 | 
			
		||||
    std::unique_ptr<KeyPressMappingSet> defaultSet;
 | 
			
		||||
 | 
			
		||||
    if (saveDifferencesFromDefaultSet)
 | 
			
		||||
    {
 | 
			
		||||
        defaultSet = std::make_unique<KeyPressMappingSet> (commandManager);
 | 
			
		||||
        defaultSet->resetToDefaultMappings();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto doc = std::make_unique<XmlElement> ("KEYMAPPINGS");
 | 
			
		||||
 | 
			
		||||
    doc->setAttribute ("basedOnDefaults", saveDifferencesFromDefaultSet);
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < mappings.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        auto& cm = *mappings.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
        for (int j = 0; j < cm.keypresses.size(); ++j)
 | 
			
		||||
        {
 | 
			
		||||
            if (defaultSet == nullptr
 | 
			
		||||
                 || ! defaultSet->containsMapping (cm.commandID, cm.keypresses.getReference (j)))
 | 
			
		||||
            {
 | 
			
		||||
                auto map = doc->createNewChildElement ("MAPPING");
 | 
			
		||||
 | 
			
		||||
                map->setAttribute ("commandId", String::toHexString ((int) cm.commandID));
 | 
			
		||||
                map->setAttribute ("description", commandManager.getDescriptionOfCommand (cm.commandID));
 | 
			
		||||
                map->setAttribute ("key", cm.keypresses.getReference (j).getTextDescription());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (defaultSet != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = 0; i < defaultSet->mappings.size(); ++i)
 | 
			
		||||
        {
 | 
			
		||||
            auto& cm = *defaultSet->mappings.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
            for (int j = 0; j < cm.keypresses.size(); ++j)
 | 
			
		||||
            {
 | 
			
		||||
                if (! containsMapping (cm.commandID, cm.keypresses.getReference (j)))
 | 
			
		||||
                {
 | 
			
		||||
                    auto map = doc->createNewChildElement ("UNMAPPING");
 | 
			
		||||
 | 
			
		||||
                    map->setAttribute ("commandId", String::toHexString ((int) cm.commandID));
 | 
			
		||||
                    map->setAttribute ("description", commandManager.getDescriptionOfCommand (cm.commandID));
 | 
			
		||||
                    map->setAttribute ("key", cm.keypresses.getReference (j).getTextDescription());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return doc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
bool KeyPressMappingSet::keyPressed (const KeyPress& key, Component* const originatingComponent)
 | 
			
		||||
{
 | 
			
		||||
    bool commandWasDisabled = false;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < mappings.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        CommandMapping& cm = *mappings.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
        if (cm.keypresses.contains (key))
 | 
			
		||||
        {
 | 
			
		||||
            if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (cm.commandID))
 | 
			
		||||
            {
 | 
			
		||||
                if ((ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) == 0)
 | 
			
		||||
                {
 | 
			
		||||
                    ApplicationCommandInfo info (0);
 | 
			
		||||
 | 
			
		||||
                    if (commandManager.getTargetForCommand (cm.commandID, info) != nullptr)
 | 
			
		||||
                    {
 | 
			
		||||
                        if ((info.flags & ApplicationCommandInfo::isDisabled) == 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            invokeCommand (cm.commandID, key, true, 0, originatingComponent);
 | 
			
		||||
                            return true;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        commandWasDisabled = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (originatingComponent != nullptr && commandWasDisabled)
 | 
			
		||||
        originatingComponent->getLookAndFeel().playAlertSound();
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool KeyPressMappingSet::keyStateChanged (const bool /*isKeyDown*/, Component* originatingComponent)
 | 
			
		||||
{
 | 
			
		||||
    bool used = false;
 | 
			
		||||
    const uint32 now = Time::getMillisecondCounter();
 | 
			
		||||
 | 
			
		||||
    for (int i = mappings.size(); --i >= 0;)
 | 
			
		||||
    {
 | 
			
		||||
        CommandMapping& cm = *mappings.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
        if (cm.wantsKeyUpDownCallbacks)
 | 
			
		||||
        {
 | 
			
		||||
            for (int j = cm.keypresses.size(); --j >= 0;)
 | 
			
		||||
            {
 | 
			
		||||
                const KeyPress key (cm.keypresses.getReference (j));
 | 
			
		||||
                const bool isDown = key.isCurrentlyDown();
 | 
			
		||||
 | 
			
		||||
                int keyPressEntryIndex = 0;
 | 
			
		||||
                bool wasDown = false;
 | 
			
		||||
 | 
			
		||||
                for (int k = keysDown.size(); --k >= 0;)
 | 
			
		||||
                {
 | 
			
		||||
                    if (key == keysDown.getUnchecked(k)->key)
 | 
			
		||||
                    {
 | 
			
		||||
                        keyPressEntryIndex = k;
 | 
			
		||||
                        wasDown = true;
 | 
			
		||||
                        used = true;
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (isDown != wasDown)
 | 
			
		||||
                {
 | 
			
		||||
                    int millisecs = 0;
 | 
			
		||||
 | 
			
		||||
                    if (isDown)
 | 
			
		||||
                    {
 | 
			
		||||
                        KeyPressTime* const k = new KeyPressTime();
 | 
			
		||||
                        k->key = key;
 | 
			
		||||
                        k->timeWhenPressed = now;
 | 
			
		||||
 | 
			
		||||
                        keysDown.add (k);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        const uint32 pressTime = keysDown.getUnchecked (keyPressEntryIndex)->timeWhenPressed;
 | 
			
		||||
 | 
			
		||||
                        if (now > pressTime)
 | 
			
		||||
                            millisecs = (int) (now - pressTime);
 | 
			
		||||
 | 
			
		||||
                        keysDown.remove (keyPressEntryIndex);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    invokeCommand (cm.commandID, key, isDown, millisecs, originatingComponent);
 | 
			
		||||
                    used = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return used;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::globalFocusChanged (Component* focusedComponent)
 | 
			
		||||
{
 | 
			
		||||
    if (focusedComponent != nullptr)
 | 
			
		||||
        focusedComponent->keyStateChanged (false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
KeyPressMappingSet::KeyPressMappingSet (ApplicationCommandManager& cm)
 | 
			
		||||
    : commandManager (cm)
 | 
			
		||||
{
 | 
			
		||||
    Desktop::getInstance().addFocusChangeListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
KeyPressMappingSet::KeyPressMappingSet (const KeyPressMappingSet& other)
 | 
			
		||||
    : KeyListener(), ChangeBroadcaster(), FocusChangeListener(), commandManager (other.commandManager)
 | 
			
		||||
{
 | 
			
		||||
    Desktop::getInstance().addFocusChangeListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
KeyPressMappingSet::~KeyPressMappingSet()
 | 
			
		||||
{
 | 
			
		||||
    Desktop::getInstance().removeFocusChangeListener (this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
Array<KeyPress> KeyPressMappingSet::getKeyPressesAssignedToCommand (const CommandID commandID) const
 | 
			
		||||
{
 | 
			
		||||
    for (int i = 0; i < mappings.size(); ++i)
 | 
			
		||||
        if (mappings.getUnchecked(i)->commandID == commandID)
 | 
			
		||||
            return mappings.getUnchecked (i)->keypresses;
 | 
			
		||||
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::addKeyPress (const CommandID commandID, const KeyPress& newKeyPress, int insertIndex)
 | 
			
		||||
{
 | 
			
		||||
    // If you specify an upper-case letter but no shift key, how is the user supposed to press it!?
 | 
			
		||||
    // Stick to lower-case letters when defining a keypress, to avoid ambiguity.
 | 
			
		||||
    jassert (! (CharacterFunctions::isUpperCase (newKeyPress.getTextCharacter())
 | 
			
		||||
                 && ! newKeyPress.getModifiers().isShiftDown()));
 | 
			
		||||
 | 
			
		||||
    if (findCommandForKeyPress (newKeyPress) != commandID)
 | 
			
		||||
    {
 | 
			
		||||
        if (newKeyPress.isValid())
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = mappings.size(); --i >= 0;)
 | 
			
		||||
            {
 | 
			
		||||
                if (mappings.getUnchecked(i)->commandID == commandID)
 | 
			
		||||
                {
 | 
			
		||||
                    mappings.getUnchecked(i)->keypresses.insert (insertIndex, newKeyPress);
 | 
			
		||||
 | 
			
		||||
                    sendChangeMessage();
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (commandID))
 | 
			
		||||
            {
 | 
			
		||||
                CommandMapping* const cm = new CommandMapping();
 | 
			
		||||
                cm->commandID = commandID;
 | 
			
		||||
                cm->keypresses.add (newKeyPress);
 | 
			
		||||
                cm->wantsKeyUpDownCallbacks = (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) != 0;
 | 
			
		||||
 | 
			
		||||
                mappings.add (cm);
 | 
			
		||||
                sendChangeMessage();
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                // If you hit this, you're trying to attach a keypress to a command ID that
 | 
			
		||||
                // doesn't exist, so the key is not being attached.
 | 
			
		||||
                jassertfalse;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void addKeyPresses (KeyPressMappingSet& set, const ApplicationCommandInfo* const ci)
 | 
			
		||||
{
 | 
			
		||||
    for (int j = 0; j < ci->defaultKeypresses.size(); ++j)
 | 
			
		||||
        set.addKeyPress (ci->commandID, ci->defaultKeypresses.getReference (j));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::resetToDefaultMappings()
 | 
			
		||||
{
 | 
			
		||||
    mappings.clear();
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < commandManager.getNumCommands(); ++i)
 | 
			
		||||
        addKeyPresses (*this, commandManager.getCommandForIndex (i));
 | 
			
		||||
 | 
			
		||||
    sendChangeMessage();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::resetToDefaultMapping (const CommandID commandID)
 | 
			
		||||
{
 | 
			
		||||
    clearAllKeyPresses (commandID);
 | 
			
		||||
 | 
			
		||||
    if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (commandID))
 | 
			
		||||
        addKeyPresses (*this, ci);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::clearAllKeyPresses()
 | 
			
		||||
{
 | 
			
		||||
    if (mappings.size() > 0)
 | 
			
		||||
    {
 | 
			
		||||
        sendChangeMessage();
 | 
			
		||||
        mappings.clear();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::clearAllKeyPresses (const CommandID commandID)
 | 
			
		||||
{
 | 
			
		||||
    for (int i = mappings.size(); --i >= 0;)
 | 
			
		||||
    {
 | 
			
		||||
        if (mappings.getUnchecked(i)->commandID == commandID)
 | 
			
		||||
        {
 | 
			
		||||
            mappings.remove (i);
 | 
			
		||||
            sendChangeMessage();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::removeKeyPress (const KeyPress& keypress)
 | 
			
		||||
{
 | 
			
		||||
    if (keypress.isValid())
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = mappings.size(); --i >= 0;)
 | 
			
		||||
        {
 | 
			
		||||
            CommandMapping& cm = *mappings.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
            for (int j = cm.keypresses.size(); --j >= 0;)
 | 
			
		||||
            {
 | 
			
		||||
                if (keypress == cm.keypresses [j])
 | 
			
		||||
                {
 | 
			
		||||
                    cm.keypresses.remove (j);
 | 
			
		||||
                    sendChangeMessage();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::removeKeyPress (const CommandID commandID, const int keyPressIndex)
 | 
			
		||||
{
 | 
			
		||||
    for (int i = mappings.size(); --i >= 0;)
 | 
			
		||||
    {
 | 
			
		||||
        if (mappings.getUnchecked(i)->commandID == commandID)
 | 
			
		||||
        {
 | 
			
		||||
            mappings.getUnchecked(i)->keypresses.remove (keyPressIndex);
 | 
			
		||||
            sendChangeMessage();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
CommandID KeyPressMappingSet::findCommandForKeyPress (const KeyPress& keyPress) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    for (int i = 0; i < mappings.size(); ++i)
 | 
			
		||||
        if (mappings.getUnchecked(i)->keypresses.contains (keyPress))
 | 
			
		||||
            return mappings.getUnchecked(i)->commandID;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool KeyPressMappingSet::containsMapping (const CommandID commandID, const KeyPress& keyPress) const noexcept
 | 
			
		||||
{
 | 
			
		||||
    for (int i = mappings.size(); --i >= 0;)
 | 
			
		||||
        if (mappings.getUnchecked(i)->commandID == commandID)
 | 
			
		||||
            return mappings.getUnchecked(i)->keypresses.contains (keyPress);
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::invokeCommand (const CommandID commandID,
 | 
			
		||||
                                        const KeyPress& key,
 | 
			
		||||
                                        const bool isKeyDown,
 | 
			
		||||
                                        const int millisecsSinceKeyPressed,
 | 
			
		||||
                                        Component* const originatingComponent) const
 | 
			
		||||
{
 | 
			
		||||
    ApplicationCommandTarget::InvocationInfo info (commandID);
 | 
			
		||||
 | 
			
		||||
    info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromKeyPress;
 | 
			
		||||
    info.isKeyDown = isKeyDown;
 | 
			
		||||
    info.keyPress = key;
 | 
			
		||||
    info.millisecsSinceKeyPressed = millisecsSinceKeyPressed;
 | 
			
		||||
    info.originatingComponent = originatingComponent;
 | 
			
		||||
 | 
			
		||||
    commandManager.invoke (info, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
bool KeyPressMappingSet::restoreFromXml (const XmlElement& xmlVersion)
 | 
			
		||||
{
 | 
			
		||||
    if (xmlVersion.hasTagName ("KEYMAPPINGS"))
 | 
			
		||||
    {
 | 
			
		||||
        if (xmlVersion.getBoolAttribute ("basedOnDefaults", true))
 | 
			
		||||
        {
 | 
			
		||||
            // if the XML was created as a set of differences from the default mappings,
 | 
			
		||||
            // (i.e. by calling createXml (true)), then we need to first restore the defaults.
 | 
			
		||||
            resetToDefaultMappings();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            // if the XML was created calling createXml (false), then we need to clear all
 | 
			
		||||
            // the keys and treat the xml as describing the entire set of mappings.
 | 
			
		||||
            clearAllKeyPresses();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for (auto* map : xmlVersion.getChildIterator())
 | 
			
		||||
        {
 | 
			
		||||
            const CommandID commandId = map->getStringAttribute ("commandId").getHexValue32();
 | 
			
		||||
 | 
			
		||||
            if (commandId != 0)
 | 
			
		||||
            {
 | 
			
		||||
                auto key = KeyPress::createFromDescription (map->getStringAttribute ("key"));
 | 
			
		||||
 | 
			
		||||
                if (map->hasTagName ("MAPPING"))
 | 
			
		||||
                {
 | 
			
		||||
                    addKeyPress (commandId, key);
 | 
			
		||||
                }
 | 
			
		||||
                else if (map->hasTagName ("UNMAPPING"))
 | 
			
		||||
                {
 | 
			
		||||
                    for (auto& m : mappings)
 | 
			
		||||
                        if (m->commandID == commandId)
 | 
			
		||||
                            m->keypresses.removeAllInstancesOf (key);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<XmlElement> KeyPressMappingSet::createXml (const bool saveDifferencesFromDefaultSet) const
 | 
			
		||||
{
 | 
			
		||||
    std::unique_ptr<KeyPressMappingSet> defaultSet;
 | 
			
		||||
 | 
			
		||||
    if (saveDifferencesFromDefaultSet)
 | 
			
		||||
    {
 | 
			
		||||
        defaultSet = std::make_unique<KeyPressMappingSet> (commandManager);
 | 
			
		||||
        defaultSet->resetToDefaultMappings();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto doc = std::make_unique<XmlElement> ("KEYMAPPINGS");
 | 
			
		||||
 | 
			
		||||
    doc->setAttribute ("basedOnDefaults", saveDifferencesFromDefaultSet);
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < mappings.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        auto& cm = *mappings.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
        for (int j = 0; j < cm.keypresses.size(); ++j)
 | 
			
		||||
        {
 | 
			
		||||
            if (defaultSet == nullptr
 | 
			
		||||
                 || ! defaultSet->containsMapping (cm.commandID, cm.keypresses.getReference (j)))
 | 
			
		||||
            {
 | 
			
		||||
                auto map = doc->createNewChildElement ("MAPPING");
 | 
			
		||||
 | 
			
		||||
                map->setAttribute ("commandId", String::toHexString ((int) cm.commandID));
 | 
			
		||||
                map->setAttribute ("description", commandManager.getDescriptionOfCommand (cm.commandID));
 | 
			
		||||
                map->setAttribute ("key", cm.keypresses.getReference (j).getTextDescription());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (defaultSet != nullptr)
 | 
			
		||||
    {
 | 
			
		||||
        for (int i = 0; i < defaultSet->mappings.size(); ++i)
 | 
			
		||||
        {
 | 
			
		||||
            auto& cm = *defaultSet->mappings.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
            for (int j = 0; j < cm.keypresses.size(); ++j)
 | 
			
		||||
            {
 | 
			
		||||
                if (! containsMapping (cm.commandID, cm.keypresses.getReference (j)))
 | 
			
		||||
                {
 | 
			
		||||
                    auto map = doc->createNewChildElement ("UNMAPPING");
 | 
			
		||||
 | 
			
		||||
                    map->setAttribute ("commandId", String::toHexString ((int) cm.commandID));
 | 
			
		||||
                    map->setAttribute ("description", commandManager.getDescriptionOfCommand (cm.commandID));
 | 
			
		||||
                    map->setAttribute ("key", cm.keypresses.getReference (j).getTextDescription());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return doc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
bool KeyPressMappingSet::keyPressed (const KeyPress& key, Component* const originatingComponent)
 | 
			
		||||
{
 | 
			
		||||
    bool commandWasDisabled = false;
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < mappings.size(); ++i)
 | 
			
		||||
    {
 | 
			
		||||
        CommandMapping& cm = *mappings.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
        if (cm.keypresses.contains (key))
 | 
			
		||||
        {
 | 
			
		||||
            if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (cm.commandID))
 | 
			
		||||
            {
 | 
			
		||||
                if ((ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) == 0)
 | 
			
		||||
                {
 | 
			
		||||
                    ApplicationCommandInfo info (0);
 | 
			
		||||
 | 
			
		||||
                    if (commandManager.getTargetForCommand (cm.commandID, info) != nullptr)
 | 
			
		||||
                    {
 | 
			
		||||
                        if ((info.flags & ApplicationCommandInfo::isDisabled) == 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            invokeCommand (cm.commandID, key, true, 0, originatingComponent);
 | 
			
		||||
                            return true;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        commandWasDisabled = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (originatingComponent != nullptr && commandWasDisabled)
 | 
			
		||||
        originatingComponent->getLookAndFeel().playAlertSound();
 | 
			
		||||
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool KeyPressMappingSet::keyStateChanged (const bool /*isKeyDown*/, Component* originatingComponent)
 | 
			
		||||
{
 | 
			
		||||
    bool used = false;
 | 
			
		||||
    const uint32 now = Time::getMillisecondCounter();
 | 
			
		||||
 | 
			
		||||
    for (int i = mappings.size(); --i >= 0;)
 | 
			
		||||
    {
 | 
			
		||||
        CommandMapping& cm = *mappings.getUnchecked(i);
 | 
			
		||||
 | 
			
		||||
        if (cm.wantsKeyUpDownCallbacks)
 | 
			
		||||
        {
 | 
			
		||||
            for (int j = cm.keypresses.size(); --j >= 0;)
 | 
			
		||||
            {
 | 
			
		||||
                const KeyPress key (cm.keypresses.getReference (j));
 | 
			
		||||
                const bool isDown = key.isCurrentlyDown();
 | 
			
		||||
 | 
			
		||||
                int keyPressEntryIndex = 0;
 | 
			
		||||
                bool wasDown = false;
 | 
			
		||||
 | 
			
		||||
                for (int k = keysDown.size(); --k >= 0;)
 | 
			
		||||
                {
 | 
			
		||||
                    if (key == keysDown.getUnchecked(k)->key)
 | 
			
		||||
                    {
 | 
			
		||||
                        keyPressEntryIndex = k;
 | 
			
		||||
                        wasDown = true;
 | 
			
		||||
                        used = true;
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (isDown != wasDown)
 | 
			
		||||
                {
 | 
			
		||||
                    int millisecs = 0;
 | 
			
		||||
 | 
			
		||||
                    if (isDown)
 | 
			
		||||
                    {
 | 
			
		||||
                        KeyPressTime* const k = new KeyPressTime();
 | 
			
		||||
                        k->key = key;
 | 
			
		||||
                        k->timeWhenPressed = now;
 | 
			
		||||
 | 
			
		||||
                        keysDown.add (k);
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        const uint32 pressTime = keysDown.getUnchecked (keyPressEntryIndex)->timeWhenPressed;
 | 
			
		||||
 | 
			
		||||
                        if (now > pressTime)
 | 
			
		||||
                            millisecs = (int) (now - pressTime);
 | 
			
		||||
 | 
			
		||||
                        keysDown.remove (keyPressEntryIndex);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    invokeCommand (cm.commandID, key, isDown, millisecs, originatingComponent);
 | 
			
		||||
                    used = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return used;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void KeyPressMappingSet::globalFocusChanged (Component* focusedComponent)
 | 
			
		||||
{
 | 
			
		||||
    if (focusedComponent != nullptr)
 | 
			
		||||
        focusedComponent->keyStateChanged (false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
@@ -1,244 +1,244 @@
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2020 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 6 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-6-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Manages and edits a list of keypresses, which it uses to invoke the appropriate
 | 
			
		||||
    command in an ApplicationCommandManager.
 | 
			
		||||
 | 
			
		||||
    Normally, you won't actually create a KeyPressMappingSet directly, because
 | 
			
		||||
    each ApplicationCommandManager contains its own KeyPressMappingSet, so typically
 | 
			
		||||
    you'd create yourself an ApplicationCommandManager, and call its
 | 
			
		||||
    ApplicationCommandManager::getKeyMappings() method to get a pointer to its
 | 
			
		||||
    KeyPressMappingSet.
 | 
			
		||||
 | 
			
		||||
    For one of these to actually use keypresses, you'll need to add it as a KeyListener
 | 
			
		||||
    to the top-level component for which you want to handle keystrokes. So for example:
 | 
			
		||||
 | 
			
		||||
    @code
 | 
			
		||||
    class MyMainWindow  : public Component
 | 
			
		||||
    {
 | 
			
		||||
        ApplicationCommandManager* myCommandManager;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        MyMainWindow()
 | 
			
		||||
        {
 | 
			
		||||
            myCommandManager = new ApplicationCommandManager();
 | 
			
		||||
 | 
			
		||||
            // first, make sure the command manager has registered all the commands that its
 | 
			
		||||
            // targets can perform..
 | 
			
		||||
            myCommandManager->registerAllCommandsForTarget (myCommandTarget1);
 | 
			
		||||
            myCommandManager->registerAllCommandsForTarget (myCommandTarget2);
 | 
			
		||||
 | 
			
		||||
            // this will use the command manager to initialise the KeyPressMappingSet with
 | 
			
		||||
            // the default keypresses that were specified when the targets added their commands
 | 
			
		||||
            // to the manager.
 | 
			
		||||
            myCommandManager->getKeyMappings()->resetToDefaultMappings();
 | 
			
		||||
 | 
			
		||||
            // having set up the default key-mappings, you might now want to load the last set
 | 
			
		||||
            // of mappings that the user configured.
 | 
			
		||||
            myCommandManager->getKeyMappings()->restoreFromXml (lastSavedKeyMappingsXML);
 | 
			
		||||
 | 
			
		||||
            // Now tell our top-level window to send any keypresses that arrive to the
 | 
			
		||||
            // KeyPressMappingSet, which will use them to invoke the appropriate commands.
 | 
			
		||||
            addKeyListener (myCommandManager->getKeyMappings());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ...
 | 
			
		||||
    }
 | 
			
		||||
    @endcode
 | 
			
		||||
 | 
			
		||||
    KeyPressMappingSet derives from ChangeBroadcaster so that interested parties can
 | 
			
		||||
    register to be told when a command or mapping is added, removed, etc.
 | 
			
		||||
 | 
			
		||||
    There's also a UI component called KeyMappingEditorComponent that can be used
 | 
			
		||||
    to easily edit the key mappings.
 | 
			
		||||
 | 
			
		||||
    @see Component::addKeyListener(), KeyMappingEditorComponent, ApplicationCommandManager
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  KeyPressMappingSet  : public KeyListener,
 | 
			
		||||
                                      public ChangeBroadcaster,
 | 
			
		||||
                                      private FocusChangeListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a KeyPressMappingSet for a given command manager.
 | 
			
		||||
 | 
			
		||||
        Normally, you won't actually create a KeyPressMappingSet directly, because
 | 
			
		||||
        each ApplicationCommandManager contains its own KeyPressMappingSet, so the
 | 
			
		||||
        best thing to do is to create your ApplicationCommandManager, and use the
 | 
			
		||||
        ApplicationCommandManager::getKeyMappings() method to access its mappings.
 | 
			
		||||
 | 
			
		||||
        When a suitable keypress happens, the manager's invoke() method will be
 | 
			
		||||
        used to invoke the appropriate command.
 | 
			
		||||
 | 
			
		||||
        @see ApplicationCommandManager
 | 
			
		||||
    */
 | 
			
		||||
    explicit KeyPressMappingSet (ApplicationCommandManager&);
 | 
			
		||||
 | 
			
		||||
    /** Creates an copy of a KeyPressMappingSet. */
 | 
			
		||||
    KeyPressMappingSet (const KeyPressMappingSet&);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~KeyPressMappingSet() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    ApplicationCommandManager& getCommandManager() const noexcept       { return commandManager; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns a list of keypresses that are assigned to a particular command.
 | 
			
		||||
 | 
			
		||||
        @param commandID        the command's ID
 | 
			
		||||
    */
 | 
			
		||||
    Array<KeyPress> getKeyPressesAssignedToCommand (CommandID commandID) const;
 | 
			
		||||
 | 
			
		||||
    /** Assigns a keypress to a command.
 | 
			
		||||
 | 
			
		||||
        If the keypress is already assigned to a different command, it will first be
 | 
			
		||||
        removed from that command, to avoid it triggering multiple functions.
 | 
			
		||||
 | 
			
		||||
        @param commandID    the ID of the command that you want to add a keypress to. If
 | 
			
		||||
                            this is 0, the keypress will be removed from anything that it
 | 
			
		||||
                            was previously assigned to, but not re-assigned
 | 
			
		||||
        @param newKeyPress  the new key-press
 | 
			
		||||
        @param insertIndex  if this is less than zero, the key will be appended to the
 | 
			
		||||
                            end of the list of keypresses; otherwise the new keypress will
 | 
			
		||||
                            be inserted into the existing list at this index
 | 
			
		||||
    */
 | 
			
		||||
    void addKeyPress (CommandID commandID,
 | 
			
		||||
                      const KeyPress& newKeyPress,
 | 
			
		||||
                      int insertIndex = -1);
 | 
			
		||||
 | 
			
		||||
    /** Reset all mappings to the defaults, as dictated by the ApplicationCommandManager.
 | 
			
		||||
        @see resetToDefaultMapping
 | 
			
		||||
    */
 | 
			
		||||
    void resetToDefaultMappings();
 | 
			
		||||
 | 
			
		||||
    /** Resets all key-mappings to the defaults for a particular command.
 | 
			
		||||
        @see resetToDefaultMappings
 | 
			
		||||
    */
 | 
			
		||||
    void resetToDefaultMapping (CommandID commandID);
 | 
			
		||||
 | 
			
		||||
    /** Removes all keypresses that are assigned to any commands. */
 | 
			
		||||
    void clearAllKeyPresses();
 | 
			
		||||
 | 
			
		||||
    /** Removes all keypresses that are assigned to a particular command. */
 | 
			
		||||
    void clearAllKeyPresses (CommandID commandID);
 | 
			
		||||
 | 
			
		||||
    /** Removes one of the keypresses that are assigned to a command.
 | 
			
		||||
        See the getKeyPressesAssignedToCommand() for the list of keypresses to
 | 
			
		||||
        which the keyPressIndex refers.
 | 
			
		||||
    */
 | 
			
		||||
    void removeKeyPress (CommandID commandID, int keyPressIndex);
 | 
			
		||||
 | 
			
		||||
    /** Removes a keypress from any command that it may be assigned to. */
 | 
			
		||||
    void removeKeyPress (const KeyPress& keypress);
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the given command is linked to this key. */
 | 
			
		||||
    bool containsMapping (CommandID commandID, const KeyPress& keyPress) const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Looks for a command that corresponds to a keypress.
 | 
			
		||||
        @returns the UID of the command or 0 if none was found
 | 
			
		||||
    */
 | 
			
		||||
    CommandID findCommandForKeyPress (const KeyPress& keyPress) const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Tries to recreate the mappings from a previously stored state.
 | 
			
		||||
 | 
			
		||||
        The XML passed in must have been created by the createXml() method.
 | 
			
		||||
 | 
			
		||||
        If the stored state makes any reference to commands that aren't
 | 
			
		||||
        currently available, these will be ignored.
 | 
			
		||||
 | 
			
		||||
        If the set of mappings being loaded was a set of differences (using createXml (true)),
 | 
			
		||||
        then this will call resetToDefaultMappings() and then merge the saved mappings
 | 
			
		||||
        on top. If the saved set was created with createXml (false), then this method
 | 
			
		||||
        will first clear all existing mappings and load the saved ones as a complete set.
 | 
			
		||||
 | 
			
		||||
        @returns true if it manages to load the XML correctly
 | 
			
		||||
        @see createXml
 | 
			
		||||
    */
 | 
			
		||||
    bool restoreFromXml (const XmlElement& xmlVersion);
 | 
			
		||||
 | 
			
		||||
    /** Creates an XML representation of the current mappings.
 | 
			
		||||
 | 
			
		||||
        This will produce a lump of XML that can be later reloaded using
 | 
			
		||||
        restoreFromXml() to recreate the current mapping state.
 | 
			
		||||
 | 
			
		||||
        @param saveDifferencesFromDefaultSet    if this is false, then all keypresses
 | 
			
		||||
                            will be saved into the XML. If it's true, then the XML will
 | 
			
		||||
                            only store the differences between the current mappings and
 | 
			
		||||
                            the default mappings you'd get from calling resetToDefaultMappings().
 | 
			
		||||
                            The advantage of saving a set of differences from the default is that
 | 
			
		||||
                            if you change the default mappings (in a new version of your app, for
 | 
			
		||||
                            example), then these will be merged into a user's saved preferences.
 | 
			
		||||
 | 
			
		||||
        @see restoreFromXml
 | 
			
		||||
    */
 | 
			
		||||
    std::unique_ptr<XmlElement> createXml (bool saveDifferencesFromDefaultSet) const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool keyPressed (const KeyPress&, Component*) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool keyStateChanged (bool isKeyDown, Component*) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void globalFocusChanged (Component*) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    ApplicationCommandManager& commandManager;
 | 
			
		||||
 | 
			
		||||
    struct CommandMapping
 | 
			
		||||
    {
 | 
			
		||||
        CommandID commandID;
 | 
			
		||||
        Array<KeyPress> keypresses;
 | 
			
		||||
        bool wantsKeyUpDownCallbacks;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OwnedArray<CommandMapping> mappings;
 | 
			
		||||
 | 
			
		||||
    struct KeyPressTime
 | 
			
		||||
    {
 | 
			
		||||
        KeyPress key;
 | 
			
		||||
        uint32 timeWhenPressed;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OwnedArray<KeyPressTime> keysDown;
 | 
			
		||||
 | 
			
		||||
    void invokeCommand (const CommandID, const KeyPress&, const bool isKeyDown,
 | 
			
		||||
                        const int millisecsSinceKeyPressed, Component* originator) const;
 | 
			
		||||
 | 
			
		||||
    KeyPressMappingSet& operator= (const KeyPressMappingSet&);
 | 
			
		||||
    JUCE_LEAK_DETECTOR (KeyPressMappingSet)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
/*
 | 
			
		||||
  ==============================================================================
 | 
			
		||||
 | 
			
		||||
   This file is part of the JUCE library.
 | 
			
		||||
   Copyright (c) 2022 - Raw Material Software Limited
 | 
			
		||||
 | 
			
		||||
   JUCE is an open source library subject to commercial or open-source
 | 
			
		||||
   licensing.
 | 
			
		||||
 | 
			
		||||
   By using JUCE, you agree to the terms of both the JUCE 7 End-User License
 | 
			
		||||
   Agreement and JUCE Privacy Policy.
 | 
			
		||||
 | 
			
		||||
   End User License Agreement: www.juce.com/juce-7-licence
 | 
			
		||||
   Privacy Policy: www.juce.com/juce-privacy-policy
 | 
			
		||||
 | 
			
		||||
   Or: You may also use this code under the terms of the GPL v3 (see
 | 
			
		||||
   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
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
//==============================================================================
 | 
			
		||||
/**
 | 
			
		||||
    Manages and edits a list of keypresses, which it uses to invoke the appropriate
 | 
			
		||||
    command in an ApplicationCommandManager.
 | 
			
		||||
 | 
			
		||||
    Normally, you won't actually create a KeyPressMappingSet directly, because
 | 
			
		||||
    each ApplicationCommandManager contains its own KeyPressMappingSet, so typically
 | 
			
		||||
    you'd create yourself an ApplicationCommandManager, and call its
 | 
			
		||||
    ApplicationCommandManager::getKeyMappings() method to get a pointer to its
 | 
			
		||||
    KeyPressMappingSet.
 | 
			
		||||
 | 
			
		||||
    For one of these to actually use keypresses, you'll need to add it as a KeyListener
 | 
			
		||||
    to the top-level component for which you want to handle keystrokes. So for example:
 | 
			
		||||
 | 
			
		||||
    @code
 | 
			
		||||
    class MyMainWindow  : public Component
 | 
			
		||||
    {
 | 
			
		||||
        ApplicationCommandManager* myCommandManager;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        MyMainWindow()
 | 
			
		||||
        {
 | 
			
		||||
            myCommandManager = new ApplicationCommandManager();
 | 
			
		||||
 | 
			
		||||
            // first, make sure the command manager has registered all the commands that its
 | 
			
		||||
            // targets can perform..
 | 
			
		||||
            myCommandManager->registerAllCommandsForTarget (myCommandTarget1);
 | 
			
		||||
            myCommandManager->registerAllCommandsForTarget (myCommandTarget2);
 | 
			
		||||
 | 
			
		||||
            // this will use the command manager to initialise the KeyPressMappingSet with
 | 
			
		||||
            // the default keypresses that were specified when the targets added their commands
 | 
			
		||||
            // to the manager.
 | 
			
		||||
            myCommandManager->getKeyMappings()->resetToDefaultMappings();
 | 
			
		||||
 | 
			
		||||
            // having set up the default key-mappings, you might now want to load the last set
 | 
			
		||||
            // of mappings that the user configured.
 | 
			
		||||
            myCommandManager->getKeyMappings()->restoreFromXml (lastSavedKeyMappingsXML);
 | 
			
		||||
 | 
			
		||||
            // Now tell our top-level window to send any keypresses that arrive to the
 | 
			
		||||
            // KeyPressMappingSet, which will use them to invoke the appropriate commands.
 | 
			
		||||
            addKeyListener (myCommandManager->getKeyMappings());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ...
 | 
			
		||||
    }
 | 
			
		||||
    @endcode
 | 
			
		||||
 | 
			
		||||
    KeyPressMappingSet derives from ChangeBroadcaster so that interested parties can
 | 
			
		||||
    register to be told when a command or mapping is added, removed, etc.
 | 
			
		||||
 | 
			
		||||
    There's also a UI component called KeyMappingEditorComponent that can be used
 | 
			
		||||
    to easily edit the key mappings.
 | 
			
		||||
 | 
			
		||||
    @see Component::addKeyListener(), KeyMappingEditorComponent, ApplicationCommandManager
 | 
			
		||||
 | 
			
		||||
    @tags{GUI}
 | 
			
		||||
*/
 | 
			
		||||
class JUCE_API  KeyPressMappingSet  : public KeyListener,
 | 
			
		||||
                                      public ChangeBroadcaster,
 | 
			
		||||
                                      private FocusChangeListener
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Creates a KeyPressMappingSet for a given command manager.
 | 
			
		||||
 | 
			
		||||
        Normally, you won't actually create a KeyPressMappingSet directly, because
 | 
			
		||||
        each ApplicationCommandManager contains its own KeyPressMappingSet, so the
 | 
			
		||||
        best thing to do is to create your ApplicationCommandManager, and use the
 | 
			
		||||
        ApplicationCommandManager::getKeyMappings() method to access its mappings.
 | 
			
		||||
 | 
			
		||||
        When a suitable keypress happens, the manager's invoke() method will be
 | 
			
		||||
        used to invoke the appropriate command.
 | 
			
		||||
 | 
			
		||||
        @see ApplicationCommandManager
 | 
			
		||||
    */
 | 
			
		||||
    explicit KeyPressMappingSet (ApplicationCommandManager&);
 | 
			
		||||
 | 
			
		||||
    /** Creates an copy of a KeyPressMappingSet. */
 | 
			
		||||
    KeyPressMappingSet (const KeyPressMappingSet&);
 | 
			
		||||
 | 
			
		||||
    /** Destructor. */
 | 
			
		||||
    ~KeyPressMappingSet() override;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    ApplicationCommandManager& getCommandManager() const noexcept       { return commandManager; }
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Returns a list of keypresses that are assigned to a particular command.
 | 
			
		||||
 | 
			
		||||
        @param commandID        the command's ID
 | 
			
		||||
    */
 | 
			
		||||
    Array<KeyPress> getKeyPressesAssignedToCommand (CommandID commandID) const;
 | 
			
		||||
 | 
			
		||||
    /** Assigns a keypress to a command.
 | 
			
		||||
 | 
			
		||||
        If the keypress is already assigned to a different command, it will first be
 | 
			
		||||
        removed from that command, to avoid it triggering multiple functions.
 | 
			
		||||
 | 
			
		||||
        @param commandID    the ID of the command that you want to add a keypress to. If
 | 
			
		||||
                            this is 0, the keypress will be removed from anything that it
 | 
			
		||||
                            was previously assigned to, but not re-assigned
 | 
			
		||||
        @param newKeyPress  the new key-press
 | 
			
		||||
        @param insertIndex  if this is less than zero, the key will be appended to the
 | 
			
		||||
                            end of the list of keypresses; otherwise the new keypress will
 | 
			
		||||
                            be inserted into the existing list at this index
 | 
			
		||||
    */
 | 
			
		||||
    void addKeyPress (CommandID commandID,
 | 
			
		||||
                      const KeyPress& newKeyPress,
 | 
			
		||||
                      int insertIndex = -1);
 | 
			
		||||
 | 
			
		||||
    /** Reset all mappings to the defaults, as dictated by the ApplicationCommandManager.
 | 
			
		||||
        @see resetToDefaultMapping
 | 
			
		||||
    */
 | 
			
		||||
    void resetToDefaultMappings();
 | 
			
		||||
 | 
			
		||||
    /** Resets all key-mappings to the defaults for a particular command.
 | 
			
		||||
        @see resetToDefaultMappings
 | 
			
		||||
    */
 | 
			
		||||
    void resetToDefaultMapping (CommandID commandID);
 | 
			
		||||
 | 
			
		||||
    /** Removes all keypresses that are assigned to any commands. */
 | 
			
		||||
    void clearAllKeyPresses();
 | 
			
		||||
 | 
			
		||||
    /** Removes all keypresses that are assigned to a particular command. */
 | 
			
		||||
    void clearAllKeyPresses (CommandID commandID);
 | 
			
		||||
 | 
			
		||||
    /** Removes one of the keypresses that are assigned to a command.
 | 
			
		||||
        See the getKeyPressesAssignedToCommand() for the list of keypresses to
 | 
			
		||||
        which the keyPressIndex refers.
 | 
			
		||||
    */
 | 
			
		||||
    void removeKeyPress (CommandID commandID, int keyPressIndex);
 | 
			
		||||
 | 
			
		||||
    /** Removes a keypress from any command that it may be assigned to. */
 | 
			
		||||
    void removeKeyPress (const KeyPress& keypress);
 | 
			
		||||
 | 
			
		||||
    /** Returns true if the given command is linked to this key. */
 | 
			
		||||
    bool containsMapping (CommandID commandID, const KeyPress& keyPress) const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Looks for a command that corresponds to a keypress.
 | 
			
		||||
        @returns the UID of the command or 0 if none was found
 | 
			
		||||
    */
 | 
			
		||||
    CommandID findCommandForKeyPress (const KeyPress& keyPress) const noexcept;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** Tries to recreate the mappings from a previously stored state.
 | 
			
		||||
 | 
			
		||||
        The XML passed in must have been created by the createXml() method.
 | 
			
		||||
 | 
			
		||||
        If the stored state makes any reference to commands that aren't
 | 
			
		||||
        currently available, these will be ignored.
 | 
			
		||||
 | 
			
		||||
        If the set of mappings being loaded was a set of differences (using createXml (true)),
 | 
			
		||||
        then this will call resetToDefaultMappings() and then merge the saved mappings
 | 
			
		||||
        on top. If the saved set was created with createXml (false), then this method
 | 
			
		||||
        will first clear all existing mappings and load the saved ones as a complete set.
 | 
			
		||||
 | 
			
		||||
        @returns true if it manages to load the XML correctly
 | 
			
		||||
        @see createXml
 | 
			
		||||
    */
 | 
			
		||||
    bool restoreFromXml (const XmlElement& xmlVersion);
 | 
			
		||||
 | 
			
		||||
    /** Creates an XML representation of the current mappings.
 | 
			
		||||
 | 
			
		||||
        This will produce a lump of XML that can be later reloaded using
 | 
			
		||||
        restoreFromXml() to recreate the current mapping state.
 | 
			
		||||
 | 
			
		||||
        @param saveDifferencesFromDefaultSet    if this is false, then all keypresses
 | 
			
		||||
                            will be saved into the XML. If it's true, then the XML will
 | 
			
		||||
                            only store the differences between the current mappings and
 | 
			
		||||
                            the default mappings you'd get from calling resetToDefaultMappings().
 | 
			
		||||
                            The advantage of saving a set of differences from the default is that
 | 
			
		||||
                            if you change the default mappings (in a new version of your app, for
 | 
			
		||||
                            example), then these will be merged into a user's saved preferences.
 | 
			
		||||
 | 
			
		||||
        @see restoreFromXml
 | 
			
		||||
    */
 | 
			
		||||
    std::unique_ptr<XmlElement> createXml (bool saveDifferencesFromDefaultSet) const;
 | 
			
		||||
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool keyPressed (const KeyPress&, Component*) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    bool keyStateChanged (bool isKeyDown, Component*) override;
 | 
			
		||||
    /** @internal */
 | 
			
		||||
    void globalFocusChanged (Component*) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    //==============================================================================
 | 
			
		||||
    ApplicationCommandManager& commandManager;
 | 
			
		||||
 | 
			
		||||
    struct CommandMapping
 | 
			
		||||
    {
 | 
			
		||||
        CommandID commandID;
 | 
			
		||||
        Array<KeyPress> keypresses;
 | 
			
		||||
        bool wantsKeyUpDownCallbacks;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OwnedArray<CommandMapping> mappings;
 | 
			
		||||
 | 
			
		||||
    struct KeyPressTime
 | 
			
		||||
    {
 | 
			
		||||
        KeyPress key;
 | 
			
		||||
        uint32 timeWhenPressed;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    OwnedArray<KeyPressTime> keysDown;
 | 
			
		||||
 | 
			
		||||
    void invokeCommand (const CommandID, const KeyPress&, const bool isKeyDown,
 | 
			
		||||
                        const int millisecsSinceKeyPressed, Component* originator) const;
 | 
			
		||||
 | 
			
		||||
    KeyPressMappingSet& operator= (const KeyPressMappingSet&);
 | 
			
		||||
    JUCE_LEAK_DETECTOR (KeyPressMappingSet)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace juce
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user