There is, unfortunately, no selection model for JTextArea
, otherwise this would have being really easy...
Instead, I was forced to add a ChangeListener
to the Caret
of each text area. This allowed me to see when the caret position was changed in real time.
The next problem occurred when I realised that only the current text area would actually show it's selection highlight's....(nb This can be easily rectified, check next update)
I had to then apply a highlighter to the unfocused text area...

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.io.File;
import java.io.FileReader;
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.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
public class CaretTest {
public static void main(String[] args) {
new CaretTest();
}
public CaretTest() {
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 {
public TestPane() {
setLayout(new GridLayout(1, 2));
JTextArea left = new JTextArea(10, 20);
JTextArea right = new JTextArea(10, 20);
left.setEditable(false);
right.setEditable(false);
left.getCaret().addChangeListener(new ChangeHandler(left, right));
right.getCaret().addChangeListener(new ChangeHandler(right, left));
left.addFocusListener(new FocusHandler(left, right));
right.addFocusListener(new FocusHandler(right, left));
JScrollPane leftSP = new JScrollPane(left);
JScrollPane rightSP = new JScrollPane(right);
leftSP.getHorizontalScrollBar().setModel(rightSP.getHorizontalScrollBar().getModel());
leftSP.getVerticalScrollBar().setModel(rightSP.getVerticalScrollBar().getModel());
add(leftSP);
add(rightSP);
FileReader reader = null;
try {
reader = new FileReader(new File("Ni.txt"));
left.read(reader, null);
reader.close();
reader = new FileReader(new File("Ni.txt"));
right.read(reader, null);
} catch (Exception exp) {
exp.printStackTrace();
} finally {
try {
reader.close();
} catch (Exception e) {
}
}
}
protected void updateHighlighting(JTextArea source, JTextArea target) {
DefaultHighlighter.DefaultHighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(target.getSelectionColor());
int start = source.getSelectionStart();
int end = source.getSelectionEnd();
try {
target.getHighlighter().addHighlight(start, end, painter);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
public class ChangeHandler implements ChangeListener {
private final JTextArea source;
private final JTextArea target;
public ChangeHandler(JTextArea source, JTextArea target) {
this.source = source;
this.target = target;
}
@Override
public void stateChanged(ChangeEvent e) {
if (e.getSource() == source.getCaret()) {
target.getHighlighter().removeAllHighlights();
updateHighlighting(source, target);
}
}
}
public class FocusHandler extends FocusAdapter {
private final JTextArea source;
private final JTextArea target;
public FocusHandler(JTextArea source, JTextArea target) {
this.source = source;
this.target = target;
}
@Override
public void focusGained(FocusEvent e) {
source.getHighlighter().removeAllHighlights();
target.getHighlighter().removeAllHighlights();
updateHighlighting(source, target);
}
}
}
}
ps- You'll need to supply your own text ;)
Update with "non-highlighter" example
Thanks to StanislavL for pointing out that you can use JTextComponent#getCaret()#setSelectionVisible(true)
to make a non focused text component show it's selected text.
I did find that focus changes made this false
again, so I've updated within the the change handler to always be true
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.io.File;
import java.io.FileReader;
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.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
public class CaretTest {
public static void main(String[] args) {
new CaretTest();
}
public CaretTest() {
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 {
public TestPane() {
setLayout(new GridLayout(1, 2));
JTextArea left = new JTextArea(10, 20);
JTextArea right = new JTextArea(10, 20);
left.setEditable(false);
right.setEditable(false);
left.getCaret().setSelectionVisible(true);
right.getCaret().setSelectionVisible(true);
left.getCaret().addChangeListener(new ChangeHandler(left, right));
right.getCaret().addChangeListener(new ChangeHandler(right, left));
JScrollPane leftSP = new JScrollPane(left);
JScrollPane rightSP = new JScrollPane(right);
leftSP.getHorizontalScrollBar().setModel(rightSP.getHorizontalScrollBar().getModel());
leftSP.getVerticalScrollBar().setModel(rightSP.getVerticalScrollBar().getModel());
add(leftSP);
add(rightSP);
FileReader reader = null;
try {
reader = new FileReader(new File("Ni.txt"));
left.read(reader, null);
reader.close();
reader = new FileReader(new File("Ni.txt"));
right.read(reader, null);
} catch (Exception exp) {
exp.printStackTrace();
} finally {
try {
reader.close();
} catch (Exception e) {
}
}
}
}
public static class ChangeHandler implements ChangeListener {
private static boolean ignoreUpdates = false;
private final JTextArea source;
private final JTextArea target;
public ChangeHandler(JTextArea source, JTextArea target) {
this.source = source;
this.target = target;
}
@Override
public void stateChanged(ChangeEvent e) {
if (e.getSource() == source.getCaret()) {
if (!ignoreUpdates) {
ignoreUpdates = true;
try {
target.getCaret().setSelectionVisible(true);
source.getCaret().setSelectionVisible(true);
target.setSelectionStart(source.getSelectionStart());
target.setSelectionEnd(source.getSelectionEnd());
} finally {
ignoreUpdates = false;
}
}
}
}
}
}