IDEA-146427 Entering and searching keyboard and mouse shortcuts in Settings | Keymap

This commit is contained in:
Sergey Malenkov
2015-11-06 16:09:08 +03:00
parent 865f0629c9
commit c5cf3ac835
11 changed files with 405 additions and 557 deletions
@@ -15,260 +15,64 @@
*/
package com.intellij.openapi.keymap.impl.ui;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.KeyboardShortcut;
import com.intellij.openapi.actionSystem.ex.QuickList;
import com.intellij.openapi.help.HelpManager;
import com.intellij.openapi.keymap.KeyMapBundle;
import com.intellij.openapi.keymap.Keymap;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.JBColor;
import com.intellij.ui.ScrollPaneFactory;
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;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.awt.BorderLayout;
import java.awt.Component;
import java.util.Collection;
import javax.swing.JComponent;
import javax.swing.JPanel;
public class KeyboardShortcutDialog extends DialogWrapper {
private StrokePanel myFirstStrokePanel;
private StrokePanel mySecondStrokePanel;
private final JCheckBox myEnableSecondKeystroke;
private final JLabel myKeystrokePreview;
private final JTextArea myConflictInfoArea;
private Keymap myKeymap;
private final String myActionId;
private final Group myMainGroup;
/**
* @author Sergey.Malenkov
*/
final class KeyboardShortcutDialog extends ShortcutDialog<KeyboardShortcut> {
private final JComponent myPreferredFocusedComponent;
KeyboardShortcutDialog(Component parent) {
super(parent, "keyboard.shortcut.dialog.title", new KeyboardShortcutPanel(new BorderLayout()));
KeyboardShortcutPanel panel = (KeyboardShortcutPanel)myShortcutPanel;
myPreferredFocusedComponent = panel.myFirstStroke;
JPanel inner = new JPanel(new BorderLayout());
inner.add(BorderLayout.CENTER, panel.mySecondStroke);
inner.add(BorderLayout.WEST, panel.mySecondStrokeEnable);
panel.add(BorderLayout.NORTH, panel.myFirstStroke);
panel.add(BorderLayout.SOUTH, inner);
panel.setBorder(JBUI.Borders.empty(0, 10));
panel.mySecondStrokeEnable.setSelected(true);
panel.mySecondStrokeEnable.setText(KeyMapBundle.message("dialog.enable.second.stroke.checkbox"));
public KeyboardShortcutDialog(Component component, String actionId, final QuickList[] quickLists) {
super(component, true);
setTitle(KeyMapBundle.message("keyboard.shortcut.dialog.title"));
myActionId = actionId;
final Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(component));
myMainGroup = ActionsTreeUtil.createMainGroup(project, myKeymap, quickLists, null, false, null); //without current filter
myEnableSecondKeystroke = new JCheckBox();
UIUtil.applyStyle(UIUtil.ComponentStyle.SMALL, myEnableSecondKeystroke);
myEnableSecondKeystroke.setBorder(new EmptyBorder(4, 0, 0, 2));
myEnableSecondKeystroke.setFocusable(false);
myKeystrokePreview = new JLabel(" ");
myConflictInfoArea = new JTextArea("");
myConflictInfoArea.setFocusable(false);
init();
}
@NotNull
protected Action[] createActions(){
return new Action[]{getOKAction(),getCancelAction(),getHelpAction()};
@Override
protected String getHelpId() {
return "preferences.keymap.shortcut";
}
protected JComponent createCenterPanel() {
JPanel panel = new JPanel(new GridBagLayout());
// First stroke
myFirstStrokePanel = new StrokePanel(KeyMapBundle.message("first.stroke.panel.title"));
panel.add(
myFirstStrokePanel,
new GridBagConstraints(0,0,2,1,1,0,GridBagConstraints.CENTER,GridBagConstraints.HORIZONTAL,new Insets(0,0,0,0),0,0)
);
// Second stroke panel
panel.add(
myEnableSecondKeystroke,
new GridBagConstraints(0,1,1,1,0,0,GridBagConstraints.NORTHWEST,GridBagConstraints.NONE,new Insets(0,0,0,0),0,0)
);
mySecondStrokePanel = new StrokePanel(KeyMapBundle.message("second.stroke.panel.title"));
panel.add(
mySecondStrokePanel,
new GridBagConstraints(1,1,1,1,1,0,GridBagConstraints.NORTHWEST,GridBagConstraints.HORIZONTAL,new Insets(0,0,0,0),0,0)
);
// Shortcut preview
JPanel previewPanel = new JPanel(new BorderLayout());
previewPanel.setBorder(IdeBorderFactory.createTitledBorder(KeyMapBundle.message("shortcut.preview.ide.border.factory.title"), true));
previewPanel.add(myKeystrokePreview);
panel.add(
previewPanel,
new GridBagConstraints(0,2,2,1,1,0,GridBagConstraints.CENTER,GridBagConstraints.HORIZONTAL,new Insets(0,0,0,0),0,0)
);
// Conflicts
JPanel conflictsPanel = new JPanel(new BorderLayout());
conflictsPanel.setBorder(IdeBorderFactory.createTitledBorder(KeyMapBundle.message("conflicts.ide.border.factory.title"),
true));
myConflictInfoArea.setEditable(false);
myConflictInfoArea.setBackground(panel.getBackground());
myConflictInfoArea.setLineWrap(true);
myConflictInfoArea.setWrapStyleWord(true);
final JScrollPane conflictInfoScroll = ScrollPaneFactory.createScrollPane(myConflictInfoArea);
conflictInfoScroll.setPreferredSize(JBUI.size(260, 60));
conflictInfoScroll.setBorder(null);
conflictsPanel.add(conflictInfoScroll);
panel.add(
conflictsPanel,
new GridBagConstraints(0,3,2,1,1,1,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(0,0,0,0),0,0)
);
myEnableSecondKeystroke.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
handleSecondKey();
updateCurrentKeyStrokeInfo();
/** TODO[anton]???? */
if (myEnableSecondKeystroke.isSelected()) {
mySecondStrokePanel.getShortcutTextField().requestFocus();
}
else {
myFirstStrokePanel.getShortcutTextField().requestFocus();
}
}
});
return panel;
@Override
public JComponent getPreferredFocusedComponent() {
return myPreferredFocusedComponent;
}
public JComponent getPreferredFocusedComponent(){
return IdeFocusTraversalPolicy.getPreferredFocusedComponent(myFirstStrokePanel);
@Override
KeyboardShortcut toShortcut(Object value) {
return value instanceof KeyboardShortcut ? (KeyboardShortcut)value : null;
}
public void setData(Keymap keymap, KeyboardShortcut shortcut) {
myKeymap = keymap;
myEnableSecondKeystroke.setSelected(false);
if (shortcut != null) {
myFirstStrokePanel.getShortcutTextField().setKeyStroke(shortcut.getFirstKeyStroke());
if (shortcut.getSecondKeyStroke() != null) {
myEnableSecondKeystroke.setSelected(true);
mySecondStrokePanel.getShortcutTextField().setKeyStroke(shortcut.getSecondKeyStroke());
}
}
handleSecondKey();
updateCurrentKeyStrokeInfo();
@Override
Collection<String> getConflicts(KeyboardShortcut shortcut, String actionId, Keymap keymap) {
return keymap.getConflicts(actionId, shortcut).keySet();
}
private void updateCurrentKeyStrokeInfo() {
if (myConflictInfoArea == null || myKeystrokePreview == null){
return;
}
myConflictInfoArea.setText(null);
myKeystrokePreview.setText(" ");
if (myKeymap == null){
return;
}
KeyboardShortcut keyboardShortcut = getKeyboardShortcut();
if (keyboardShortcut == null){
return;
}
String strokeText = getTextByKeyStroke(keyboardShortcut.getFirstKeyStroke());
String suffixText = getTextByKeyStroke(keyboardShortcut.getSecondKeyStroke());
if(suffixText != null && suffixText.length() > 0) {
strokeText += ',' + suffixText;
}
myKeystrokePreview.setText(strokeText);
StringBuilder buffer = new StringBuilder();
Map<String, ArrayList<KeyboardShortcut>> conflicts = myKeymap.getConflicts(myActionId, keyboardShortcut);
Set<String> keys = conflicts.keySet();
String[] actionIds = ArrayUtil.toStringArray(keys);
boolean loaded = true;
for (String actionId : actionIds) {
String actionPath = myMainGroup.getActionQualifiedPath(actionId);
if (actionPath == null) {
loaded = false;
}
if (buffer.length() > 1) {
buffer.append('\n');
}
buffer.append('[');
buffer.append(actionPath != null ? actionPath : actionId);
buffer.append(']');
}
if (buffer.length() == 0) {
myConflictInfoArea.setForeground(UIUtil.getTextAreaForeground());
myConflictInfoArea.setText(KeyMapBundle.message("no.conflict.info.message"));
}
else {
myConflictInfoArea.setForeground(JBColor.RED);
if (loaded) {
myConflictInfoArea.setText(KeyMapBundle.message("assigned.to.info.message", buffer.toString()));
}
else {
myConflictInfoArea.setText("Assigned to " + buffer.toString() + " which is now not loaded but may be loaded later");
}
}
}
private void handleSecondKey() {
mySecondStrokePanel.setEnabled(myEnableSecondKeystroke.isSelected());
}
@Nullable
public KeyboardShortcut getKeyboardShortcut() {
KeyStroke firstStroke = myFirstStrokePanel.getKeyStroke();
return firstStroke == null ? null : new KeyboardShortcut(firstStroke, myEnableSecondKeystroke.isSelected() ? mySecondStrokePanel.getKeyStroke() : null);
}
static String getTextByKeyStroke(KeyStroke keyStroke) {
if(keyStroke == null) {
return "";
}
return KeymapUtil.getKeystrokeText(keyStroke);
}
protected void doHelpAction() {
HelpManager.getInstance().invokeHelp("preferences.keymap.shortcut");
}
private class StrokePanel extends JPanel {
private final ShortcutTextField myShortcutTextField;
public StrokePanel(String borderText) {
setLayout(new BorderLayout());
setBorder(IdeBorderFactory.createTitledBorder(borderText, false));
myShortcutTextField = new ShortcutTextField(){
protected void updateCurrentKeyStrokeInfo() {
KeyboardShortcutDialog.this.updateCurrentKeyStrokeInfo();
}
};
add(myShortcutTextField);
}
public ShortcutTextField getShortcutTextField() {
return myShortcutTextField;
}
public void setEnabled(boolean state) {
myShortcutTextField.setEnabled(state);
repaint();
}
public KeyStroke getKeyStroke() {
return myShortcutTextField.getKeyStroke();
}
@Override
String getActionPath(String actionId) {
String actionPath = super.getActionPath(actionId);
return actionPath != null ? actionPath : actionId;
}
}
@@ -16,7 +16,6 @@
package com.intellij.openapi.keymap.impl.ui;
import com.intellij.openapi.actionSystem.KeyboardShortcut;
import com.intellij.openapi.actionSystem.Shortcut;
import java.awt.LayoutManager;
import java.awt.event.ItemEvent;
@@ -24,19 +23,16 @@ import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
/**
* @author Sergey.Malenkov
*/
final class KeyboardShortcutPanel extends JPanel {
final class KeyboardShortcutPanel extends ShortcutPanel<KeyboardShortcut> {
final ShortcutTextField myFirstStroke = new ShortcutTextField();
final ShortcutTextField mySecondStroke = new ShortcutTextField();
final JCheckBox mySecondStrokeEnable = new JCheckBox();
private KeyboardShortcut myShortcut;
private final ItemListener myItemListener = new ItemListener() {
@Override
public void itemStateChanged(ItemEvent event) {
@@ -50,37 +46,35 @@ final class KeyboardShortcutPanel extends JPanel {
private final PropertyChangeListener myPropertyListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
setShortcut(newShortcut());
if (null == myFirstStroke.getKeyStroke()) {
myFirstStroke.requestFocus();
if (KeyboardShortcutPanel.this != event.getSource()) {
setShortcut(newShortcut());
if (null == myFirstStroke.getKeyStroke()) {
myFirstStroke.requestFocus();
}
else if (null == mySecondStroke.getKeyStroke() && mySecondStrokeEnable.isSelected()) {
mySecondStroke.requestFocus();
}
}
else if (null == mySecondStroke.getKeyStroke() && mySecondStrokeEnable.isSelected()) {
mySecondStroke.requestFocus();
else if (event.getNewValue() instanceof KeyboardShortcut) {
KeyboardShortcut shortcut = (KeyboardShortcut)event.getNewValue();
myFirstStroke.setKeyStroke(shortcut.getFirstKeyStroke());
mySecondStroke.setKeyStroke(shortcut.getSecondKeyStroke());
}
else {
myFirstStroke.setKeyStroke(null);
mySecondStroke.setKeyStroke(null);
}
}
};
KeyboardShortcutPanel(LayoutManager layout) {
super(layout);
addPropertyChangeListener("shortcut", myPropertyListener);
myFirstStroke.addPropertyChangeListener("keyStroke", myPropertyListener);
mySecondStroke.addPropertyChangeListener("keyStroke", myPropertyListener);
mySecondStrokeEnable.addItemListener(myItemListener);
}
KeyboardShortcut getShortcut() {
return myShortcut;
}
void setShortcut(KeyboardShortcut shortcut) {
Shortcut old = myShortcut;
if (old == null || !old.equals(shortcut)) {
myShortcut = shortcut;
myFirstStroke.setKeyStroke(shortcut == null ? null : shortcut.getFirstKeyStroke());
mySecondStroke.setKeyStroke(shortcut == null ? null : shortcut.getSecondKeyStroke());
firePropertyChange("shortcut", old, shortcut);
}
}
private KeyboardShortcut newShortcut() {
KeyStroke key = myFirstStroke.getKeyStroke();
return key == null ? null : new KeyboardShortcut(key, !mySecondStrokeEnable.isSelected() ? null : mySecondStroke.getKeyStroke());
@@ -36,7 +36,6 @@ 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.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
@@ -479,13 +478,8 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
@NotNull Keymap keymap,
@NotNull Component parent,
@NotNull QuickList[] quickLists) {
KeyboardShortcutDialog dialog = new KeyboardShortcutDialog(parent, actionId, quickLists);
dialog.setData(keymap, shortcut instanceof KeyboardShortcut ? (KeyboardShortcut)shortcut : null);
if (!dialog.showAndGet()) {
return;
}
KeyboardShortcut keyboardShortcut = dialog.getKeyboardShortcut();
KeyboardShortcutDialog dialog = new KeyboardShortcutDialog(parent);
KeyboardShortcut keyboardShortcut = dialog.showAndGet(shortcut, actionId, keymap, quickLists);
if (keyboardShortcut == null) {
return;
}
@@ -535,22 +529,8 @@ public class KeymapPanel extends JPanel implements SearchableConfigurable, Confi
Keymap keymap = createKeymapCopyIfNeeded();
MouseShortcut mouseShortcut = shortcut instanceof MouseShortcut ? (MouseShortcut)shortcut : null;
Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(this));
MouseShortcutDialog dialog = new MouseShortcutDialog(
this,
mouseShortcut,
keymap,
actionId,
ActionsTreeUtil.createMainGroup(project, keymap, myQuickLists, null, true, null),
restrictions
);
if (!dialog.showAndGet()) {
return;
}
mouseShortcut = dialog.getMouseShortcut();
MouseShortcutDialog dialog = new MouseShortcutDialog(this, restrictions.allowMouseDoubleClick);
MouseShortcut mouseShortcut = dialog.showAndGet(shortcut, actionId, keymap, myQuickLists);
if (mouseShortcut == null) {
return;
}
@@ -17,275 +17,68 @@ package com.intellij.openapi.keymap.impl.ui;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.MouseShortcut;
import com.intellij.openapi.actionSystem.Shortcut;
import com.intellij.openapi.keymap.KeyMapBundle;
import com.intellij.openapi.keymap.Keymap;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.keymap.impl.ShortcutRestrictions;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.JBColor;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import org.intellij.lang.annotations.JdkConstants;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.BorderLayout;
import java.awt.Component;
import java.util.Arrays;
import java.util.Collection;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
/**
* @author Vladimir Kondratyev
* @author Sergey.Malenkov
*/
class MouseShortcutDialog extends DialogWrapper{
private final Keymap myKeymap;
private final String myActionId;
private final Group myMainGroup;
final class MouseShortcutDialog extends ShortcutDialog<MouseShortcut> {
private final JLabel myText = new JLabel("", SwingConstants.CENTER);
private final JRadioButton myRbSingleClick;
private final JRadioButton myRbDoubleClick;
private final JLabel myLblPreview;
private final JLabel myClickPad;
private final JTextArea myTarConflicts;
MouseShortcutDialog(Component component, boolean allowDoubleClick) {
super(component, "mouse.shortcut.dialog.title", new MouseShortcutPanel(allowDoubleClick));
private final MouseShortcutConsumer myShortcutConsumer;
private int myButton;
@JdkConstants.InputEventMask private int myModifiers;
/**
* @param shortcut dialog will be initialized with this <code>shortcut</code>. It can be <code>null</code>
* if dialog is used to create new mouse shortcut.
*/
public MouseShortcutDialog(
JComponent parentComponent,
MouseShortcut shortcut,
@NotNull Keymap keymap,
@NotNull String actiondId,
@NotNull Group mainGroup,
@NotNull ShortcutRestrictions restrictions
){
super(parentComponent,true);
setTitle(KeyMapBundle.message("mouse.shortcut.dialog.title"));
myKeymap=keymap;
myActionId=actiondId;
myMainGroup=mainGroup;
myRbSingleClick=new JRadioButton(KeyMapBundle.message("mouse.shortcut.dialog.single.click.radio"));
myRbDoubleClick=new JRadioButton(KeyMapBundle.message("mouse.shortcut.dialog.double.click.radio"));
ButtonGroup buttonGroup=new ButtonGroup();
buttonGroup.add(myRbSingleClick);
buttonGroup.add(myRbDoubleClick);
myLblPreview=new JLabel(" ");
myTarConflicts=new JTextArea();
myTarConflicts.setFocusable(false);
myTarConflicts.setEditable(false);
myTarConflicts.setBackground(UIUtil.getPanelBackground());
myTarConflicts.setLineWrap(true);
myTarConflicts.setWrapStyleWord(true);
if(shortcut!=null){
if(shortcut.getClickCount()==1){
myRbSingleClick.setSelected(true);
}else{
myRbDoubleClick.setSelected(true);
}
myButton=shortcut.getButton();
myModifiers=shortcut.getModifiers();
}else{
myRbSingleClick.setSelected(true);
myButton=-1;
myModifiers=-1;
}
myRbDoubleClick.setEnabled(restrictions.allowMouseDoubleClick);
updatePreviewAndConflicts();
myClickPad = new JLabel(
KeyMapBundle.message("mouse.shortcut.label"),
AllIcons.General.Mouse, SwingConstants.LEADING
);
myShortcutConsumer = new MouseShortcutConsumer(shortcut) {
@Override
public void consume(MouseShortcut shortcut) {
myButton = shortcut.getButton();
myModifiers = shortcut.getModifiers();
updatePreviewAndConflicts();
}
};
myClickPad.addHierarchyListener(myShortcutConsumer);
myShortcutPanel.add(BorderLayout.NORTH, new JLabel(AllIcons.General.Mouse, SwingConstants.CENTER));
myShortcutPanel.add(BorderLayout.CENTER, myText);
myShortcutPanel.setBorder(BorderFactory.createCompoundBorder(
JBUI.Borders.customLine(MouseShortcutPanel.BORDER, 1, 0, 1, 0),
JBUI.Borders.empty(20)
));
init();
}
/**
* @return created/edited shortcut. Returns <code>null</code> if shortcut is invalid.
*/
public MouseShortcut getMouseShortcut(){
if (myButton > 3 && myButton != MouseShortcut.BUTTON_WHEEL_UP && myButton != MouseShortcut.BUTTON_WHEEL_DOWN && getClickCount() == 2) {
return null;
}
if(myButton!=-1 && myModifiers!=-1){
return new MouseShortcut(myButton,myModifiers,getClickCount());
}else{
return null;
}
}
@Override
protected String getHelpId() {
return "preferences.mouse.shortcut";
}
protected JComponent createCenterPanel(){
JPanel panel=new JPanel(new GridBagLayout());
// Single/Double click
JPanel clickCountPanel=new JPanel(new GridBagLayout());
clickCountPanel.setBorder(IdeBorderFactory.createTitledBorder(
KeyMapBundle.message("mouse.shortcut.dialog.click.count.border"), true));
panel.add(
clickCountPanel,
new GridBagConstraints(0,0,1,1,1,0,GridBagConstraints.CENTER,GridBagConstraints.HORIZONTAL,new Insets(0,0,0,0),0,0)
);
clickCountPanel.add(
myRbSingleClick,
new GridBagConstraints(0,0,1,1,1,0,GridBagConstraints.WEST,GridBagConstraints.NONE,new Insets(0,0,0,10),0,0)
);
clickCountPanel.add(
myRbDoubleClick,
new GridBagConstraints(1,0,1,1,1,0,GridBagConstraints.EAST,GridBagConstraints.NONE,new Insets(0,0,0,0),0,0)
);
ActionListener listener=new ActionListener(){
public void actionPerformed(ActionEvent e){
updatePreviewAndConflicts();
}
};
myRbSingleClick.addActionListener(listener);
myRbDoubleClick.addActionListener(listener);
// Click pad
panel.add(
myClickPad,
new GridBagConstraints(0,1,1,1,1,0,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(0,0,4,0),0,0)
);
myClickPad.setBorder(BorderFactory.createCompoundBorder(
IdeBorderFactory.createTitledBorder(KeyMapBundle.message("mouse.shortcut.dialog.click.pad.border"), true),
JBUI.Borders.empty(20, 0, 20, 20)));
// Shortcut preview
JPanel previewPanel=new JPanel(new GridBagLayout());
previewPanel.setBorder(IdeBorderFactory.createTitledBorder(
KeyMapBundle.message("mouse.shortcut.dialog.shortcut.preview.border"), true));
panel.add(
previewPanel,
new GridBagConstraints(0,2,1,1,1,0,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(0,0,4,0),0,0)
);
previewPanel.add(
myLblPreview,
new GridBagConstraints(0,0,1,1,1,1,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(2,2,2,2),0,0)
);
// Conflicts panel
JPanel conflictsPanel=new JPanel(new GridBagLayout());
conflictsPanel.setBorder(IdeBorderFactory.createTitledBorder(
KeyMapBundle.message("mouse.shortcut.dialog.conflicts.border"), true));
panel.add(
conflictsPanel,
new GridBagConstraints(0,3,1,1,1,1,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(0,0,0,0),0,0)
);
myTarConflicts.setPreferredSize(JBUI.size(260,60));
JScrollPane scrollPane= ScrollPaneFactory.createScrollPane(myTarConflicts);
scrollPane.setBorder(null);
conflictsPanel.add(
scrollPane,
new GridBagConstraints(0,0,1,1,1,1,GridBagConstraints.CENTER,GridBagConstraints.BOTH,new Insets(0,0,0,0),0,0)
);
return panel;
@Override
MouseShortcut toShortcut(Object value) {
return value instanceof MouseShortcut ? (MouseShortcut)value : null;
}
private int getClickCount() {
return myRbSingleClick.isSelected() ? 1 : 2;
}
/**
* Updates all UI controls
*/
private void updatePreviewAndConflicts(){
if(myButton==-1||myModifiers==-1){
return;
}
myTarConflicts.setText(null);
// Set text into preview area
// empty string should have same height
myLblPreview.setText(KeymapUtil.getMouseShortcutText(myButton,myModifiers,getClickCount()) + " ");
// Detect conflicts
final MouseShortcut mouseShortcut=new MouseShortcut(myButton,myModifiers,getClickCount());
if (myButton > 3 && getClickCount() == 2) {
myTarConflicts.setForeground(JBColor.RED);
myTarConflicts.setText(KeyMapBundle.message("mouse.shortcut.dialog.side.buttons.with.double.click", myButton));
return;
}
StringBuilder buffer = new StringBuilder();
String[] actionIds = myKeymap.getActionIds(mouseShortcut);
for (String actionId : actionIds) {
if (actionId.equals(myActionId)) {
continue;
}
String actionPath = myMainGroup.getActionQualifiedPath(actionId);
// actionPath == null for editor actions having corresponding $-actions
if (actionPath == null) {
continue;
}
Shortcut[] shortcuts = myKeymap.getShortcuts(actionId);
for (Shortcut shortcut1 : shortcuts) {
if (!(shortcut1 instanceof MouseShortcut)) {
continue;
}
MouseShortcut shortcut = (MouseShortcut)shortcut1;
if (shortcut.getButton() != mouseShortcut.getButton() || shortcut.getModifiers() != mouseShortcut.getModifiers()) {
continue;
}
if (buffer.length() > 1) {
buffer.append('\n');
}
buffer.append('[');
buffer.append(actionPath);
buffer.append(']');
break;
}
}
if (buffer.length() == 0) {
myTarConflicts.setForeground(UIUtil.getTextAreaForeground());
myTarConflicts.setText(KeyMapBundle.message("mouse.shortcut.dialog.no.conflicts.area"));
@Override
void setShortcut(MouseShortcut shortcut) {
super.setShortcut(shortcut);
if (shortcut == null) {
myText.setForeground(MouseShortcutPanel.FOREGROUND);
myText.setText(KeyMapBundle.message("dialog.mouse.pad.default.text"));
}
else {
myTarConflicts.setForeground(JBColor.RED);
myTarConflicts.setText(KeyMapBundle.message("mouse.shortcut.dialog.assigned.to.area", buffer.toString()));
myText.setForeground(UIUtil.getLabelForeground());
myText.setText(KeyMapBundle.message("dialog.mouse.pad.shortcut.text", KeymapUtil.getMouseShortcutText(
shortcut.getButton(),
shortcut.getModifiers(),
shortcut.getClickCount())));
}
}
@Override
Collection<String> getConflicts(MouseShortcut shortcut, String actionId, Keymap keymap) {
return Arrays.asList(keymap.getActionIds(shortcut));
}
}
@@ -22,18 +22,16 @@ import java.awt.BorderLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import javax.swing.JPanel;
/**
* @author Sergey.Malenkov
*/
final class MouseShortcutPanel extends JPanel {
final class MouseShortcutPanel extends ShortcutPanel<MouseShortcut> {
static final JBColor FOREGROUND = new JBColor(0x8C8C8C, 0x8C8C8C);
static final JBColor BACKGROUND = new JBColor(0xF5F5F5, 0x4B4F52);
static final JBColor BORDER = new JBColor(0xDEDEDE, 0x383B3D);
private MouseShortcut myShortcut;
private final int myClickCount;
private final MouseAdapter myMouseListener = new MouseAdapter() {
@Override
public void mouseWheelMoved(MouseWheelEvent event) {
@@ -46,35 +44,26 @@ final class MouseShortcutPanel extends JPanel {
int button = MouseShortcut.getButton(event);
int clickCount = event instanceof MouseWheelEvent ? 1 : event.getClickCount();
if (0 <= button && clickCount < 3) {
if (0 <= button && clickCount <= myClickCount) {
int modifiers = event.getModifiersEx();
if (myShortcut == null
|| button != myShortcut.getButton()
|| modifiers != myShortcut.getModifiers()
|| clickCount != myShortcut.getClickCount()) {
setShortcut(new MouseShortcut(button, modifiers, clickCount));
}
setShortcut(new MouseShortcut(button, modifiers, clickCount));
}
}
};
MouseShortcutPanel() {
MouseShortcutPanel(boolean allowDoubleClick) {
super(new BorderLayout());
myClickCount = allowDoubleClick ? 2 : 1;
addMouseListener(myMouseListener);
addMouseWheelListener(myMouseListener);
setBackground(BACKGROUND);
setOpaque(true);
}
MouseShortcut getShortcut() {
return myShortcut;
}
void setShortcut(MouseShortcut shortcut) {
MouseShortcut old = myShortcut;
MouseShortcut old = getShortcut();
if (old != null || shortcut != null) {
myShortcut = shortcut;
firePropertyChange("shortcut", old, shortcut);
super.setShortcut(shortcut);
}
}
}
@@ -0,0 +1,185 @@
/*
* 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.
* 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.icons.AllIcons;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.Shortcut;
import com.intellij.openapi.actionSystem.ex.QuickList;
import com.intellij.openapi.keymap.KeyMapBundle;
import com.intellij.openapi.keymap.Keymap;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.SimpleColoredComponent;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.components.GradientViewport;
import com.intellij.ui.components.JBPanel;
import com.intellij.ui.components.panels.VerticalLayout;
import com.intellij.util.ui.ButtonlessScrollBarUI;
import com.intellij.util.ui.JBUI;
import org.jetbrains.annotations.Nullable;
import java.awt.BorderLayout;
import java.awt.Component;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
/**
* @author Sergey.Malenkov
*/
abstract class ShortcutDialog<T extends Shortcut> extends DialogWrapper {
private final SimpleColoredComponent myAction = new SimpleColoredComponent();
private final JBPanel myConflictsContainer = new JBPanel(new VerticalLayout(0));
private final JBPanel myConflictsPanel = new JBPanel(new BorderLayout())
.withBorder(JBUI.Borders.empty(5, 10, 0, 10))
.withPreferredHeight(64)
.withMinimumHeight(64);
protected final ShortcutPanel<T> myShortcutPanel;
private final Project myProject;
private String myActionId;
private Keymap myKeymap;
private Group myGroup;
ShortcutDialog(Component parent, String title, ShortcutPanel<T> panel) {
super(parent, true);
myShortcutPanel = panel;
myProject = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(parent));
setTitle(KeyMapBundle.message(title));
}
String getActionPath(String actionId) {
return myGroup == null ? null : myGroup.getActionQualifiedPath(actionId);
}
abstract Collection<String> getConflicts(T shortcut, String actionId, Keymap keymap);
abstract T toShortcut(Object value);
void setShortcut(T shortcut) {
if (!equal(shortcut, myShortcutPanel.getShortcut())) {
myShortcutPanel.setShortcut(shortcut);
}
myConflictsContainer.removeAll();
if (shortcut != null) {
for (String id : getConflicts(shortcut, myActionId, myKeymap)) {
String path = id.equals(myActionId) ? null : getActionPath(id);
if (path != null) {
SimpleColoredComponent component = new SimpleColoredComponent();
fill(component, id, path);
myConflictsContainer.add(VerticalLayout.TOP, component);
}
}
myConflictsPanel.revalidate();
}
myConflictsPanel.setVisible(0 < myConflictsContainer.getComponentCount());
}
T showAndGet(Shortcut shortcut, String id, Keymap keymap, QuickList... lists) {
myActionId = id;
myKeymap = keymap;
myGroup = ActionsTreeUtil.createMainGroup(myProject, keymap, lists, null, false, null);
fill(myAction, id, getActionPath(id));
setShortcut(toShortcut(shortcut));
return showAndGet() ? myShortcutPanel.getShortcut() : null;
}
@Nullable
@Override
protected Border createContentPaneBorder() {
return JBUI.Borders.empty();
}
@Nullable
@Override
protected JComponent createSouthPanel() {
JComponent panel = super.createSouthPanel();
if (panel != null) {
panel.setBorder(JBUI.Borders.empty(8, 12));
}
return panel;
}
@Nullable
@Override
protected JComponent createNorthPanel() {
myAction.setIpad(JBUI.insets(10, 10, 5, 10));
myShortcutPanel.addPropertyChangeListener("shortcut", new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
setShortcut(toShortcut(event.getNewValue()));
}
});
JBPanel result = new JBPanel(new BorderLayout()).withPreferredWidth(300).withMinimumWidth(200);
result.add(BorderLayout.NORTH, myAction);
result.add(BorderLayout.SOUTH, myShortcutPanel);
return result;
}
@Override
protected JComponent createCenterPanel() {
JLabel icon = new JLabel(AllIcons.General.BalloonWarning);
icon.setVerticalAlignment(SwingConstants.TOP);
JLabel label = new JLabel(KeyMapBundle.message("dialog.conflicts.text"));
label.setBorder(JBUI.Borders.emptyLeft(2));
JScrollPane scroll = ScrollPaneFactory.createScrollPane(null, true);
scroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setViewport(new GradientViewport(myConflictsContainer, JBUI.insets(5), false));
scroll.getVerticalScrollBar().setUI(ButtonlessScrollBarUI.createTransparent());
scroll.getVerticalScrollBar().setUnitIncrement(JBUI.scale(10));
JBPanel panel = new JBPanel(new BorderLayout());
panel.add(BorderLayout.NORTH, label);
panel.add(BorderLayout.CENTER, scroll);
myConflictsPanel.add(BorderLayout.WEST, icon);
myConflictsPanel.add(BorderLayout.CENTER, panel);
myConflictsContainer.setOpaque(false);
return myConflictsPanel;
}
private static boolean equal(Shortcut newShortcut, Shortcut oldShortcut) {
return newShortcut == null ? oldShortcut == null : newShortcut.equals(oldShortcut);
}
private static void fill(SimpleColoredComponent component, String id, String path) {
if (path == null) {
component.append(id, SimpleTextAttributes.REGULAR_ITALIC_ATTRIBUTES);
}
else {
int index = path.lastIndexOf(" | ");
if (index < 0) {
component.append(path, SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
else {
component.append(path.substring(index + 3), SimpleTextAttributes.REGULAR_ATTRIBUTES);
component.append(" in ", SimpleTextAttributes.GRAYED_ATTRIBUTES);
component.append(path.substring(0, index), SimpleTextAttributes.GRAYED_ATTRIBUTES);
}
}
}
}
@@ -40,7 +40,7 @@ import javax.swing.event.ChangeListener;
*/
final class ShortcutFilteringPanel extends JPanel {
final KeyboardShortcutPanel myKeyboardPanel = new KeyboardShortcutPanel(new VerticalLayout(JBUI.scale(2)));
final MouseShortcutPanel myMousePanel = new MouseShortcutPanel();
final MouseShortcutPanel myMousePanel = new MouseShortcutPanel(true);
private Shortcut myShortcut;
private JBPopup myPopup;
@@ -0,0 +1,42 @@
/*
* 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.
* 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.openapi.actionSystem.Shortcut;
import java.awt.LayoutManager;
import javax.swing.JPanel;
/**
* @author Sergey.Malenkov
*/
class ShortcutPanel<T extends Shortcut> extends JPanel {
private T myShortcut;
ShortcutPanel(LayoutManager layout) {
super(layout);
}
T getShortcut() {
return myShortcut;
}
void setShortcut(T shortcut) {
T old = myShortcut;
myShortcut = shortcut;
firePropertyChange("shortcut", old, shortcut);
}
}
@@ -31,10 +31,10 @@ import javax.swing.text.DefaultCaret;
import java.awt.*;
import java.awt.event.KeyEvent;
public class ShortcutTextField extends JTextField {
public final class ShortcutTextField extends JTextField {
private KeyStroke myKeyStroke;
public ShortcutTextField() {
ShortcutTextField() {
enableEvents(AWTEvent.KEY_EVENT_MASK);
setFocusTraversalKeysEnabled(false);
putClientProperty("JTextField.variant", "search");
@@ -62,21 +62,17 @@ public class ShortcutTextField extends JTextField {
}
}
public void setKeyStroke(KeyStroke keyStroke) {
void setKeyStroke(KeyStroke keyStroke) {
KeyStroke old = myKeyStroke;
if (old != null || keyStroke != null) {
myKeyStroke = keyStroke;
super.setText(KeymapUtil.getKeystrokeText(keyStroke));
setCaretPosition(0);
updateCurrentKeyStrokeInfo();
firePropertyChange("keyStroke", old, keyStroke);
}
}
protected void updateCurrentKeyStrokeInfo() {
}
public KeyStroke getKeyStroke() {
KeyStroke getKeyStroke() {
return myKeyStroke;
}
@@ -96,6 +96,11 @@ mouse.wheel.rotate.down.shortcut.text={0}Wheel down
configuration.all.keymaps.should.have.non.empty.names.error.message=All keymaps should have non empty names
configuration.all.keymaps.should.have.unique.names.error.message=All keymaps should have unique names
dialog.enable.second.stroke.checkbox=Second stroke:
dialog.mouse.pad.default.text=<html><center>Enter a shortcut here: <br>single or double-click, scroll the wheel,<br> modify with Ctrl, Alt and Shift
dialog.mouse.pad.shortcut.text=<html><center> <br>{0}<br><br>
dialog.conflicts.text=Already assigned to:
filter.clear.action.text=Clear Filtering
filter.settings.popup.title=Find Shortcut
filter.enable.second.stroke.checkbox=Second stroke
@@ -16,6 +16,7 @@
package com.intellij.ui.components;
import com.intellij.util.ui.JBFont;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.components.JBComponent;
import javax.swing.*;
@@ -27,6 +28,12 @@ import java.awt.*;
*/
@SuppressWarnings("unchecked")
public class JBPanel<T extends JBPanel> extends JPanel implements JBComponent<T> {
private Integer myPreferredWidth;
private Integer myPreferredHeight;
private Integer myMaximumWidth;
private Integer myMaximumHeight;
private Integer myMinimumWidth;
private Integer myMinimumHeight;
public JBPanel(LayoutManager layout, boolean isDoubleBuffered) {
super(layout, isDoubleBuffered);
@@ -67,4 +74,57 @@ public class JBPanel<T extends JBPanel> extends JPanel implements JBComponent<T>
setOpaque(true);
return (T)this;
}
public T withPreferredWidth(int width) {
myPreferredWidth = width;
return (T)this;
}
public T withPreferredHeight(int height) {
myPreferredHeight = height;
return (T)this;
}
public T withMaximumWidth(int width) {
myMaximumWidth = width;
return (T)this;
}
public T withMaximumHeight(int height) {
myMaximumHeight = height;
return (T)this;
}
public T withMinimumWidth(int width) {
myMinimumWidth = width;
return (T)this;
}
public T withMinimumHeight(int height) {
myMinimumHeight = height;
return (T)this;
}
@Override
public Dimension getPreferredSize() {
return getSize(super.getPreferredSize(), myPreferredWidth, myPreferredHeight, isPreferredSizeSet());
}
@Override
public Dimension getMaximumSize() {
return getSize(super.getMaximumSize(), myMaximumWidth, myMaximumHeight, isMaximumSizeSet());
}
@Override
public Dimension getMinimumSize() {
return getSize(super.getMinimumSize(), myMinimumWidth, myMinimumHeight, isMinimumSizeSet());
}
private static Dimension getSize(Dimension size, Integer width, Integer height, boolean isSet) {
if (!isSet && size != null) {
if (width != null) size.width = JBUI.scale(width);
if (height != null) size.height = JBUI.scale(height);
}
return size;
}
}