1 /* 2 ============================================================================== 3 4 This file is part of the JUCE library. 5 Copyright (c) 2020 - Raw Material Software Limited 6 7 JUCE is an open source library subject to commercial or open-source 8 licensing. 9 10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License 11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). 12 13 End User License Agreement: www.juce.com/juce-6-licence 14 Privacy Policy: www.juce.com/juce-privacy-policy 15 16 Or: You may also use this code under the terms of the GPL v3 (see 17 www.gnu.org/licenses). 18 19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER 20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE 21 DISCLAIMED. 22 23 ============================================================================== 24 */ 25 26 namespace juce 27 { 28 29 //============================================================================== 30 /** 31 Manages and edits a list of keypresses, which it uses to invoke the appropriate 32 command in an ApplicationCommandManager. 33 34 Normally, you won't actually create a KeyPressMappingSet directly, because 35 each ApplicationCommandManager contains its own KeyPressMappingSet, so typically 36 you'd create yourself an ApplicationCommandManager, and call its 37 ApplicationCommandManager::getKeyMappings() method to get a pointer to its 38 KeyPressMappingSet. 39 40 For one of these to actually use keypresses, you'll need to add it as a KeyListener 41 to the top-level component for which you want to handle keystrokes. So for example: 42 43 @code 44 class MyMainWindow : public Component 45 { 46 ApplicationCommandManager* myCommandManager; 47 48 public: 49 MyMainWindow() 50 { 51 myCommandManager = new ApplicationCommandManager(); 52 53 // first, make sure the command manager has registered all the commands that its 54 // targets can perform.. 55 myCommandManager->registerAllCommandsForTarget (myCommandTarget1); 56 myCommandManager->registerAllCommandsForTarget (myCommandTarget2); 57 58 // this will use the command manager to initialise the KeyPressMappingSet with 59 // the default keypresses that were specified when the targets added their commands 60 // to the manager. 61 myCommandManager->getKeyMappings()->resetToDefaultMappings(); 62 63 // having set up the default key-mappings, you might now want to load the last set 64 // of mappings that the user configured. 65 myCommandManager->getKeyMappings()->restoreFromXml (lastSavedKeyMappingsXML); 66 67 // Now tell our top-level window to send any keypresses that arrive to the 68 // KeyPressMappingSet, which will use them to invoke the appropriate commands. 69 addKeyListener (myCommandManager->getKeyMappings()); 70 } 71 72 ... 73 } 74 @endcode 75 76 KeyPressMappingSet derives from ChangeBroadcaster so that interested parties can 77 register to be told when a command or mapping is added, removed, etc. 78 79 There's also a UI component called KeyMappingEditorComponent that can be used 80 to easily edit the key mappings. 81 82 @see Component::addKeyListener(), KeyMappingEditorComponent, ApplicationCommandManager 83 84 @tags{GUI} 85 */ 86 class JUCE_API KeyPressMappingSet : public KeyListener, 87 public ChangeBroadcaster, 88 private FocusChangeListener 89 { 90 public: 91 //============================================================================== 92 /** Creates a KeyPressMappingSet for a given command manager. 93 94 Normally, you won't actually create a KeyPressMappingSet directly, because 95 each ApplicationCommandManager contains its own KeyPressMappingSet, so the 96 best thing to do is to create your ApplicationCommandManager, and use the 97 ApplicationCommandManager::getKeyMappings() method to access its mappings. 98 99 When a suitable keypress happens, the manager's invoke() method will be 100 used to invoke the appropriate command. 101 102 @see ApplicationCommandManager 103 */ 104 explicit KeyPressMappingSet (ApplicationCommandManager&); 105 106 /** Creates an copy of a KeyPressMappingSet. */ 107 KeyPressMappingSet (const KeyPressMappingSet&); 108 109 /** Destructor. */ 110 ~KeyPressMappingSet() override; 111 112 //============================================================================== getCommandManager()113 ApplicationCommandManager& getCommandManager() const noexcept { return commandManager; } 114 115 //============================================================================== 116 /** Returns a list of keypresses that are assigned to a particular command. 117 118 @param commandID the command's ID 119 */ 120 Array<KeyPress> getKeyPressesAssignedToCommand (CommandID commandID) const; 121 122 /** Assigns a keypress to a command. 123 124 If the keypress is already assigned to a different command, it will first be 125 removed from that command, to avoid it triggering multiple functions. 126 127 @param commandID the ID of the command that you want to add a keypress to. If 128 this is 0, the keypress will be removed from anything that it 129 was previously assigned to, but not re-assigned 130 @param newKeyPress the new key-press 131 @param insertIndex if this is less than zero, the key will be appended to the 132 end of the list of keypresses; otherwise the new keypress will 133 be inserted into the existing list at this index 134 */ 135 void addKeyPress (CommandID commandID, 136 const KeyPress& newKeyPress, 137 int insertIndex = -1); 138 139 /** Reset all mappings to the defaults, as dictated by the ApplicationCommandManager. 140 @see resetToDefaultMapping 141 */ 142 void resetToDefaultMappings(); 143 144 /** Resets all key-mappings to the defaults for a particular command. 145 @see resetToDefaultMappings 146 */ 147 void resetToDefaultMapping (CommandID commandID); 148 149 /** Removes all keypresses that are assigned to any commands. */ 150 void clearAllKeyPresses(); 151 152 /** Removes all keypresses that are assigned to a particular command. */ 153 void clearAllKeyPresses (CommandID commandID); 154 155 /** Removes one of the keypresses that are assigned to a command. 156 See the getKeyPressesAssignedToCommand() for the list of keypresses to 157 which the keyPressIndex refers. 158 */ 159 void removeKeyPress (CommandID commandID, int keyPressIndex); 160 161 /** Removes a keypress from any command that it may be assigned to. */ 162 void removeKeyPress (const KeyPress& keypress); 163 164 /** Returns true if the given command is linked to this key. */ 165 bool containsMapping (CommandID commandID, const KeyPress& keyPress) const noexcept; 166 167 //============================================================================== 168 /** Looks for a command that corresponds to a keypress. 169 @returns the UID of the command or 0 if none was found 170 */ 171 CommandID findCommandForKeyPress (const KeyPress& keyPress) const noexcept; 172 173 //============================================================================== 174 /** Tries to recreate the mappings from a previously stored state. 175 176 The XML passed in must have been created by the createXml() method. 177 178 If the stored state makes any reference to commands that aren't 179 currently available, these will be ignored. 180 181 If the set of mappings being loaded was a set of differences (using createXml (true)), 182 then this will call resetToDefaultMappings() and then merge the saved mappings 183 on top. If the saved set was created with createXml (false), then this method 184 will first clear all existing mappings and load the saved ones as a complete set. 185 186 @returns true if it manages to load the XML correctly 187 @see createXml 188 */ 189 bool restoreFromXml (const XmlElement& xmlVersion); 190 191 /** Creates an XML representation of the current mappings. 192 193 This will produce a lump of XML that can be later reloaded using 194 restoreFromXml() to recreate the current mapping state. 195 196 @param saveDifferencesFromDefaultSet if this is false, then all keypresses 197 will be saved into the XML. If it's true, then the XML will 198 only store the differences between the current mappings and 199 the default mappings you'd get from calling resetToDefaultMappings(). 200 The advantage of saving a set of differences from the default is that 201 if you change the default mappings (in a new version of your app, for 202 example), then these will be merged into a user's saved preferences. 203 204 @see restoreFromXml 205 */ 206 std::unique_ptr<XmlElement> createXml (bool saveDifferencesFromDefaultSet) const; 207 208 //============================================================================== 209 /** @internal */ 210 bool keyPressed (const KeyPress&, Component*) override; 211 /** @internal */ 212 bool keyStateChanged (bool isKeyDown, Component*) override; 213 /** @internal */ 214 void globalFocusChanged (Component*) override; 215 216 private: 217 //============================================================================== 218 ApplicationCommandManager& commandManager; 219 220 struct CommandMapping 221 { 222 CommandID commandID; 223 Array<KeyPress> keypresses; 224 bool wantsKeyUpDownCallbacks; 225 }; 226 227 OwnedArray<CommandMapping> mappings; 228 229 struct KeyPressTime 230 { 231 KeyPress key; 232 uint32 timeWhenPressed; 233 }; 234 235 OwnedArray<KeyPressTime> keysDown; 236 237 void invokeCommand (const CommandID, const KeyPress&, const bool isKeyDown, 238 const int millisecsSinceKeyPressed, Component* originator) const; 239 240 KeyPressMappingSet& operator= (const KeyPressMappingSet&); 241 JUCE_LEAK_DETECTOR (KeyPressMappingSet) 242 }; 243 244 } // namespace juce 245