SyntaxHighlighter

quarta-feira, 13 de outubro de 2010

Um console java bacana

Na correria pra fechar todas as tarefas antes do feriadão acabei não conseguindo publicar este post dentro do prazo que gostaria. Acabei subestimando o uso dos componentes de texto imaginando que um simples uso de método atenderia minhas necessidades. De qualquer modo, inspirado pelas belas praias de Maceió, consegui finalmente colocar no ar um componente console amigável capaz de formatar as entradas de conteúdo e ainda por cima controlar a quantidade de linhas inseridas neste componente.




Essa semana pintou a dúvida de como fazer um console em java. Na verdade a necessidade era criar um painel onde pudesse diferenciar a formatação do texto (especialmente a cor) dependendo da fonte de envio, por exemplo, para os logs vindos do banco de dados imprimiria em azul, os logs do modem, em verde, e assim por diante.

Antes de pensar em construir um console o primeiro componente que tentei utilizar foi o JTextArea. Como havia comentado, meu objetivo era diferenciar visualmente os logs vindos de diferentes partes da aplicação, basicamente modificando a cor de exibição do texto. Pesquisando um pouco descobri que o JTextArea lhe permite modificar os atributos do texto porém de modo uniforme, ou seja, para todo o componente. Essa descoberta inviabilizou o uso deste componente e me fez pensar em como seria a implementação do que eu estava precisando, certamente não era o primeiro e nem o último a precisar disso.

