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.
Why does this approach behave differently for those keys?
How can I stop the space from being entered? (Maybe I want to extend this behavior to commas and the like later on.)
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?
Is this even the best way to build this kind of tag input field?
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");
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I need to change the default language into Hebrew in all the text areas in the app. I tried to use Set Local, but it didn't work.
Any idea?
The below code demonstrates the only way I know of to configure a JTextComponent (which JTextArea is a subclass of) so that when it gains input focus it displays Hebrew characters. In other words no need for the user to explicitly change the language after the JTextComponent has gained focus and before the user starts entering text.
import java.awt.BorderLayout;
import java.awt.ComponentOrientation;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.im.InputContext;
import java.util.Locale;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.WindowConstants;
public class DfltHbrw implements ActionListener, FocusListener, Runnable {
private static final Locale HEBREW = new Locale("iw", "IL");
private static final String EXIT = "\u05E1\u05D2\u05D5\u05E8";
private JFrame frame;
private JTextArea textArea;
#Override // java.awt.event.ActionEvent
public void actionPerformed(ActionEvent event) {
String actionCommand = event.getActionCommand();
if (EXIT.equals(actionCommand)) {
System.exit(0);
}
}
#Override // java.awt.event.FocusListener
public void focusGained(FocusEvent event) {
InputContext ic = textArea.getInputContext();
ic.selectInputMethod(HEBREW);
}
#Override // java.awt.event.FocusListener
public void focusLost(FocusEvent event) {
// Do nothing.
}
#Override // java.lang.Runnable
public void run() {
showGui();
}
private JPanel createButtons() {
JPanel buttonsPanel = new JPanel();
JButton button = new JButton(EXIT);
button.addActionListener(this);
buttonsPanel.add(button);
return buttonsPanel;
}
private JScrollPane createTextArea() {
textArea = new JTextArea(20, 60);
textArea.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
textArea.addFocusListener(this);
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
return scrollPane;
}
private void showGui() {
frame = new JFrame("Hebrew");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.add(createTextArea(), BorderLayout.CENTER);
frame.add(createButtons(), BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
/**
* Start here.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new DfltHbrw());
}
}
I only added the JButton so that there would be more than one [GUI] component so as to allow transferring focus from one component to another.
The relevant part of the above code is the focusGained() method. I added a focus listener to the JTextArea which calls that method when the JTextArea gains focus. In fact the focusGained() method does the actual work of configuring the JTextArea for displaying Hebrew when the user starts to enter text into it.
Note that the same focusGained() method can be used to configure the JTextArea to display any [supported] language. All you need to change is the Locale argument that is passed to the selectInputMethod() method.
Also note that I only set the ComponentOrientation so that the Hebrew text in the JTextArea would look "natural". It is not required in order to configure the JTextArea for displaying Hebrew.
I am trying to use KeyEvent to limit the characters in a jTextField that it can take as input on the KeyTyped event using the following code:
private void userIDFieldKeyTyped(java.awt.event.KeyEvent evt) {
char c = evt.getKeyChar();
if(c!=KeyEvent.VK_BACK_SPACE && c!=KeyEvent.VK_DELETE){
if (Character.isLetter(c) && Character.isDigit(c)){
} else {
evt.consume();
}
}
}
But the above code is not working as expected, it do not take any character as input, as it is clear from the code I am trying to input only AlphaNumeric characters, is there any way to remove this problem? Also I have tried using KeyEvent.VK_ALPHANUMERIC but it didn't work either.
Edit:
What I mean by limiting the characters is that the text field can take input only certain characters which I have allowed and not that to stop takinginput after a certain number of characters has been alread been entered.
I think that JFormattedTextField handles all your requirements. It can limit the number of characters entered, as well as filter out non-required characters. Here is a small example. The MaskFormatter is responsible for handling the required limitations.
import java.awt.EventQueue;
import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
import javax.swing.text.MaskFormatter;
/**
* For testing class {#code javax.swing.JFormattedTextField}
*/
public class FmTxtFld implements Runnable {
private JFrame frame;
private JFormattedTextField fmtTxtFld;
#Override // java.lang.Runnable
public void run() {
try {
showGui();
}
catch (ParseException xParse) {
xParse.printStackTrace();
}
}
private void showGui() throws ParseException {
frame = new JFrame("FmTxtFld");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
// Ten alphanumeric characters, i.e. letter or digit.
MaskFormatter formatter = new MaskFormatter("AAAAAAAAAA"); //throws java.text.ParseException
fmtTxtFld = new JFormattedTextField(formatter);
fmtTxtFld.setColumns(10);
JLabel label = new JLabel("JFormattedTextField");
JPanel panel = new JPanel();
panel.add(label);
panel.add(fmtTxtFld);
frame.add(panel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
FmTxtFld instance = new FmTxtFld();
EventQueue.invokeLater(instance);
}
}
If you run the above code you will see that the text field only echoes valid characters, i.e. only letters and digits, and will also not accept more that 10 characters.
I’m trying to check the number button that has been pressed in an if statement. I’ve tried to google the question but perhaps I cannot phrase the question well enough.
Here is the code, I read that less code is easier to understand, so I’ve tried to condense my question as much as possible, I hope I haven't condensed it too much.
JButton One = new JButton("1");
One.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textArea.append("1");
}
});
if(textArea.equals("1")){
System.out.println("test");//doesnt print
}
use a variable to store the text:
String theText;
theText = textArea.getText();
then do string comparison:
if(theText.equals("1")) {}
or shorthand:
if(textArea.getText().equals("1")) {}
Also, the if statement should be inside the actionPerformed method otherwise it has already been executed (resulting in FALSE) by the time the button is clicked. Use textArea.append() if you want to append a new "1" to each previous "1" in the text area every time the button is clicked, otherwise use textArea.setText() to just continually overwrite the previous "1" that was set from any previous button click. A working example can be seen here:
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JTextArea;
import javax.swing.JPanel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
public class Tester {
public static void main(String args[]) {
JButton aButton = new JButton("Button");
JPanel aPanel = new JPanel();
JTextArea aTextArea = new JTextArea();
JFrame aFrame = new JFrame();
aFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
aFrame.setSize(200, 200);
aPanel.add(aTextArea);
aPanel.add(aButton);
aFrame.add(aPanel);
aFrame.setVisible(true);
aButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
aTextArea.setText("1");
if(aTextArea.getText().equals("1")) {
System.out.println("Test Works");
}
}});
}
}
UPDATE:
If you would like to not only check the contents of the text area when the button is pressed, but whenever text is entered. Kind of the same "idea" to your if-statement being outside of the button action listener. You need to use either a FocusListener or a KeyListener on the text area. A focus listener will execute when you either click in, or click out of the text area. A key listener will execute on various types of key press/release etc. I think what you are looking for is a KeyListener based on your comments. I've provided an example of each, that works with my previous example:
/*
This is probably not the one you want
*/
aTextArea.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
}
public void focusLost(FocusEvent e) {
if(aTextArea.getText().equals("1")) {
System.out.println("Test Worls");
}
}});
/*
With this KeyListener
The second someone types "1" in the text area
It compares the strings, and the test works
*/
aTextArea.addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
String currentText = aTextArea.getText();
if(currentText.equals("1")) {
JOptionPane.showMessageDialog(null, "Test Works");
}
}
public void keyPressed(KeyEvent e) {
}
});
Try inserting the above code in the example I had previously provided. Remember to comment out the button ActionListener and to import the following:
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
I thought I can use the string representation of a keyboard key to configure shortcuts, i.e. let somebody define a keystroke in a resource bundle and pass that value directly to javax.swing.KeyStroke.getKeyStroke(String). But this does not work for international keyboards, e.g. arabic. There the keyboard keys are only high level key typed events, but shortcuts need to be defined as low level key pressed events with their key code representation. If one knows the according key code one can use the english representation of that key to get it working, but where should one know this from? I wrote a key listener that tells that, but I need something while parsing a String programmatically. Any ideas to use the natural language dependent key representation for shortcuts?
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
public class InternationalKeyStroke {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.setPreferredSize(new Dimension(600, 400));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
final JLabel label = new JLabel("Type the key you want to know the key code of!");
panel.add(label);
frame.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
String text = "Key " + e.getKeyChar() + " needs to be configured with char ='" + (char) e.getKeyCode() + "'";
label.setText(text);
System.out.println(text);
}
});
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
KeyStroke working = KeyStroke.getKeyStroke("control typed A");
label.registerKeyboardAction(new AbstractAction() {
#Override
public void actionPerformed(ActionEvent event) {
System.out.println("Working action " + event);
}
}, working, JComponent.WHEN_IN_FOCUSED_WINDOW);
KeyStroke target = KeyStroke.getKeyStroke("control typed ش");
System.out.println(target);
label.registerKeyboardAction(new AbstractAction() {
#Override
public void actionPerformed(ActionEvent event) {
System.out.println("Not working Action " + event);
}
}, target, JComponent.WHEN_IN_FOCUSED_WINDOW);
}
});
}
}
This seems not to be possible. I checked Microsoft Office and Eclipse and they all don't use the real unicode letter of the current locale, but the letter of the according virtual key code...
I am trying to do a small app that compares two similar texts contained in 2 JTextarea. I am wondering if it's possible to select text from the first JTextarea and automatically select the text on the second JTeaxtarea (lets consider that it's guarantee that the 2 JTextarea will have the same text for now) ?
Should I share events or listeners ?
Thank you
This would be so much easier if JTextComponent supported a selection model...
Basically, what you can do is attach a ChangeListener to the JTextArea's Caret and monitor for changes to the Caret, changing the selection of the other JTextArea in response...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
public class MirrorTextSelection {
public static void main(String[] args) {
new MirrorTextSelection();
}
public MirrorTextSelection() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTextArea left;
private JTextArea right;
private DefaultHighlighter.DefaultHighlightPainter highlightPainter;
public TestPane() {
highlightPainter = new DefaultHighlighter.DefaultHighlightPainter(UIManager.getColor("TextArea.selectionBackground"));
left = new JTextArea(20, 20);
left.setWrapStyleWord(true);
left.setLineWrap(true);
right = new JTextArea(20, 20);
right.setWrapStyleWord(true);
right.setLineWrap(true);
left.setText("I am trying to do a small app that compares two similar texts contained in 2 JTextarea. I am wondering if it's possible to select text from the first JTextarea and automatically select the text on the second JTeaxtarea (lets consider that it's guarantee that the 2 JTextarea will have the same text for now) ? Should I share events or listeners ? Thank you");
right.setText("I am trying to do a small app that compares two similar texts contained in 2 JTextarea. I am wondering if it's possible to select text from the first JTextarea and automatically select the text on the second JTeaxtarea (lets consider that it's guarantee that the 2 JTextarea will have the same text for now) ? Should I share events or listeners ? Thank you");
setLayout(new GridLayout(0, 2));
add(new JScrollPane(left));
add(new JScrollPane(right));
left.getCaret().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
int dot = left.getCaret().getDot();
int mark = left.getCaret().getMark();
right.setCaretPosition(mark);
right.moveCaretPosition(dot);
}
});
}
}
}
Now, when you run this, you will find that the right side doesn't seem to get highlighted...what?!
The selection is changing, it's just not been rendered because the component doesn't have focus...
Instead, you could use a Highlighter to highlight the text...
private DefaultHighlighter.DefaultHighlightPainter highlightPainter;
//...
highlightPainter = new DefaultHighlighter.DefaultHighlightPainter(UIManager.getColor("TextArea.selectionBackground"));
left.getCaret().addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
int dot = left.getCaret().getDot();
int mark = left.getCaret().getMark();
right.getHighlighter().removeAllHighlights();
try {
int start = Math.min(dot, mark);
int end = Math.max(dot, mark);
right.getHighlighter().addHighlight(start, end, highlightPainter);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
});
Okay, this is now working and you can control the background color of the highlight...
There is another alternative...We can replace the Caret of the right JTextArea with one that doesn't hide the selection when focus is lost...
public class HighlightCaret extends DefaultCaret {
#Override
public void install(JTextComponent c) {
super.install(c);
setSelectionVisible(true);
}
#Override
public void focusGained(FocusEvent e) {
JTextComponent component = getComponent();
if (component.isEnabled()) {
if (component.isEditable()) {
setVisible(true);
}
setSelectionVisible(true);
}
}
#Override
public void focusLost(FocusEvent e) {
setVisible(false);
}
}
Then we set the Caret to right...
right.setCaret(nwe HighlightCaret());
This means we don't need the Highlighter code, we can stick with the original and we get control over not only the background selection color but also the foreground selection color as well...