1 19 20 package org.netbeans.modules.editor.options; 21 22 import java.beans.*; 23 import java.awt.event.*; 24 import java.awt.Dialog ; 25 import java.awt.Component ; 26 import javax.swing.*; 27 import java.util.*; 28 29 import org.openide.*; 30 import org.openide.util.NbBundle; 31 import org.netbeans.editor.*; 32 import org.netbeans.modules.editor.NbEditorKit; 33 import org.openide.util.HelpCtx; 34 import org.openide.util.Lookup; 35 36 37 40 public class KeyBindingsEditorPanel extends javax.swing.JPanel { 41 42 private ActionDescriptor[] acts; 43 private int actionIndex; 44 private String kitClassName; 45 private KeyBindingsEditor editor; 46 private ButtonGroup sortGroup; 47 48 49 public KeyBindingsEditorPanel( KeyBindingsEditor editor ) { 50 this.editor = editor; 51 initComponents (); 52 53 getAccessibleContext().setAccessibleDescription(getBundleString("ACSD_KBEP_Panel")); sequencesLabel.setDisplayedMnemonic(getBundleString("KBEP_Sequences_Mnemonic").charAt (0)); actionsList.getAccessibleContext().setAccessibleName(getBundleString("ACSN_KBEP_Actions")); actionsList.getAccessibleContext().setAccessibleDescription(getBundleString("ACSD_KBEP_Actions")); sequencesList.getAccessibleContext().setAccessibleDescription(getBundleString("ACSD_KBEP_Sequences")); nameSortRadioButton.getAccessibleContext().setAccessibleDescription(getBundleString("ACSD_KBEP_name_sort_button")); actionSortRadioButton.getAccessibleContext().setAccessibleDescription(getBundleString("ACSD_KBEP_action_sort_button")); addSequenceButton.getAccessibleContext().setAccessibleDescription(getBundleString("ACSD_KBEP_Add")); removeSequenceButton.getAccessibleContext().setAccessibleDescription(getBundleString("ACSD_KBEP_Remove")); 63 sortGroup = new ButtonGroup (); 65 sortGroup.add (actionSortRadioButton); 66 sortGroup.add (nameSortRadioButton); 67 } 68 69 private String getBundleString(String s) { 70 return NbBundle.getMessage(KeyBindingsEditorPanel.class, s); 71 } 72 73 74 78 public void setValue( List l ) { 79 if (l != null) 80 kitClassName = (String )l.get( 0 ); 81 else 82 return; 83 84 Class kitClass = null; 85 try { 86 ClassLoader loader = (ClassLoader )Lookup.getDefault().lookup(ClassLoader .class); 87 kitClass = Class.forName( kitClassName, true, loader); 88 } catch( ClassNotFoundException e ) { 89 org.openide.ErrorManager.getDefault().notify(org.openide.ErrorManager.INFORMATIONAL, e); 90 return; 91 } 92 93 Class actionKitClass = kitClass; 96 if( actionKitClass == BaseKit.class ) 97 actionKitClass = NbEditorKit.class; Action[] actions = BaseKit.getKit( actionKitClass ).getActions(); 99 100 TreeMap treeMap = new TreeMap( ); 102 for( int i=0; i<actions.length; i++ ) { 104 if (actions[i]!=null){ 105 Object internalActionObj = actions[i].getValue(BaseAction.NO_KEYBINDING); 106 if (internalActionObj instanceof Boolean ){ 107 if (((Boolean )internalActionObj).booleanValue()){ 108 continue; } 110 } 111 } 112 ActionDescriptor val = new ActionDescriptor( actions[i] ); 113 treeMap.put( val.name, val ); 114 } 115 116 Class parent = kitClass.getSuperclass(); 118 Settings.KitAndValue[] kv = Settings.getValueHierarchy( parent, SettingsNames.KEY_BINDING_LIST ); 119 for( int i=kv.length - 1; i >= 0; i--) 121 addKeyBindingList( treeMap, ((List)kv[i].value).iterator(), true ); 122 123 124 addKeyBindingList( treeMap, l.listIterator( 1 ), false ); 126 127 acts = (ActionDescriptor[])treeMap.values().toArray( new ActionDescriptor[0] ); 129 130 if( acts.length > 0 ) addSequenceButton.setEnabled( true ); 132 133 Arrays.sort (acts); 135 136 actionsList.setListData( acts ); 137 actionsList.setSelectedIndex( actionIndex ); 138 updateSequences( 0 ); 139 140 if (ActionDescriptor.getSortMode () == ActionDescriptor.SORT_BY_ACTION) { 142 actionSortRadioButton.setSelected (true); 143 } else { 144 nameSortRadioButton.setSelected (true); 145 } 146 } 147 148 private void addKeyBindingList( Map target, Iterator source, boolean inherited ) { 149 while( source.hasNext() ) { 150 MultiKeyBinding b = (MultiKeyBinding)source.next(); 151 ActionDescriptor ad = (ActionDescriptor)target.get( b.actionName ); 152 153 if( ad != null ) { KeySequence sequence = getKeySequenceForBinding( inherited, b ); 155 156 if( sequence == null ) { 157 } else { 158 ad.sequences.add( sequence ); 159 } 160 } else { 161 } 164 } 165 } 166 167 private KeySequence getKeySequenceForBinding( boolean inherited, MultiKeyBinding binding ) { 168 KeyStroke[] sequence = binding.keys; 169 if( sequence == null ) { if( binding.key == null ) return null; 171 sequence = new KeyStroke[1]; 172 sequence[0] = binding.key; 173 } 174 return new KeySequence( inherited, sequence ); 175 } 176 177 180 public List getValue() { 181 Vector val = new Vector(); 182 val.add( kitClassName ); 184 185 for( int i=0; i<acts.length; i++ ) { 187 String name = acts[i].name; 188 for( Iterator iter=acts[i].sequences.iterator(); iter.hasNext(); ) { 189 KeySequence seq = (KeySequence)iter.next(); 190 if( !seq.isInherited() ) { val.add( new MultiKeyBinding( seq.getKeyStrokes(), name ) ); 192 } 193 } 194 } 195 196 return val; 198 } 199 200 private void updateSequences( int index ) { 202 Vector bindings = acts[actionIndex].sequences; 203 sequencesList.setListData( bindings ); 205 if( bindings.size() > 0 ) sequencesList.setSelectedIndex( index ); 207 } 208 209 private void updateRemoveButton() { 211 int id = sequencesList.getSelectedIndex() ; 212 Vector b = acts[actionIndex].sequences; 213 214 boolean enable = id >= 0 && id < b.size() && !((KeySequence)b.get( id )).isInherited(); 215 removeSequenceButton.setEnabled( enable ); 216 } 217 218 219 private void notifyEditor() { 220 if( editor != null ) editor.customEditorChange(); 221 } 222 223 private void sortActionsList () { 224 int mode = actionSortRadioButton.isSelected () ? ActionDescriptor.SORT_BY_ACTION 226 : ActionDescriptor.SORT_BY_NAME; 227 ActionDescriptor.setSortMode (mode); 228 ActionDescriptor ad = acts [actionIndex]; 229 Arrays.sort (acts); 231 232 int newIndex = 0; 234 for (int x=0; x < acts.length; x++) { 235 if (acts [x] == ad) { 236 newIndex = x; 237 break; 238 } 239 } 240 actionsList.setListData (acts); 241 actionIndex = newIndex; 244 actionsList.setSelectedIndex (actionIndex); 245 actionsList.ensureIndexIsVisible (actionIndex); 246 actionsList.requestFocus (); 247 } 248 249 252 private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; 254 255 actionsPanel = new javax.swing.JPanel (); 256 sortButtonsPanel = new javax.swing.JPanel (); 257 nameSortRadioButton = new javax.swing.JRadioButton (); 258 actionSortRadioButton = new javax.swing.JRadioButton (); 259 actionsScrollPane = new javax.swing.JScrollPane (); 260 actionsList = new javax.swing.JList (); 261 sequencesPanel = new javax.swing.JPanel (); 262 sequencesLabel = new javax.swing.JLabel (); 263 sequencesScrollPane = new javax.swing.JScrollPane (); 264 sequencesList = new javax.swing.JList (); 265 addSequenceButton = new javax.swing.JButton (); 266 removeSequenceButton = new javax.swing.JButton (); 267 268 setLayout(new java.awt.BorderLayout (0, 12)); 269 270 setBorder(new javax.swing.border.EmptyBorder (new java.awt.Insets (12, 12, 11, 11))); 271 actionsPanel.setLayout(new java.awt.GridBagLayout ()); 272 273 actionsPanel.setBorder(new javax.swing.border.TitledBorder (new javax.swing.border.EmptyBorder (new java.awt.Insets (0, 0, 0, 0)), getBundleString("KBEP_Actions"))); 274 sortButtonsPanel.setLayout(new java.awt.GridBagLayout ()); 275 276 nameSortRadioButton.setMnemonic(getBundleString ("KBEP_name_sort_button_mnemonic").charAt (0)); 277 nameSortRadioButton.setText(getBundleString ("KBEP_name_sort_button")); 278 nameSortRadioButton.addActionListener(new java.awt.event.ActionListener () { 279 public void actionPerformed(java.awt.event.ActionEvent evt) { 280 nameSortRadioButtonActionPerformed(evt); 281 } 282 }); 283 284 gridBagConstraints = new java.awt.GridBagConstraints (); 285 gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; 286 sortButtonsPanel.add(nameSortRadioButton, gridBagConstraints); 287 288 actionSortRadioButton.setMnemonic(getBundleString ("KBEP_action_sort_button_mnemonic").charAt (0)); 289 actionSortRadioButton.setText(getBundleString ("KBEP_action_sort_button")); 290 actionSortRadioButton.addActionListener(new java.awt.event.ActionListener () { 291 public void actionPerformed(java.awt.event.ActionEvent evt) { 292 actionSortRadioButtonActionPerformed(evt); 293 } 294 }); 295 296 gridBagConstraints = new java.awt.GridBagConstraints (); 297 gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; 298 gridBagConstraints.weightx = 1.0; 299 gridBagConstraints.insets = new java.awt.Insets (0, 12, 0, 0); 300 sortButtonsPanel.add(actionSortRadioButton, gridBagConstraints); 301 302 gridBagConstraints = new java.awt.GridBagConstraints (); 303 gridBagConstraints.gridx = 0; 304 gridBagConstraints.gridy = 0; 305 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; 306 gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; 307 gridBagConstraints.insets = new java.awt.Insets (0, 0, 5, 0); 308 actionsPanel.add(sortButtonsPanel, gridBagConstraints); 309 310 actionsList.addListSelectionListener(new javax.swing.event.ListSelectionListener () { 311 public void valueChanged(javax.swing.event.ListSelectionEvent evt) { 312 actionsListValueChanged(evt); 313 } 314 }); 315 316 actionsScrollPane.setViewportView(actionsList); 317 318 gridBagConstraints = new java.awt.GridBagConstraints (); 319 gridBagConstraints.gridx = 0; 320 gridBagConstraints.gridy = 1; 321 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; 322 gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; 323 gridBagConstraints.weightx = 1.0; 324 gridBagConstraints.weighty = 1.0; 325 actionsPanel.add(actionsScrollPane, gridBagConstraints); 326 327 add(actionsPanel, java.awt.BorderLayout.CENTER); 328 329 sequencesPanel.setLayout(new java.awt.GridBagLayout ()); 330 331 sequencesLabel.setLabelFor(sequencesList); 332 sequencesLabel.setText(getBundleString("KBEP_Sequences")); 333 gridBagConstraints = new java.awt.GridBagConstraints (); 334 gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; 335 gridBagConstraints.insets = new java.awt.Insets (0, 0, 2, 12); 336 sequencesPanel.add(sequencesLabel, gridBagConstraints); 337 338 sequencesScrollPane.setPreferredSize(new java.awt.Dimension (259, 80)); 339 sequencesList.setCellRenderer(new KeySequenceCellRenderer()); 340 sequencesList.addListSelectionListener(new javax.swing.event.ListSelectionListener () { 341 public void valueChanged(javax.swing.event.ListSelectionEvent evt) { 342 sequencesListValueChanged(evt); 343 } 344 }); 345 346 sequencesScrollPane.setViewportView(sequencesList); 347 348 gridBagConstraints = new java.awt.GridBagConstraints (); 349 gridBagConstraints.gridx = 0; 350 gridBagConstraints.gridy = 1; 351 gridBagConstraints.gridheight = 3; 352 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; 353 gridBagConstraints.weightx = 1.0; 354 gridBagConstraints.weighty = 1.0; 355 gridBagConstraints.insets = new java.awt.Insets (0, 0, 0, 12); 356 sequencesPanel.add(sequencesScrollPane, gridBagConstraints); 357 358 addSequenceButton.setMnemonic(getBundleString ("KBEP_Add_Mnemonic").charAt (0)); 359 addSequenceButton.setText(getBundleString( "KBEP_Add" )); 360 addSequenceButton.setEnabled(false); 361 addSequenceButton.addActionListener(new java.awt.event.ActionListener () { 362 public void actionPerformed(java.awt.event.ActionEvent evt) { 363 addSequenceButtonActionPerformed(evt); 364 } 365 }); 366 367 gridBagConstraints = new java.awt.GridBagConstraints (); 368 gridBagConstraints.gridx = 1; 369 gridBagConstraints.gridy = 1; 370 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 371 gridBagConstraints.insets = new java.awt.Insets (0, 0, 5, 0); 372 sequencesPanel.add(addSequenceButton, gridBagConstraints); 373 374 removeSequenceButton.setMnemonic(getBundleString ("KBEP_Remove_Mnemonic").charAt (0)); 375 removeSequenceButton.setText(getBundleString( "KBEP_Remove" )); 376 removeSequenceButton.setEnabled(false); 377 removeSequenceButton.addActionListener(new java.awt.event.ActionListener () { 378 public void actionPerformed(java.awt.event.ActionEvent evt) { 379 removeSequenceButtonActionPerformed(evt); 380 } 381 }); 382 383 gridBagConstraints = new java.awt.GridBagConstraints (); 384 gridBagConstraints.gridx = 1; 385 gridBagConstraints.gridy = 2; 386 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 387 sequencesPanel.add(removeSequenceButton, gridBagConstraints); 388 389 add(sequencesPanel, java.awt.BorderLayout.SOUTH); 390 391 } 393 private void nameSortRadioButtonActionPerformed (java.awt.event.ActionEvent evt) { sortActionsList (); 395 } 397 private void actionSortRadioButtonActionPerformed (java.awt.event.ActionEvent evt) { sortActionsList (); 399 } 401 private void sequencesListValueChanged (javax.swing.event.ListSelectionEvent evt) { updateRemoveButton(); 403 } 405 private void addSequenceButtonActionPerformed (java.awt.event.ActionEvent evt) { KeySequence newSequence = new KeySequenceRequester().getKeySequence(); 408 if( newSequence == null ) return; 410 acts[actionIndex].sequences.add( newSequence ); 412 updateSequences( acts[actionIndex].sequences.size()-1 ); 414 notifyEditor(); 415 } 417 private void removeSequenceButtonActionPerformed (java.awt.event.ActionEvent evt) { int index = sequencesList.getSelectedIndex(); 420 if( index >= 0 ) { 421 acts[actionIndex].sequences.remove( index ); 422 if( index >= acts[actionIndex].sequences.size() ) index--; 423 updateSequences( index ); 424 notifyEditor(); 425 } 426 } 428 private void actionsListValueChanged (javax.swing.event.ListSelectionEvent evt) { if (actionsList.getSelectedIndex () < 0) return; 430 actionIndex = actionsList.getSelectedIndex(); 431 updateSequences( 0 ); 432 } 434 435 private javax.swing.JList sequencesList; 437 private javax.swing.JButton addSequenceButton; 438 private javax.swing.JPanel sortButtonsPanel; 439 private javax.swing.JPanel actionsPanel; 440 private javax.swing.JScrollPane sequencesScrollPane; 441 private javax.swing.JRadioButton actionSortRadioButton; 442 private javax.swing.JPanel sequencesPanel; 443 private javax.swing.JScrollPane actionsScrollPane; 444 private javax.swing.JRadioButton nameSortRadioButton; 445 private javax.swing.JButton removeSequenceButton; 446 private javax.swing.JLabel sequencesLabel; 447 private javax.swing.JList actionsList; 448 450 453 private class KeySequenceRequester { 454 455 KeySequenceInputPanel input; 456 DialogDescriptor dd; 457 Dialog dial; 458 459 Object [] buttons = { new JButton( getBundleString( "KBEP_OK_LABEL" ) ), new JButton( getBundleString("KBEP_CLEAR_LABEL" ) ), DialogDescriptor.CANCEL_OPTION }; 462 KeySequence retVal = null; 463 464 KeySequenceRequester() { 465 ((JButton)buttons[0]).getAccessibleContext().setAccessibleDescription(LocaleSupport.getString("ACSD_KBEP_OK")); ((JButton)buttons[1]).getAccessibleContext().setAccessibleDescription(LocaleSupport.getString("ACSD_KBEP_CLEAR")); ((JButton)buttons[1]).setMnemonic (getBundleString("KBEP_CLEAR_Mnemonic").charAt (0)); ((JButton)buttons[0]).setEnabled( false ); 470 input = new KeySequenceInputPanel(); 472 input.addPropertyChangeListener( new PropertyChangeListener() { 473 public void propertyChange( PropertyChangeEvent evt ) { 474 if( KeySequenceInputPanel.PROP_KEYSEQUENCE != evt.getPropertyName() ) return; 475 KeyStroke[] seq = input.getKeySequence(); 476 String warn = getCollisionString( seq ); 477 input.setInfoText( warn == null ? "" : warn ); ((JButton)buttons[0]).setEnabled( warn == null ); 479 } 480 } ); 481 482 dd = new DialogDescriptor ( input, getBundleString( "KBEP_AddSequence" ), true, buttons, buttons[0], DialogDescriptor.BOTTOM_ALIGN, HelpCtx.DEFAULT_HELP, new ActionListener(){ 484 public void actionPerformed( ActionEvent evt ) { 485 if( evt.getSource() == buttons[1] ) { input.clear(); input.requestFocus(); } else if( evt.getSource() == buttons[0] ) { retVal = new KeySequence( false, input.getKeySequence() ); 490 dial.dispose(); } 492 } 493 }); 494 495 } 496 497 KeySequence getKeySequence() { 498 dial = org.openide.DialogDisplayer.getDefault().createDialog(dd); 499 input.requestFocus(); dial.setVisible(true); return retVal; 502 } 503 504 String getCollisionString( KeyStroke[] seq ) { 505 if( seq.length == 0 ) return ""; 507 for( int i=0; i<acts.length; i++ ) { Iterator iter = acts[i].sequences.iterator(); 509 while( iter.hasNext() ) { 510 KeyStroke[] s1 = ((KeySequence)iter.next()).getKeyStrokes(); 511 if( isOverlapingSequence( s1, seq ) ) { 512 Object [] values = { Utilities.keySequenceToString( s1 ), acts[i] }; 513 return NbBundle.getMessage(KeyBindingsEditorPanel.class, "KBEP_FMT_Collision" , values ); 514 } 515 } 516 } 517 return null; } 519 520 private boolean isOverlapingSequence( KeyStroke[] s1, KeyStroke[] s2 ) { 521 int l = Math.min( s1.length, s2.length ); 522 while( l-- > 0 ) if( !s1[l].equals( s2[l] ) ) return false; 523 return true; 524 } 525 } 526 527 528 529 536 private static final class ActionDescriptor implements Comparable { 537 538 public static final int SORT_BY_ACTION = 0; 539 public static final int SORT_BY_NAME = 1; 540 private static int sortMode = SORT_BY_NAME; 541 542 String name; 543 String displayName; 544 Vector sequences; 545 546 ActionDescriptor( Action a ) { 547 name = (String )a.getValue( Action.NAME ); 548 String shortDesc = (String )a.getValue( Action.SHORT_DESCRIPTION ); 549 displayName = shortDesc == null ? name : shortDesc + " [" + name + "]"; sequences = new Vector(); 551 } 552 553 public String toString() { 554 return displayName; 555 } 556 557 public int compareTo( Object o ) { 559 if (sortMode == SORT_BY_ACTION) { 560 return name.compareTo( ((ActionDescriptor)o).name ); 561 } else { 562 return displayName.compareToIgnoreCase (((ActionDescriptor)o).displayName); 563 } 564 } 565 566 public static void setSortMode (int sMode) { 567 sortMode = sMode; 568 } 569 public static int getSortMode () { 570 return sortMode; 571 } 572 } 573 574 578 private final static class KeySequence { 579 private boolean inherited; 580 private KeyStroke[] sequence; 581 582 KeySequence( boolean inherited, KeyStroke[] sequence) { 583 this.inherited = inherited; 584 this.sequence = sequence; 585 } 586 587 KeyStroke[] getKeyStrokes() { 588 return sequence; 589 } 590 591 boolean isInherited() { 592 return inherited; 593 } 594 595 public String toString() { 596 return Utilities.keySequenceToString( sequence ); 597 } 598 } 599 600 605 private final static class KeySequenceCellRenderer extends JLabel implements ListCellRenderer { 606 607 public KeySequenceCellRenderer() { 608 setOpaque( true ); 609 } 610 611 public Component getListCellRendererComponent( JList list, Object value, 612 int index, boolean isSelected, boolean cellHasFocus 613 ) { 614 setText( value.toString() ); 615 setBackground( isSelected ? list.getSelectionBackground() : list.getBackground() ); 616 617 if( (value instanceof KeySequence) && ((KeySequence)value).isInherited() ) 618 setForeground( java.awt.Color.gray ); 619 else 620 setForeground( isSelected ? list.getSelectionForeground() : list.getForeground() ); 621 622 setEnabled(list.isEnabled()); 623 setFont(list.getFont()); 624 return this; 625 } 626 } 627 628 } 629 | Popular Tags |