Parti então para a implementação de um console onde pudesse na adição de novas mensagens especificar o formato desejado de exibição do texto. Os componentes para quem precisa manter diferentes formatos de texto/conteúdo são o JEditorPane e o JTextPane. Pra minha surpresa, nenhum dos meus livros cobria estes dois componentes, apenas deixava uma referência a um livro específico sobre componentes swing e uma mensagem dizendo que se tratava de componentes complexos :-(. De fato os componentes JEditorPane e JTextPane são bastante complexos, no entanto, para o uso no qual precisava, não foi necessário estuda-los completamente.

Já que a idéia era construir um console, pensei em um atributo que gostaria de encontrar em qualquer proposta deste genero, um setup de quantidade máxima de dados mantidos no console. Uma vez ultrapassado este valor, o próprio console remove o excedente, como uma FIFO, mantendo um certo controle sobre a quantidade de memória utilizada pelo componente. Os demais atributos seriam a possibilidade de setup rápido das mensagem enviadas, facilitando a vida de quem utiliza o console.

O código abaixo, embora extenso, detalha completamente a implementação do console proposto. A primeira parte descreve a implementação da classe JavaMilkConsole e a segunda parte demonstra o uso deste componente (imagem exibida inicialmente). O código está bastante comentado afim de facilitar a compreensão de todos (qualquer nível).

package javamilk;

import java.awt.Color;
import java.awt.Font;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

/**
 * Essa classe implementa um console de visualização extendendo o
 * componente JTextPane
 * @author giuliano
 */
public class JavaMilkConsole extends JTextPane{

    //Constantes
    private static final Color TEXTCOLOR = Color.BLACK;
    private static final Color TEXTBGCOLOR = Color.WHITE;
    private static final int TEXTFONTSIZE = 12;
    private static final String TEXTFONTFAMILY = Font.SANS_SERIF;
    private static final Boolean TEXTBOLD = false;
    private static final int MAXROW = 100;

    //Variáveis que mantém o setup atual do estilo
    private Color textColor = TEXTCOLOR;
    private Color textBgColor = TEXTBGCOLOR;
    private int textFontSize = TEXTFONTSIZE;
    private String textFontFamily = TEXTFONTFAMILY;
    private Boolean textBold = TEXTBOLD;

    //Strings que definem os estilos que compoe este console
    private static final String PADRAO = "padrao";

    //Conteúdo exibido no console
    private StyledDocument conteudo;

    //Estilos que criaremos no console
    private Style padrao;    //Estilo padrão - único para este console

    //A classe console oferece um modo de escrita semelhante ao da classe
    //System. Por essa razão o objeto "out" foi mantido público.
    //Exempo de uso será. objJavaMilkConsole.out.println("minha msg");
    public PrintWriter out;

    //Buffer do console
    int[] bufRows;  //Buffer que guarda a quantidade de caracteres por linha
    int actNumRows; //Numero atual de rows no console
    int p2Fill;     //Aponta para a posição a ser preenchida
    int p2Remove;   //Aponta para a posição a ser removida

    
    /**
     * Construtor padrão da classe JavaMilkConsole
     * @param maxRow Tamanho do buffer de linhas do console. Deve ser maior ou
     * igual a 1, caso contrário o tamanho padrão será assumido.
     */
    public JavaMilkConsole(int maxRow){
        //Já que a idéia é ser um console de visualização vamos inibir a edição
        setEditable(false);

        //JavaMilkConsole herda tudo de JTextPane e consequentemente de JEditorPane
        //Essa duas classes possuem como modelo de dados a classe StyledDocument
        //Vamos puxar uma referência a este modelo pra que possamos adicionar
        //os estilos que queremos mostrar no console
        conteudo = this.getStyledDocument();

        //O modelo de dados StyledDocument trabalha em hierarquia, dessa forma
        //podemos criar um estilo padrão e descender os demais. A influencia
        //disso é que uma vez configurado um parâmetro no estilo pai os demais
        //herdam essa característica.
        //Como a idéia deste console é permitir que o usuário modifique a
        //qualquer momento a configuração de estilo (cor, fundo, fonte, etc) não
        //faz sentido oferecer mais um estilo (pelo menos não neste momento).
        padrao = conteudo.addStyle(PADRAO, null);  //estilo padrão é o root

        //Agora que temos um estilo definido, vamos fazer a configuração default
        //dele. Daqui a pouco iremos implementar os métodos que permitirão
        //modificar isso em tempo de execução.
        //Pra configurar cada estilo, vamos usar a classe StyleContants
        //Repare que o primeiro argumento é sempre o nome do estilo, seguido
        //da configura referente ao método.
        StyleConstants.setFontFamily(padrao,TEXTFONTFAMILY);
        StyleConstants.setFontSize(padrao, TEXTFONTSIZE);
        StyleConstants.setForeground(padrao, TEXTCOLOR);
        StyleConstants.setBackground(padrao, TEXTBGCOLOR);
        StyleConstants.setBold(padrao, TEXTBOLD);

        //Como dito anteriormente, oferecemos um mecanismo de escrita no console
        //semelhante ao da classe "System".
        //Pra que isso seja possível, precisamos utilizar interna que direcione
        //o fluxo de dados para dentro do nosso console. Veja a implementação
        //da classe ConsoleWriter.
        out = new PrintWriter(new ConsoleWriter());

        //Inicializando buffer
        if(maxRow<1) maxRow=MAXROW;
        bufRows = new int[maxRow+1];
        actNumRows = 0;
        p2Fill = 0;
        p2Remove = 0;
    }

    /**
     * Construtor sobrecarregado sem a opção de especificar tamanho do buffer.
     * Neste caso o tamanho é o default configurado na classe.
     */
    public JavaMilkConsole(){
        this(MAXROW);
    }

    /**
     * Método publico que permite modificar a cor de exibição do texto
     * @param cor cor do texto que desejamos
     */
    public void setFontForeground(Color cor){
        textColor = cor;
        StyleConstants.setForeground(padrao, textColor);
    }

    /**
     * Método público que permite modificar a cor de fundo do texto
     * @param cor cor de fundo que desejamos
     */
    public void setFontBackground(Color cor){
        textBgColor = cor;
        StyleConstants.setBackground(padrao, textBgColor);
    }

    /**
     * Método público que permite modificar o tamanho da fonte
     * @param tamanho Tamanho de exibição da fonte
     */
    public void setFontSize(int tamanho){
        textFontSize = tamanho;
        StyleConstants.setFontSize(padrao, textFontSize);
    }

    /**
     * Método público que pemite modifica a familia a qual a fonte pertence
     * @param familia Familia da fonte
     */
    public void setFontFamily(String familia){
        textFontFamily = familia;
        StyleConstants.setFontFamily(padrao, textFontFamily);
    }
    
    /**
     * Método público que permite tornar o texto bold (negrito)
     * @param bold
     */
    public void setFontBold(Boolean negrito){
        textBold = negrito;
        StyleConstants.setBold(padrao, textBold);
    }

    /**
     * Apaga todo o conteúdo exibido atualmente no console.
     * Vale lembrar que a implementação deste método não é thread safety
     * (pretendo mostrar isso em outro post).
     */
    public void clearContents(){
        try
        {
            //remove todo o conteúdo do console
            conteudo.remove(0, conteudo.getLength());
            //Zera as todo buffer
            resetBuffer();
        } catch (BadLocationException ex)
        {
           ex.printStackTrace();
        }
    }

    /**
     * Adiciona uma mensagem no console. Lembre-se que este método não adiciona
     * por padrão a quebra de linha, logo, caso queira esse efeito, adicione o
     * caracter especial \n em sua mensagem.
     * @param msg Mensagem a ser adicionada no console
     */
    public void appendMessage(String msg){
        insertMessage(msg);
    }

    /**
     * Adiciona uma mensagem no console, na cor especificada.
     * Lembre-se que este método não adiciona por padrão a quebra de linha,
     * logo, caso queira esse efeito, adicione o caracter especial \n em sua
     * mensagem.
     * @param msg Mensagem a ser adicionada no console
     * @param corTexto Cor do texto
     */
    public void appendMessage(String msg, Color corTexto){
        //Alteramos a cor do texto do estilo padrão
        StyleConstants.setForeground(padrao, corTexto);
        //Adicionamos a mensagem no console - com o estilo modificado
        insertMessage(msg);
        //Voltamos o estilo ao setup anterior, já que esta chamada não
        //deve modificar permanentemente o estilo. As demais chamadas seguiram
        //o estilo definido no console (por padrão ou através de métodos
        //específicos).
        StyleConstants.setForeground(padrao, textColor);
    }

    /**
     * Adiciona uma mensagem no console, na cor especificada (texto e fundo).
     * Lembre-se que este método não adiciona por padrão a quebra de linha,
     * logo, caso queira esse efeito, adicione o caracter especial \n em sua
     * mensagem.
     * @param msg Mensagem a ser adicionada no console
     * @param corTexto Cor do texto
     * @param corFundoTexto Cor de fundo do texto
     */
    public void appendMessage(String msg, Color corTexto, Color corFundoTexto){
        //Alteramos a cor do texto do estilo padrão
        StyleConstants.setForeground(padrao, corTexto);
        //Alteramos a cor de fundo
        StyleConstants.setBackground(padrao, corFundoTexto);
        //Adicionamos a mensagem no console - com o estilo modificado
        insertMessage(msg);
        //Voltamos o estilo ao setup anterior, já que esta chamada não
        //deve modificar permanentemente o estilo. As demais chamadas seguiram
        //o estilo definido no console (por padrão ou através de métodos
        //específicos).
        StyleConstants.setForeground(padrao, textColor);
        StyleConstants.setBackground(padrao, textBgColor);
    }
    
    /**
     * Adiciona uma mensagem no console, na cor especificada (texto e fundo) e
     * em negrito.
     * Lembre-se que este método não adiciona por padrão a quebra de linha,
     * logo, caso queira esse efeito, adicione o caracter especial \n em sua
     * mensagem.
     * @param msg Mensagem a ser adicionada no console
     * @param corTexto Cor do texto
     * @param corFundoTexto Cor de fundo do texto
     * @param negrito se o texto deve ser impresso em negrito
     */
    public void appendMessage(String msg, Color corTexto, Color corFundoTexto,
            Boolean negrito){
        //Alteramos a cor do texto do estilo padrão
        StyleConstants.setForeground(padrao, corTexto);
        //Alteramos a cor de fundo
        StyleConstants.setBackground(padrao, corFundoTexto);
        //Alteramos o estilo
        StyleConstants.setBold(padrao, negrito);
        //Adicionamos a mensagem no console - com o estilo modificado
        insertMessage(msg);
        //Voltamos o estilo ao setup anterior, já que esta chamada não
        //deve modificar permanentemente o estilo. As demais chamadas seguiram
        //o estilo definido no console (por padrão ou através de métodos
        //específicos).
        StyleConstants.setForeground(padrao, textColor);
        StyleConstants.setBackground(padrao, textBgColor);
        StyleConstants.setBold(padrao, textBold);
    }

    /**
     * Adiciona uma mensagem no console em negrito.
     * Lembre-se que este método não adiciona por padrão a quebra de linha,
     * logo, caso queira esse efeito, adicione o caracter especial \n em sua
     * mensagem.
     * @param msg Mensagem a ser adicionada no console
     * @param negrito se o texto deve ser impresso em negrito
     */
    public void appendMessage(String msg, Boolean negrito){
        //Alteramos o estilo
        StyleConstants.setBold(padrao, negrito);
        //Adicionamos a mensagem no console - com o estilo modificado
        insertMessage(msg);
        //Voltamos o estilo ao setup anterior, já que esta chamada não
        //deve modificar permanentemente o estilo. As demais chamadas seguiram
        //o estilo definido no console (por padrão ou através de métodos 
        //específicos).
        StyleConstants.setBold(padrao, textBold);
    }

    //Métodos internos
    /**
     * Método interno para adicionar conteúdo no console.
     * Este método é thread safety. Por alguma razão se não for implementado
     * deste modo (thread safety) o scroll automático não ocorre quando a
     * inserção do texto é feito dentro de uma thread (veja o exemplo de uso
     * deste console). Vou investigar melhor isso e coloco em um post futuro.
     * @param msg Mensagem a ser adicionada no console.
     */
    private void insertMessage(final String msg){

        Runnable threadSafetyOp = new Runnable(){
            public void run()
            {
                try
                {
                    //Adicionamos a nova mensagem no conteúdo do console
                    //Repare que a msg é adicionada no termino do conteúdo atual
                    //e o estilo utilizado é o padrao definido pro console
                    conteudo.insertString(conteudo.getLength(), msg, padrao);

                    //Chama o método para atualizar o controlador de linhas do console
                    //Remove (se necessário) as linhas excedentes
                    updateConsole(msg);

                } catch (BadLocationException ex)
                {
                    ex.printStackTrace();
                }
            }
        };

        if(SwingUtilities.isEventDispatchThread()){
            threadSafetyOp.run();
        }
        else{
            SwingUtilities.invokeLater(threadSafetyOp);
        }
    }
    
    /**
     * Ultima mensagem inserida no buffer
     * @param msg
     */
    private void updateConsole(String msg){
        //Varre toda a mensagem
        for(int c=0;c= bufRows.length){
                    //Significa que precisamos remover conteúdo do console
                    try
                    {
                        //Remove linha apontada
                        conteudo.remove(0, bufRows[p2Remove]);
                        //Zera a posição no buffer
                        bufRows[p2Remove]=0;
                        //move ponteiro para a proxima linha
                        if(p2Remove >= bufRows.length-1){
                            //Se for a ponta do buffer, retorne para o inicio
                            p2Remove=0;
                        }
                        else{
                            //Caso contrário, incremente-o
                            p2Remove++;
                        }
                    }
                    catch (BadLocationException ex)
                    {
                        ex.printStackTrace();
                    }
                }
                else{
                    actNumRows++;
                }

                //Atualize o buffer
                bufRows[p2Fill] = bufRows[p2Fill] + 1;
                if(p2Fill >= bufRows.length-1){
                    //Se for a ponta do buffer, retorne para o inicio
                    p2Fill=0;
                }
                else{
                    //Caso contrário, incremente-o
                    p2Fill++;
                }
            }
            else{
               //atualizo a quantidade de caracteres da linha atual
                bufRows[p2Fill] = bufRows[p2Fill] + 1;
            }
        }
    }
    
    /**
     * Inicializa conteúdo buffer e coloca os ponteiros em posição de reset.
     */
    private void resetBuffer(){
        actNumRows = 0;
        p2Fill = 0;
        p2Remove = 0;
        //Reinicializa valor do buffer
        for(int i=0; i < bufRows.length; i++){
            bufRows[i]=0;
        }
    }

    //Classes internas
    //Com a implementação da classe abstrata Writer poderemos direcionar o
    //fluxo de dados para dentro do nosso console
    private class ConsoleWriter extends Writer{

        @Override
        public void write(char[] cbuf, int off, int len) throws IOException
        {
            //Adiciona a nova mensagem no console
            insertMessage(new String(cbuf,off,len));
        }

        @Override
        public void flush() throws IOException
        {
            //Não faremos nada
        }

        @Override
        public void close() throws IOException
        {
            //Não faremos nada
        }
    }
}

