JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_KeyPressMappingSet.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - 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 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-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
26namespace juce
27{
28
34
40
45
46//==============================================================================
48{
49 for (int i = 0; i < mappings.size(); ++i)
50 if (mappings.getUnchecked (i)->commandID == commandID)
51 return mappings.getUnchecked (i)->keypresses;
52
53 return {};
54}
55
56void KeyPressMappingSet::addKeyPress (const CommandID commandID, const KeyPress& newKeyPress, int insertIndex)
57{
58 // If you specify an upper-case letter but no shift key, how is the user supposed to press it!?
59 // Stick to lower-case letters when defining a keypress, to avoid ambiguity.
61 && ! newKeyPress.getModifiers().isShiftDown()));
62
63 if (findCommandForKeyPress (newKeyPress) != commandID)
64 {
65 if (newKeyPress.isValid())
66 {
67 for (int i = mappings.size(); --i >= 0;)
68 {
69 if (mappings.getUnchecked (i)->commandID == commandID)
70 {
71 mappings.getUnchecked (i)->keypresses.insert (insertIndex, newKeyPress);
72
74 return;
75 }
76 }
77
78 if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (commandID))
79 {
80 CommandMapping* const cm = new CommandMapping();
81 cm->commandID = commandID;
82 cm->keypresses.add (newKeyPress);
83 cm->wantsKeyUpDownCallbacks = (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) != 0;
84
85 mappings.add (cm);
87 }
88 else
89 {
90 // If you hit this, you're trying to attach a keypress to a command ID that
91 // doesn't exist, so the key is not being attached.
93 }
94 }
95 }
96}
97
98static void addKeyPresses (KeyPressMappingSet& set, const ApplicationCommandInfo* const ci)
99{
100 for (int j = 0; j < ci->defaultKeypresses.size(); ++j)
101 set.addKeyPress (ci->commandID, ci->defaultKeypresses.getReference (j));
102}
103
105{
106 mappings.clear();
107
108 for (int i = 0; i < commandManager.getNumCommands(); ++i)
109 addKeyPresses (*this, commandManager.getCommandForIndex (i));
110
112}
113
115{
116 clearAllKeyPresses (commandID);
117
118 if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (commandID))
119 addKeyPresses (*this, ci);
120}
121
123{
124 if (mappings.size() > 0)
125 {
127 mappings.clear();
128 }
129}
130
132{
133 for (int i = mappings.size(); --i >= 0;)
134 {
135 if (mappings.getUnchecked (i)->commandID == commandID)
136 {
137 mappings.remove (i);
139 }
140 }
141}
142
144{
145 if (keypress.isValid())
146 {
147 for (int i = mappings.size(); --i >= 0;)
148 {
149 CommandMapping& cm = *mappings.getUnchecked (i);
150
151 for (int j = cm.keypresses.size(); --j >= 0;)
152 {
153 if (keypress == cm.keypresses [j])
154 {
155 cm.keypresses.remove (j);
157 }
158 }
159 }
160 }
161}
162
164{
165 for (int i = mappings.size(); --i >= 0;)
166 {
167 if (mappings.getUnchecked (i)->commandID == commandID)
168 {
169 mappings.getUnchecked (i)->keypresses.remove (keyPressIndex);
171 break;
172 }
173 }
174}
175
176//==============================================================================
178{
179 for (int i = 0; i < mappings.size(); ++i)
180 if (mappings.getUnchecked (i)->keypresses.contains (keyPress))
181 return mappings.getUnchecked (i)->commandID;
182
183 return 0;
184}
185
186bool KeyPressMappingSet::containsMapping (const CommandID commandID, const KeyPress& keyPress) const noexcept
187{
188 for (int i = mappings.size(); --i >= 0;)
189 if (mappings.getUnchecked (i)->commandID == commandID)
190 return mappings.getUnchecked (i)->keypresses.contains (keyPress);
191
192 return false;
193}
194
195void KeyPressMappingSet::invokeCommand (const CommandID commandID,
196 const KeyPress& key,
197 const bool isKeyDown,
198 const int millisecsSinceKeyPressed,
199 Component* const originatingComponent) const
200{
202
204 info.isKeyDown = isKeyDown;
205 info.keyPress = key;
206 info.millisecsSinceKeyPressed = millisecsSinceKeyPressed;
207 info.originatingComponent = originatingComponent;
208
209 commandManager.invoke (info, false);
210}
211
212//==============================================================================
214{
215 if (xmlVersion.hasTagName ("KEYMAPPINGS"))
216 {
217 if (xmlVersion.getBoolAttribute ("basedOnDefaults", true))
218 {
219 // if the XML was created as a set of differences from the default mappings,
220 // (i.e. by calling createXml (true)), then we need to first restore the defaults.
222 }
223 else
224 {
225 // if the XML was created calling createXml (false), then we need to clear all
226 // the keys and treat the xml as describing the entire set of mappings.
228 }
229
230 for (auto* map : xmlVersion.getChildIterator())
231 {
232 const CommandID commandId = map->getStringAttribute ("commandId").getHexValue32();
233
234 if (commandId != 0)
235 {
236 auto key = KeyPress::createFromDescription (map->getStringAttribute ("key"));
237
238 if (map->hasTagName ("MAPPING"))
239 {
240 addKeyPress (commandId, key);
241 }
242 else if (map->hasTagName ("UNMAPPING"))
243 {
244 for (auto& m : mappings)
245 if (m->commandID == commandId)
246 m->keypresses.removeAllInstancesOf (key);
247 }
248 }
249 }
250
251 return true;
252 }
253
254 return false;
255}
256
258{
260
262 {
263 defaultSet = std::make_unique<KeyPressMappingSet> (commandManager);
264 defaultSet->resetToDefaultMappings();
265 }
266
267 auto doc = std::make_unique<XmlElement> ("KEYMAPPINGS");
268
269 doc->setAttribute ("basedOnDefaults", saveDifferencesFromDefaultSet);
270
271 for (int i = 0; i < mappings.size(); ++i)
272 {
273 auto& cm = *mappings.getUnchecked (i);
274
275 for (int j = 0; j < cm.keypresses.size(); ++j)
276 {
277 if (defaultSet == nullptr
278 || ! defaultSet->containsMapping (cm.commandID, cm.keypresses.getReference (j)))
279 {
280 auto map = doc->createNewChildElement ("MAPPING");
281
282 map->setAttribute ("commandId", String::toHexString ((int) cm.commandID));
283 map->setAttribute ("description", commandManager.getDescriptionOfCommand (cm.commandID));
284 map->setAttribute ("key", cm.keypresses.getReference (j).getTextDescription());
285 }
286 }
287 }
288
289 if (defaultSet != nullptr)
290 {
291 for (int i = 0; i < defaultSet->mappings.size(); ++i)
292 {
293 auto& cm = *defaultSet->mappings.getUnchecked (i);
294
295 for (int j = 0; j < cm.keypresses.size(); ++j)
296 {
297 if (! containsMapping (cm.commandID, cm.keypresses.getReference (j)))
298 {
299 auto map = doc->createNewChildElement ("UNMAPPING");
300
301 map->setAttribute ("commandId", String::toHexString ((int) cm.commandID));
302 map->setAttribute ("description", commandManager.getDescriptionOfCommand (cm.commandID));
303 map->setAttribute ("key", cm.keypresses.getReference (j).getTextDescription());
304 }
305 }
306 }
307 }
308
309 return doc;
310}
311
312//==============================================================================
313bool KeyPressMappingSet::keyPressed (const KeyPress& key, Component* const originatingComponent)
314{
315 bool commandWasDisabled = false;
316
317 for (int i = 0; i < mappings.size(); ++i)
318 {
319 CommandMapping& cm = *mappings.getUnchecked (i);
320
321 if (cm.keypresses.contains (key))
322 {
323 if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (cm.commandID))
324 {
326 {
327 ApplicationCommandInfo info (0);
328
329 if (commandManager.getTargetForCommand (cm.commandID, info) != nullptr)
330 {
332 {
333 invokeCommand (cm.commandID, key, true, 0, originatingComponent);
334 return true;
335 }
336
337 commandWasDisabled = true;
338 }
339 }
340 }
341 }
342 }
343
344 if (originatingComponent != nullptr && commandWasDisabled)
345 originatingComponent->getLookAndFeel().playAlertSound();
346
347 return false;
348}
349
350bool KeyPressMappingSet::keyStateChanged (const bool /*isKeyDown*/, Component* originatingComponent)
351{
352 bool used = false;
354
355 for (int i = mappings.size(); --i >= 0;)
356 {
357 CommandMapping& cm = *mappings.getUnchecked (i);
358
359 if (cm.wantsKeyUpDownCallbacks)
360 {
361 for (int j = cm.keypresses.size(); --j >= 0;)
362 {
363 const KeyPress key (cm.keypresses.getReference (j));
364 const bool isDown = key.isCurrentlyDown();
365
366 int keyPressEntryIndex = 0;
367 bool wasDown = false;
368
369 for (int k = keysDown.size(); --k >= 0;)
370 {
371 if (key == keysDown.getUnchecked (k)->key)
372 {
374 wasDown = true;
375 used = true;
376 break;
377 }
378 }
379
380 if (isDown != wasDown)
381 {
382 int millisecs = 0;
383
384 if (isDown)
385 {
386 KeyPressTime* const k = new KeyPressTime();
387 k->key = key;
388 k->timeWhenPressed = now;
389
390 keysDown.add (k);
391 }
392 else
393 {
394 const uint32 pressTime = keysDown.getUnchecked (keyPressEntryIndex)->timeWhenPressed;
395
396 if (now > pressTime)
397 millisecs = (int) (now - pressTime);
398
399 keysDown.remove (keyPressEntryIndex);
400 }
401
402 invokeCommand (cm.commandID, key, isDown, millisecs, originatingComponent);
403 used = true;
404 }
405 }
406 }
407 }
408
409 return used;
410}
411
413{
414 if (focusedComponent != nullptr)
415 focusedComponent->keyStateChanged (false);
416}
417
418} // namespace juce
One of these objects holds a list of all the commands your app can perform, and despatches these comm...
ApplicationCommandTarget * getTargetForCommand(CommandID commandID, ApplicationCommandInfo &upToDateInfo)
Tries to find the best target to use to perform a given command.
int getNumCommands() const noexcept
Returns the number of commands that have been registered.
const ApplicationCommandInfo * getCommandForID(CommandID commandID) const noexcept
Returns the details about a given command ID.
const ApplicationCommandInfo * getCommandForIndex(int index) const noexcept
Returns the details about one of the registered commands.
bool invoke(const ApplicationCommandTarget::InvocationInfo &invocationInfo, bool asynchronously)
Sends a command to the default target.
String getDescriptionOfCommand(CommandID commandID) const noexcept
Returns the description field for a command.
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:56
Holds a list of ChangeListeners, and sends messages to them when instructed.
void sendChangeMessage()
Causes an asynchronous change message to be sent to all the registered listeners.
static bool isUpperCase(juce_wchar character) noexcept
Checks whether a unicode character is upper-case.
The base class for all JUCE user-interface objects.
LookAndFeel & getLookAndFeel() const noexcept
Finds the appropriate look-and-feel to use for this component.
void removeFocusChangeListener(FocusChangeListener *listener)
Unregisters a FocusChangeListener that was added with addFocusChangeListener().
void addFocusChangeListener(FocusChangeListener *listener)
Registers a FocusChangeListener that will receive a callback whenever the focused component changes.
static Desktop &JUCE_CALLTYPE getInstance()
There's only one desktop object, and this method will return it.
Classes can implement this interface and register themselves with the Desktop class to receive callba...
Receives callbacks when keys are pressed.
Manages and edits a list of keypresses, which it uses to invoke the appropriate command in an Applica...
void addKeyPress(CommandID commandID, const KeyPress &newKeyPress, int insertIndex=-1)
Assigns a keypress to a command.
void resetToDefaultMapping(CommandID commandID)
Resets all key-mappings to the defaults for a particular command.
Array< KeyPress > getKeyPressesAssignedToCommand(CommandID commandID) const
Returns a list of keypresses that are assigned to a particular command.
bool containsMapping(CommandID commandID, const KeyPress &keyPress) const noexcept
Returns true if the given command is linked to this key.
void removeKeyPress(CommandID commandID, int keyPressIndex)
Removes one of the keypresses that are assigned to a command.
~KeyPressMappingSet() override
Destructor.
void globalFocusChanged(Component *) override
Callback to indicate that the currently focused component has changed.
void clearAllKeyPresses()
Removes all keypresses that are assigned to any commands.
void resetToDefaultMappings()
Reset all mappings to the defaults, as dictated by the ApplicationCommandManager.
CommandID findCommandForKeyPress(const KeyPress &keyPress) const noexcept
Looks for a command that corresponds to a keypress.
bool keyPressed(const KeyPress &, Component *) override
Called to indicate that a key has been pressed.
KeyPressMappingSet(ApplicationCommandManager &)
Creates a KeyPressMappingSet for a given command manager.
bool keyStateChanged(bool isKeyDown, Component *) override
Called when any key is pressed or released.
bool restoreFromXml(const XmlElement &xmlVersion)
Tries to recreate the mappings from a previously stored state.
std::unique_ptr< XmlElement > createXml(bool saveDifferencesFromDefaultSet) const
Creates an XML representation of the current mappings.
Represents a key press, including any modifier keys that are needed.
bool isCurrentlyDown() const
Checks whether the user is currently holding down the keys that make up this KeyPress.
static KeyPress createFromDescription(const String &textVersion)
Converts a textual key description to a KeyPress.
virtual void playAlertSound()
Plays the system's default 'beep' noise, to alert the user about something very important.
int size() const noexcept
Returns the number of items currently in the array.
ObjectClass * getUnchecked(int index) const noexcept
Returns a pointer to the object at this index in the array, without checking whether the index is in-...
void remove(int indexToRemove, bool deleteObject=true)
Removes an object from the array.
void clear(bool deleteObjects=true)
Clears the array, optionally deleting the objects inside it first.
ObjectClass * add(ObjectClass *newObject)
Appends a new object to the end of the array.
static String toHexString(IntegerType number)
Returns a string representing this numeric value in hexadecimal.
static uint32 getMillisecondCounter() noexcept
Returns the number of millisecs since a fixed event (usually system startup).
Used to build a tree of elements representing an XML document.
#define jassert(expression)
Platform-independent assertion macro.
#define jassertfalse
This will always cause an assertion failure.
typedef int
JUCE Namespace.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
unsigned int uint32
A platform-independent 32-bit unsigned integer type.
int CommandID
A type used to hold the unique ID for an application command.
Holds information describing an application command.
int flags
A bitwise-OR of the values specified in the CommandFlags enum.
@ wantsKeyUpDownCallbacks
If this flag is present, then when a KeyPressMappingSet invokes the command, it will call the command...
@ isDisabled
Indicates that the command can't currently be performed.
Contains contextual details about the invocation of a command.
@ fromKeyPress
The command is being invoked by a key-press.