Package org.gocha.gui

Source Code of org.gocha.gui.Highlighter

/*******************************************************************************
gocha.org-lib-java Библеотека общего назначения
(с) Камнев Георгий Павлович 2009 GPLv2

Данная программа является свободным программным обеспечением. Вы вправе
распространять ее и/или модифицировать в соответствии с условиями версии 2
либо по вашему выбору с условиями более поздней версии
Стандартной Общественной Лицензии GNU, опубликованной Free Software Foundation.

Мы распространяем данную программу в надежде на то, что она будет вам полезной,
однако НЕ ПРЕДОСТАВЛЯЕМ НА НЕЕ НИКАКИХ ГАРАНТИЙ,
в том числе ГАРАНТИИ ТОВАРНОГО СОСТОЯНИЯ ПРИ ПРОДАЖЕ
и ПРИГОДНОСТИ ДЛЯ ИСПОЛЬЗОВАНИЯ В КОНКРЕТНЫХ ЦЕЛЯХ.
Для получения более подробной информации ознакомьтесь
со Стандартной Общественной Лицензией GNU.

Вместе с данной программой вы должны были получить экземпляр
Стандартной Общественной Лицензии GNU.
Если вы его не получили, сообщите об этом в Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*******************************************************************************/

package org.gocha.gui;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyledDocument;
import org.gocha.gui.highlight.TextStyleObject;
import org.gocha.gui.highlight.TextStyles;
import org.gocha.text.regex.Matcher;
import org.gocha.text.regex.Pattern;

/**
* @author gocha
*/
public class Highlighter
{
    private JTextPane textPane = null;
    private String oldText = null;
    private Timer timer = null;
    private boolean useBackgroundThread = true;
    private Thread thread = null;

    public Highlighter()
    {
        initTimer();
    }

    public void reinit()
    {
        reinitStyles();
        highlight();
    }

    private void reinitStyles()
    {
        if( this.styles!=null && getTextPane()!=null )
        {
            this.styles.applyTo(getTextPane());
        }
    }

    private TextStyles styles = null;

    public TextStyles getStyles()
    {
        return styles;
    }

    public void setStyles(TextStyles styles)
    {
        this.styles = styles;
        reinitStyles();
    }

    public JTextPane getTextPane()
    {
        return textPane;
    }

    public void setTextPane(JTextPane textPane)
    {
        this.textPane = textPane;
        needHighlight = true;
    }

    private Pattern pattern = null;

    public Pattern getPattern()
    {
        return pattern;
    }

    public void setPattern(Pattern pattern)
    {
        this.pattern = pattern;
    }

    private boolean needHighlight = false;

    public void stop()
    {
        start(false);
    }

    public void start()
    {
        start(true);
    }

    public void start(boolean start)
    {
        if( isStarted()!=start )
        {
            if( start )
            {
                if( timer!=null )
                {
                    timer.start();
                }

                if( useBackgroundThread )
                {
                    if( thread==null )
                    {
                        thread = new Thread(backgroundHighlightWorker);
                        thread.setDaemon(true);
                        thread.setPriority(Thread.MIN_PRIORITY);
                    }
                    if( !thread.isAlive() )thread.start();
                }
            }else{
                if( timer!=null )
                {
                    timer.stop();
                }

                if( useBackgroundThread )
                {
                    if( thread!=null && thread.isAlive() )
                    {
                        thread.interrupt();
                        while(true)
                        {
                            try {
                                Thread.sleep(10);
                            }
                            catch (InterruptedException ex) {
                                break;
                            }

                            if( !thread.isAlive() )break;
                        }
                    }
                   
                    if( thread!=null && thread.isAlive() )
                    {
                        thread.stop();
                    }

                    thread = null;
                }
            }
        }
    }

    public boolean isStarted()
    {
        if( timer==null )return false;
        return timer.isRunning();
    }

    private Style clearStyle = null;
    public void clearHighlight()
    {
        JTextPane tpane = getTextPane();
        if( tpane==null )return;

        if( clearStyle==null )
            clearStyle = tpane.getStyledDocument().addStyle(null, null);

        StyledDocument sdoc = tpane.getStyledDocument();
        int len = sdoc.getLength();
        if( len>0 )
        {
            sdoc.setCharacterAttributes(0, len, clearStyle, true);
        }
    }

    public int getDelay()
    {
        if( timer==null )return 1000;
        return timer.getDelay();
    }

    public void setDelay(int delay)
    {
        boolean started = isStarted();
        stop();
        if( timer!=null )timer.setDelay(delay);
        start(started);
    }

    private void initTimer() {
        timer = new Timer(1000, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onTimer();
            }
        });
//        timer.start();
    }

    private void onTimer()
    {
        checkChanged();
        if( needHighlight )
        {
            highlight();
            needHighlight = false;
        }
    }

    /**
     * Возвращает текущий редактируемый текст
     * @return Текущий редактируемый текст
     */
    private String existsText()
    {
        if( textPane==null )return "";
       
        int len = textPane.getStyledDocument().getLength();
        String txt = null;
        try {
            txt = textPane.getStyledDocument().getText(0, len);
        } catch (BadLocationException ex) {
            ex.printStackTrace();
        }
        return txt;
    }

    /**
     * Проверяет необходимо ли произвести подсветку
     */
    private void checkChanged()
    {
        if( oldText==null )
        {
            oldText = existsText();
            needHighlight = true;
            return;
        }

        String existsTxt = existsText();
        if( !oldText.equals(existsTxt) )
        {
            oldText = existsTxt;
            needHighlight = true;
        }
    }

    private void highlight()
    {
        if( textPane==null )return;
        if( pattern==null )return;
        if( styles==null )return;

        if( useBackgroundThread )
        {
            addHighlightRequest(textPane, pattern, styles);
        }else{
            awtThreadHighlight();
        }
    }

    /**
     * Выполняется в потоке AWT
     */
    private void awtThreadHighlight()
    {
        StyledDocument sdoc = textPane.getStyledDocument();
        int sdocLen = sdoc.getLength();
        String sdocText = null;
        try {
            sdocText = sdoc.getText(0, sdocLen);
        }
        catch (BadLocationException ex) {
            ex.printStackTrace();
        }
        if( sdocText==null )return;

        clearApplyStyleQueue();
        Matcher matched = pattern.match(sdocText, 0);

        if( matched.isMatched() )
        {
            for( Matcher m : matched.walk() )
            {
                addMatched(m);
            }
        }

        execApplyStyleQueue();
    }

    /**
     * Задания фоновому потоку на подстветку
     */
    private final Queue<Runnable> queueHighlightRequest = new ArrayDeque<Runnable>();

    /**
     * Добавляем задание на перерисовку/подстветку синтаксиса
     * @param textPane Подсвечиваемый текст
     * @param ptrn Синтаксис
     * @param styles Стиль подстветки
     */
    private void addHighlightRequest(JTextPane textPane, Pattern ptrn, TextStyles styles)
    {
        if( textPane==null )return;
        if( ptrn==null )return;
        if( styles==null )return;
       
        StyledDocument sdoc = textPane.getStyledDocument();
        int sdocLen = sdoc.getLength();
        String __sourceText = null;
       
        try {
            __sourceText = sdoc.getText(0, sdocLen);
        }
        catch (BadLocationException ex) {
            return;
        }

        // Исходный текст
        final String fSourceText = __sourceText;
        final Pattern fPattern = ptrn;

        Runnable bgRun = new Runnable() {
            @Override
            public void run()
            {
                final Matcher matched = fPattern.match(fSourceText, 0);
                if( !matched.isMatched() )return;

                Runnable awtRun = new Runnable() {
                    @Override
                    public void run()
                    {
                        String nowText = existsText();
                        if( !fSourceText.equals(nowText) )return;

                        clearApplyStyleQueue();
                        for( Matcher m : matched.walk() )
                        {
                            addMatched(m);
                        }
                        execApplyStyleQueue();
                    }
                };

                SwingUtilities.invokeLater(awtRun);
            }
        };

        synchronized( queueHighlightRequest )
        {
            queueHighlightRequest.add(bgRun);
        }
    }

    /**
     * Фоновый поток (тело цикла).
     * Читает задания на подсветку синтаксиса и выполняет последнее задание.
     */
    private Runnable backgroundHighlightWorker = new Runnable() {
        @Override
        public void run()
        {
//            System.out.println("Background highlight started");

            while(true)
            {
                if( Thread.interrupted() )break;
                Runnable job = null;

                // Берем последнее (актуальное) задание
                synchronized(queueHighlightRequest)
                {
                    while(!queueHighlightRequest.isEmpty())
                    {
                        job = queueHighlightRequest.poll();
                    }
                }

                // Запускаем задание на исполнение
                if( job!=null )job.run();

                try {
                    Thread.sleep(10);
                }
                catch (InterruptedException ex) {
//                    Logger.getLogger(Highlighter.class.getName()).log(Level.SEVERE, null, ex);
                    break;
                }
            }

//            System.out.println("Background highlight stopped");
        }
    };

    private Queue<Runnable> queueApplyStyle = new ArrayDeque<Runnable>();

    private void clearApplyStyleQueue()
    {
        queueApplyStyle.clear();
    }

    private void execApplyStyleQueue()
    {
        while(true)
        {
            Runnable r = queueApplyStyle.poll();
            if( r==null )break;
            r.run();
        }
    }

    private void addMatched(Matcher m)
    {
        if( m==null )return;

        Runnable r = createApplyStyle(m);
        if( r==null )return;

        queueApplyStyle.add(r);
    }

    private Runnable createApplyStyle(Matcher m)
    {
        final Matcher fM = m;

        return new Runnable() {
            @Override
            public void run()
            {
                String name = fM.getName();
                if( name!=null )applyStyle(name, fM);
            }
        };
    }

    private void applyStyle(String id,Matcher m)
    {
        if( id==null )return;
        if( m==null )return;
        if( !m.isMatched() )return;
        if( getStyles()==null )return;

        JTextPane tp = getTextPane();
        if( tp==null )return;

        StyledDocument doc = tp.getStyledDocument();
        TextStyleObject so = getStyles().get(id);
        Style s = so==null ? null : so.getStyle();
        if( s==null )return;

        int begin = m.getBegin();
        int len = m.getLength();

        doc.setCharacterAttributes(begin, len, s, true);
    }
}
TOP

Related Classes of org.gocha.gui.Highlighter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.