Agora o código exemplo de uso do componente JavaMilkConsole.

package javamilk;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;

/**
 * Classe simples pra demonstrar o uso do componente JavaMilkConsole.
 * @author giuliano
 */
public class JavaMilkDemo {

    public JavaMilkDemo(){
        //Criamos os alguns objetos de controle
        JButton printMsg = new JButton("Imprimir Mensagem");
        JButton clearConsole = new JButton("Limpar Console");
        JButton threadMsg = new JButton("Thread de Mensagens");
        final JTextField textMsg = new JTextField();

        //Painel que irá abrigar os controles - botões e caixa de texto
        JPanel painelControle = new JPanel();
        //Borda do painel de controle
        painelControle.setBorder(new EmptyBorder(5,0,5,0));
        //Layout do painel de controle - horizontal
        painelControle.setLayout(new BoxLayout(painelControle,BoxLayout.X_AXIS));
        //Adicionamos os componentes de controle que criamos - a ordem
        //de inserção é importante
        painelControle.add(textMsg);
        //Criar um componente invisivel pra espaçar um componente de outro
        painelControle.add(Box.createHorizontalStrut(5));
        //repetimos os 2 passos anteriores até adicionar todos os componentes
        painelControle.add(printMsg);
        painelControle.add(Box.createHorizontalStrut(5));
        painelControle.add(clearConsole);
        painelControle.add(Box.createHorizontalStrut(5));
        painelControle.add(threadMsg);

        //Segund painel de componentes - para customizar a mensagem
        final JButton fgColorText = new JButton("Cor do texto");
        fgColorText.setBackground(Color.BLUE);
        final JButton bgColorText = new JButton("Cor de fundo do texto");
        bgColorText.setBackground(Color.WHITE);
        final JCheckBox boldText = new JCheckBox("Negrito");
        //Painel para abrigar os controles acima
        final JPanel painelSetup = new JPanel();
        painelSetup.setBorder(new EmptyBorder(5,0,5,0));
        painelSetup.setLayout(new BoxLayout(painelSetup,BoxLayout.X_AXIS));
        //Adicionando os componentes
        painelSetup.add(boldText);
        painelSetup.add(Box.createHorizontalStrut(5));
        painelSetup.add(fgColorText);
        painelSetup.add(Box.createHorizontalStrut(5));
        painelSetup.add(bgColorText);

        //
        JPanel painelControleSetup = new JPanel();
        painelControleSetup.setBorder(new EmptyBorder(5,0,5,0));
        painelControleSetup.setLayout(new BorderLayout());
        painelControleSetup.add(painelControle,BorderLayout.NORTH);
        painelControleSetup.add(painelSetup,BorderLayout.SOUTH);


        //Agora vamos criar o um objeto console - repare que passei o argumento
        //50 no construtor, o que significa que teremos no máximo 50 linhas no
        //console.
        final JavaMilkConsole console = new JavaMilkConsole(50);
        //Se for o caso de fazermos algum setup no console, este pode
        //ser feito aqui (cor do texto, fundo, tamanho do buffer, etc)
        //console.setBackground(Color.GREEN);
        //console.setFontBackground(Color.GREEN);
        
        //Vamos criar um painel pra abrigar o console + painel de controles
        JPanel painelPrincipal = new JPanel();
        //definir a borda do painel
        painelPrincipal.setBorder(new EmptyBorder(5,5,5,5));
        //definir um gerenciador de layout
        painelPrincipal.setLayout(new BorderLayout());
        //Finalmente adicionar os componentes: console + painel de controles
        painelPrincipal.add(new JScrollPane(console), BorderLayout.CENTER);
        //painelPrincipal.add(painelControle, BorderLayout.SOUTH);
        painelPrincipal.add(painelControleSetup,BorderLayout.SOUTH);

        //Até aqui temos nossos paineis criados - falta adiciona-los em uma
        //janela (JFrame) e criar a lógica de controle

        //Criando a janela
        JFrame janela = new JFrame();
        janela.setTitle("JavaMilk Demo - como usar o JavaMilkConsole");
        janela.setContentPane(painelPrincipal);
        janela.getRootPane().setDefaultButton(printMsg);
        janela.setSize(740, 480);
        janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        janela.setVisible(true);

        //Criando a lógica de controle
        //Quando clicarmos no botão printMsg
        printMsg.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e)
            {
                //Imprima mensagem de texto, usando o setup de cor texto,
                //cor de fundo, bold e font
                console.appendMessage(textMsg.getText() + "\n",
                        fgColorText.getBackground(),bgColorText.getBackground(),
                        boldText.isSelected());
                //console.appendMessage(textMsg.getText() + "\n",true);
            }
        });

        //Quando clicarmos no botão clearConsole
        clearConsole.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e)
            {
                //Apaga todo conteúdo do console
                console.clearContents();
            }
        });

        //Quando clicarmos no botão threadMsg
        threadMsg.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent e)
            {
                //Criar uma thread pra ficar imprimindo no console
                //usando o modo stream
                new Thread(){
                    @Override
                    public void run(){
                        for(int i=0; i<100;i++){
                            try
                            {
                                console.out.println("Mensagem " + i + " vinda da thread " + this.getName());
                                //Coloque a thread em sleep por um tempo
                                Thread.sleep(500);
                            } catch (InterruptedException ex)
                            {
                                ex.printStackTrace();
                            }
                        }
                    }
                }.start();
            }
        });

        //Quando clicamos no botão fgColorText
        fgColorText.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent e)
            {
                Color fgColor = JColorChooser.showDialog(painelSetup, "", fgColorText.getBackground());
                if(fgColor != null){
                    fgColorText.setBackground(fgColor);
                }
            }
        });

        //Quando clicarmos no botão bgColorText
        bgColorText.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent e)
            {
                Color bgColor = JColorChooser.showDialog(painelSetup, "", bgColorText.getBackground());
                if(bgColor != null){
                    bgColorText.setBackground(bgColor);
                }
            }
        });

    }


    public static void main(String[] args){

        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new JavaMilkDemo();
            }
        });

    }
}

