|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 |
- /*
- ==============================================================================
-
- 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
|