Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Highlight current row in JTextPane

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();
        }
    }
}
like image 855
Preachie Avatar asked Mar 23 '11 12:03

Preachie


1 Answers

http://tips4java.wordpress.com/2008/10/29/line-painter/

I think this is what you are looking for. I took that LinePainter class and copied your constructor over into a main method, took out your highlighter parts and added a new LinePainter(textPane); Works like a charm

like image 72
spedsal Avatar answered Sep 27 '22 22:09

spedsal