1

I am working on a JTextPane that works (almost) exactly like the tags input field here on stackoverflow. For that I am converting text to components as soon as a user hits enter, tab or space. Naturally I do not want any of those characters to actually be input to the text pane. I found this solution, SSCCE:

import java.awt.Color;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.StyledDocument;

@SuppressWarnings("serial")
public class TagTextPane extends JTextPane {

    public TagTextPane() {
        this.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "finalizeTag");
        this.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "finalizeTag");
        this.getInputMap().put(KeyStroke.getKeyStroke("TAB"), "focusNext");
        this.getActionMap().put("focusNext", new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent arg0) {
                finalizeTag();
                transferFocus();
            }
        });
        this.getActionMap().put("finalizeTag", new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent e) {
                finalizeTag();
            }
        });
    }

    private void finalizeTag() {
        StyledDocument doc = (StyledDocument) getDocument();
        Element element = doc.getCharacterElement(getCaretPosition() - 1);
        int start = element.getStartOffset();
        int len = element.getEndOffset() - start;
        String tag = "";
        try {
            tag = this.getDocument().getText(start, len);
        } catch (BadLocationException e) {
        }
        this.setSelectionStart(start);
        this.setSelectionEnd(start + len);
        JLabel label = new JLabel(tag);
        label.setBorder(BorderFactory.createLineBorder(Color.BLACK));
        this.insertComponent(label);        
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("TagTextPaneTest");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        TagTextPane tPane = new TagTextPane();
        frame.setSize(400, 100);
        frame.getContentPane().add(tPane);
        frame.setVisible(true);
    }
}

Hitting "tab" usually causes a tab character to be inserted and hitting "enter" usually causes a line break to be entered, as well as hitting "space" causes a space to be entered. The weird thing is that my code stops line breaks and tabs from being entered, but still allows spaces to be entered normally, while performing the intended action.

  1. Why does this approach behave differently for those keys?

  2. How can I stop the space from being entered? (Maybe I want to extend this behavior to commas and the like later on.)

  3. How can I optimally control the behavior of certain characters and keystrokes in a JTextComponent? I have seen this, where the suggestion is to use a KeyListener, but I have also seen this, where Rob Camick points out, why a KeyListener should not be used. So should I rather use a DocumentFilter, or is that breaking a butterfly on a wheel?

  4. Is this even the best way to build this kind of tag input field?

Community
  • 1
  • 1
AplusKminus
  • 1,542
  • 1
  • 19
  • 32
  • See [*How to Use Key Bindings*](http://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html) about the special action name "none". – trashgod Apr 03 '13 at 10:54
  • @trashgod I have read that, but it I interpret that to mean, that if I do not want the key to cause any other behavior (which is what I want), then I cannot use the "none" action. But the point is that I am not using the "none" action, I am already using a properly named action. – AplusKminus Apr 03 '13 at 10:59
  • Oops, I though `SPACE` had a binding. Maybe use a `DocumentListener`? – trashgod Apr 03 '13 at 11:07
  • @trashgod Maybe. For that I would need to understand this better. Could you point me to a tutorial or something, where I can understand the exact chain of events that unfolds, between the user hitting a key, and the programmatically intended action beginning? – AplusKminus Apr 03 '13 at 11:11
  • See the [tutorial](http://docs.oracle.com/javase/tutorial/uiswing/events/documentlistener.html) and other [examples](http://stackoverflow.com/search?q=user%3A230513+DocumentListener) – trashgod Apr 03 '13 at 11:14
  • @trashgod You didn't read me right: I want to understand how the key stroke is propagated. Through what and where it causes behavior. I am familiar with Listeners of all kinds, but the processing of a key stroke is more than that. – AplusKminus Apr 03 '13 at 11:17
  • The easiest way to see is to break in the debugger. – trashgod Apr 03 '13 at 11:19
  • @trashgod What do I break? Where do I break? I cannot set a breakpoint in code that belongs to the JVM, can I? I want to see which component or listener or whateveritis receives first notice of a key stroke, and what happens next. I would not only like to see this in this exact case, but to get a general understanding of this process. – AplusKminus Apr 03 '13 at 11:24
  • A complete tutorial is beyond the scope of SO; yes, you can break in Java library classes; I use NetBeans. – trashgod Apr 03 '13 at 11:28

1 Answers1

2
this.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "finalizeTag");

This is handling the keyPressed event for the space character.

You want to handle the keyTyped event of the space character:

this.getInputMap().put(KeyStroke.getKeyStroke(' '), "finalizeTag");
camickr
  • 321,443
  • 19
  • 166
  • 288
  • Aha, "If `typed`, `pressed` or `released` is not specified, `pressed` is assumed." – trashgod Apr 03 '13 at 19:31
  • That answers my questions no 2 and 3, but does it answer no 1? The point is, that I register the events for all three keys the same way (assuming `pressed`), but they behave differently, since neither a tab nor a newline is actually typed. I could understand, if the new line worked differently, because it is not the same character on different systems, but the tab should be, right? – AplusKminus Apr 03 '13 at 21:18
  • @SheridanVespo, all keys behave the same, that is, your finalizeTag Action is invoked. When you press the spacebar this also generates a keyTyped event which is the event that actually adds the " " to the document after the finalizeTag Action has executed. The Tab and Enter keys don't generate a keyTyped event so nothing is added to the document. – camickr Apr 04 '13 at 01:20