Hello All,
I'm trying to write a simple html editor that will highlight tags, and it does, but if the user types the caret jumps to the very end of the document, how can I stop this from happening?
here's my code:

import javax.swing.*;

/**
 * Main frame for the program.
 * @author Bill
 *
 */
public class MainFrame extends JFrame {
    private static final long serialVersionUID = -7853618434340029314L;
    JTextPane editor = new JTextPane();
    SyntaxDocument2 sd = new SyntaxDocument2();
    /**
     * Make a main frame.
     */
    public MainFrame() {
        this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        this.setSize( 500, 500 );
        editor.setDocument( sd );
        this.add( editor );
        this.setVisible( true );
    }
    /**
     * entry point of the program
     * @param args command line arguments
     */
    public static void main(String[] args) {
        new MainFrame();
    }
}
/**
 * 
 */
import java.awt.Color;

import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;


/**
 * @author Bill
 *
 */
public class SyntaxDocument2 extends DefaultStyledDocument {
    private static final long serialVersionUID = 8802669064394912853L;
    SimpleAttributeSet norm = new SimpleAttributeSet();
    SimpleAttributeSet tag = new SimpleAttributeSet();
    SimpleAttributeSet string = new SimpleAttributeSet();
    /**
     * Sets some stuff up.
     */
    public SyntaxDocument2() {
        StyleConstants.setForeground( tag, Color.green );
        StyleConstants.setForeground( string, Color.red );
    }
    /**
     * @see javax.swing.text.AbstractDocument#insertString(int, java.lang.String, javax.swing.text.AttributeSet)
     */
    @Override
    public void insertString( int offs, String str, AttributeSet a )
            throws BadLocationException {
        super.insertString( offs, str, a );
        
        String str2 = super.getText( 0, super.getLength() );
        
        super.remove( 0, super.getLength() );
        
        int off = 0;
        char c;
        boolean inTag=false;
        boolean inString=false;
        for(int i = 0;i<str2.length();i++,off++) {
            c=str2.charAt( i );
            
            switch( c ) {
            case '<':
                inTag=true;
                super.insertString( off, ""+c, tag );
                break;
            case '>':
                inTag=false;
                super.insertString( off, ""+c, tag );
                break;
            case '"':
                if(inTag) {
                    inString=!inString;
                    super.insertString( off, ""+c, string );
                } else {
                    super.insertString( off, ""+c, norm );
                }
                break;
            default:
                if(inTag) {
                    if(inString) {
                        super.insertString( off, ""+c, string );
                    } else {
                        super.insertString( off, ""+c, tag );                        
                    }
                } else {
                    super.insertString( off, ""+c, norm );
                }
                break;
            }
        }
    }
}

This may be a sloppy method of doing so, but you can send a reference to the JTextPane that you are using and manipulate the Caret after you are done sending characters to the Document.

import javax.swing.*;

/**
 * Main frame for the program.
 * @author Bill
 *
 */
public class MainFrame extends JFrame {
    private static final long serialVersionUID = -7853618434340029314L;
    JTextPane editor = null;
    SyntaxDocument2 sd = null;
    
    /**
     * Make a main frame.
     */
    public MainFrame() {
        this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        editor = new JTextPane();
        sd = new SyntaxDocument2( editor );
        this.setSize( 500, 500 );
        editor.setDocument( sd );
        this.add( editor );
        this.setVisible( true );
    }
    
    /**
     * entry point of the program
     * @param args command line arguments
     */
    public static void main(String[] args) {
        new MainFrame();
    }
}
/**
 * 
 */
import java.awt.Color;

import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;


/**
 * @author Bill
 *
 */
public class SyntaxDocument2 extends DefaultStyledDocument {
    private static final long serialVersionUID = 8802669064394912853L;
    SimpleAttributeSet norm = new SimpleAttributeSet();
    SimpleAttributeSet tag = new SimpleAttributeSet();
    SimpleAttributeSet string = new SimpleAttributeSet();
    private JTextPane jp = null; // A reference to a JTextPane
    
    /**
     * Sets some stuff up.
     */
    public SyntaxDocument2( JTextPane jp ) {
    	this.jp = jp;  // assigning the passed reference to the globally scoped one
        StyleConstants.setForeground( tag, Color.green );
        StyleConstants.setForeground( string, Color.red );
    }
    /**
     * @see javax.swing.text.AbstractDocument#insertString(int, java.lang.String, javax.swing.text.AttributeSet)
     */
    @Override
    public void insertString( int offs, String str, AttributeSet a )
            throws BadLocationException {
        super.insertString( offs, str, a );
        
        String str2 = super.getText( 0, super.getLength() );
        
        
        super.remove( 0, super.getLength() );
        
        int off = 0;
        char c;
        boolean inTag=false;
        boolean inString=false;
        for(int i = 0;i<str2.length();i++,off++) {
            c=str2.charAt( i );
            
            switch( c ) {
            case '<':
                inTag=true;
                super.insertString( off, ""+c, tag );
                break;
            case '>':
                inTag=false;
                super.insertString( off, ""+c, tag );
                break;
            case '"':
                if(inTag) {
                    inString=!inString;
                    super.insertString( off, ""+c, string );
                } else {
                    super.insertString( off, ""+c, norm );
                }
                break;
            default:
                if(inTag) {
                    if(inString) {
                        super.insertString( off, ""+c, string );
                    } else {
                        super.insertString( off, ""+c, tag );                        
                    }
                } else {
                    super.insertString( off, ""+c, norm );
                }
                break;
            }
        }
      
        /*
         * Assuming the '^' char is our cursor,
         * the desired result for input into the document
         * is as follows--
         * 
         * [H, i, , T, h,^ r, e]
         * [1, 2, 3, 4, 5, 6, 7]
         * 
         * after inserting e past h,
         * 
         * [H, i, , T, h, e,^ r, e]
         * [1, 2, 3, 4, 5, 6, 7, 8]
         * 
         * -- basically we want the cursor to act like a regular
         * cursor in a text document. That is, we want the cursor
         * to increment by one after we insert a character into the
         * document.
         * 
         * http://java.sun.com/javase/6/docs/api/javax/swing/text/Caret.html
         * Shows the Caret API used in the JTextPane. Although a JTextPane
         * will view/modify its Document, ultimately the interface needed to
         * operate on the JTextPane itself is its Caret.
         * 
         * I'm assuming that the JTextPane is listening for property and
         * key events, but I'm not entirely sure of the exact relationship
         * between the events occurring on the pane and the Caret's position.
         * 
         * This is an expirimental solution. There may be a way to send a
         * hint to the JTextPane (a flag, or maybe another command) such that
         * the Caret can be placed without requiring a reference to an existing
         * JTextPane. This method may not be incredibly practical, but it does
         * form a solution.
         */
        
        jp.getCaret().setDot(offs+1);
        
    }
}

thanks for the reply (and welcome to daniweb first post I see) I ended up doing that already, I figured it could be a temporary solution, until someone here might come up with something else, maybe it's the only way, I don't know

thanks for trying Intrade.

Anyone else have a suggestion?

Your caret is going to the end of the document because you're removing and re-inserting the entire content. The caret is just staying at the position of the last insert. You can fix that by changing these two lines in your SyntaxDocument2.insertString() method

String str2 = super.getText( 0, super.getLength() );
        
        super.remove( 0, super.getLength() );

slightly to get only the "upstream" portion of the document plus the string you are entering

String str2 = super.getText( 0, [B]offs+str.length()[/B] );

        super.remove( 0, [B]str2.length()[/B] );

That way you are only operating on the portion up to and including your current insert position and the caret stays at that position.

There is probably an easier way to do that with a DocumentFilter that doesn't require removing and re-inserting all of that content, but I've never used styled documents much so I can't say exactly without playing around with it more. You might take a look at them and see if it could save you all that insert, remove, and re-insert overhead.

Your caret is going to the end of the document because you're removing and re-inserting the entire content. The caret is just staying at the position of the last insert. You can fix that by changing these two lines in your SyntaxDocument2.insertString() method

String str2 = super.getText( 0, super.getLength() );
        
        super.remove( 0, super.getLength() );

slightly to get only the "upstream" portion of the document plus the string you are entering

String str2 = super.getText( 0, [B]offs+str.length()[/B] );

        super.remove( 0, [B]str2.length()[/B] );

That way you are only operating on the portion up to and including your current insert position and the caret stays at that position.

There is probably an easier way to do that with a DocumentFilter that doesn't require removing and re-inserting all of that content, but I've never used styled documents much so I can't say exactly without playing around with it more. You might take a look at them and see if it could save you all that insert, remove, and re-insert overhead.

If I understand the logic correctly--

-Step 2: grab the characters from position 0 to the Caret's current position plus the length of the String that was inserted

-Step 3: remove the characters from the document up to the already inserted String

//... And then of course iterate through each character up to the length of the removed content and insert each character given the appropriate styling per character.

Result: Given that the Caret is positioned at the location of the last insert, the Caret's new position should be at the position just before the 2nd half of the entire String.


If its true that the Caret is positioned at its last insert, couldn't we just insert a 0-lengthed String into the document at a desired position, or is that impossible, or did you provide your solution because it was more efficient?

Thanks

-Intrade

I just fixed the behavior that he was unhappy with, but yes, it is a bit more efficient. He was re-processing the entire content of the document when he only needed to re-process up to the current insertion point for the highlighting.

Comments
Very good information, thanks

Thanks for the information, I didn't realize that there were more posts here, because I didn't get the normal e-mail from daniweb. thanks i'll use that Ezzaral.

This question has already been answered. Start a new discussion instead.