All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.inet.jortho.AutoSpellChecker Maven / Gradle / Ivy

/*
 *  JOrtho
 *
 *  Copyright (C) 2005-2009 by i-net software
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License as 
 *  published by the Free Software Foundation; either version 2 of the
 *  License, or (at your option) any later version. 
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 *  USA.
 *  
 *  Created on 05.11.2005
 */
package com.inet.jortho;

import java.util.Locale;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.text.Highlighter.Highlight;

/**
 * This class check a JTextComponent automatically (in the background) for orthography. Spell error are
 * highlighted with a red zigzag line.
 * 
 * @author Volker Berlin
 */
class AutoSpellChecker implements DocumentListener, LanguageChangeListener {
    private static final RedZigZagPainter painter = new RedZigZagPainter();

    private final JTextComponent                jText;
    private final SpellCheckerOptions options;

    private Dictionary                    dictionary;

    private Locale                        locale;

    
    public AutoSpellChecker(JTextComponent text, SpellCheckerOptions options){
        this.jText = text;
        this.options = options == null ? SpellChecker.getOptions() : options;
        jText.getDocument().addDocumentListener( this );

        SpellChecker.addLanguageChangeLister( this );
        dictionary = SpellChecker.getCurrentDictionary();
        locale = SpellChecker.getCurrentLocale();
        checkAll();
    }

    /**
     * Remove the AutoSpellChecker from the given JTextComponent.
     * 
     * @param text
     *            the JTextComponent
     */
    static void disable( JTextComponent text ){
        AbstractDocument doc = (AbstractDocument)text.getDocument();
        for(DocumentListener listener : doc.getDocumentListeners()){
            if(listener instanceof AutoSpellChecker){
                AutoSpellChecker autoSpell = (AutoSpellChecker)listener;
                doc.removeDocumentListener( autoSpell );
                removeHighlights(text);
            }
        }
    }

    /**
     * Remove all SpellChecker highlights from the given JTextComponent.
     * 
     * @param text
     *            the JTextComponent
     */
	private static void removeHighlights( JTextComponent text ) {
        Highlighter highlighter = text.getHighlighter();
        for( Highlight highlight : highlighter.getHighlights() ) {
            if( highlight.getPainter() == painter ) {
                highlighter.removeHighlight( highlight );
            }
        }
    }
    
    /**
     * Refresh the highlighting. This can be useful if the dictionary was modify.
     * 
     * @param text
     *            the JTextComponent
     */
    static void refresh( JTextComponent text ){
        AbstractDocument doc = (AbstractDocument)text.getDocument();
        for(DocumentListener listener : doc.getDocumentListeners()){
            if( listener instanceof AutoSpellChecker ){
                AutoSpellChecker autoSpell = (AutoSpellChecker)listener;
                autoSpell.checkAll();
            }
        }
    }

    /*====================================================================
     * 
     * Methods of interface DocumentListener
     * 
     *===================================================================*/

    /**
     * {@inheritDoc}
     */
    public void changedUpdate( DocumentEvent ev ) {
        //Nothing
    }

    /**
     * {@inheritDoc}
     */
    public void insertUpdate( DocumentEvent ev ) {
        checkElements( ev.getOffset(), ev.getLength() );
    }

    /**
     * {@inheritDoc}
     */
    public void removeUpdate( DocumentEvent ev ) {
        checkElements( ev.getOffset(), 0 );
    }

    /**
     * Check the Elements on the given position.
     */
    private void checkElements( int offset, int length ) {
        int end = offset + length;
        Document document = jText.getDocument();
        Element element;

        do{
            try {
                // We need to use a ParagraphElement because a CharacterElement produce problems with formating in a word
                element = ((AbstractDocument)document).getParagraphElement( offset );
            } catch( java.lang.Exception ex ) {
                return;
            }
            checkElement( element );
            int endOffset = element.getEndOffset();
            offset = endOffset > offset ? endOffset : offset + 1;
        }while( offset <= end && offset < document.getLength() );
    }

    /**
     * Check the spelling of the text of an element.
     * 
     * @param element
     *            the to checking Element
     */
    private void checkElement( javax.swing.text.Element element ) {
        try {
            int i = element.getStartOffset();
            int j = element.getEndOffset();
            Highlighter highlighter = jText.getHighlighter();
            Highlight[] highlights = highlighter.getHighlights();
            for( int k = highlights.length; --k >= 0; ) {
                Highlight highlight = highlights[k];
                int hlStartOffset = highlight.getStartOffset();
                int hlEndOffset = highlight.getEndOffset();
                if( (i <= hlStartOffset && hlStartOffset <= j) || 
                    (i <= hlEndOffset && hlEndOffset <= j) ) {
                    if( highlight.getPainter() == painter ) {
                        highlighter.removeHighlight( highlight );
                    }
                }
            }

            int l = ((AbstractDocument)jText.getDocument()).getLength();
            j = Math.min( j, l );
            if( i >= j )
                return;

            // prevent a NPE if the dictionary is currently not loaded.
            Dictionary dic = dictionary;
            Locale loc = locale;
            if( dic == null || loc == null ){
                return;
            }
            
            Tokenizer tok = new Tokenizer( jText, dic, loc, i, j, options );
            String word;
            while( (word = tok.nextInvalidWord()) != null ) {
                int wordOffset = tok.getWordOffset();
                highlighter.addHighlight( wordOffset, wordOffset + word.length(), painter );
            }
        } catch( BadLocationException e ) {
        	SpellChecker.getMessageHandler().handleException( e );
        }
    }

    /**
//     * Check the completely text. Because this can consume many times with large Documents that this will do in a thread
     * in the background step by step.
     */
    private void checkAll() {
        if( jText == null ) {
            //the needed objects does not exists
            return;
        }
        if( dictionary == null ) {
            removeHighlights( jText );
            return;
        }
        if( jText.getDocument().getLength() == 0 ){
            // no text, no highlights
            return;
        }

        Thread thread = new Thread( new Runnable() {
            public void run() {
                Document document = jText.getDocument();
                for( int i = 0; i < document.getLength(); ) {
                    try {
                        final Element element = ((AbstractDocument)document).getParagraphElement( i );
                        i = element.getEndOffset();
                        SwingUtilities.invokeLater( new Runnable() {
                            public void run() {
                                checkElement( element );
                            }

                        } );
                    } catch( java.lang.Exception ex ) {
                        return;
                    }
                }
            }
        }, "JOrtho checkall" );
        thread.setPriority( Thread.NORM_PRIORITY - 1 );
        thread.setDaemon( true );
        thread.start();
    }

    /**
     * {@inheritDoc}
     */
    public void languageChanged( LanguageChangeEvent ev ) {
        dictionary = SpellChecker.getCurrentDictionary();
        locale = SpellChecker.getCurrentLocale();
        checkAll();
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy