I'm trying for more than 2 days to implement a specific requirement for a text editor window... unfortunately without success so far :(
The goal is to get a text editor window which will highlight the current row, like other text editors do. With current row I mean the row where currently the cursor/caret is positioned.
I already found two different approaches but unfortunately I'm not able to adopt them so they work as expected.
The first approach is to overwrite the DefaultHighlighter
(http://snippets.dzone.com/posts/show/6688).
In the second approach the HighlighterPainter
will be overwritten instead (http://www.jroller.com/santhosh/date/20050622).
Right now I'm trying to adopt the first approach in my project but as I said it is not working as desired.
At the end of this post I'm posting a small sample application which demonstrates the problem.
- If I start the program, the caret is placed at the beginning of the first line. However, the line is not highlighted.
- Now I type in some characters. Those chars will be highlighted but only those chars not the complete line
- I hit enter to move to the next line. The first line is not highlighted anymore what is correct. The second line isn't highlighted as well, what is not correct. Again, when I type in some chars, those will be higlighted but not the complete row.
- When I now move back the caret to the first line, either by cursor up key or mouse clicking, the complete first line will be highlighted, not only the existing chars. This is the behavior I want right from the start.
I hope anybody can tell me what I'm doing wrong here... or explain why it is not possible to resolve that issue at all. Any alternative solutions how I could realize the line highlighting are also highly appreciated!
Thanks a lot in advance Cheers Preachie
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JTextPane;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;
import javax.swing.text.JTextComponent;
public class HighlightProblem extends JFrame {
private static final long serialVersionUID = 1L;
private final JTextPane textPane;
private final Highlighter.HighlightPainter cyanPainter;
public HighlightProblem() {
cyanPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.CYAN);
textPane = new JTextPane();
textPane.setPreferredSize(new Dimension(500, 300));
textPane.setHighlighter(new LineHighlighter());
textPane.addCaretListener(new CaretListener() {
@Override
public void caretUpdate(CaretEvent e) {
setHighlight(e);
}
});
getContentPane().add(textPane);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String[] args) {
new HighlightProblem();
}
public void setHighlight(CaretEvent e) {
textPane.getHighlighter().removeAllHighlights();
int currentLine = getLineFromOffset(textPane, e.getDot());
int startPos = getLineStartOffsetForLine(textPane, currentLine);
int endOffset = getLineEndOffsetForLine(textPane, currentLine);
try {
textPane.getHighlighter().addHighlight(startPos, endOffset, cyanPainter);
} catch (Exception ex) {
ex.printStackTrace();
}
textPane.repaint();
}
public int getLineFromOffset(JTextComponent component, int offset) {
return component.getDocument().getDefaultRootElement().getElementIndex(offset);
}
public int getLineStartOffsetForLine(JTextComponent component, int line) {
return component.getDocument().getDefaultRootElement().getElement(line).getStartOffset();
}
public int getLineEndOffsetForLine(JTextComponent component, int line) {
return component.getDocument().getDefaultRootElement().getElement(line).getEndOffset();
}
public class LineHighlighter extends DefaultHighlighter {
private JTextComponent component;
@Override
public final void install(final JTextComponent c) {
super.install(c);
this.component = c;
}
@Override
public final void deinstall(final JTextComponent c) {
super.deinstall(c);
this.component = null;
}
@Override
public final void paint(final Graphics g) {
final Highlighter.Highlight[] highlights = getHighlights();
final int len = highlights.length;
for (int i = 0; i < len; i++) {
Highlighter.Highlight info = highlights[i];
if (info.getClass().getName().indexOf("LayeredHighlightInfo") > -1) {
// Avoid allocing unless we need it.
final Rectangle a = this.component.getBounds();
final Insets insets = this.component.getInsets();
a.x = insets.left;
a.y = insets.top;
// a.width -= insets.left + insets.right + 100;
a.height -= insets.top + insets.bottom;
final Highlighter.HighlightPainter p = info.getPainter();
p.paint(g, info.getStartOffset(), info.getEndOffset(), a, this.component);
}
}
}
@Override
public void removeAllHighlights() {
textPane.repaint(0, 0, textPane.getWidth(), textPane.getHeight());
super.removeAllHighlights();
}
}
}