ultddave
ultddave

Reputation: 198

Prevent typing in the same style in JTextPane

forgive me for a possible misleading title, but the problem is a bit hard to describe.

I'm currently trying to create a basic texteditor using a JTextPane in Java and I've run into an issue.

As you know in most texteditors you can put your caret/cursor behind a piece of styled text (for example text which is bold) and then you can continue typing in that same style (Eg. append more bold characters).

Luckely this is present by default in the JTextPane, but I want to disable it for a certain style. Mainly the URL-style I coded (basicly this one just sets the HTML.Attribute.HREF attribute in the style to an URL).

So if I would put my caret behind a word (or piece of text) which is an URL, I want to ensure that the next characters which will be added, will not be in the URL-style.

Eg. I think tinymce has this behaviour:

Is there a way to enforce this behaviour in a JTextPane?

I was thinking about something like this:

The code i use for setting the URL-style to the selected text can be found below. "dot" and "mark" are retrieved from the caret.

        SimpleAttributeSet attr = new SimpleAttributeSet(doc.getCharacterElement(dot).getAttributes());
        StyleConstants.setUnderline(attr, true);
        StyleConstants.setForeground(attr, Color.BLUE);
        attr.addAttribute(HTML.Attribute.HREF, url);
        doc.setCharacterAttributes((dot < mark) ? dot : mark, length, attr, true);

(Note: To be able to tell the difference between normal "blue underlined" text and an URL, the HREF attribute is used for an URL.)

PS: This is my first question here, so hopefully I gave enough information. ;)

Language: Java, JDK 1.7

Thanks in advance.

Upvotes: 0

Views: 624

Answers (2)

ultddave
ultddave

Reputation: 198

I thought I'd share my solution to the problem (found with the help of StanislavL's answer - thanks again for putting me on the right track).

The following method is called from within a caretlistener, passing the attributes found via the "getInputAttributes"-function and the dot and mark of the caret.

private void blockURLTyping(MutableAttributeSet inputAttr, int dot, int mark)
{
    StyledDocument doc = getStyledDocument();
    int begin = (dot < mark) ? dot - 1 : mark - 1;
    if(begin >= 0)
    {
        Element dotEl = doc.getCharacterElement(begin);
        Element markEl = doc.getCharacterElement((dot < mark) ? mark : dot);
        AttributeSet dotAttr = dotEl.getAttributes();
        AttributeSet markAttr = markEl.getAttributes();
        if(dotAttr.isDefined(HTML.Attribute.HREF)) // Ensure atleast one of them isn't null
        {
            if(dotAttr.getAttribute(HTML.Attribute.HREF) == markAttr.getAttribute(HTML.Attribute.HREF))
            {
                inputAttr.addAttribute(HTML.Attribute.HREF, dotAttr.getAttribute(HTML.Attribute.HREF));
                inputAttr.addAttribute(StyleConstants.Foreground, Color.BLUE);
                inputAttr.addAttribute(StyleConstants.Underline, true);
                return;
            }
        }
    }
    if(inputAttr.isDefined(HTML.Attribute.HREF)) // In all other cases => remove
    {
        inputAttr.removeAttribute(HTML.Attribute.HREF);
        inputAttr.removeAttribute(StyleConstants.Foreground);
        inputAttr.removeAttribute(StyleConstants.Underline);
    }
}

Important note; The inputAttributes do not update when the caretposition changes but stays within the same element. So: when the caret is positioned at the end of the URL, behind the last character => you remove the three attributes you can see in the code above => However when the caret is moved to another position within the URL, the attribute stays removed because the set does not update.

So in practice this means that when you remove attributes from the attributeset, they will stay removed untill the StyledEditorKit updates the inputattributes.

To work around this problem I decided to add the attributes again if the caret is in the middle of an URL, allowing you to insert characters in the middle of the URL - but not append or prepend characters (like I wanted).

The code can probably be optimized a bit more because in most cases dot==mark, but I wanted to share this solution.

PS: The comparison of the HREF-attributes is to deal with the situation where two different URLs are positioned next to eachother in a text. It basicly should check if they are both different instances of a certain object even if the URL itself might be the same.

Code that calls this function:

@Override
protected void fireCaretUpdate(CaretEvent e) 
{
    super.fireCaretUpdate(e);
    MutableAttributeSet attr = getStyledEditorKit().getInputAttributes();
    int dot = e.getDot();
    int mark = e.getMark();

    blockURLTyping(attr, dot, mark);
    ...
}

Upvotes: 2

StanislavL
StanislavL

Reputation: 57381

Add a CaretListener to detect move and check whether current caret position needs the style reset. If it's detected use

StyledEditorKit's method

public MutableAttributeSet getInputAttributes()

Here just remove the attributes you don't need (URL, blue, underline).

Upvotes: 3

Related Questions