/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: EditKeyBinding.java * * Copyright (c) 2003 Sun Microsystems and Static Free Software * * 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 { private static final long serialVersionUID = 1L; /** 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) { //JFrame top = (JFrame) Main.getCurrentJFrame(); EMenuBar menuBar = Main.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 }