1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00
JUCE/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp
2012-03-20 16:00:06 +00:00

425 lines
14 KiB
C++

/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-11 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
KeyPressMappingSet::KeyPressMappingSet (ApplicationCommandManager* const commandManager_)
: commandManager (commandManager_)
{
// A manager is needed to get the descriptions of commands, and will be called when
// a command is invoked. So you can't leave this null..
jassert (commandManager_ != nullptr);
Desktop::getInstance().addFocusChangeListener (this);
}
KeyPressMappingSet::KeyPressMappingSet (const KeyPressMappingSet& other)
: 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 Array<KeyPress>();
}
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;
}
}
const ApplicationCommandInfo* const ci = commandManager->getCommandForID (commandID);
if (ci != nullptr)
{
CommandMapping* const cm = new CommandMapping();
cm->commandID = commandID;
cm->keypresses.add (newKeyPress);
cm->wantsKeyUpDownCallbacks = (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) != 0;
mappings.add (cm);
sendChangeMessage();
}
}
}
}
void KeyPressMappingSet::resetToDefaultMappings()
{
mappings.clear();
for (int i = 0; i < commandManager->getNumCommands(); ++i)
{
const ApplicationCommandInfo* const ci = commandManager->getCommandForIndex (i);
for (int j = 0; j < ci->defaultKeypresses.size(); ++j)
{
addKeyPress (ci->commandID,
ci->defaultKeypresses.getReference (j));
}
}
sendChangeMessage();
}
void KeyPressMappingSet::resetToDefaultMapping (const CommandID commandID)
{
clearAllKeyPresses (commandID);
const ApplicationCommandInfo* const ci = commandManager->getCommandForID (commandID);
for (int j = 0; j < ci->defaultKeypresses.size(); ++j)
{
addKeyPress (ci->commandID,
ci->defaultKeypresses.getReference (j));
}
}
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* const 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();
}
forEachXmlChildElement (xmlVersion, map)
{
const CommandID commandId = map->getStringAttribute ("commandId").getHexValue32();
if (commandId != 0)
{
const KeyPress key (KeyPress::createFromDescription (map->getStringAttribute ("key")));
if (map->hasTagName ("MAPPING"))
{
addKeyPress (commandId, key);
}
else if (map->hasTagName ("UNMAPPING"))
{
if (containsMapping (commandId, key))
removeKeyPress (key);
}
}
}
return true;
}
return false;
}
XmlElement* KeyPressMappingSet::createXml (const bool saveDifferencesFromDefaultSet) const
{
ScopedPointer <KeyPressMappingSet> defaultSet;
if (saveDifferencesFromDefaultSet)
{
defaultSet = new KeyPressMappingSet (commandManager);
defaultSet->resetToDefaultMappings();
}
XmlElement* const doc = new XmlElement ("KEYMAPPINGS");
doc->setAttribute ("basedOnDefaults", saveDifferencesFromDefaultSet);
int i;
for (i = 0; i < mappings.size(); ++i)
{
const CommandMapping* const cm = mappings.getUnchecked(i);
for (int j = 0; j < cm->keypresses.size(); ++j)
{
if (defaultSet == nullptr
|| ! defaultSet->containsMapping (cm->commandID, cm->keypresses.getReference (j)))
{
XmlElement* const 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 (i = 0; i < defaultSet->mappings.size(); ++i)
{
const CommandMapping* const cm = defaultSet->mappings.getUnchecked(i);
for (int j = 0; j < cm->keypresses.size(); ++j)
{
if (! containsMapping (cm->commandID, cm->keypresses.getReference (j)))
{
XmlElement* const 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* originatingComponent)
{
bool commandWasDisabled = false;
for (int i = 0; i < mappings.size(); ++i)
{
CommandMapping* const cm = mappings.getUnchecked(i);
if (cm->keypresses.contains (key))
{
const ApplicationCommandInfo* const ci = commandManager->getCommandForID (cm->commandID);
if (ci != nullptr
&& (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) == 0)
{
ApplicationCommandInfo info (0);
if (commandManager->getTargetForCommand (cm->commandID, info) != 0)
{
if ((info.flags & ApplicationCommandInfo::isDisabled) == 0)
{
invokeCommand (cm->commandID, key, true, 0, originatingComponent);
return true;
}
else
{
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* const 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);
}