IDEA-161842 Key Mapping: Ctrl-Y not assigned to "Redo" is problematic

This commit is contained in:
Dmitry Batrak
2018-12-04 18:04:01 +03:00
parent 282efb3cfb
commit a674ff0247
4 changed files with 129 additions and 0 deletions
@@ -0,0 +1,109 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.editor.actions;
import com.intellij.application.options.schemes.SchemeNameGenerator;
import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionUtil;
import com.intellij.openapi.keymap.KeyMapBundle;
import com.intellij.openapi.keymap.Keymap;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.keymap.ex.KeymapManagerEx;
import com.intellij.openapi.ui.Messages;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
public class CtrlYActionChooser {
private static final String ASK_ABOUT_SHORTCUT = "ask.about.ctrl.y.shortcut";
@Nullable
private static Keymap getCurrentKeymap() {
KeymapManager keymapManager = KeymapManager.getInstance();
return keymapManager == null ? null : keymapManager.getActiveKeymap();
}
@NotNull
private static Keymap getRootKeymap(@NotNull Keymap keymap) {
while (keymap.canModify()) {
Keymap parent = keymap.getParent();
if (parent == null) {
break;
}
else {
keymap = parent;
}
}
return keymap;
}
private static boolean isCtrlY(AWTEvent event) {
if (!(event instanceof KeyEvent)) return false;
KeyEvent keyEvent = (KeyEvent)event;
int modifiers = keyEvent.getModifiers();
return (keyEvent.getKeyCode() == KeyEvent.VK_Y &&
((modifiers & InputEvent.CTRL_MASK) != 0) &&
((modifiers & InputEvent.SHIFT_MASK) == 0) &&
((modifiers & InputEvent.ALT_GRAPH_MASK) == 0) &&
((modifiers & InputEvent.ALT_MASK) == 0) &&
((modifiers & InputEvent.META_MASK) == 0));
}
private static void patchKeymap(@NotNull Keymap currentKeymap) {
KeymapManager keymapManager = KeymapManager.getInstance();
assert keymapManager instanceof KeymapManagerEx;
Keymap[] allKeymaps = ((KeymapManagerEx)keymapManager).getAllKeymaps();
String name = SchemeNameGenerator.getUniqueName(KeyMapBundle.message("keymap.with.patched.redo.name",
currentKeymap.getPresentableName()),
n -> ContainerUtil.exists(allKeymaps, t -> n.equals(t.getName()) ||
n.equals(t.getPresentableName())));
Keymap newKeymap = currentKeymap.deriveKeymap(name);
KeyboardShortcut shortcut = KeyboardShortcut.fromString("control Y");
newKeymap.removeShortcut(IdeActions.ACTION_EDITOR_DELETE_LINE, shortcut);
newKeymap.addShortcut(IdeActions.ACTION_REDO, shortcut);
((KeymapManagerEx)keymapManager).getSchemeManager().addScheme(newKeymap);
((KeymapManagerEx)keymapManager).setActiveKeymap(newKeymap);
}
private static void invokeRedo(DataContext dataContext) {
AnAction action = ActionManager.getInstance().getAction(IdeActions.ACTION_REDO);
if (action != null) ActionUtil.invokeAction(action, dataContext, ActionPlaces.MAIN_MENU, null, null);
}
public static boolean isCurrentShortcutOk(DataContext dataContext) {
if (!PropertiesComponent.getInstance().isValueSet(ASK_ABOUT_SHORTCUT)) return true;
Keymap keymap = getCurrentKeymap();
if (keymap == null) return true;
Keymap rootKeymap = getRootKeymap(keymap);
if (!KeymapManager.DEFAULT_IDEA_KEYMAP.equals(rootKeymap.getName())) return true;
AWTEvent event = IdeEventQueue.getInstance().getTrueCurrentEvent();
if (!isCtrlY(event)) return true;
int savedCount = IdeEventQueue.getInstance().getEventCount();
int choice = Messages.showDialog(KeyMapBundle.message("keymap.patch.dialog.message"), KeyMapBundle.message("keymap.patch.dialog.title"),
new String[]{
KeyMapBundle.message("keymap.patch.dialog.redo.option"),
KeyMapBundle.message("keymap.patch.dialog.delete.line.option"),
KeyMapBundle.message("keymap.patch.dialog.cancel.option")
}, -1, Messages.getInformationIcon(), null);
IdeEventQueue.getInstance().setEventCount(savedCount); // keep data context valid after showing modal dialog
if (choice < 0 || choice > 1) return false;
PropertiesComponent.getInstance().unsetValue(ASK_ABOUT_SHORTCUT);
if (choice == 1) return true;
patchKeymap(keymap);
invokeRedo(dataContext);
return false;
}
public static void askAboutShortcut() {
PropertiesComponent.getInstance().setValue(ASK_ABOUT_SHORTCUT, true);
}
}
@@ -27,6 +27,8 @@ import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler;
import com.intellij.openapi.editor.ex.util.EditorUtil;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
@@ -37,6 +39,11 @@ public class DeleteLineAction extends TextComponentEditorAction {
}
private static class Handler extends EditorWriteActionHandler {
@Override
public void doExecute(@NotNull Editor editor, @Nullable Caret caret, DataContext dataContext) {
if (CtrlYActionChooser.isCurrentShortcutOk(dataContext)) super.doExecute(editor, caret, dataContext);
}
@Override
public void executeWriteAction(final Editor editor, Caret caret, DataContext dataContext) {
FeatureUsageTracker.getInstance().triggerFeatureUsed("editor.delete.line");
@@ -6,10 +6,12 @@ import com.intellij.configurationStore.SchemeDataHolder
import com.intellij.ide.WelcomeWizardUtil
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ConfigImportHelper
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.RoamingType
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.intellij.openapi.editor.actions.CtrlYActionChooser
import com.intellij.openapi.keymap.Keymap
import com.intellij.openapi.keymap.KeymapManagerListener
import com.intellij.openapi.keymap.ex.KeymapManagerEx
@@ -65,6 +67,10 @@ class KeymapManagerImpl(defaultKeymap: DefaultKeymap, factory: SchemeManagerFact
schemeManager.loadSchemes()
ourKeymapManagerInitialized = true
if (ConfigImportHelper.isFirstSession() && !ConfigImportHelper.isConfigImported()) {
CtrlYActionChooser.askAboutShortcut();
}
}
private fun fireActiveKeymapChanged(newScheme: Keymap?) {
@@ -101,3 +101,10 @@ filter.first.stroke.input=First Stroke:
filter.second.stroke.input=Second Stroke:
filter.shortcut.action.text=Find Actions by Shortcut
disable.mnemonic.in.controls.check.box=Disable mnemonics in controls
keymap.with.patched.redo.name={0} Proper Redo
keymap.patch.dialog.title=Map Action to Ctrl+Y
# suppress inspection "TrailingSpacesInProperty"
keymap.patch.dialog.message=Use Ctrl+Y to Redo or to Delete Line?\n\nThis behavior can be changed later in Settings | Keymap.\n
keymap.patch.dialog.redo.option=Redo
keymap.patch.dialog.delete.line.option=Delete Line
keymap.patch.dialog.cancel.option=Cancel