/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: EditKeyBinding.java
*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.user.dialogs;
import com.sun.electric.Main;
import com.sun.electric.tool.user.KeyBindingManager;
import com.sun.electric.tool.user.menus.EMenuBar;
import com.sun.electric.tool.user.menus.EMenuItem;
import com.sun.electric.tool.user.ui.KeyBindings;
import com.sun.electric.tool.user.ui.KeyStrokePair;
import java.awt.event.InputEvent;
import java.util.Iterator;
import java.util.List;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.ListModel;
/**
* Class to manage binding of keys to pulldown menus.
*/
public class EditKeyBinding extends EDialog {
/** The MenuItem we are adding a key binding to */ private EMenuItem menuItem;
/** The MenuBar that item is a part of */ private EMenuBar menuBar;
/** Last stroke1 input */ private KeyStroke key1;
/** Last stroke2 input */ private KeyStroke key2;
/**
* Creates new form EditKeyBinding.
* @param parent
* @param modal blocks access to other windows if true
* @param item the menu item which we are adding a Key Binding to.
*/
public EditKeyBinding(EMenuItem item, EMenuBar menuBar, java.awt.Frame parent, boolean modal) {
super(parent, modal);
menuItem = item;
this.menuBar = menuBar;
key1 = null;
key2 = null;
initComponents(); // prebuilt by netbeans
initDialog();
finishInitialization();
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
private void initComponents() {//GEN-BEGIN:initComponents
java.awt.GridBagConstraints gridBagConstraints;
header = new javax.swing.JLabel();
stroke1Label = new javax.swing.JLabel();
stroke1Input = new javax.swing.JTextField();
jSeparator1 = new javax.swing.JSeparator();
optionalLabel = new javax.swing.JLabel();
stroke2Label = new javax.swing.JLabel();
stroke2Input = new javax.swing.JTextField();
jSeparator2 = new javax.swing.JSeparator();
conflictsLabel = new javax.swing.JLabel();
conflictsList = new javax.swing.JList();
jSeparator3 = new javax.swing.JSeparator();
jPanel1 = new javax.swing.JPanel();
addButton = new javax.swing.JButton();
cancelButton = new javax.swing.JButton();
getContentPane().setLayout(new java.awt.GridBagLayout());
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent evt) {
closeDialog(evt);
}
});
header.setText("jLabel1");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.insets = new java.awt.Insets(2, 6, 2, 6);
getContentPane().add(header, gridBagConstraints);
stroke1Label.setText("First stroke:");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.insets = new java.awt.Insets(2, 6, 2, 6);
getContentPane().add(stroke1Label, gridBagConstraints);
stroke1Input.addKeyListener(new java.awt.event.KeyAdapter() {
public void keyPressed(java.awt.event.KeyEvent evt) {
stroke1InputKeyPressed(evt);
}
public void keyReleased(java.awt.event.KeyEvent evt) {
stroke1InputKeyReleased(evt);
}
public void keyTyped(java.awt.event.KeyEvent evt) {
stroke1InputKeyTyped(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.weightx = 0.5;
gridBagConstraints.insets = new java.awt.Insets(2, 7, 2, 7);
getContentPane().add(stroke1Input, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.insets = new java.awt.Insets(2, 6, 2, 6);
getContentPane().add(jSeparator1, gridBagConstraints);
optionalLabel.setText("(Optional):");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.insets = new java.awt.Insets(2, 6, 2, 6);
getContentPane().add(optionalLabel, gridBagConstraints);
stroke2Label.setText("Second stroke:");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.insets = new java.awt.Insets(2, 6, 2, 6);
getContentPane().add(stroke2Label, gridBagConstraints);
stroke2Input.addKeyListener(new java.awt.event.KeyAdapter() {
public void keyPressed(java.awt.event.KeyEvent evt) {
stroke2InputKeyPressed(evt);
}
public void keyReleased(java.awt.event.KeyEvent evt) {
stroke2InputKeyReleased(evt);
}
public void keyTyped(java.awt.event.KeyEvent evt) {
stroke2InputKeyTyped(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 4;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.weightx = 0.5;
gridBagConstraints.insets = new java.awt.Insets(2, 6, 2, 6);
getContentPane().add(stroke2Input, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 5;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.insets = new java.awt.Insets(2, 6, 2, 6);
getContentPane().add(jSeparator2, gridBagConstraints);
conflictsLabel.setText("Conflicts:");
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 6;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.insets = new java.awt.Insets(2, 6, 2, 6);
getContentPane().add(conflictsLabel, gridBagConstraints);
conflictsList.setBorder(new javax.swing.border.EtchedBorder());
conflictsList.setPreferredSize(new java.awt.Dimension(220, 80));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 7;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 2.0;
gridBagConstraints.insets = new java.awt.Insets(2, 6, 2, 6);
getContentPane().add(conflictsList, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 8;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.insets = new java.awt.Insets(2, 6, 2, 6);
getContentPane().add(jSeparator3, gridBagConstraints);
jPanel1.setLayout(new java.awt.GridBagLayout());
addButton.setText("Add");
addButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
addButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(6, 6, 6, 6);
jPanel1.add(addButton, gridBagConstraints);
cancelButton.setText("Cancel");
cancelButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cancelButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(6, 6, 6, 6);
jPanel1.add(cancelButton, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 9;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.insets = new java.awt.Insets(2, 6, 2, 6);
getContentPane().add(jPanel1, gridBagConstraints);
pack();
}//GEN-END:initComponents
private void initDialog() {
String description = menuItem.getDescription();
header.setText("Add Key Binding for: "+description);
pack();
}
// ------------------------------------- Actions ------------------------------------
/** Ignore input key typed events */
private void stroke2InputKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_stroke2InputKeyTyped
evt.consume();
}//GEN-LAST:event_stroke2InputKeyTyped
/** Ignore input key released events */
private void stroke2InputKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_stroke2InputKeyReleased
evt.consume();
}//GEN-LAST:event_stroke2InputKeyReleased
/**
* Respond to key input to text box. Show string in the text box
* representing that KeyStroke.
* @param evt the KeyEvent
*/
private void stroke2InputKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_stroke2InputKeyPressed
if (!KeyBindingManager.validKeyEvent(evt)) return;
key2 = KeyStroke.getKeyStrokeForEvent(evt);
stroke2Input.setText(KeyStrokePair.keyStrokeToString(key2));
updateConflicts();
evt.consume();
}//GEN-LAST:event_stroke2InputKeyPressed
/** Ignore input key typed events */
private void stroke1InputKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_stroke1InputKeyTyped
evt.consume();
}//GEN-LAST:event_stroke1InputKeyTyped
/** Ignore input key released events */
private void stroke1InputKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_stroke1InputKeyReleased
evt.consume();
}//GEN-LAST:event_stroke1InputKeyReleased
/**
* Respond to key input to text box. Show string in the text box
* representing that KeyStroke.
* @param evt the KeyEvent
*/
private void stroke1InputKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_stroke1InputKeyPressed
if (!KeyBindingManager.validKeyEvent(evt)) return;
key1 = KeyStroke.getKeyStrokeForEvent(evt);
stroke1Input.setText(KeyStrokePair.keyStrokeToString(key1));
addButton.setEnabled(updateConflicts());
evt.consume();
}//GEN-LAST:event_stroke1InputKeyPressed
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
setVisible(false);
dispose();
}//GEN-LAST:event_cancelButtonActionPerformed
private void addButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addButtonActionPerformed
KeyStroke prefixStroke = getStroke(stroke1Input);
KeyStroke stroke = getStroke(stroke2Input);
if (prefixStroke == null) {
dialogBadInput();
return;
}
ListModel model = conflictsList.getModel();
// if conflicts exist, ask user if they want to remove or leave them (or do nothing)
int removeConflicts = 1;
if (model.getSize() != 0) {
removeConflicts = removeConflictsDialog(model);
// removeConflicts: 0 to remove all, 1 to leave all conflicts, 2 to cancel (default)
}
if (removeConflicts == 2) {
return; // do nothing
} else if (removeConflicts == 0) {
List<KeyBindings> conflicts = getConflicts();
for (KeyBindings k : conflicts) {
// remove all bindings in k
for (Iterator<KeyStrokePair> kit = k.getKeyStrokePairs(); kit.hasNext(); ) {
KeyStrokePair pair = kit.next();
menuBar.removeKeyBinding(k.getActionDesc(), pair);
}
}
}
// add new key binding
if (stroke == null) {
stroke = prefixStroke; prefixStroke = null;
}
menuBar.addUserKeyBinding(menuItem, stroke, prefixStroke);
setVisible(false);
}//GEN-LAST:event_addButtonActionPerformed
private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
}//GEN-LAST:event_closeDialog
// --------------------------------- List Box ----------------------------------
/**
* Method to check conflicts with previous defined key strokes.
* @return False if the conflicts can't be generated (alt- definitions).
*/
private boolean updateConflicts() {
List<KeyBindings> conflicts = getConflicts();
if (conflicts == null || conflicts.size() == 0) {
clearConflictsList();
} else
{
// create array of strings to display in list box
String [] objects = new String[conflicts.size()];
for (int i=0; i<conflicts.size(); i++) {
KeyBindings k = conflicts.get(i);
String s = k.getActionDesc() + " [ "+k.bindingsToString()+" ]";
objects[i] = s;
}
// update list box
conflictsList.setListData(objects);
return true;
}
// warn if an ALT key conflicts with a pulldown menu
if ((key1.getModifiers() & InputEvent.ALT_MASK) != 0)
{
Main top = (Main) Main.getCurrentJFrame();
EMenuBar menuBar = top.getEMenuBar();
for (EMenuItem menu: menuBar.getItems())
{
char reservedAlt = menu.getMnemonicChar();
if (reservedAlt == 0) continue;
KeyStroke ksRes = KeyStroke.getKeyStroke(reservedAlt, InputEvent.ALT_MASK);
if (ksRes == key1)
{
String [] objects = new String[1];
objects[0] = "Pulldown menu: " + menu.getText();
System.out.println("Stroke is pre-reserved for \"" + objects[0] + "\" and it can't be modified.");
conflictsList.setListData(objects);
return false;
}
}
}
// // check for reserved keystrokes (doesn't work)
// KeyStroke key1 = getStroke(stroke1Input);
// Keymap map = JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
// for ( ; map != null; map = map.getResolveParent())
// {
// KeyStroke[] keys = map.getBoundKeyStrokes();
// for (int i=0; i<keys.length; i++)
// {
// if (keys[i] == key1)
// {
// System.out.println(key1 + " IS RESERVED");
// }
// }
// }
return true;
}
/**
* Clear conflicts list
*/
private void clearConflictsList() {
conflictsList.setListData(new Object [] {});
}
/**
* Get list of KeyBindings that conflict with key combo user
* has entered into this dialog.
* @return a list of KeyBindings, or null if none.
*/
private List<KeyBindings> getConflicts() {
// grab key combo from input boxes
KeyStroke key1 = getStroke(stroke1Input);
if (key1 == null) { clearConflictsList(); return null; }
KeyStroke key2 = getStroke(stroke2Input);
// if key2 is null, then key1 is the primary stroke.
// otherwise, key1 is the prefix stroke, and key2 is the primary stroke
KeyStroke prefixStroke = key1, stroke = key2; // assumes key2 non-null
if (key2 == null) {
stroke = key1; prefixStroke = null;
}
KeyStrokePair pair = KeyStrokePair.getKeyStrokePair(prefixStroke, stroke);
return KeyBindingManager.getConflictsAllManagers(pair);
}
/**
* Get KeyStroke from input field.
* @param field a JTextField with text that describes a KeyStroke
* @return the KeyStroke, or null if invalid text in field.
*/
private KeyStroke getStroke(JTextField field) {
String str = field.getText();
return KeyStrokePair.stringToKeyStroke(str);
}
// ------------------------------ Helper Dialogs ------------------------------
/**
* Pops up a JOptionPain dialog box to ask user if they want to remove or leave
* conflicting bindings, or just cancel.
* @return 0 to remove all, 1 to leave all conflicts, 2 to cancel (default)
*/
private int removeConflictsDialog(ListModel model) {
StringBuffer buf = new StringBuffer();
buf.append("The following conflicts exists. Would you like to remove them?\n");
for (int i=0; i<model.getSize(); i++) {
buf.append(model.getElementAt(i).toString()+"\n");
}
Object [] options = {"Remove All", "Leave All", "Cancel"};
int n = JOptionPane.showOptionDialog(this,
buf.toString(),
"Conflicts Exist", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
null, options, options[2]);
return n;
}
private void dialogBadInput() {
JOptionPane.showMessageDialog(null, "I cannot bind to the First Stroke key you have specified. Sorry",
"Invalid Input", JOptionPane.WARNING_MESSAGE);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton addButton;
private javax.swing.JButton cancelButton;
private javax.swing.JLabel conflictsLabel;
private javax.swing.JList conflictsList;
private javax.swing.JLabel header;
private javax.swing.JPanel jPanel1;
private javax.swing.JSeparator jSeparator1;
private javax.swing.JSeparator jSeparator2;
private javax.swing.JSeparator jSeparator3;
private javax.swing.JLabel optionalLabel;
private javax.swing.JTextField stroke1Input;
private javax.swing.JLabel stroke1Label;
private javax.swing.JTextField stroke2Input;
private javax.swing.JLabel stroke2Label;
// End of variables declaration//GEN-END:variables
}