IDEA-142159 Settings | Keymap: adding and removing keyboard and mouse shortcuts is not saved

KeymapImpl:
clear myActionId2ListOfShortcuts on copy
reduce memory usage — use our OrderedSet instead of LinkedHashSet

part of IDEA-CR-3443 review
This commit is contained in:
Vladimir Krivosheev
2015-07-02 14:10:04 +02:00
parent f18b59f9cd
commit a6b1d4a2ec
8 changed files with 205 additions and 217 deletions
@@ -25,7 +25,7 @@ import java.util.Collection;
* @author traff
*/
public abstract class AbstractCollectionComboBoxModel<T> extends CollectionListModel<T> implements ComboBoxModel {
private T mySelection;
protected T mySelection;
public AbstractCollectionComboBoxModel(@Nullable T selection) {
mySelection = selection;
@@ -30,7 +30,9 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.OrderedSet;
import gnu.trove.THashMap;
import gnu.trove.TObjectObjectProcedure;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -39,7 +41,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.lang.reflect.Field;
import java.util.*;
@@ -77,7 +78,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
private KeymapImpl myParent;
private boolean myCanModify = true;
private final Map<String, LinkedHashSet<Shortcut>> myActionId2ListOfShortcuts = new THashMap<String, LinkedHashSet<Shortcut>>();
private final THashMap<String, OrderedSet<Shortcut>> myActionId2ListOfShortcuts = new THashMap<String, OrderedSet<Shortcut>>();
/**
* Don't use this field directly! Use it only through <code>getKeystroke2ListOfIds</code>.
@@ -134,21 +135,26 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
@NotNull
public KeymapImpl copy() {
KeymapImpl newKeymap = new KeymapImpl();
return copyTo(newKeymap);
return copyTo(new KeymapImpl());
}
@NotNull
public KeymapImpl copyTo(@NotNull KeymapImpl otherKeymap) {
public KeymapImpl copyTo(@NotNull final KeymapImpl otherKeymap) {
otherKeymap.myParent = myParent;
otherKeymap.myName = myName;
otherKeymap.myCanModify = canModify();
otherKeymap.cleanShortcutsCache();
for (Map.Entry<String, LinkedHashSet<Shortcut>> entry : myActionId2ListOfShortcuts.entrySet()) {
otherKeymap.myActionId2ListOfShortcuts.put(entry.getKey(), new LinkedHashSet<Shortcut>(entry.getValue()));
}
otherKeymap.myActionId2ListOfShortcuts.clear();
otherKeymap.myActionId2ListOfShortcuts.ensureCapacity(myActionId2ListOfShortcuts.size());
myActionId2ListOfShortcuts.forEachEntry(new TObjectObjectProcedure<String, OrderedSet<Shortcut>>() {
@Override
public boolean execute(String actionId, OrderedSet<Shortcut> shortcuts) {
otherKeymap.myActionId2ListOfShortcuts.put(actionId, new OrderedSet<Shortcut>(shortcuts));
return true;
}
});
return otherKeymap;
}
@@ -195,9 +201,9 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
private void addShortcutSilently(String actionId, Shortcut shortcut, final boolean checkParentShortcut) {
LinkedHashSet<Shortcut> list = myActionId2ListOfShortcuts.get(actionId);
OrderedSet<Shortcut> list = myActionId2ListOfShortcuts.get(actionId);
if (list == null) {
list = new LinkedHashSet<Shortcut>();
list = new OrderedSet<Shortcut>();
myActionId2ListOfShortcuts.put(actionId, list);
Shortcut[] boundShortcuts = getBoundShortcuts(actionId);
if (boundShortcuts != null) {
@@ -230,7 +236,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
@Override
public void removeShortcut(String actionId, Shortcut toDelete) {
LinkedHashSet<Shortcut> list = myActionId2ListOfShortcuts.get(actionId);
OrderedSet<Shortcut> list = myActionId2ListOfShortcuts.get(actionId);
if (list != null) {
Iterator<Shortcut> it = list.iterator();
while (it.hasNext()) {
@@ -253,7 +259,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
if (inherited != null) {
boolean affected = false;
LinkedHashSet<Shortcut> newShortcuts = new LinkedHashSet<Shortcut>(inherited.length);
OrderedSet<Shortcut> newShortcuts = new OrderedSet<Shortcut>(inherited.length);
for (Shortcut eachInherited : inherited) {
if (toDelete.equals(eachInherited)) {
// skip this one
@@ -306,7 +312,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
private <T extends Shortcut>void addAction2ShortcutsMap(final String actionId, final Map<T, List<String>> strokesMap, final Class<T> shortcutClass) {
LinkedHashSet<Shortcut> listOfShortcuts = _getShortcuts(actionId);
OrderedSet<Shortcut> listOfShortcuts = _getShortcuts(actionId);
for (Shortcut shortcut : listOfShortcuts) {
if (!shortcutClass.isAssignableFrom(shortcut.getClass())) {
continue;
@@ -328,7 +334,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
private void addKeystrokesMap(final String actionId, final Map<KeyStroke, List<String>> strokesMap) {
LinkedHashSet<Shortcut> listOfShortcuts = _getShortcuts(actionId);
OrderedSet<Shortcut> listOfShortcuts = _getShortcuts(actionId);
for (Shortcut shortcut : listOfShortcuts) {
if (!(shortcut instanceof KeyboardShortcut)) {
continue;
@@ -347,14 +353,14 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
}
private LinkedHashSet<Shortcut> _getShortcuts(final String actionId) {
private OrderedSet<Shortcut> _getShortcuts(final String actionId) {
KeymapManagerEx keymapManager = getKeymapManager();
LinkedHashSet<Shortcut> listOfShortcuts = myActionId2ListOfShortcuts.get(actionId);
OrderedSet<Shortcut> listOfShortcuts = myActionId2ListOfShortcuts.get(actionId);
if (listOfShortcuts != null) {
return listOfShortcuts;
}
else {
listOfShortcuts = new LinkedHashSet<Shortcut>();
listOfShortcuts = new OrderedSet<Shortcut>();
}
final String actionBinding = keymapManager.getActionBinding(actionId);
@@ -519,7 +525,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
@NotNull
@Override
public Shortcut[] getShortcuts(String actionId) {
LinkedHashSet<Shortcut> shortcuts = myActionId2ListOfShortcuts.get(actionId);
OrderedSet<Shortcut> shortcuts = myActionId2ListOfShortcuts.get(actionId);
if (shortcuts == null) {
Shortcut[] boundShortcuts = getBoundShortcuts(actionId);
@@ -539,7 +545,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
@Nullable
private Shortcut[] getOwnShortcuts(String actionId) {
LinkedHashSet<Shortcut> own = myActionId2ListOfShortcuts.get(actionId);
OrderedSet<Shortcut> own = myActionId2ListOfShortcuts.get(actionId);
if (own == null) return null;
return own.isEmpty() ? ourEmptyShortcutsArray : own.toArray(new Shortcut[own.size()]);
}
@@ -685,7 +691,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
}
// Add read shortcuts
for (String id : id2shortcuts.keySet()) {
myActionId2ListOfShortcuts.put(id, new LinkedHashSet<Shortcut>(2)); // It's a trick! After that parent's shortcuts are not added to the keymap
myActionId2ListOfShortcuts.put(id, new OrderedSet<Shortcut>(2)); // It's a trick! After that parent's shortcuts are not added to the keymap
ArrayList<Shortcut> shortcuts = id2shortcuts.get(id);
for (Shortcut shortcut : shortcuts) {
addShortcutSilently(id, shortcut, false);
@@ -711,7 +717,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
for (String actionId : ownActionIds) {
Element actionElement = new Element(ACTION);
actionElement.setAttribute(ID_ATTRIBUTE, actionId);
// Save keyboad shortcuts
// Save keyboard shortcuts
Shortcut[] shortcuts = getShortcuts(actionId);
for (Shortcut shortcut : shortcuts) {
if (shortcut instanceof KeyboardShortcut) {
@@ -805,23 +811,23 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
// modifiers
int modifiers = shortcut.getModifiers();
if ((MouseEvent.SHIFT_DOWN_MASK & modifiers) > 0) {
if ((InputEvent.SHIFT_DOWN_MASK & modifiers) > 0) {
buffer.append(SHIFT);
buffer.append(' ');
}
if ((MouseEvent.CTRL_DOWN_MASK & modifiers) > 0) {
if ((InputEvent.CTRL_DOWN_MASK & modifiers) > 0) {
buffer.append(CONTROL);
buffer.append(' ');
}
if ((MouseEvent.META_DOWN_MASK & modifiers) > 0) {
if ((InputEvent.META_DOWN_MASK & modifiers) > 0) {
buffer.append(META);
buffer.append(' ');
}
if ((MouseEvent.ALT_DOWN_MASK & modifiers) > 0) {
if ((InputEvent.ALT_DOWN_MASK & modifiers) > 0) {
buffer.append(ALT);
buffer.append(' ');
}
if ((MouseEvent.ALT_GRAPH_DOWN_MASK & modifiers) > 0) {
if ((InputEvent.ALT_GRAPH_DOWN_MASK & modifiers) > 0) {
buffer.append(ALT_GRAPH);
buffer.append(' ');
}
@@ -849,7 +855,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap {
cleanShortcutsCache();
}
public boolean hasOwnActionId(String actionId) {
public boolean hasOwnActionId(@NotNull String actionId) {
return myActionId2ListOfShortcuts.containsKey(actionId);
}
@@ -86,7 +86,6 @@ public class ActionsTree {
if (rowBounds.intersects(clip)) {
Object node = getPathForRow(row).getLastPathComponent();
if (node instanceof DefaultMutableTreeNode) {
Object data = ((DefaultMutableTreeNode)node).getUserObject();
Rectangle fullRowRect = new Rectangle(visibleRect.x, rowBounds.y, visibleRect.width, rowBounds.height);
@@ -106,30 +105,20 @@ public class ActionsTree {
@Override
public void mouseMoved(MouseEvent e) {
String description = getDescription(e);
if (description != null) {
ActionMenu.showDescriptionInStatusBar(true, myTree, description);
}
else {
ActionMenu.showDescriptionInStatusBar(false, myTree, null);
}
ActionMenu.showDescriptionInStatusBar(description != null, myTree, description);
}
@Nullable
private String getDescription(@NotNull MouseEvent e) {
TreePath path = myTree.getPathForLocation(e.getX(), e.getY());
if (path == null) return null;
DefaultMutableTreeNode node = path == null ? null : (DefaultMutableTreeNode)path.getLastPathComponent();
Object userObject = node == null ? null : node.getUserObject();
if (!(userObject instanceof String)) {
return null;
}
DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
if (node == null) return null;
Object userObject = node.getUserObject();
if (!(userObject instanceof String)) return null;
String actionId = (String)userObject;
AnAction action = ActionManager.getInstance().getActionOrStub(actionId);
if (action == null) return null;
return action.getTemplatePresentation().getDescription();
AnAction action = ActionManager.getInstance().getActionOrStub((String)userObject);
return action == null ? null : action.getTemplatePresentation().getDescription();
}
});
@@ -140,6 +129,11 @@ public class ActionsTree {
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
}
// silently replace current map
void setKeymap(@NotNull Keymap keymap) {
myKeymap = keymap;
}
public JComponent getComponent() {
return myComponent;
}
@@ -171,7 +165,7 @@ public class ActionsTree {
return (QuickList)userObject;
}
public void reset(Keymap keymap, final QuickList[] allQuickLists) {
public void reset(@NotNull Keymap keymap, @NotNull QuickList[] allQuickLists) {
reset(keymap, allQuickLists, myFilter, null);
}
@@ -188,7 +182,7 @@ public class ActionsTree {
reset(myKeymap, currentQuickListIds, filter, null);
}
private void reset(final Keymap keymap, final QuickList[] allQuickLists, String filter, @Nullable KeyboardShortcut shortcut) {
private void reset(@NotNull Keymap keymap, @NotNull QuickList[] allQuickLists, String filter, @Nullable KeyboardShortcut shortcut) {
myKeymap = keymap;
final PathsKeeper pathsKeeper = new PathsKeeper();
@@ -198,11 +192,11 @@ public class ActionsTree {
ActionManager actionManager = ActionManager.getInstance();
Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(myComponent));
Group mainGroup = ActionsTreeUtil.createMainGroup(project, myKeymap, allQuickLists, filter, true,
ActionsTreeUtil.isActionFiltered(actionManager, myKeymap, shortcut, filter, true));
Group mainGroup = ActionsTreeUtil.createMainGroup(project, keymap, allQuickLists, filter, true,
ActionsTreeUtil.isActionFiltered(actionManager, keymap, shortcut, filter, true));
if ((filter != null && filter.length() > 0 || shortcut != null) && mainGroup.initIds().isEmpty()){
mainGroup = ActionsTreeUtil.createMainGroup(project, myKeymap, allQuickLists, filter, false,
ActionsTreeUtil.isActionFiltered(actionManager, myKeymap, shortcut, filter, false));
mainGroup = ActionsTreeUtil.createMainGroup(project, keymap, allQuickLists, filter, false,
ActionsTreeUtil.isActionFiltered(actionManager, keymap, shortcut, filter, false));
}
myRoot = ActionsTreeUtil.createNode(mainGroup);
myMainGroup = mainGroup;
@@ -251,14 +245,7 @@ public class ActionsTree {
if (userObject instanceof QuickList) {
userObject = ((QuickList)userObject).getActionId();
}
if (userObject instanceof String) {
Shortcut[] shortcuts = myKeymap.getShortcuts((String)userObject);
return KeymapUtil.getShortcutsText(shortcuts);
}
else {
return "";
}
return userObject instanceof String ? KeymapUtil.getShortcutsText(myKeymap.getShortcuts((String)userObject)) : "";
}
else {
return "???";
@@ -34,6 +34,7 @@ import com.intellij.util.ArrayUtil;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
@@ -214,7 +215,8 @@ public class KeyboardShortcutDialog extends DialogWrapper {
myConflictInfoArea.setForeground(JBColor.RED);
if (loaded) {
myConflictInfoArea.setText(KeyMapBundle.message("assigned.to.info.message", buffer.toString()));
} else {
}
else {
myConflictInfoArea.setText("Assigned to " + buffer.toString() + " which is now not loaded but may be loaded later");
}
}
@@ -224,13 +226,10 @@ public class KeyboardShortcutDialog extends DialogWrapper {
mySecondStrokePanel.setEnabled(myEnableSecondKeystroke.isSelected());
}
@Nullable
public KeyboardShortcut getKeyboardShortcut() {
KeyStroke firstStroke = myFirstStrokePanel.getKeyStroke();
if (firstStroke == null) {
return null;
}
KeyStroke secondStroke = myEnableSecondKeystroke.isSelected() ? mySecondStrokePanel.getKeyStroke() : null;
return new KeyboardShortcut(firstStroke, secondStroke);
return firstStroke == null ? null : new KeyboardShortcut(firstStroke, myEnableSecondKeystroke.isSelected() ? mySecondStrokePanel.getKeyStroke() : null);
}
static String getTextByKeyStroke(KeyStroke keyStroke) {
@@ -119,8 +119,6 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
});
private Keymap mySelectedKeymap;
private JButton myCopyButton;
private JButton myDeleteButton;
private JButton myResetToDefault;
@@ -146,7 +144,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Override
public void propertyChange(@NotNull final PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("ancestor") && evt.getNewValue() != null && evt.getOldValue() == null && myQuickListsModified) {
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
myQuickListsModified = false;
}
}
@@ -185,9 +183,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
myEditor.getComboBox().addActionListener(new ActionListener() {
@Override
public void actionPerformed(@NotNull ActionEvent e) {
if (myEditor.getModel().getSelectedItem() != mySelectedKeymap) {
processCurrentKeymapChanged(myQuickLists);
}
currentKeymapChanged();
}
});
panel.add(createKeymapNamePanel(), new GridBagConstraints(3, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 10, 0, 0), 0, 0));
@@ -207,10 +203,13 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Override
public void processCurrentKeymapChanged(@NotNull QuickList[] ids) {
myQuickLists = ids;
currentKeymapChanged();
}
private void currentKeymapChanged() {
myResetToDefault.setEnabled(false);
Keymap selectedKeymap = myEditor.getModel().getSelected();
mySelectedKeymap = selectedKeymap;
boolean editable = selectedKeymap != null && selectedKeymap.canModify();
myDeleteButton.setEnabled(editable);
@@ -228,16 +227,12 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
else {
myBaseKeymapLabel.setText(KeyMapBundle.message("based.on.keymap.label", parent.getPresentableName()));
if (selectedKeymap.canModify() && ((KeymapImpl)mySelectedKeymap).getOwnActionIds().length > 0) {
if (selectedKeymap.canModify() && ((KeymapImpl)selectedKeymap).getOwnActionIds().length > 0) {
myResetToDefault.setEnabled(true);
}
}
resetActionsTree(selectedKeymap);
}
private void resetActionsTree(@NotNull Keymap keymap) {
myActionsTree.reset(myEditor.getMutable(keymap), myQuickLists);
myActionsTree.reset(selectedKeymap, myQuickLists);
}
private JPanel createKeymapButtonsPanel() {
@@ -450,7 +445,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
private JPanel createFilteringPanel() {
//noinspection ConstantConditions
resetActionsTree(myEditor.getModel().getSelected());
myActionsTree.reset(myEditor.getModel().getSelected(), myQuickLists);
final JLabel firstLabel = new JLabel(KeyMapBundle.message("filter.first.stroke.input"));
final JCheckBox enable2Shortcut = new JCheckBox(KeyMapBundle.message("filter.second.stroke.input"));
@@ -482,12 +477,11 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
});
IJSwingUtilities.adjustComponentsOnMac(firstLabel, firstShortcut);
//noinspection deprecation
JPanel filterComponent = FormBuilder.createFormBuilder()
.addLabeledComponent(firstLabel, firstShortcut, true)
.addComponent(enable2Shortcut)
.setVerticalGap(0)
.setIndent(5)
.setHorizontalGap(5)
.addComponent(secondShortcut)
.getPanel();
@@ -525,31 +519,21 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
myActionsTree.filter(option, myQuickLists);
}
private void addKeyboardShortcut(Shortcut shortcut) {
String actionId = myActionsTree.getSelectedActionId();
if (actionId == null) {
return;
}
if (!createKeymapCopyIfNeeded()) return;
private void addKeyboardShortcut(@NotNull String actionId, @Nullable Shortcut shortcut) {
Keymap keymap = createKeymapCopyIfNeeded();
KeyboardShortcutDialog dialog = new KeyboardShortcutDialog(this, actionId, myQuickLists);
KeyboardShortcut selectedKeyboardShortcut = shortcut instanceof KeyboardShortcut ? (KeyboardShortcut)shortcut : null;
dialog.setData(mySelectedKeymap, selectedKeyboardShortcut);
dialog.setData(keymap, shortcut instanceof KeyboardShortcut ? (KeyboardShortcut)shortcut : null);
if (!dialog.showAndGet()) {
return;
}
KeyboardShortcut keyboardShortcut = dialog.getKeyboardShortcut();
if (keyboardShortcut == null) {
return;
}
Map<String, ArrayList<KeyboardShortcut>> conflicts = mySelectedKeymap.getConflicts(actionId, keyboardShortcut);
Map<String, ArrayList<KeyboardShortcut>> conflicts = keymap.getConflicts(actionId, keyboardShortcut);
if (!conflicts.isEmpty()) {
int result = Messages.showYesNoCancelDialog(
this,
@@ -563,7 +547,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
if (result == Messages.YES) {
for (String id : conflicts.keySet()) {
for (KeyboardShortcut s : conflicts.get(id)) {
mySelectedKeymap.removeShortcut(id, s);
keymap.removeShortcut(id, s);
}
}
}
@@ -573,20 +557,20 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
// if shortcut is already registered to this action, just select it in the list
Shortcut[] shortcuts = mySelectedKeymap.getShortcuts(actionId);
Shortcut[] shortcuts = keymap.getShortcuts(actionId);
for (Shortcut s : shortcuts) {
if (s.equals(keyboardShortcut)) {
return;
}
}
mySelectedKeymap.addShortcut(actionId, keyboardShortcut);
keymap.addShortcut(actionId, keyboardShortcut);
if (StringUtil.startsWithChar(actionId, '$')) {
mySelectedKeymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), keyboardShortcut);
keymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), keyboardShortcut);
}
repaintLists();
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
}
private void addMouseShortcut(Shortcut shortcut, ShortcutRestrictions restrictions) {
@@ -595,14 +579,14 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
return;
}
if (!createKeymapCopyIfNeeded()) return;
Keymap keymap = createKeymapCopyIfNeeded();
MouseShortcut mouseShortcut = shortcut instanceof MouseShortcut ? (MouseShortcut)shortcut : null;
MouseShortcutDialog dialog = new MouseShortcutDialog(
this,
mouseShortcut,
mySelectedKeymap,
keymap,
actionId,
myActionsTree.getMainGroup(),
restrictions
@@ -617,7 +601,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
return;
}
String[] actionIds = mySelectedKeymap.getActionIds(mouseShortcut);
String[] actionIds = keymap.getActionIds(mouseShortcut);
if (actionIds.length > 1 || (actionIds.length == 1 && !actionId.equals(actionIds[0]))) {
int result = Messages.showYesNoCancelDialog(
this,
@@ -630,7 +614,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
if (result == Messages.YES) {
for (String id : actionIds) {
mySelectedKeymap.removeShortcut(id, mouseShortcut);
keymap.removeShortcut(id, mouseShortcut);
}
}
else if (result != Messages.NO) {
@@ -640,50 +624,51 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
// if shortcut is already registered to this action, just select it in the list
Shortcut[] shortcuts = mySelectedKeymap.getShortcuts(actionId);
Shortcut[] shortcuts = keymap.getShortcuts(actionId);
for (Shortcut shortcut1 : shortcuts) {
if (shortcut1.equals(mouseShortcut)) {
return;
}
}
mySelectedKeymap.addShortcut(actionId, mouseShortcut);
keymap.addShortcut(actionId, mouseShortcut);
if (StringUtil.startsWithChar(actionId, '$')) {
mySelectedKeymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), mouseShortcut);
keymap.addShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), mouseShortcut);
}
repaintLists();
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
}
private void repaintLists() {
myActionsTree.getComponent().repaint();
}
private boolean createKeymapCopyIfNeeded() {
if (mySelectedKeymap.canModify()) return true;
final Keymap selectedKeymap = myEditor.getModel().getSelected();
if (selectedKeymap == null) {
return false;
@NotNull
private Keymap createKeymapCopyIfNeeded() {
Keymap keymap = myEditor.getModel().getSelected();
assert keymap != null;
if (keymap.canModify()) {
Keymap mutable = myEditor.getMutable(keymap);
myActionsTree.setKeymap(mutable);
return mutable;
}
KeymapImpl newKeymap = ((KeymapImpl)selectedKeymap).deriveKeymap();
String newKeymapName = KeyMapBundle.message("new.keymap.name", selectedKeymap.getPresentableName());
String newKeymapName = KeyMapBundle.message("new.keymap.name", keymap.getPresentableName());
if (!tryNewKeymapName(newKeymapName)) {
for (int i = 0; ; i++) {
newKeymapName = KeyMapBundle.message("new.indexed.keymap.name", selectedKeymap.getPresentableName(), i);
newKeymapName = KeyMapBundle.message("new.indexed.keymap.name", keymap.getPresentableName(), i);
if (tryNewKeymapName(newKeymapName)) {
break;
}
}
}
KeymapImpl newKeymap = ((KeymapImpl)keymap).deriveKeymap();
newKeymap.setName(newKeymapName);
newKeymap.setCanModify(true);
final int indexOf = myEditor.getModel().getElementIndex(selectedKeymap);
int indexOf = myEditor.getModel().getElementIndex(keymap);
if (indexOf >= 0) {
myEditor.getModel().add(indexOf + 1, newKeymap);
}
@@ -692,28 +677,8 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
myEditor.getModel().setSelectedItem(newKeymap);
processCurrentKeymapChanged(myQuickLists);
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(myQuickLists);
currentKeymapChanged();
return newKeymap;
}
private void copyKeymap() {
@@ -738,7 +703,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
myEditor.getModel().add(newKeymap);
myEditor.getModel().setSelectedItem(newKeymap);
myEditor.getComboBox().getEditor().selectAll();
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
}
private boolean tryNewKeymapName(String name) {
@@ -759,7 +724,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
}
myEditor.getModel().remove(keymap);
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
}
private void resetKeymap() {
@@ -768,7 +733,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
return;
}
((KeymapImpl)keymap).clearOwnActionsIds();
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
}
@Override
@@ -784,11 +749,11 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
myNonEnglishKeyboardSupportOption.setSelected(KeyboardSettingsExternalizable.getInstance().isNonEnglishKeyboardSupportEnabled());
}
mySelectedKeymap = null;
Keymap selectedKeymap = null;
List<Keymap> list = getManagerKeymaps();
for (Keymap keymap : list) {
if (mySelectedKeymap == null && keymap == KeymapManagerEx.getInstanceEx().getActiveKeymap()) {
mySelectedKeymap = keymap;
if (selectedKeymap == null && keymap == KeymapManagerEx.getInstanceEx().getActiveKeymap()) {
selectedKeymap = keymap;
}
}
myEditor.reset(list);
@@ -797,23 +762,23 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
KeymapImpl keymap = new KeymapImpl();
keymap.setName(KeyMapBundle.message("keymap.no.name"));
myEditor.getModel().add(keymap);
mySelectedKeymap = keymap;
selectedKeymap = keymap;
}
myEditor.getModel().setSelectedItem(mySelectedKeymap);
myEditor.getModel().setSelectedItem(selectedKeymap);
processCurrentKeymapChanged(myQuickLists);
currentKeymapChanged();
}
@Override
public void apply() throws ConfigurationException {
myEditor.ensureNonEmptyNames("Quick list should have non empty name");
myEditor.ensureNonEmptyNames(KeyMapBundle.message("configuration.all.keymaps.should.have.non.empty.names.error.message"));
ensureUniqueKeymapNames();
KeymapManagerImpl keymapManager = (KeymapManagerImpl)KeymapManager.getInstance();
// we must specify the same filter, which was used to get original items
keymapManager.setKeymaps(myEditor.apply(), KEYMAP_FILTER);
keymapManager.setActiveKeymap(mySelectedKeymap);
keymapManager.setActiveKeymap(myEditor.getModel().getSelected());
ActionToolbarImpl.updateAllToolbarsImmediately();
}
@@ -828,7 +793,7 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Override
public boolean isModified() {
return !Comparing.equal(mySelectedKeymap, KeymapManager.getInstance().getActiveKeymap()) || myEditor.isModified();
return !Comparing.equal(myEditor.getModel().getSelected(), KeymapManager.getInstance().getActiveKeymap()) || myEditor.isModified();
}
@NotNull
@@ -873,34 +838,59 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Nullable
public Shortcut[] getCurrentShortcuts(@NotNull String actionId) {
return mySelectedKeymap == null ? null : mySelectedKeymap.getShortcuts(actionId);
Keymap keymap = myEditor.getModel().getSelected();
return keymap == null ? null : keymap.getShortcuts(actionId);
}
private void editSelection(InputEvent e) {
final String actionId = myActionsTree.getSelectedActionId();
if (actionId == null) return;
String actionId = myActionsTree.getSelectedActionId();
if (actionId == null) {
return;
}
DefaultActionGroup group = createEditActionGroup(actionId);
if (e instanceof MouseEvent && ((MouseEvent)e).isPopupTrigger()) {
ActionManager.getInstance()
.createActionPopupMenu(ActionPlaces.UNKNOWN, group)
.getComponent()
.show(e.getComponent(), ((MouseEvent)e).getX(), ((MouseEvent)e).getY());
}
else {
DataContext dataContext = DataManager.getInstance().getDataContext(this);
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);
}
}
}
@NotNull
private DefaultActionGroup createEditActionGroup(@NotNull final String actionId) {
DefaultActionGroup group = new DefaultActionGroup();
final Shortcut[] shortcuts = getCurrentShortcuts(actionId);
final Set<String> abbreviations = AbbreviationManager.getInstance().getAbbreviations(actionId);
final ShortcutRestrictions restrictions = ActionShortcutRestrictions.getInstance().getForActionId(actionId);
if (restrictions.allowKeyboardShortcut) {
group.add(new DumbAwareAction("Add Keyboard Shortcut") {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Shortcut firstKeyboard = null;
assert shortcuts != null;
for (Shortcut shortcut : shortcuts) {
Shortcut firstShortcut = null;
Keymap keymap = myEditor.getModel().getSelected();
assert keymap != null;
for (Shortcut shortcut : keymap.getShortcuts(actionId)) {
if (shortcut instanceof KeyboardShortcut) {
firstKeyboard = shortcut;
firstShortcut = shortcut;
break;
}
}
addKeyboardShortcut(firstKeyboard);
addKeyboardShortcut(actionId, firstShortcut);
}
});
}
@@ -910,8 +900,9 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Shortcut firstMouse = null;
assert shortcuts != null;
for (Shortcut shortcut : shortcuts) {
Keymap keymap = myEditor.getModel().getSelected();
assert keymap != null;
for (Shortcut shortcut : keymap.getShortcuts(actionId)) {
if (shortcut instanceof MouseShortcut) {
firstMouse = shortcut;
break;
@@ -926,10 +917,9 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
group.add(new DumbAwareAction("Add Abbreviation") {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
final String abbr = Messages.showInputDialog("Enter new abbreviation:", "Abbreviation", null);
String abbr = Messages.showInputDialog("Enter new abbreviation:", "Abbreviation", null);
if (abbr != null) {
String actionId = myActionsTree.getSelectedActionId();
AbbreviationManager.getInstance().register(abbr, actionId);
AbbreviationManager.getInstance().register(abbr, myActionsTree.getSelectedActionId());
repaintLists();
}
}
@@ -943,29 +933,32 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
group.addSeparator();
assert shortcuts != null;
for (final Shortcut shortcut : shortcuts) {
Keymap keymap = myEditor.getModel().getSelected();
assert keymap != null;
for (final Shortcut shortcut : keymap.getShortcuts(actionId)) {
group.add(new DumbAwareAction("Remove " + KeymapUtil.getShortcutText(shortcut)) {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
removeShortcut(shortcut);
Keymap keymap = createKeymapCopyIfNeeded();
keymap.removeShortcut(actionId, shortcut);
if (StringUtil.startsWithChar(actionId, '$')) {
keymap.removeShortcut(KeyMapBundle.message("editor.shortcut", actionId.substring(1)), shortcut);
}
repaintLists();
currentKeymapChanged();
}
});
}
if (Registry.is("actionSystem.enableAbbreviations")) {
for (final String abbreviation : abbreviations) {
for (final String abbreviation : AbbreviationManager.getInstance().getAbbreviations(actionId)) {
group.addAction(new DumbAwareAction("Remove Abbreviation '" + abbreviation + "'") {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
AbbreviationManager.getInstance().remove(abbreviation, actionId);
repaintLists();
}
@Override
public void update(@NotNull AnActionEvent e) {
super.update(e);
}
});
}
}
@@ -973,37 +966,17 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
group.add(new DumbAwareAction("Reset Shortcuts") {
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
((KeymapImpl)mySelectedKeymap).clearOwnActionsId(actionId);
processCurrentKeymapChanged(myQuickLists);
((KeymapImpl)createKeymapCopyIfNeeded()).clearOwnActionsId(actionId);
currentKeymapChanged();
repaintLists();
}
@Override
public void update(@NotNull AnActionEvent e) {
e.getPresentation().setVisible(mySelectedKeymap.canModify() && ((KeymapImpl)mySelectedKeymap).hasOwnActionId(actionId));
super.update(e);
e.getPresentation().setVisible(((KeymapImpl)myEditor.getModel().getSelected()).hasOwnActionId(actionId));
}
});
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);
}
}
return group;
}
}
@@ -130,10 +130,7 @@ public abstract class CollectionModelEditor<T, E extends CollectionItemEditor<T>
mutable = itemEditor.clone(item, false);
modifiedToOriginal.put(mutable, item);
originalToModified.put(item, mutable);
List<T> items = getItems();
// silently replace item
items.set(index == -1 ? ContainerUtil.indexOfIdentity(items, item) : index, mutable);
silentlyReplaceItem(item, mutable, index);
}
return mutable;
}
@@ -148,6 +145,12 @@ public abstract class CollectionModelEditor<T, E extends CollectionItemEditor<T>
}
}
protected void silentlyReplaceItem(@NotNull T oldItem, @NotNull T newItem, int index) {
// silently replace item
List<T> items = getItems();
items.set(index == -1 ? ContainerUtil.indexOfIdentity(items, oldItem) : index, newItem);
}
protected final boolean areSelectedItemsRemovable(@NotNull ListSelectionModel selectionMode) {
int minSelectionIndex = selectionMode.getMinSelectionIndex();
int maxSelectionIndex = selectionMode.getMaxSelectionIndex();
@@ -19,7 +19,6 @@ import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.CollectionListModel;
import com.intellij.ui.MutableCollectionComboBoxModel;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.TObjectObjectProcedure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -85,11 +84,7 @@ public abstract class ListModelEditorBase<T> extends CollectionModelEditor<T, Li
@Override
public boolean execute(T newItem, T oldItem) {
itemEditor.applyModifiedProperties(newItem, oldItem);
int index = ContainerUtil.indexOfIdentity(items, newItem);
if (index == -1) {
LOG.error("Inconsistent model", newItem.toString());
}
model.setElementAt(oldItem, index);
silentlyReplaceItem(newItem, oldItem, -1);
return true;
}
});
@@ -98,12 +93,24 @@ public abstract class ListModelEditorBase<T> extends CollectionModelEditor<T, Li
return items;
}
@Override
protected void silentlyReplaceItem(@NotNull T oldItem, @NotNull T newItem, int index) {
super.silentlyReplaceItem(oldItem, newItem, index);
model.checkSelectionOnSilentReplace(oldItem, newItem);
}
protected final class MyModel extends MutableCollectionComboBoxModel<T> {
@NotNull
final List<T> items() {
return super.getInternalList();
}
void checkSelectionOnSilentReplace(@NotNull T oldItem, @NotNull T newItem) {
if (mySelection == oldItem) {
mySelection = newItem;
}
}
@Override
protected void itemReplaced(@NotNull T existingItem, @Nullable T newItem) {
helper.remove(existingItem);
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2015 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.
@@ -29,6 +29,13 @@ public class OrderedSet<T> extends ArrayList<T> implements Set<T>, RandomAccess
this(ContainerUtil.<T>canonicalStrategy());
}
public OrderedSet(@NotNull Collection<T> set) {
super(set.size());
myHashSet = new OpenTHashSet<T>(set.size());
addAll(set);
}
public OrderedSet(@NotNull TObjectHashingStrategy<T> hashingStrategy) {
this(hashingStrategy, 4);
}
@@ -38,6 +45,12 @@ public class OrderedSet<T> extends ArrayList<T> implements Set<T>, RandomAccess
myHashSet = new OpenTHashSet<T>(capacity, hashingStrategy);
}
public OrderedSet(int capacity) {
super(capacity);
myHashSet = new OpenTHashSet<T>(capacity);
}
@Override
public boolean removeAll(@NotNull Collection<?> c) {
boolean removed = false;