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"
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()
59 keyMappings
->clearAllKeyPresses();
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
);
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
)
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
);
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
)
157 StringArray
ApplicationCommandManager::getCommandCategories() const
161 for (int i
= 0; i
< commands
.size(); ++i
)
162 s
.addIfNotAlreadyThere (commands
.getUnchecked(i
)->categoryName
, false);
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
);
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
191 jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
193 ApplicationCommandInfo
commandInfo (0);
194 ApplicationCommandTarget
* const target
= getTargetForCommand (info_
.commandID
, commandInfo
);
196 if (target
== nullptr)
199 ApplicationCommandTarget::InvocationInfo
info (info_
);
200 info
.commandFlags
= commandInfo
.flags
;
202 sendListenerInvokeCallback (info
);
204 const bool ok
= target
->invoke (info
, asynchronously
);
206 commandStatusChanged();
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
);
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);
252 ApplicationCommandTarget
* ApplicationCommandManager::findDefaultComponentTarget()
254 Component
* c
= Component::getCurrentlyFocusedComponent();
258 TopLevelWindow
* const activeWindow
= TopLevelWindow::getActiveTopLevelWindow();
260 if (activeWindow
!= nullptr)
262 c
= activeWindow
->getPeer()->getLastFocusedSubcomponent();
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)
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
291 if (resizableWindow
!= nullptr && resizableWindow
->getContentComponent() != nullptr)
292 c
= resizableWindow
->getContentComponent();
294 ApplicationCommandTarget
* const target
= findTargetForComponent (c
);
296 if (target
!= nullptr)
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();