Blindly add a few stuff from VST
[juce-lv2.git] / juce / source / src / application / juce_ApplicationCommandManager.cpp
blobab6c3d059fa29618ff354a326ea17217a75e778c
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_ApplicationCommandManager.h"
31 #include "juce_Application.h"
32 #include "../gui/components/windows/juce_ComponentPeer.h"
33 #include "../gui/components/keyboard/juce_KeyPressMappingSet.h"
34 #include "../gui/components/windows/juce_ResizableWindow.h"
35 #include "../gui/components/juce_Desktop.h"
36 #include "../events/juce_MessageManager.h"
37 #include "../threads/juce_Process.h"
40 //==============================================================================
41 ApplicationCommandManager::ApplicationCommandManager()
42 : firstTarget (nullptr)
44 keyMappings = new KeyPressMappingSet (this);
46 Desktop::getInstance().addFocusChangeListener (this);
49 ApplicationCommandManager::~ApplicationCommandManager()
51 Desktop::getInstance().removeFocusChangeListener (this);
52 keyMappings = nullptr;
55 //==============================================================================
56 void ApplicationCommandManager::clearCommands()
58 commands.clear();
59 keyMappings->clearAllKeyPresses();
60 triggerAsyncUpdate();
63 void ApplicationCommandManager::registerCommand (const ApplicationCommandInfo& newCommand)
65 // zero isn't a valid command ID!
66 jassert (newCommand.commandID != 0);
68 // the name isn't optional!
69 jassert (newCommand.shortName.isNotEmpty());
71 if (getCommandForID (newCommand.commandID) == 0)
73 ApplicationCommandInfo* const newInfo = new ApplicationCommandInfo (newCommand);
74 newInfo->flags &= ~ApplicationCommandInfo::isTicked;
75 commands.add (newInfo);
77 keyMappings->resetToDefaultMapping (newCommand.commandID);
79 triggerAsyncUpdate();
81 else
83 // trying to re-register the same command with different parameters?
84 jassert (newCommand.shortName == getCommandForID (newCommand.commandID)->shortName
85 && (newCommand.description == getCommandForID (newCommand.commandID)->description || newCommand.description.isEmpty())
86 && newCommand.categoryName == getCommandForID (newCommand.commandID)->categoryName
87 && newCommand.defaultKeypresses == getCommandForID (newCommand.commandID)->defaultKeypresses
88 && (newCommand.flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor))
89 == (getCommandForID (newCommand.commandID)->flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor)));
93 void ApplicationCommandManager::registerAllCommandsForTarget (ApplicationCommandTarget* target)
95 if (target != nullptr)
97 Array <CommandID> commandIDs;
98 target->getAllCommands (commandIDs);
100 for (int i = 0; i < commandIDs.size(); ++i)
102 ApplicationCommandInfo info (commandIDs.getUnchecked(i));
103 target->getCommandInfo (info.commandID, info);
105 registerCommand (info);
110 void ApplicationCommandManager::removeCommand (const CommandID commandID)
112 for (int i = commands.size(); --i >= 0;)
114 if (commands.getUnchecked (i)->commandID == commandID)
116 commands.remove (i);
117 triggerAsyncUpdate();
119 const Array <KeyPress> keys (keyMappings->getKeyPressesAssignedToCommand (commandID));
121 for (int j = keys.size(); --j >= 0;)
122 keyMappings->removeKeyPress (keys.getReference (j));
127 void ApplicationCommandManager::commandStatusChanged()
129 triggerAsyncUpdate();
132 //==============================================================================
133 const ApplicationCommandInfo* ApplicationCommandManager::getCommandForID (const CommandID commandID) const noexcept
135 for (int i = commands.size(); --i >= 0;)
136 if (commands.getUnchecked(i)->commandID == commandID)
137 return commands.getUnchecked(i);
139 return nullptr;
142 String ApplicationCommandManager::getNameOfCommand (const CommandID commandID) const noexcept
144 const ApplicationCommandInfo* const ci = getCommandForID (commandID);
146 return ci != nullptr ? ci->shortName : String::empty;
149 String ApplicationCommandManager::getDescriptionOfCommand (const CommandID commandID) const noexcept
151 const ApplicationCommandInfo* const ci = getCommandForID (commandID);
153 return ci != nullptr ? (ci->description.isNotEmpty() ? ci->description : ci->shortName)
154 : String::empty;
157 StringArray ApplicationCommandManager::getCommandCategories() const
159 StringArray s;
161 for (int i = 0; i < commands.size(); ++i)
162 s.addIfNotAlreadyThere (commands.getUnchecked(i)->categoryName, false);
164 return s;
167 Array<CommandID> ApplicationCommandManager::getCommandsInCategory (const String& categoryName) const
169 Array <CommandID> results;
171 for (int i = 0; i < commands.size(); ++i)
172 if (commands.getUnchecked(i)->categoryName == categoryName)
173 results.add (commands.getUnchecked(i)->commandID);
175 return results;
178 //==============================================================================
179 bool ApplicationCommandManager::invokeDirectly (const CommandID commandID, const bool asynchronously)
181 ApplicationCommandTarget::InvocationInfo info (commandID);
182 info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct;
184 return invoke (info, asynchronously);
187 bool ApplicationCommandManager::invoke (const ApplicationCommandTarget::InvocationInfo& info_, const bool asynchronously)
189 // This call isn't thread-safe for use from a non-UI thread without locking the message
190 // manager first..
191 jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
193 ApplicationCommandInfo commandInfo (0);
194 ApplicationCommandTarget* const target = getTargetForCommand (info_.commandID, commandInfo);
196 if (target == nullptr)
197 return false;
199 ApplicationCommandTarget::InvocationInfo info (info_);
200 info.commandFlags = commandInfo.flags;
202 sendListenerInvokeCallback (info);
204 const bool ok = target->invoke (info, asynchronously);
206 commandStatusChanged();
208 return ok;
211 //==============================================================================
212 ApplicationCommandTarget* ApplicationCommandManager::getFirstCommandTarget (const CommandID)
214 return firstTarget != nullptr ? firstTarget
215 : findDefaultComponentTarget();
218 void ApplicationCommandManager::setFirstCommandTarget (ApplicationCommandTarget* const newTarget) noexcept
220 firstTarget = newTarget;
223 ApplicationCommandTarget* ApplicationCommandManager::getTargetForCommand (const CommandID commandID,
224 ApplicationCommandInfo& upToDateInfo)
226 ApplicationCommandTarget* target = getFirstCommandTarget (commandID);
228 if (target == nullptr)
229 target = JUCEApplication::getInstance();
231 if (target != nullptr)
232 target = target->getTargetForCommand (commandID);
234 if (target != nullptr)
235 target->getCommandInfo (commandID, upToDateInfo);
237 return target;
240 //==============================================================================
241 ApplicationCommandTarget* ApplicationCommandManager::findTargetForComponent (Component* c)
243 ApplicationCommandTarget* target = dynamic_cast <ApplicationCommandTarget*> (c);
245 if (target == nullptr && c != nullptr)
246 // (unable to use the syntax findParentComponentOfClass <ApplicationCommandTarget> () because of a VC6 compiler bug)
247 target = c->findParentComponentOfClass ((ApplicationCommandTarget*) nullptr);
249 return target;
252 ApplicationCommandTarget* ApplicationCommandManager::findDefaultComponentTarget()
254 Component* c = Component::getCurrentlyFocusedComponent();
256 if (c == nullptr)
258 TopLevelWindow* const activeWindow = TopLevelWindow::getActiveTopLevelWindow();
260 if (activeWindow != nullptr)
262 c = activeWindow->getPeer()->getLastFocusedSubcomponent();
264 if (c == nullptr)
265 c = activeWindow;
269 if (c == nullptr && Process::isForegroundProcess())
271 // getting a bit desperate now - try all desktop comps..
272 for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
274 ApplicationCommandTarget* const target
275 = findTargetForComponent (Desktop::getInstance().getComponent (i)
276 ->getPeer()->getLastFocusedSubcomponent());
278 if (target != nullptr)
279 return target;
283 if (c != nullptr)
285 ResizableWindow* const resizableWindow = dynamic_cast <ResizableWindow*> (c);
287 // if we're focused on a ResizableWindow, chances are that it's the content
288 // component that really should get the event. And if not, the event will
289 // still be passed up to the top level window anyway, so let's send it to the
290 // content comp.
291 if (resizableWindow != nullptr && resizableWindow->getContentComponent() != nullptr)
292 c = resizableWindow->getContentComponent();
294 ApplicationCommandTarget* const target = findTargetForComponent (c);
296 if (target != nullptr)
297 return target;
300 return JUCEApplication::getInstance();
303 //==============================================================================
304 void ApplicationCommandManager::addListener (ApplicationCommandManagerListener* const listener)
306 listeners.add (listener);
309 void ApplicationCommandManager::removeListener (ApplicationCommandManagerListener* const listener)
311 listeners.remove (listener);
314 void ApplicationCommandManager::sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo& info)
316 listeners.call (&ApplicationCommandManagerListener::applicationCommandInvoked, info);
319 void ApplicationCommandManager::handleAsyncUpdate()
321 listeners.call (&ApplicationCommandManagerListener::applicationCommandListChanged);
324 void ApplicationCommandManager::globalFocusChanged (Component*)
326 commandStatusChanged();
330 END_JUCE_NAMESPACE