views:

193

answers:

2

In Java, by using non-default system look and feel we will have different keymap.

For example I'm using Mac OS X and use Substance look and feel (non-default system look and feel). The effect is I'm loosing my "meta" key for select all on text component In mac os x should be "meta + a", but using Substance we have to use "ctrl + a" (and a lot more such as "next word", "prev word", "end line, "begin line", etc) So we didn't have the mac os x feel by using non-default system look and feel (Substance look and feel).

Is there a way to use non-default system look and feel but use system (native) keymap?

+1  A: 

The work around

A less elegant solution, you could try adding a key listener to override the default "ctrl + a" behavior by implementing a keyPressed method (please note that the following sample does not disallow "ctrl + a" just adds support for "meta + a"):

@Override
public void keyPressed(final KeyEvent e) {
  // Get the default toolkit shortcut mask ("meta" for OSX).
  int keyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();

  // You could also check modifiers against KeyEvent.META_MASK...
  if (e.getModifiers() == keyMask && e.getKeyCode() == KeyEvent.VK_A) {
    // Select everything (assumes member of child text component class).
    this.selectAll();

    // We handled this keystroke, 'a' will be ignored by underlying text component.
    e.consume(); 
  }
}

A better alternative would be to use inputMaps (see comment by uudashr below).


Thoughts on the root cause

Unfortunately, as the class name suggests the look and feel (or LAF) is a combination of appearance i.e. look, as well as "system behavior", i.e. feel. If you dig around the substance source, the SubstanceLookAndFeel overrides the BasicLookAndFeel that ships with swing. It looks as though it is within the BasicLookAndFeel the offending behavior is set in initComponentDefaults. You should be able to get the UIDefaults from the LAF by calling getDefaults().

Problems from here are:

  • "System behaviors" which you wish to change are intermingled with appearance settings you wish to leave untouched.
  • I have also been unable to find any easy way to inject these defaults into substance at the LAF level... Anyone with other ideas on this?
Clinton
No, thats not an elegant solution.I can do something like (might more elegant):InputMap inputMap = textComponent.getInputMap(); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_DOWN_MASK), "none"); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.META_DOWN_MASK), DefaultEditorKit.selectAllAction);The "none" will disabled the default ctrl + a.But this is not what I want. Why? Because I have to know all the mac os x behavior. If there is a way just like getting a system behavior and install it on the new look and feel it would be great.
uudashr
+1 For your comment, you have a more elegant work around. See edited answer on some other ideas about the root cause...
Clinton
A: 

One possibility is to translate META key events to CTRL key events. So when a user on OS X presses the META key it is translated to a CTRL key instead. This should work correctly for key shortcuts that only have CTRL and META swapped between LAF's. If there are other combos that are more complicated you could always do more complex matching and translation. Code to do the basic translation is below, I tested it with a JMenuItem with a key accelerator of CTRL+O, so now META+O activates the accelerator.

java.awt.Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {

            public void eventDispatched(AWTEvent event) {
                KeyEvent kev = (KeyEvent) event;
                if (kev.getID() == KeyEvent.KEY_PRESSED || kev.getID() == KeyEvent.KEY_RELEASED || kev.getID() == KeyEvent.KEY_PRESSED) {
                    if ((kev.getModifiersEx() & KeyEvent.META_DOWN_MASK) != 0 && !((kev.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0)) {
                        kev.consume(); // Drop the original event, this is really optional.
                        KeyEvent fake = new KeyEvent(kev.getComponent(),
                                kev.getID(),
                                kev.getWhen(),
                                (kev.getModifiersEx() & ~KeyEvent.META_DOWN_MASK) | KeyEvent.CTRL_DOWN_MASK,
                                kev.getKeyCode(), kev.getKeyChar());
                        java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(fake);
                    }
                }
            }
        }, KeyEvent.KEY_EVENT_MASK);

This installs an AWTEventListener on the AWTEvent queue, and will affect all key events.

GalaJon