Confesso que com a inserção do código o post ficou gigantesco. Embora pareça assustador mais da metado do contéudo são comentário que deixei no código afim de facilitar quem se aventurar a entende-lo. A construção deste componente trouxe a necessidade de aprender componentes novos e complexos e rever diversos conceitos da linguagem. Avalio como positivo, afinal o que vale é a jornada. Até a próxima.


Referências
1 - Livro Core Swing: Advanced Programming
2 - Tutorial sobre componentes de texto - Text Swing
3 - Exemplo de implementação de um console simples usando o JTextArea
4 - Tutorial sobre como utilizar o componente JColorChooser
5 - Ótimo exemplo de implementação de um console usando JTextPane

4 comentários:

  1. Esse trecho do exemplo que você deixou está incompleto, você tem o restante dele ainda?

    private void updateConsole(String msg){
    //Varre toda a mensagem
    for(int c=0;c= bufRows.length){
    //Significa que precisamos remover conteúdo do console
    try
    {
    //Remove linha apontada
    conteudo.remove(0, bufRows[p2Remove]);
    //Zera a posição no buffer
    bufRows[p2Remove]=0;
    //move ponteiro para a proxima linha
    if(p2Remove >= bufRows.length-1){
    //Se for a ponta do buffer, retorne para o inicio
    p2Remove=0;
    }
    else{
    //Caso contrário, incremente-o
    p2Remove++;
    }
    }
    catch (BadLocationException ex)
    {
    ex.printStackTrace();
    }
    }
    else{
    actNumRows++;
    }

    //Atualize o buffer
    bufRows[p2Fill] = bufRows[p2Fill] + 1;
    if(p2Fill >= bufRows.length-1){
    //Se for a ponta do buffer, retorne para o inicio
    p2Fill=0;
    }
    else{
    //Caso contrário, incremente-o
    p2Fill++;
    }
    }
    else{
    //atualizo a quantidade de caracteres da linha atual
    bufRows[p2Fill] = bufRows[p2Fill] + 1;
    }
    }
    }

    ResponderExcluir
  2. Oi Alex, tudo bem?

    O código parece completo ... me mande seu email e eu lhe envio o exemplo que tenho comigo.

    Um abraço.
    Giuliano

    ResponderExcluir
  3. Olá, é Exatamente o Problema que estou tendo, atualmente contrui uma especie de interpretador Portugol-Java(para Universidade a qual estudo) para controle de dispositivos eletronicos via porta Serial, tentei como vc a utilização do JTextArea, mas agora estou tentando via JEditorPane, contudo estou tendo problemas na alteração das cores das palavras reservadas, tentei seguir seu código mas como o Alex citou o updateConsole parece ter alguma inconsistencia, teria como enviar ao meu e-mail seu exemplo? Obrigado! magno_ritzmann@yahoo.com.br

    ResponderExcluir
  4. Este comentário foi removido pelo autor.

    ResponderExcluir