From a6b1d4a2ec8dfca6e93ca7b1dad5cfce430b5dc9 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Thu, 2 Jul 2015 10:58:13 +0200 Subject: [PATCH] IDEA-142159 Settings | Keymap: adding and removing keyboard and mouse shortcuts is not saved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KeymapImpl: clear myActionId2ListOfShortcuts on copy reduce memory usage — use our OrderedSet instead of LinkedHashSet part of IDEA-CR-3443 review --- .../ui/AbstractCollectionComboBoxModel.java | 2 +- .../openapi/keymap/impl/KeymapImpl.java | 60 +++-- .../openapi/keymap/impl/ui/ActionsTree.java | 53 ++-- .../impl/ui/KeyboardShortcutDialog.java | 11 +- .../openapi/keymap/impl/ui/KeymapPanel.java | 251 ++++++++---------- .../util/ui/CollectionModelEditor.java | 11 +- .../intellij/util/ui/ListModelEditorBase.java | 19 +- .../intellij/util/containers/OrderedSet.java | 15 +- 8 files changed, 205 insertions(+), 217 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/AbstractCollectionComboBoxModel.java b/platform/platform-api/src/com/intellij/ui/AbstractCollectionComboBoxModel.java index 92329b428aa3..996ae90ea7b4 100644 --- a/platform/platform-api/src/com/intellij/ui/AbstractCollectionComboBoxModel.java +++ b/platform/platform-api/src/com/intellij/ui/AbstractCollectionComboBoxModel.java @@ -25,7 +25,7 @@ import java.util.Collection; * @author traff */ public abstract class AbstractCollectionComboBoxModel extends CollectionListModel implements ComboBoxModel { - private T mySelection; + protected T mySelection; public AbstractCollectionComboBoxModel(@Nullable T selection) { mySelection = selection; diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.java index 20b8af578520..2f69a039db5c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapImpl.java @@ -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> myActionId2ListOfShortcuts = new THashMap>(); + private final THashMap> myActionId2ListOfShortcuts = new THashMap>(); /** * Don't use this field directly! Use it only through getKeystroke2ListOfIds. @@ -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> entry : myActionId2ListOfShortcuts.entrySet()) { - otherKeymap.myActionId2ListOfShortcuts.put(entry.getKey(), new LinkedHashSet(entry.getValue())); - } + otherKeymap.myActionId2ListOfShortcuts.clear(); + otherKeymap.myActionId2ListOfShortcuts.ensureCapacity(myActionId2ListOfShortcuts.size()); + myActionId2ListOfShortcuts.forEachEntry(new TObjectObjectProcedure>() { + @Override + public boolean execute(String actionId, OrderedSet shortcuts) { + otherKeymap.myActionId2ListOfShortcuts.put(actionId, new OrderedSet(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 list = myActionId2ListOfShortcuts.get(actionId); + OrderedSet list = myActionId2ListOfShortcuts.get(actionId); if (list == null) { - list = new LinkedHashSet(); + list = new OrderedSet(); 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 list = myActionId2ListOfShortcuts.get(actionId); + OrderedSet list = myActionId2ListOfShortcuts.get(actionId); if (list != null) { Iterator it = list.iterator(); while (it.hasNext()) { @@ -253,7 +259,7 @@ public class KeymapImpl extends ExternalizableSchemeAdapter implements Keymap { if (inherited != null) { boolean affected = false; - LinkedHashSet newShortcuts = new LinkedHashSet(inherited.length); + OrderedSet newShortcuts = new OrderedSet(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 void addAction2ShortcutsMap(final String actionId, final Map> strokesMap, final Class shortcutClass) { - LinkedHashSet listOfShortcuts = _getShortcuts(actionId); + OrderedSet 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> strokesMap) { - LinkedHashSet listOfShortcuts = _getShortcuts(actionId); + OrderedSet 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 _getShortcuts(final String actionId) { + private OrderedSet _getShortcuts(final String actionId) { KeymapManagerEx keymapManager = getKeymapManager(); - LinkedHashSet listOfShortcuts = myActionId2ListOfShortcuts.get(actionId); + OrderedSet listOfShortcuts = myActionId2ListOfShortcuts.get(actionId); if (listOfShortcuts != null) { return listOfShortcuts; } else { - listOfShortcuts = new LinkedHashSet(); + listOfShortcuts = new OrderedSet(); } 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 shortcuts = myActionId2ListOfShortcuts.get(actionId); + OrderedSet 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 own = myActionId2ListOfShortcuts.get(actionId); + OrderedSet 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(2)); // It's a trick! After that parent's shortcuts are not added to the keymap + myActionId2ListOfShortcuts.put(id, new OrderedSet(2)); // It's a trick! After that parent's shortcuts are not added to the keymap ArrayList 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); } diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java index a10e6360d3c9..fdd72694b8bc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/ActionsTree.java @@ -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 "???"; diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeyboardShortcutDialog.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeyboardShortcutDialog.java index 0894b0dd7afc..b08cf0fdd087 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeyboardShortcutDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeyboardShortcutDialog.java @@ -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) { diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java index 36a5af720474..3df7eb33ef35 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/ui/KeymapPanel.java @@ -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> conflicts = mySelectedKeymap.getConflicts(actionId, keyboardShortcut); + Map> 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 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 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; } } diff --git a/platform/platform-impl/src/com/intellij/util/ui/CollectionModelEditor.java b/platform/platform-impl/src/com/intellij/util/ui/CollectionModelEditor.java index 395327aa653c..f1dd95078ec2 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/CollectionModelEditor.java +++ b/platform/platform-impl/src/com/intellij/util/ui/CollectionModelEditor.java @@ -130,10 +130,7 @@ public abstract class CollectionModelEditor mutable = itemEditor.clone(item, false); modifiedToOriginal.put(mutable, item); originalToModified.put(item, mutable); - - List 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 } } + protected void silentlyReplaceItem(@NotNull T oldItem, @NotNull T newItem, int index) { + // silently replace item + List 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(); diff --git a/platform/platform-impl/src/com/intellij/util/ui/ListModelEditorBase.java b/platform/platform-impl/src/com/intellij/util/ui/ListModelEditorBase.java index d46b94b73445..6ba98bf6648b 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/ListModelEditorBase.java +++ b/platform/platform-impl/src/com/intellij/util/ui/ListModelEditorBase.java @@ -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 extends CollectionModelEditor extends CollectionModelEditor { @NotNull final List 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); diff --git a/platform/util/src/com/intellij/util/containers/OrderedSet.java b/platform/util/src/com/intellij/util/containers/OrderedSet.java index 0ff04acbe345..e5ac58899dd9 100644 --- a/platform/util/src/com/intellij/util/containers/OrderedSet.java +++ b/platform/util/src/com/intellij/util/containers/OrderedSet.java @@ -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 extends ArrayList implements Set, RandomAccess this(ContainerUtil.canonicalStrategy()); } + public OrderedSet(@NotNull Collection set) { + super(set.size()); + + myHashSet = new OpenTHashSet(set.size()); + addAll(set); + } + public OrderedSet(@NotNull TObjectHashingStrategy hashingStrategy) { this(hashingStrategy, 4); } @@ -38,6 +45,12 @@ public class OrderedSet extends ArrayList implements Set, RandomAccess myHashSet = new OpenTHashSet(capacity, hashingStrategy); } + public OrderedSet(int capacity) { + super(capacity); + + myHashSet = new OpenTHashSet(capacity); + } + @Override public boolean removeAll(@NotNull Collection c) { boolean removed = false;