Add remaining files
[juce-lv2.git] / juce / source / src / gui / components / keyboard / juce_KeyPressMappingSet.cpp
blob656f8647d43429d4cf0adb4df139cfdc3f14c6a3
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../../../core/juce_StandardHeader.h"
28 BEGIN_JUCE_NAMESPACE
30 #include "juce_KeyPressMappingSet.h"
31 #include "../../../core/juce_Time.h"
32 #include "../../../core/juce_PlatformUtilities.h"
33 #include "../lookandfeel/juce_LookAndFeel.h"
36 //==============================================================================
37 KeyPressMappingSet::KeyPressMappingSet (ApplicationCommandManager* const commandManager_)
38 : commandManager (commandManager_)
40 // A manager is needed to get the descriptions of commands, and will be called when
41 // a command is invoked. So you can't leave this null..
42 jassert (commandManager_ != nullptr);
44 Desktop::getInstance().addFocusChangeListener (this);
47 KeyPressMappingSet::KeyPressMappingSet (const KeyPressMappingSet& other)
48 : commandManager (other.commandManager)
50 Desktop::getInstance().addFocusChangeListener (this);
53 KeyPressMappingSet::~KeyPressMappingSet()
55 Desktop::getInstance().removeFocusChangeListener (this);
58 //==============================================================================
59 const Array <KeyPress> KeyPressMappingSet::getKeyPressesAssignedToCommand (const CommandID commandID) const
61 for (int i = 0; i < mappings.size(); ++i)
62 if (mappings.getUnchecked(i)->commandID == commandID)
63 return mappings.getUnchecked (i)->keypresses;
65 return Array <KeyPress> ();
68 void KeyPressMappingSet::addKeyPress (const CommandID commandID,
69 const KeyPress& newKeyPress,
70 int insertIndex)
72 // If you specify an upper-case letter but no shift key, how is the user supposed to press it!?
73 // Stick to lower-case letters when defining a keypress, to avoid ambiguity.
74 jassert (! (CharacterFunctions::isUpperCase (newKeyPress.getTextCharacter())
75 && ! newKeyPress.getModifiers().isShiftDown()));
77 if (findCommandForKeyPress (newKeyPress) != commandID)
79 removeKeyPress (newKeyPress);
81 if (newKeyPress.isValid())
83 for (int i = mappings.size(); --i >= 0;)
85 if (mappings.getUnchecked(i)->commandID == commandID)
87 mappings.getUnchecked(i)->keypresses.insert (insertIndex, newKeyPress);
89 sendChangeMessage();
90 return;
94 const ApplicationCommandInfo* const ci = commandManager->getCommandForID (commandID);
96 if (ci != nullptr)
98 CommandMapping* const cm = new CommandMapping();
99 cm->commandID = commandID;
100 cm->keypresses.add (newKeyPress);
101 cm->wantsKeyUpDownCallbacks = (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) != 0;
103 mappings.add (cm);
104 sendChangeMessage();
110 void KeyPressMappingSet::resetToDefaultMappings()
112 mappings.clear();
114 for (int i = 0; i < commandManager->getNumCommands(); ++i)
116 const ApplicationCommandInfo* const ci = commandManager->getCommandForIndex (i);
118 for (int j = 0; j < ci->defaultKeypresses.size(); ++j)
120 addKeyPress (ci->commandID,
121 ci->defaultKeypresses.getReference (j));
125 sendChangeMessage();
128 void KeyPressMappingSet::resetToDefaultMapping (const CommandID commandID)
130 clearAllKeyPresses (commandID);
132 const ApplicationCommandInfo* const ci = commandManager->getCommandForID (commandID);
134 for (int j = 0; j < ci->defaultKeypresses.size(); ++j)
136 addKeyPress (ci->commandID,
137 ci->defaultKeypresses.getReference (j));
141 void KeyPressMappingSet::clearAllKeyPresses()
143 if (mappings.size() > 0)
145 sendChangeMessage();
146 mappings.clear();
150 void KeyPressMappingSet::clearAllKeyPresses (const CommandID commandID)
152 for (int i = mappings.size(); --i >= 0;)
154 if (mappings.getUnchecked(i)->commandID == commandID)
156 mappings.remove (i);
157 sendChangeMessage();
162 void KeyPressMappingSet::removeKeyPress (const KeyPress& keypress)
164 if (keypress.isValid())
166 for (int i = mappings.size(); --i >= 0;)
168 CommandMapping* const cm = mappings.getUnchecked(i);
170 for (int j = cm->keypresses.size(); --j >= 0;)
172 if (keypress == cm->keypresses [j])
174 cm->keypresses.remove (j);
175 sendChangeMessage();
182 void KeyPressMappingSet::removeKeyPress (const CommandID commandID, const int keyPressIndex)
184 for (int i = mappings.size(); --i >= 0;)
186 if (mappings.getUnchecked(i)->commandID == commandID)
188 mappings.getUnchecked(i)->keypresses.remove (keyPressIndex);
189 sendChangeMessage();
190 break;
195 //==============================================================================
196 CommandID KeyPressMappingSet::findCommandForKeyPress (const KeyPress& keyPress) const noexcept
198 for (int i = 0; i < mappings.size(); ++i)
199 if (mappings.getUnchecked(i)->keypresses.contains (keyPress))
200 return mappings.getUnchecked(i)->commandID;
202 return 0;
205 bool KeyPressMappingSet::containsMapping (const CommandID commandID, const KeyPress& keyPress) const noexcept
207 for (int i = mappings.size(); --i >= 0;)
208 if (mappings.getUnchecked(i)->commandID == commandID)
209 return mappings.getUnchecked(i)->keypresses.contains (keyPress);
211 return false;
214 void KeyPressMappingSet::invokeCommand (const CommandID commandID,
215 const KeyPress& key,
216 const bool isKeyDown,
217 const int millisecsSinceKeyPressed,
218 Component* const originatingComponent) const
220 ApplicationCommandTarget::InvocationInfo info (commandID);
222 info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromKeyPress;
223 info.isKeyDown = isKeyDown;
224 info.keyPress = key;
225 info.millisecsSinceKeyPressed = millisecsSinceKeyPressed;
226 info.originatingComponent = originatingComponent;
228 commandManager->invoke (info, false);
231 //==============================================================================
232 bool KeyPressMappingSet::restoreFromXml (const XmlElement& xmlVersion)
234 if (xmlVersion.hasTagName ("KEYMAPPINGS"))
236 if (xmlVersion.getBoolAttribute ("basedOnDefaults", true))
238 // if the XML was created as a set of differences from the default mappings,
239 // (i.e. by calling createXml (true)), then we need to first restore the defaults.
240 resetToDefaultMappings();
242 else
244 // if the XML was created calling createXml (false), then we need to clear all
245 // the keys and treat the xml as describing the entire set of mappings.
246 clearAllKeyPresses();
249 forEachXmlChildElement (xmlVersion, map)
251 const CommandID commandId = map->getStringAttribute ("commandId").getHexValue32();
253 if (commandId != 0)
255 const KeyPress key (KeyPress::createFromDescription (map->getStringAttribute ("key")));
257 if (map->hasTagName ("MAPPING"))
259 addKeyPress (commandId, key);
261 else if (map->hasTagName ("UNMAPPING"))
263 if (containsMapping (commandId, key))
264 removeKeyPress (key);
269 return true;
272 return false;
275 XmlElement* KeyPressMappingSet::createXml (const bool saveDifferencesFromDefaultSet) const
277 ScopedPointer <KeyPressMappingSet> defaultSet;
279 if (saveDifferencesFromDefaultSet)
281 defaultSet = new KeyPressMappingSet (commandManager);
282 defaultSet->resetToDefaultMappings();
285 XmlElement* const doc = new XmlElement ("KEYMAPPINGS");
287 doc->setAttribute ("basedOnDefaults", saveDifferencesFromDefaultSet);
289 int i;
290 for (i = 0; i < mappings.size(); ++i)
292 const CommandMapping* const cm = mappings.getUnchecked(i);
294 for (int j = 0; j < cm->keypresses.size(); ++j)
296 if (defaultSet == nullptr
297 || ! defaultSet->containsMapping (cm->commandID, cm->keypresses.getReference (j)))
299 XmlElement* const map = doc->createNewChildElement ("MAPPING");
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());
308 if (defaultSet != nullptr)
310 for (i = 0; i < defaultSet->mappings.size(); ++i)
312 const CommandMapping* const cm = defaultSet->mappings.getUnchecked(i);
314 for (int j = 0; j < cm->keypresses.size(); ++j)
316 if (! containsMapping (cm->commandID, cm->keypresses.getReference (j)))
318 XmlElement* const map = doc->createNewChildElement ("UNMAPPING");
320 map->setAttribute ("commandId", String::toHexString ((int) cm->commandID));
321 map->setAttribute ("description", commandManager->getDescriptionOfCommand (cm->commandID));
322 map->setAttribute ("key", cm->keypresses.getReference (j).getTextDescription());
328 return doc;
331 //==============================================================================
332 bool KeyPressMappingSet::keyPressed (const KeyPress& key,
333 Component* originatingComponent)
335 bool used = false;
337 const CommandID commandID = findCommandForKeyPress (key);
339 const ApplicationCommandInfo* const ci = commandManager->getCommandForID (commandID);
341 if (ci != nullptr
342 && (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) == 0)
344 ApplicationCommandInfo info (0);
346 if (commandManager->getTargetForCommand (commandID, info) != 0
347 && (info.flags & ApplicationCommandInfo::isDisabled) == 0)
349 invokeCommand (commandID, key, true, 0, originatingComponent);
350 used = true;
352 else
354 if (originatingComponent != nullptr)
355 originatingComponent->getLookAndFeel().playAlertSound();
359 return used;
362 bool KeyPressMappingSet::keyStateChanged (const bool /*isKeyDown*/, Component* originatingComponent)
364 bool used = false;
365 const uint32 now = Time::getMillisecondCounter();
367 for (int i = mappings.size(); --i >= 0;)
369 CommandMapping* const cm = mappings.getUnchecked(i);
371 if (cm->wantsKeyUpDownCallbacks)
373 for (int j = cm->keypresses.size(); --j >= 0;)
375 const KeyPress key (cm->keypresses.getReference (j));
376 const bool isDown = key.isCurrentlyDown();
378 int keyPressEntryIndex = 0;
379 bool wasDown = false;
381 for (int k = keysDown.size(); --k >= 0;)
383 if (key == keysDown.getUnchecked(k)->key)
385 keyPressEntryIndex = k;
386 wasDown = true;
387 used = true;
388 break;
392 if (isDown != wasDown)
394 int millisecs = 0;
396 if (isDown)
398 KeyPressTime* const k = new KeyPressTime();
399 k->key = key;
400 k->timeWhenPressed = now;
402 keysDown.add (k);
404 else
406 const uint32 pressTime = keysDown.getUnchecked (keyPressEntryIndex)->timeWhenPressed;
408 if (now > pressTime)
409 millisecs = now - pressTime;
411 keysDown.remove (keyPressEntryIndex);
414 invokeCommand (cm->commandID, key, isDown, millisecs, originatingComponent);
415 used = true;
421 return used;
424 void KeyPressMappingSet::globalFocusChanged (Component* focusedComponent)
426 if (focusedComponent != nullptr)
427 focusedComponent->keyStateChanged (false);
431 END_JUCE_NAMESPACE