views:

126

answers:

3

Hi all.

Problem: I have CaretListener and DocumentListener listening on a JTextPane.

I need an algorithm that is able to tell which line is the caret at in a JTextPane, here's an illustrative example:

alt text

Result: 3rd line

alt text

Result: 2nd line

alt text

Result: 4th line

and if the algorithm can tell which line the caret is in the JTextPane, it should be fairly easy to substring whatever that is in between the parentheses as the picture (caret is at character m of metadata):

alt text

--

This is how I divide the entire text that I retrieved from the JTextPane into sentences:

String[] lines = textPane.getText().split("\r?\n|\r", -1);

The sentences in the textPane is separated with \n.

Problem is, how can I manipulate the caret to let me know at which position and which line it is in? I know the dot of the caret says at which position it is, but I can't tell which line it is at. Assuming if I know which line the caret is, then I can just do lines[<line number>] and manipulate the string from there.

In Short: How do I use CaretListener and/or DocumentListener to know which line the caret is currently at, and retrieve the line for further string manipulation? Please help. Thanks.

Do let me know if further clarification is needed. Thanks for your time.

+1  A: 

Use the notion of paragraph in in the StyledDocument with JTextPane.getStyledDocument.

With the position of cursor, you know the current paragraph with StyledDocument.getParagraph(pos). Then you iterate throw the paragraphs from StyledDocument.getRootElements and children, to search the number of current paragraph, so the number of current line.

Sorry for my english.

Istao
@Istao: It's alright, thanks for trying to answer. English is not my native language as well. =)
Alex Cheng
+1  A: 

Here is teh codes:

static int getLineOfOffset(JTextComponent comp, int offset) throws BadLocationException {
    Document doc = comp.getDocument();
    if (offset < 0) {
        throw new BadLocationException("Can't translate offset to line", -1);
    } else if (offset > doc.getLength()) {
        throw new BadLocationException("Can't translate offset to line", doc.getLength() + 1);
    } else {
        Element map = doc.getDefaultRootElement();
        return map.getElementIndex(offset);
    }
}

static int getLineStartOffset(JTextComponent comp, int line) throws BadLocationException {
    Element map = comp.getDocument().getDefaultRootElement();
    if (line < 0) {
        throw new BadLocationException("Negative line", -1);
    } else if (line >= map.getElementCount()) {
        throw new BadLocationException("No such line", comp.getDocument().getLength() + 1);
    } else {
        Element lineElem = map.getElement(line);
        return lineElem.getStartOffset();
    }
}

...

public void caretUpdate(CaretEvent e) {
    int dot = e.getDot();
    int line = getLineOfOffset(textComponent, dot);
    int positionInLine = dot - getLineStartOffset(textComponent, line);
    ...
}
@joppux: Wow thanks! Can you please explain a bit of your code?
Alex Cheng
Text component document model (subclass of Document) consists of Elements (class javax.swing.text.Element). For most Documents Elements are lines of text, so working with Elements is working with lines. Most of the code is simply checking for wrong arguments, and the rest is simple.
Well, I looked in docs, so it seems that it works only for PlainDocument, more complex documents (as DefaultStyledDocument or HTMLDocument) need more complex code.
@joppux: Eh? I am using StyledDocument instance of AbstractDocument and it worked. Hmm.
Alex Cheng
+1  A: 

This is how I divide the entire text that I retrieved from the JTextPane into sentences:

Your solution is not very efficient.

First of all only a "\n" is ever stored in a Document not matter what OS is being used. So you can simplify the regex.

However, there is no need to use a regex to parse the entire Document since you only care about 1 line in the Document. Now that you know about the Element interface you can make the code more efficient.

a) get the line as demonstrated previously
b) now you can get the Element represented by that line, from the root Element
c) now you can use the start/end offsets and the getText(...) method to get only the text for that particular line.

camickr
@camickr: I'm sorry I'm not well versed with the Element, especially on (b) that you're pointing out. Do you mind clarifying by giving an example?
Alex Cheng
Well, start by reading the API. The start/end offset methods are in the Element class and the getText() method as you know is in the Document class. Regarding an example you already have a working example. joppux already showed you how to get the start offset. So its 2 more lines of code to get the end offset and then use the start and end offset extract the text using the getText() method. If you have a problem then post your SSCCE: http://sscce.org. You learn more by trying and making mistakes then by just copying code. Take the time to understand how the code you copy works.
camickr
@camickr: Oh. I was thinking how am I supposed to retrieve the text from Element, hence the question. I think I got the endOffset correct, however using what you'd suggested breaks my current implementation. I'll stick to my old code till I figure out what is wrong. Anyway, thanks.
Alex Cheng