Add remaining files
[juce-lv2.git] / juce / source / src / gui / components / keyboard / juce_KeyMappingEditorComponent.cpp
blobc453da78e20214b6c8f1a56d7e9df5bade3453a8
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 // N.B. these two includes are put here deliberately to avoid problems with
31 // old GCCs failing on long include paths
32 #include "../../../containers/juce_Array.h"
33 #include "../../../containers/juce_OwnedArray.h"
35 #include "juce_KeyMappingEditorComponent.h"
36 #include "../menus/juce_PopupMenu.h"
37 #include "../windows/juce_AlertWindow.h"
38 #include "../lookandfeel/juce_LookAndFeel.h"
39 #include "../../../text/juce_LocalisedStrings.h"
42 //==============================================================================
43 class KeyMappingEditorComponent::ChangeKeyButton : public Button
45 public:
46 ChangeKeyButton (KeyMappingEditorComponent& owner_,
47 const CommandID commandID_,
48 const String& keyName,
49 const int keyNum_)
50 : Button (keyName),
51 owner (owner_),
52 commandID (commandID_),
53 keyNum (keyNum_)
55 setWantsKeyboardFocus (false);
56 setTriggeredOnMouseDown (keyNum >= 0);
58 setTooltip (keyNum_ < 0 ? TRANS("adds a new key-mapping")
59 : TRANS("click to change this key-mapping"));
62 void paintButton (Graphics& g, bool /*isOver*/, bool /*isDown*/)
64 getLookAndFeel().drawKeymapChangeButton (g, getWidth(), getHeight(), *this,
65 keyNum >= 0 ? getName() : String::empty);
68 static void menuCallback (int result, ChangeKeyButton* button)
70 if (button != nullptr)
72 switch (result)
74 case 1: button->assignNewKey(); break;
75 case 2: button->owner.getMappings().removeKeyPress (button->commandID, button->keyNum); break;
76 default: break;
81 void clicked()
83 if (keyNum >= 0)
85 // existing key clicked..
86 PopupMenu m;
87 m.addItem (1, TRANS("change this key-mapping"));
88 m.addSeparator();
89 m.addItem (2, TRANS("remove this key-mapping"));
91 m.showMenuAsync (PopupMenu::Options(),
92 ModalCallbackFunction::forComponent (menuCallback, this));
94 else
96 assignNewKey(); // + button pressed..
100 void fitToContent (const int h) noexcept
102 if (keyNum < 0)
104 setSize (h, h);
106 else
108 Font f (h * 0.6f);
109 setSize (jlimit (h * 4, h * 8, 6 + f.getStringWidth (getName())), h);
113 //==============================================================================
114 class KeyEntryWindow : public AlertWindow
116 public:
117 KeyEntryWindow (KeyMappingEditorComponent& owner_)
118 : AlertWindow (TRANS("New key-mapping"),
119 TRANS("Please press a key combination now..."),
120 AlertWindow::NoIcon),
121 owner (owner_)
123 addButton (TRANS("Ok"), 1);
124 addButton (TRANS("Cancel"), 0);
126 // (avoid return + escape keys getting processed by the buttons..)
127 for (int i = getNumChildComponents(); --i >= 0;)
128 getChildComponent (i)->setWantsKeyboardFocus (false);
130 setWantsKeyboardFocus (true);
131 grabKeyboardFocus();
134 bool keyPressed (const KeyPress& key)
136 lastPress = key;
137 String message (TRANS("Key: ") + owner.getDescriptionForKeyPress (key));
139 const CommandID previousCommand = owner.getMappings().findCommandForKeyPress (key);
141 if (previousCommand != 0)
142 message << "\n\n" << TRANS("(Currently assigned to \"")
143 << owner.getMappings().getCommandManager()->getNameOfCommand (previousCommand) << "\")";
145 setMessage (message);
146 return true;
149 bool keyStateChanged (bool)
151 return true;
154 KeyPress lastPress;
156 private:
157 KeyMappingEditorComponent& owner;
159 JUCE_DECLARE_NON_COPYABLE (KeyEntryWindow);
162 static void assignNewKeyCallback (int result, ChangeKeyButton* button, KeyPress newKey)
164 if (result != 0 && button != nullptr)
165 button->setNewKey (newKey, true);
168 void setNewKey (const KeyPress& newKey, bool dontAskUser)
170 if (newKey.isValid())
172 const CommandID previousCommand = owner.getMappings().findCommandForKeyPress (newKey);
174 if (previousCommand == 0 || dontAskUser)
176 owner.getMappings().removeKeyPress (newKey);
178 if (keyNum >= 0)
179 owner.getMappings().removeKeyPress (commandID, keyNum);
181 owner.getMappings().addKeyPress (commandID, newKey, keyNum);
183 else
185 AlertWindow::showOkCancelBox (AlertWindow::WarningIcon,
186 TRANS("Change key-mapping"),
187 TRANS("This key is already assigned to the command \"")
188 + owner.getMappings().getCommandManager()->getNameOfCommand (previousCommand)
189 + TRANS("\"\n\nDo you want to re-assign it to this new command instead?"),
190 TRANS("Re-assign"),
191 TRANS("Cancel"),
192 this,
193 ModalCallbackFunction::forComponent (assignNewKeyCallback,
194 this, KeyPress (newKey)));
199 static void keyChosen (int result, ChangeKeyButton* button)
201 if (result != 0 && button != nullptr && button->currentKeyEntryWindow != nullptr)
203 button->currentKeyEntryWindow->setVisible (false);
204 button->setNewKey (button->currentKeyEntryWindow->lastPress, false);
207 button->currentKeyEntryWindow = nullptr;
210 void assignNewKey()
212 currentKeyEntryWindow = new KeyEntryWindow (owner);
213 currentKeyEntryWindow->enterModalState (true, ModalCallbackFunction::forComponent (keyChosen, this));
216 private:
217 KeyMappingEditorComponent& owner;
218 const CommandID commandID;
219 const int keyNum;
220 ScopedPointer<KeyEntryWindow> currentKeyEntryWindow;
222 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeKeyButton);
225 //==============================================================================
226 class KeyMappingEditorComponent::ItemComponent : public Component
228 public:
229 ItemComponent (KeyMappingEditorComponent& owner_, const CommandID commandID_)
230 : owner (owner_), commandID (commandID_)
232 setInterceptsMouseClicks (false, true);
234 const bool isReadOnly = owner.isCommandReadOnly (commandID);
236 const Array <KeyPress> keyPresses (owner.getMappings().getKeyPressesAssignedToCommand (commandID));
238 for (int i = 0; i < jmin ((int) maxNumAssignments, keyPresses.size()); ++i)
239 addKeyPressButton (owner.getDescriptionForKeyPress (keyPresses.getReference (i)), i, isReadOnly);
241 addKeyPressButton (String::empty, -1, isReadOnly);
244 void addKeyPressButton (const String& desc, const int index, const bool isReadOnly)
246 ChangeKeyButton* const b = new ChangeKeyButton (owner, commandID, desc, index);
247 keyChangeButtons.add (b);
249 b->setEnabled (! isReadOnly);
250 b->setVisible (keyChangeButtons.size() <= (int) maxNumAssignments);
251 addChildComponent (b);
254 void paint (Graphics& g)
256 g.setFont (getHeight() * 0.7f);
257 g.setColour (findColour (KeyMappingEditorComponent::textColourId));
259 g.drawFittedText (owner.getMappings().getCommandManager()->getNameOfCommand (commandID),
260 4, 0, jmax (40, getChildComponent (0)->getX() - 5), getHeight(),
261 Justification::centredLeft, true);
264 void resized()
266 int x = getWidth() - 4;
268 for (int i = keyChangeButtons.size(); --i >= 0;)
270 ChangeKeyButton* const b = keyChangeButtons.getUnchecked(i);
272 b->fitToContent (getHeight() - 2);
273 b->setTopRightPosition (x, 1);
274 x = b->getX() - 5;
278 private:
279 KeyMappingEditorComponent& owner;
280 OwnedArray<ChangeKeyButton> keyChangeButtons;
281 const CommandID commandID;
283 enum { maxNumAssignments = 3 };
285 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemComponent);
288 //==============================================================================
289 class KeyMappingEditorComponent::MappingItem : public TreeViewItem
291 public:
292 MappingItem (KeyMappingEditorComponent& owner_, const CommandID commandID_)
293 : owner (owner_), commandID (commandID_)
297 const String getUniqueName() const { return String ((int) commandID) + "_id"; }
298 bool mightContainSubItems() { return false; }
299 int getItemHeight() const { return 20; }
301 Component* createItemComponent()
303 return new ItemComponent (owner, commandID);
306 private:
307 KeyMappingEditorComponent& owner;
308 const CommandID commandID;
310 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MappingItem);
314 //==============================================================================
315 class KeyMappingEditorComponent::CategoryItem : public TreeViewItem
317 public:
318 CategoryItem (KeyMappingEditorComponent& owner_, const String& name)
319 : owner (owner_), categoryName (name)
323 const String getUniqueName() const { return categoryName + "_cat"; }
324 bool mightContainSubItems() { return true; }
325 int getItemHeight() const { return 28; }
327 void paintItem (Graphics& g, int width, int height)
329 g.setFont (height * 0.6f, Font::bold);
330 g.setColour (owner.findColour (KeyMappingEditorComponent::textColourId));
332 g.drawText (categoryName,
333 2, 0, width - 2, height,
334 Justification::centredLeft, true);
337 void itemOpennessChanged (bool isNowOpen)
339 if (isNowOpen)
341 if (getNumSubItems() == 0)
343 Array <CommandID> commands (owner.getMappings().getCommandManager()->getCommandsInCategory (categoryName));
345 for (int i = 0; i < commands.size(); ++i)
347 if (owner.shouldCommandBeIncluded (commands[i]))
348 addSubItem (new MappingItem (owner, commands[i]));
352 else
354 clearSubItems();
358 private:
359 KeyMappingEditorComponent& owner;
360 String categoryName;
362 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CategoryItem);
365 //==============================================================================
366 class KeyMappingEditorComponent::TopLevelItem : public TreeViewItem,
367 public ChangeListener,
368 public ButtonListener
370 public:
371 TopLevelItem (KeyMappingEditorComponent& owner_)
372 : owner (owner_)
374 setLinesDrawnForSubItems (false);
375 owner.getMappings().addChangeListener (this);
378 ~TopLevelItem()
380 owner.getMappings().removeChangeListener (this);
383 bool mightContainSubItems() { return true; }
384 const String getUniqueName() const { return "keys"; }
386 void changeListenerCallback (ChangeBroadcaster*)
388 const OpennessRestorer opennessRestorer (*this);
389 clearSubItems();
391 const StringArray categories (owner.getMappings().getCommandManager()->getCommandCategories());
393 for (int i = 0; i < categories.size(); ++i)
395 const Array <CommandID> commands (owner.getMappings().getCommandManager()->getCommandsInCategory (categories[i]));
396 int count = 0;
398 for (int j = 0; j < commands.size(); ++j)
399 if (owner.shouldCommandBeIncluded (commands[j]))
400 ++count;
402 if (count > 0)
403 addSubItem (new CategoryItem (owner, categories[i]));
407 static void resetToDefaultsCallback (int result, KeyMappingEditorComponent* owner)
409 if (result != 0 && owner != nullptr)
410 owner->getMappings().resetToDefaultMappings();
413 void buttonClicked (Button*)
415 AlertWindow::showOkCancelBox (AlertWindow::QuestionIcon,
416 TRANS("Reset to defaults"),
417 TRANS("Are you sure you want to reset all the key-mappings to their default state?"),
418 TRANS("Reset"),
419 String::empty,
420 &owner,
421 ModalCallbackFunction::forComponent (resetToDefaultsCallback, &owner));
424 private:
425 KeyMappingEditorComponent& owner;
429 //==============================================================================
430 KeyMappingEditorComponent::KeyMappingEditorComponent (KeyPressMappingSet& mappingManager,
431 const bool showResetToDefaultButton)
432 : mappings (mappingManager),
433 resetButton (TRANS ("reset to defaults"))
435 treeItem = new TopLevelItem (*this);
437 if (showResetToDefaultButton)
439 addAndMakeVisible (&resetButton);
440 resetButton.addListener (treeItem);
443 addAndMakeVisible (&tree);
444 tree.setColour (TreeView::backgroundColourId, findColour (backgroundColourId));
445 tree.setRootItemVisible (false);
446 tree.setDefaultOpenness (true);
447 tree.setRootItem (treeItem);
450 KeyMappingEditorComponent::~KeyMappingEditorComponent()
452 tree.setRootItem (nullptr);
455 //==============================================================================
456 void KeyMappingEditorComponent::setColours (const Colour& mainBackground,
457 const Colour& textColour)
459 setColour (backgroundColourId, mainBackground);
460 setColour (textColourId, textColour);
461 tree.setColour (TreeView::backgroundColourId, mainBackground);
464 void KeyMappingEditorComponent::parentHierarchyChanged()
466 treeItem->changeListenerCallback (nullptr);
469 void KeyMappingEditorComponent::resized()
471 int h = getHeight();
473 if (resetButton.isVisible())
475 const int buttonHeight = 20;
476 h -= buttonHeight + 8;
477 int x = getWidth() - 8;
479 resetButton.changeWidthToFitText (buttonHeight);
480 resetButton.setTopRightPosition (x, h + 6);
483 tree.setBounds (0, 0, getWidth(), h);
486 //==============================================================================
487 bool KeyMappingEditorComponent::shouldCommandBeIncluded (const CommandID commandID)
489 const ApplicationCommandInfo* const ci = mappings.getCommandManager()->getCommandForID (commandID);
491 return ci != nullptr && (ci->flags & ApplicationCommandInfo::hiddenFromKeyEditor) == 0;
494 bool KeyMappingEditorComponent::isCommandReadOnly (const CommandID commandID)
496 const ApplicationCommandInfo* const ci = mappings.getCommandManager()->getCommandForID (commandID);
498 return ci != nullptr && (ci->flags & ApplicationCommandInfo::readOnlyInKeyEditor) != 0;
501 const String KeyMappingEditorComponent::getDescriptionForKeyPress (const KeyPress& key)
503 return key.getTextDescription();
506 END_JUCE_NAMESPACE