/* * 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.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.keymap.*; 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.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.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.IdeFrame; 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.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; 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 JCheckBox myNonEnglishKeyboardSupportOption; private JLabel myBaseKeymapLabel; private ActionsTree myActionsTree; private FilterComponent myFilterComponent; private JBPopup myPopup = null; private TreeExpansionMonitor myTreeExpansionMonitor; private boolean myQuickListsModified = false; 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() { @Override 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() { @Override 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; } @Override public Runnable enableSearch(final String option) { return new Runnable() { @Override public void run() { showOption(option); } }; } @Override public void processCurrentKeymapChanged(QuickList[] ids) { myQuickLists = ids; myCopyButton.setEnabled(false); myDeleteButton.setEnabled(false); myResetToDefault.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); } 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); IdeFrame ideFrame = IdeFocusManager.getGlobalInstance().getLastFocusedFrame(); if (ideFrame != null && KeyboardSettingsExternalizable.isSupportedKeyboardLayout(ideFrame.getComponent())) { String displayLanguage = ideFrame.getComponent().getInputContext().getLocale().getDisplayLanguage(); myNonEnglishKeyboardSupportOption = new JCheckBox(new AbstractAction(displayLanguage + " " + KeyMapBundle.message("use.non.english.keyboard.layout.support")) { @Override public void actionPerformed(ActionEvent e) { KeyboardSettingsExternalizable.getInstance().setNonEnglishKeyboardSupportEnabled(myNonEnglishKeyboardSupportOption.isSelected()); } }); myNonEnglishKeyboardSupportOption.setSelected(KeyboardSettingsExternalizable.getInstance().isNonEnglishKeyboardSupportEnabled()); panel.add(myNonEnglishKeyboardSupportOption, gc); } myCopyButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { copyKeymap(); } }); myResetToDefault.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { resetKeymap(); } }); myDeleteButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { deleteKeymap(); } }); return panel; } 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() { @Override public void expandAll() { TreeUtil.expandAll(myActionsTree.getTree()); } @Override public boolean canExpand() { return true; } @Override public void collapseAll() { TreeUtil.collapseAll(myActionsTree.getTree(), 0); } @Override 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.Actions.Properties) { { 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) { @Override public void filter() { alarm.cancelAllRequests(); alarm.addRequest(new Runnable() { @Override public void run() { if (!myFilterComponent.isShowing()) return; if (!myTreeExpansionMonitor.isFreeze()) 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(); } } }, 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) { @Override 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) { @Override public void actionPerformed(AnActionEvent e) { 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() { @Override public void actionPerformed(ActionEvent e) { secondShortcut.setEnabled(enable2Shortcut.isSelected()); if (enable2Shortcut.isSelected()) { secondShortcut.requestFocusInWindow(); } } }); firstShortcut.getDocument().addDocumentListener(new DocumentAdapter() { @Override protected void textChanged(DocumentEvent e) { filterTreeByShortcut(firstShortcut, enable2Shortcut, secondShortcut); } }); secondShortcut.getDocument().addDocumentListener(new DocumentAdapter() { @Override 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); IdeFocusManager.getGlobalInstance().doForceFocusWhenFocusSettlesDown(firstShortcut); return filterComponent; } private void filterTreeByShortcut(final ShortcutTextField firstShortcut, final JCheckBox enable2Shortcut, final ShortcutTextField secondShortcut) { final KeyStroke keyStroke = firstShortcut.getKeyStroke(); if (keyStroke != null) { if (!myTreeExpansionMonitor.isFreeze()) myTreeExpansionMonitor.freeze(); myActionsTree.filterTree(new KeyboardShortcut(keyStroke, enable2Shortcut.isSelected() ? secondShortcut.getKeyStroke() : null), getCurrentQuickListIds()); final JTree tree = myActionsTree.getTree(); TreeUtil.expandAll(tree); } } 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()); } @Override @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); } } } @Override public void reset() { if (myNonEnglishKeyboardSupportOption != null) { KeyboardSettingsExternalizable.getInstance().setNonEnglishKeyboardSupportEnabled(false); myNonEnglishKeyboardSupportOption.setSelected(KeyboardSettingsExternalizable.getInstance().isNonEnglishKeyboardSupportEnabled()); } 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); } @Override 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); } } @Override 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() { @Override protected void textChanged(DocumentEvent e) { if (myKeymap != null && myKeymap.canModify()) { myKeymap.setName(getField().getText()); } } }); } @Override public void setItem(Object anObject) { if (anObject instanceof KeymapImpl) { myKeymap = (KeymapImpl)anObject; getField().setText(myKeymap.getPresentableName()); } } @Override public Object getItem() { return myKeymap; } } @Override @Nls public String getDisplayName() { return KeyMapBundle.message("keymap.display.name"); } @Override public String getHelpTopic() { return "preferences.keymap"; } @Override public JComponent createComponent() { return this; } @Override 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 (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); } }); } 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); } }); } 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); } } } }