blob: ebb3686969f07ef7ead00e03a344a7628c1227e3 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.keymap.impl.ui;
import com.intellij.CommonBundle;
import com.intellij.application.options.ExportSchemeAction;
import com.intellij.application.options.SchemesToImportPopup;
import com.intellij.icons.AllIcons;
import com.intellij.ide.CommonActionsManager;
import com.intellij.ide.DataManager;
import com.intellij.ide.TreeExpander;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.QuickList;
import com.intellij.openapi.actionSystem.ex.QuickListsManager;
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.keymap.KeyMapBundle;
import com.intellij.openapi.keymap.Keymap;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.keymap.ex.KeymapManagerEx;
import com.intellij.openapi.keymap.impl.ActionShortcutRestrictions;
import com.intellij.openapi.keymap.impl.KeymapImpl;
import com.intellij.openapi.keymap.impl.KeymapManagerImpl;
import com.intellij.openapi.keymap.impl.ShortcutRestrictions;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SchemesManager;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.FixedComboBoxEditor;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.packageDependencies.ui.TreeExpansionMonitor;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.DoubleClickListener;
import com.intellij.ui.FilterComponent;
import com.intellij.ui.ListCellRendererWrapper;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.Alarm;
import com.intellij.util.IJSwingUtilities;
import com.intellij.util.containers.HashMap;
import com.intellij.util.ui.FormBuilder;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
import java.util.List;
public class KeymapPanel extends JPanel implements SearchableConfigurable, Configurable.NoScroll, KeymapListener, Disposable {
private JComboBox myKeymapList;
private final DefaultComboBoxModel myKeymapListModel = new DefaultComboBoxModel();
private KeymapImpl mySelectedKeymap;
private JButton myCopyButton;
private JButton myDeleteButton;
private JButton myResetToDefault;
private JLabel myBaseKeymapLabel;
private ActionsTree myActionsTree;
private FilterComponent myFilterComponent;
private JBPopup myPopup = null;
private TreeExpansionMonitor myTreeExpansionMonitor;
private boolean myQuickListsModified = false;
private JButton myExportButton;
private QuickList[] myQuickLists = QuickListsManager.getInstance().getAllQuickLists();
public KeymapPanel() {
setLayout(new BorderLayout());
JPanel keymapPanel = new JPanel(new BorderLayout());
keymapPanel.add(createKeymapListPanel(), BorderLayout.NORTH);
keymapPanel.add(createKeymapSettingsPanel(), BorderLayout.CENTER);
add(keymapPanel, BorderLayout.CENTER);
addPropertyChangeListener(new PropertyChangeListener(){
public void propertyChange(final PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("ancestor") && evt.getNewValue() != null && evt.getOldValue() == null && myQuickListsModified) {
processCurrentKeymapChanged(getCurrentQuickListIds());
myQuickListsModified = false;
}
}
});
ApplicationManager.getApplication().getMessageBus().connect(this).subscribe(CHANGE_TOPIC, this);
}
@Override
public void quickListRenamed(final QuickList oldQuickList, final QuickList newQuickList){
for (Keymap keymap : getAllKeymaps()) {
KeymapImpl impl = (KeymapImpl)keymap;
String actionId = oldQuickList.getActionId();
String newActionId = newQuickList.getActionId();
Shortcut[] shortcuts = impl.getShortcuts(actionId);
if (shortcuts != null) {
for (Shortcut shortcut : shortcuts) {
impl.removeShortcut(actionId, shortcut);
impl.addShortcut(newActionId, shortcut);
}
}
}
myQuickListsModified = true;
}
private JPanel createKeymapListPanel() {
JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
myKeymapList = new ComboBox(myKeymapListModel);
myKeymapList.setEditor(new MyEditor());
myKeymapList.setRenderer(new MyKeymapRenderer(myKeymapList.getRenderer()));
JLabel keymapLabel = new JLabel(KeyMapBundle.message("keymaps.border.factory.title"));
keymapLabel.setLabelFor(myKeymapList);
panel.add(keymapLabel, new GridBagConstraints(0,0, 1, 1, 0,0,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,0,0,0), 0,0));
panel.add(myKeymapList, new GridBagConstraints(1,0,1,1,0,0,GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0,4,0,0),0,0));
panel.add(createKeymapButtonsPanel(), new GridBagConstraints(2,0,1,1,0,0,GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0,0,0,0),0,0));
myKeymapList.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (myKeymapListModel.getSelectedItem() != mySelectedKeymap) processCurrentKeymapChanged(getCurrentQuickListIds());
}
});
panel.add(createKeymapNamePanel(), new GridBagConstraints(3,0,1,1,1,0,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(0,10,0,0),0,0));
return panel;
}
public Runnable enableSearch(final String option) {
return new Runnable(){
public void run() {
showOption(option);
}
};
}
@Override
public void processCurrentKeymapChanged(QuickList[] ids) {
myQuickLists = ids;
myCopyButton.setEnabled(false);
myDeleteButton.setEnabled(false);
myResetToDefault.setEnabled(false);
if (myExportButton != null) {
myExportButton.setEnabled(false);
}
KeymapImpl selectedKeymap = getSelectedKeymap();
mySelectedKeymap = selectedKeymap;
if (selectedKeymap == null) {
myActionsTree.reset(new KeymapImpl(), getCurrentQuickListIds());
return;
}
myKeymapList.setEditable(mySelectedKeymap.canModify());
myCopyButton.setEnabled(true);
myBaseKeymapLabel.setText("");
Keymap parent = mySelectedKeymap.getParent();
if (parent != null && mySelectedKeymap.canModify()) {
myBaseKeymapLabel.setText(KeyMapBundle.message("based.on.keymap.label", parent.getPresentableName()));
if (mySelectedKeymap.canModify() && mySelectedKeymap.getOwnActionIds().length > 0){
myResetToDefault.setEnabled(true);
}
}
if(mySelectedKeymap.canModify()) {
myDeleteButton.setEnabled(true);
if (!getSchemesManager().isShared(mySelectedKeymap)) {
if (myExportButton != null) {
myExportButton.setEnabled(true);
}
}
}
myActionsTree.reset(mySelectedKeymap, getCurrentQuickListIds());
}
private KeymapImpl getSelectedKeymap() {
return (KeymapImpl)myKeymapList.getSelectedItem();
}
List<Keymap> getAllKeymaps() {
ListModel model = myKeymapList.getModel();
List<Keymap> result = new ArrayList<Keymap>();
for (int i = 0; i < model.getSize(); i++) {
result.add((Keymap)model.getElementAt(i));
}
return result;
}
private JPanel createKeymapButtonsPanel() {
final JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 0));
panel.setLayout(new GridBagLayout());
myCopyButton = new JButton(KeyMapBundle.message("copy.keymap.button"));
Insets insets = new Insets(2, 2, 2, 2);
myCopyButton.setMargin(insets);
final GridBagConstraints gc = new GridBagConstraints(GridBagConstraints.RELATIVE, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0), 0, 0);
panel.add(myCopyButton, gc);
myResetToDefault = new JButton(CommonBundle.message("button.reset"));
myResetToDefault.setMargin(insets);
panel.add(myResetToDefault, gc);
myDeleteButton = new JButton(KeyMapBundle.message("delete.keymap.button"));
myDeleteButton.setMargin(insets);
gc.weightx = 1;
panel.add(myDeleteButton, gc);
final SchemesManager<Keymap, KeymapImpl> schemesManager = getSchemesManager();
if (schemesManager.isExportAvailable()) {
myExportButton = new JButton("Share...");
myExportButton.setMnemonic('S');
myExportButton.addActionListener(new ActionListener(){
public void actionPerformed(final ActionEvent e) {
KeymapImpl selected = getSelectedKeymap();
ExportSchemeAction.doExport(selected, schemesManager);
}
});
myExportButton.setMargin(insets);
panel.add(myExportButton, gc);
}
if (schemesManager.isImportAvailable()) {
JButton importButton = new JButton("Import Shared...");
importButton.setMnemonic('I');
importButton.addActionListener(new ActionListener(){
public void actionPerformed(final ActionEvent e) {
SchemesToImportPopup<Keymap, KeymapImpl> popup = new SchemesToImportPopup<Keymap, KeymapImpl>(panel){
protected void onSchemeSelected(final KeymapImpl scheme) {
if (scheme != null) {
scheme.setCanModify(true);
myKeymapListModel.addElement(scheme);
myKeymapList.setSelectedItem(scheme);
processCurrentKeymapChanged(getCurrentQuickListIds());
}
}
};
popup.show(schemesManager, collectKeymaps(myKeymapListModel));
}
});
importButton.setMargin(insets);
panel.add(importButton,gc);
}
myCopyButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
copyKeymap();
}
}
);
myResetToDefault.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
resetKeymap();
}
});
myDeleteButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
deleteKeymap();
}
}
);
return panel;
}
private static Collection<Keymap> collectKeymaps(final DefaultComboBoxModel list) {
HashSet<Keymap> names = new HashSet<Keymap>();
for (int i = 0; i < list.getSize(); i++) {
names.add((Keymap)list.getElementAt(i));
}
return names;
}
private static SchemesManager<Keymap,KeymapImpl> getSchemesManager() {
return ((KeymapManagerEx)KeymapManager.getInstance()).getSchemesManager();
}
private JPanel createKeymapSettingsPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
myActionsTree = new ActionsTree();
panel.add(createToolbarPanel(), BorderLayout.NORTH);
panel.add(myActionsTree.getComponent(), BorderLayout.CENTER);
myTreeExpansionMonitor = TreeExpansionMonitor.install(myActionsTree.getTree());
new DoubleClickListener() {
@Override
protected boolean onDoubleClick(MouseEvent e) {
editSelection(e);
return true;
}
}.installOn(myActionsTree.getTree());
myActionsTree.getTree().addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
editSelection(e);
e.consume();
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
editSelection(e);
e.consume();
}
}
});
return panel;
}
private JPanel createToolbarPanel() {
final JPanel panel = new JPanel(new GridBagLayout());
DefaultActionGroup group = new DefaultActionGroup();
final JComponent toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true).getComponent();
final CommonActionsManager commonActionsManager = CommonActionsManager.getInstance();
final TreeExpander treeExpander = new TreeExpander() {
public void expandAll() {
TreeUtil.expandAll(myActionsTree.getTree());
}
public boolean canExpand() {
return true;
}
public void collapseAll() {
TreeUtil.collapseAll(myActionsTree.getTree(), 0);
}
public boolean canCollapse() {
return true;
}
};
group.add(commonActionsManager.createExpandAllAction(treeExpander, myActionsTree.getTree()));
group.add(commonActionsManager.createCollapseAllAction(treeExpander, myActionsTree.getTree()));
group.add(new AnAction("Edit Shortcut", "Edit Shortcut", AllIcons.ToolbarDecorator.Edit) {
{
registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0)), myActionsTree.getTree());
}
@Override
public void update(AnActionEvent e) {
final String actionId = myActionsTree.getSelectedActionId();
e.getPresentation().setEnabled(actionId != null);
}
@Override
public void actionPerformed(AnActionEvent e) {
editSelection(e.getInputEvent());
}
});
panel.add(toolbar, new GridBagConstraints(0,0,1,1,1,0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(8,0,0,0), 0,0));
group = new DefaultActionGroup();
final JComponent searchToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true).getComponent();
final Alarm alarm = new Alarm();
myFilterComponent = new FilterComponent("KEYMAP", 5){
public void filter() {
alarm.cancelAllRequests();
alarm.addRequest(new Runnable() {
public void run() {
if (!myFilterComponent.isShowing()) return;
myTreeExpansionMonitor.freeze();
final String filter = getFilter();
myActionsTree.filter(filter, getCurrentQuickListIds());
final JTree tree = myActionsTree.getTree();
TreeUtil.expandAll(tree);
if (filter == null || filter.length() == 0) {
TreeUtil.collapseAll(tree, 0);
myTreeExpansionMonitor.restore();
}
else {
myTreeExpansionMonitor.unfreeze();
}
}
}, 300);
}
};
myFilterComponent.reset();
panel.add(myFilterComponent, new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(8, 0, 0, 0), 0,0));
group.add(new DumbAwareAction(KeyMapBundle.message("filter.shortcut.action.text"),
KeyMapBundle.message("filter.shortcut.action.text"),
AllIcons.Actions.ShortcutFilter) {
public void actionPerformed(AnActionEvent e) {
myFilterComponent.reset();
if (myPopup == null || myPopup.getContent() == null){
myPopup = JBPopupFactory.getInstance().createComponentPopupBuilder(createFilteringPanel(), null)
.setRequestFocus(true)
.setTitle(KeyMapBundle.message("filter.settings.popup.title"))
.setCancelKeyEnabled(false)
.setMovable(true)
.createPopup();
}
myPopup.showUnderneathOf(searchToolbar);
}
});
group.add(new DumbAwareAction(KeyMapBundle.message("filter.clear.action.text"),
KeyMapBundle.message("filter.clear.action.text"), AllIcons.Actions.GC) {
public void actionPerformed(AnActionEvent e) {
myTreeExpansionMonitor.freeze();
myActionsTree.filter(null, getCurrentQuickListIds()); //clear filtering
TreeUtil.collapseAll(myActionsTree.getTree(), 0);
myTreeExpansionMonitor.restore();
}
});
panel.add(searchToolbar, new GridBagConstraints(2, 0, 1, 1, 0, 0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(8, 0, 0, 0), 0,0));
return panel;
}
private JPanel createKeymapNamePanel() {
JPanel panel = new JPanel(new GridBagLayout());
myBaseKeymapLabel = new JLabel(KeyMapBundle.message("parent.keymap.label"));
panel.add(myBaseKeymapLabel,
new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 16, 0, 0), 0, 0));
return panel;
}
private JPanel createFilteringPanel() {
myActionsTree.reset(getSelectedKeymap(), getCurrentQuickListIds());
final JLabel firstLabel = new JLabel(KeyMapBundle.message("filter.first.stroke.input"));
final JCheckBox enable2Shortcut = new JCheckBox(KeyMapBundle.message("filter.second.stroke.input"));
final ShortcutTextField firstShortcut = new ShortcutTextField();
final ShortcutTextField secondShortcut = new ShortcutTextField();
enable2Shortcut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
secondShortcut.setEnabled(enable2Shortcut.isSelected());
if (enable2Shortcut.isSelected()){
secondShortcut.requestFocusInWindow();
}
}
});
firstShortcut.getDocument().addDocumentListener(new DocumentAdapter() {
protected void textChanged(DocumentEvent e) {
filterTreeByShortcut(firstShortcut, enable2Shortcut, secondShortcut);
}
});
secondShortcut.getDocument().addDocumentListener(new DocumentAdapter() {
protected void textChanged(DocumentEvent e) {
filterTreeByShortcut(firstShortcut, enable2Shortcut, secondShortcut);
}
});
IJSwingUtilities.adjustComponentsOnMac(firstLabel, firstShortcut);
JPanel filterComponent = FormBuilder.createFormBuilder()
.addLabeledComponent(firstLabel, firstShortcut, true)
.addComponent(enable2Shortcut)
.setVerticalGap(0)
.setIndent(5)
.addComponent(secondShortcut)
.getPanel();
filterComponent.setBorder(new EmptyBorder(UIUtil.PANEL_SMALL_INSETS));
enable2Shortcut.setSelected(false);
secondShortcut.setEnabled(false);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
firstShortcut.requestFocus();
}
});
return filterComponent;
}
private void filterTreeByShortcut(final ShortcutTextField firstShortcut,
final JCheckBox enable2Shortcut,
final ShortcutTextField secondShortcut) {
final KeyStroke keyStroke = firstShortcut.getKeyStroke();
if (keyStroke != null){
myTreeExpansionMonitor.freeze();
myActionsTree.filterTree(new KeyboardShortcut(keyStroke, enable2Shortcut.isSelected() ? secondShortcut.getKeyStroke() : null),
getCurrentQuickListIds());
final JTree tree = myActionsTree.getTree();
TreeUtil.expandAll(tree);
myTreeExpansionMonitor.restore();
}
}
public void showOption(String option){
createFilteringPanel();
myFilterComponent.setFilter(option);
myActionsTree.filter(option, getCurrentQuickListIds());
}
private void addKeyboardShortcut(Shortcut shortcut) {
String actionId = myActionsTree.getSelectedActionId();
if (actionId == null) {
return;
}
if (!createKeymapCopyIfNeeded()) return;
KeyboardShortcutDialog dialog = new KeyboardShortcutDialog(this, actionId, getCurrentQuickListIds());
KeyboardShortcut selectedKeyboardShortcut = shortcut instanceof KeyboardShortcut ? (KeyboardShortcut)shortcut : null;
dialog.setData(mySelectedKeymap, selectedKeyboardShortcut);
dialog.show();
if (!dialog.isOK()){
return;
}
KeyboardShortcut keyboardShortcut = dialog.getKeyboardShortcut();
if (keyboardShortcut == null) {
return;
}
HashMap<String, ArrayList<KeyboardShortcut>> conflicts = mySelectedKeymap.getConflicts(actionId, keyboardShortcut);
if(conflicts.size() > 0) {
int result = Messages.showYesNoCancelDialog(
this,
KeyMapBundle.message("conflict.shortcut.dialog.message"),
KeyMapBundle.message("conflict.shortcut.dialog.title"),
KeyMapBundle.message("conflict.shortcut.dialog.remove.button"),
KeyMapBundle.message("conflict.shortcut.dialog.leave.button"),
KeyMapBundle.message("conflict.shortcut.dialog.cancel.button"),
Messages.getWarningIcon());
if(result == Messages.YES) {
for (String id : conflicts.keySet()) {
for (KeyboardShortcut s : conflicts.get(id)) {
mySelectedKeymap.removeShortcut(id, s);
}
}
}
else if (result != Messages.NO) {
return;
}
}
// if shortcut is already registered to this action, just select it in the list
Shortcut[] shortcuts = mySelectedKeymap.getShortcuts(actionId);
for (Shortcut s : shortcuts) {
if (s.equals(keyboardShortcut)) {
return;
}
}
mySelectedKeymap.addShortcut(actionId, keyboardShortcut);
if (StringUtil.startsWithChar(actionId, '$')) {
mySelectedKeymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), keyboardShortcut);
}
repaintLists();
processCurrentKeymapChanged(getCurrentQuickListIds());
}
private QuickList[] getCurrentQuickListIds() {
return myQuickLists;
}
private void addMouseShortcut(Shortcut shortcut, ShortcutRestrictions restrictions){
String actionId = myActionsTree.getSelectedActionId();
if (actionId == null) {
return;
}
if (!createKeymapCopyIfNeeded()) return;
MouseShortcut mouseShortcut = shortcut instanceof MouseShortcut ? (MouseShortcut)shortcut : null;
MouseShortcutDialog dialog = new MouseShortcutDialog(
this,
mouseShortcut,
mySelectedKeymap,
actionId,
myActionsTree.getMainGroup(),
restrictions
);
dialog.show();
if (!dialog.isOK()){
return;
}
mouseShortcut = dialog.getMouseShortcut();
if (mouseShortcut == null){
return;
}
String[] actionIds = mySelectedKeymap.getActionIds(mouseShortcut);
if(actionIds.length > 1 || (actionIds.length == 1 && !actionId.equals(actionIds[0]))) {
int result = Messages.showYesNoCancelDialog(
this,
KeyMapBundle.message("conflict.shortcut.dialog.message"),
KeyMapBundle.message("conflict.shortcut.dialog.title"),
KeyMapBundle.message("conflict.shortcut.dialog.remove.button"),
KeyMapBundle.message("conflict.shortcut.dialog.leave.button"),
KeyMapBundle.message("conflict.shortcut.dialog.cancel.button"),
Messages.getWarningIcon());
if(result == Messages.YES) {
for (String id : actionIds) {
mySelectedKeymap.removeShortcut(id, mouseShortcut);
}
}
else if (result != Messages.NO) {
return;
}
}
// if shortcut is aleady registered to this action, just select it in the list
Shortcut[] shortcuts = mySelectedKeymap.getShortcuts(actionId);
for (Shortcut shortcut1 : shortcuts) {
if (shortcut1.equals(mouseShortcut)) {
return;
}
}
mySelectedKeymap.addShortcut(actionId, mouseShortcut);
if (StringUtil.startsWithChar(actionId, '$')) {
mySelectedKeymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), mouseShortcut);
}
repaintLists();
processCurrentKeymapChanged(getCurrentQuickListIds());
}
private void repaintLists() {
myActionsTree.getComponent().repaint();
}
private boolean createKeymapCopyIfNeeded() {
if (mySelectedKeymap.canModify()) return true;
final KeymapImpl selectedKeymap = getSelectedKeymap();
if(selectedKeymap == null) {
return false;
}
KeymapImpl newKeymap = selectedKeymap.deriveKeymap();
String newKeymapName = KeyMapBundle.message("new.keymap.name", selectedKeymap.getPresentableName());
if(!tryNewKeymapName(newKeymapName)) {
for(int i=0; ; i++) {
newKeymapName = KeyMapBundle.message("new.indexed.keymap.name", selectedKeymap.getPresentableName(), i);
if(tryNewKeymapName(newKeymapName)) {
break;
}
}
}
newKeymap.setName(newKeymapName);
newKeymap.setCanModify(true);
final int indexOf = myKeymapListModel.getIndexOf(selectedKeymap);
if (indexOf >= 0) {
myKeymapListModel.insertElementAt(newKeymap, indexOf + 1);
} else {
myKeymapListModel.addElement(newKeymap);
}
myKeymapList.setSelectedItem(newKeymap);
processCurrentKeymapChanged(getCurrentQuickListIds());
return true;
}
private void removeShortcut(Shortcut shortcut) {
String actionId = myActionsTree.getSelectedActionId();
if (actionId == null) {
return;
}
if (!createKeymapCopyIfNeeded()) return;
if(shortcut == null) return;
mySelectedKeymap.removeShortcut(actionId, shortcut);
if (StringUtil.startsWithChar(actionId, '$')) {
mySelectedKeymap.removeShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), shortcut);
}
repaintLists();
processCurrentKeymapChanged(getCurrentQuickListIds());
}
private void copyKeymap() {
KeymapImpl keymap = getSelectedKeymap();
if(keymap == null) {
return;
}
KeymapImpl newKeymap = keymap.deriveKeymap();
String newKeymapName = KeyMapBundle.message("new.keymap.name", keymap.getPresentableName());
if(!tryNewKeymapName(newKeymapName)) {
for(int i=0; ; i++) {
newKeymapName = KeyMapBundle.message("new.indexed.keymap.name", keymap.getPresentableName(), i);
if(tryNewKeymapName(newKeymapName)) {
break;
}
}
}
newKeymap.setName(newKeymapName);
newKeymap.setCanModify(true);
myKeymapListModel.addElement(newKeymap);
myKeymapList.setSelectedItem(newKeymap);
myKeymapList.getEditor().selectAll();
processCurrentKeymapChanged(getCurrentQuickListIds());
}
private boolean tryNewKeymapName(String name) {
for(int i=0; i<myKeymapListModel.getSize(); i++) {
Keymap k = (Keymap)myKeymapListModel.getElementAt(i);
if(name.equals(k.getPresentableName())) {
return false;
}
}
return true;
}
private void deleteKeymap() {
Keymap keymap = getSelectedKeymap();
if(keymap == null) {
return;
}
int result = Messages.showYesNoDialog(this, KeyMapBundle.message("delete.keymap.dialog.message"),
KeyMapBundle.message("delete.keymap.dialog.title"), Messages.getWarningIcon());
if (result != Messages.YES) {
return;
}
myKeymapListModel.removeElement(myKeymapList.getSelectedItem());
processCurrentKeymapChanged(getCurrentQuickListIds());
}
private void resetKeymap() {
Keymap keymap = getSelectedKeymap();
if(keymap == null) {
return;
}
((KeymapImpl)keymap).clearOwnActionsIds();
processCurrentKeymapChanged(getCurrentQuickListIds());
}
@NotNull
public String getId() {
return "preferences.keymap";
}
private static final class MyKeymapRenderer extends ListCellRendererWrapper<Keymap> {
public MyKeymapRenderer(final ListCellRenderer listCellRenderer) {
super();
}
@Override
public void customize(JList list, Keymap keymap, int index, boolean selected, boolean hasFocus) {
if (keymap != null) {
String name = keymap.getPresentableName();
if (name == null) {
name = KeyMapBundle.message("keymap.noname.presentable.name");
}
setText(name);
}
}
}
public void reset() {
myKeymapListModel.removeAllElements();
KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
Keymap[] keymaps = keymapManager.getAllKeymaps();
for (Keymap keymap1 : keymaps) {
KeymapImpl keymap = (KeymapImpl)keymap1;
if (keymap.canModify()) {
keymap = keymap.copy(true);
}
myKeymapListModel.addElement(keymap);
if (Comparing.equal(keymapManager.getActiveKeymap(), keymap1)) {
mySelectedKeymap = keymap;
}
}
if(myKeymapListModel.getSize() == 0) {
KeymapImpl keymap = new KeymapImpl();
keymap.setName(KeyMapBundle.message("keymap.no.name"));
myKeymapListModel.addElement(keymap);
}
myKeymapList.setSelectedItem(mySelectedKeymap);
}
public void apply() throws ConfigurationException{
ensureNonEmptyKeymapNames();
ensureUniqueKeymapNames();
final KeymapManagerImpl keymapManager = (KeymapManagerImpl)KeymapManager.getInstance();
keymapManager.removeAllKeymapsExceptUnmodifiable();
for(int i = 0; i < myKeymapListModel.getSize(); i++){
final Keymap modelKeymap = (Keymap)myKeymapListModel.getElementAt(i);
if(modelKeymap.canModify()) {
final KeymapImpl keymapToAdd = ((KeymapImpl)modelKeymap).copy(true);
keymapManager.addKeymap(keymapToAdd);
}
}
keymapManager.setActiveKeymap(mySelectedKeymap);
ActionToolbarImpl.updateAllToolbarsImmediately();
}
private void ensureNonEmptyKeymapNames() throws ConfigurationException {
for(int i = 0; i < myKeymapListModel.getSize(); i++){
final Keymap modelKeymap = (Keymap)myKeymapListModel.getElementAt(i);
if (StringUtil.isEmptyOrSpaces(modelKeymap.getName())) {
throw new ConfigurationException(KeyMapBundle.message("configuration.all.keymaps.should.have.non.empty.names.error.message"));
}
}
}
private void ensureUniqueKeymapNames() throws ConfigurationException {
final Set<String> keymapNames = new HashSet<String>();
for(int i = 0; i < myKeymapListModel.getSize(); i++){
final Keymap modelKeymap = (Keymap)myKeymapListModel.getElementAt(i);
String name = modelKeymap.getName();
if (keymapNames.contains(name)) {
throw new ConfigurationException(KeyMapBundle.message("configuration.all.keymaps.should.have.unique.names.error.message"));
}
keymapNames.add(name);
}
}
public boolean isModified() {
KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
if (!Comparing.equal(mySelectedKeymap, keymapManager.getActiveKeymap())) {
return true;
}
Keymap[] managerKeymaps = keymapManager.getAllKeymaps();
Keymap[] panelKeymaps = new Keymap[myKeymapListModel.getSize()];
for(int i = 0; i < myKeymapListModel.getSize(); i++){
panelKeymaps[i] = (Keymap)myKeymapListModel.getElementAt(i);
}
return !Comparing.equal(managerKeymaps, panelKeymaps);
}
public void selectAction(String actionId) {
myActionsTree.selectAction(actionId);
}
private static class MyEditor extends FixedComboBoxEditor {
private KeymapImpl myKeymap = null;
public MyEditor() {
getField().getDocument().addDocumentListener(new DocumentAdapter() {
protected void textChanged(DocumentEvent e) {
if (myKeymap != null && myKeymap.canModify()){
myKeymap.setName(getField().getText());
}
}
});
}
public void setItem(Object anObject) {
if (anObject instanceof KeymapImpl){
myKeymap = (KeymapImpl)anObject;
getField().setText(myKeymap.getPresentableName());
}
}
public Object getItem() {
return myKeymap;
}
}
@Nls
public String getDisplayName() {
return KeyMapBundle.message("keymap.display.name");
}
public String getHelpTopic() {
return "preferences.keymap";
}
public JComponent createComponent() {
return this;
}
public void disposeUIResources() {
if (myPopup != null && myPopup.isVisible()) {
myPopup.cancel();
}
if (myFilterComponent != null) {
myFilterComponent.dispose();
}
Disposer.dispose(this);
}
@Override
public void dispose() {
}
private void editSelection(InputEvent e) {
final String actionId = myActionsTree.getSelectedActionId();
if (actionId == null) return;
DefaultActionGroup group = new DefaultActionGroup();
final Shortcut[] shortcuts = mySelectedKeymap.getShortcuts(actionId);
final Set<String> abbreviations = AbbreviationManager.getInstance().getAbbreviations(actionId);
final ShortcutRestrictions restrictions = ActionShortcutRestrictions.getForActionId(actionId);
if (restrictions.allowKeyboardShortcut) {
group.add(new DumbAwareAction("Add Keyboard Shortcut") {
@Override
public void actionPerformed(AnActionEvent e) {
Shortcut firstKeyboard = null;
for (Shortcut shortcut : shortcuts) {
if (shortcut instanceof KeyboardShortcut) {
firstKeyboard = shortcut;
break;
}
}
addKeyboardShortcut(firstKeyboard);
}
});
}
if (restrictions.allowMouseShortcut) {
group.add(new DumbAwareAction("Add Mouse Shortcut") {
@Override
public void actionPerformed(AnActionEvent e) {
Shortcut firstMouse = null;
for (Shortcut shortcut : shortcuts) {
if (shortcut instanceof MouseShortcut) {
firstMouse = shortcut;
break;
}
}
addMouseShortcut(firstMouse, restrictions);
}
});
}
if (Registry.is("actionSystem.enableAbbreviations") && restrictions.allowAbbreviation) {
group.add(new DumbAwareAction("Add Abbreviation") {
@Override
public void actionPerformed(AnActionEvent e) {
final String abbr = Messages.showInputDialog("Enter new abbreviation:", "Abbreviation", null);
if (abbr != null) {
String actionId = myActionsTree.getSelectedActionId();
AbbreviationManager.getInstance().register(abbr, actionId);
repaintLists();
}
}
@Override
public void update(AnActionEvent e) {
final boolean enabled = myActionsTree.getSelectedActionId() != null;
e.getPresentation().setEnabledAndVisible(enabled);
}
});
}
group.addSeparator();
for (final Shortcut shortcut : shortcuts) {
group.add(new DumbAwareAction("Remove " + KeymapUtil.getShortcutText(shortcut)) {
@Override
public void actionPerformed(AnActionEvent e) {
removeShortcut(shortcut);
}
});
}
if (Registry.is("actionSystem.enableAbbreviations")) {
for (final String abbreviation : abbreviations) {
group.addAction(new DumbAwareAction("Remove Abbreviation '" + abbreviation + "'") {
@Override
public void actionPerformed(AnActionEvent e) {
AbbreviationManager.getInstance().remove(abbreviation, actionId);
repaintLists();
}
@Override
public void update(AnActionEvent e) {
super.update(e);
}
});
}
}
group.add(new Separator());
group.add(new DumbAwareAction("Reset Shortcuts") {
@Override
public void actionPerformed(AnActionEvent e) {
mySelectedKeymap.clearOwnActionsId(actionId);
processCurrentKeymapChanged(getCurrentQuickListIds());
repaintLists();
}
@Override
public void update(AnActionEvent e) {
e.getPresentation().setVisible(mySelectedKeymap.canModify() && mySelectedKeymap.hasOwnActionId(actionId));
super.update(e);
}
});
if (e instanceof MouseEvent && ((MouseEvent)e).isPopupTrigger()) {
final ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group);
popupMenu.getComponent().show(e.getComponent(), ((MouseEvent)e).getX(), ((MouseEvent)e).getY());
}
else {
final DataContext dataContext = DataManager.getInstance().getDataContext(this);
final ListPopup popup = JBPopupFactory.getInstance()
.createActionGroupPopup("Edit Shortcuts",
group,
dataContext,
JBPopupFactory.ActionSelectionAid.SPEEDSEARCH,
true);
if (e instanceof MouseEvent) {
popup.show(new RelativePoint((MouseEvent)e));
}
else {
popup.showInBestPositionFor(dataContext);
}
}
}
}