Dando Replace num JTextPane sem perder o Estilo (Imagens, texto estilizado) [?]

6 respostas
gregowbr
JTextPane pane = new JTextPane();
                 ImageIcon icon = new ImageIcon("C:\\foto.jpg");
                  StyledDocument doc;
                  Style iconStyle;


                        iconStyle = doc.addStyle(null, null);
                        StyleConstants.setIcon(iconStyle, icon);
                        insertIcon(iconStyle, doc, valor + location_imagem1);

//insertIcon é um método para inserir o Icone no JTextPane

Mas é o seguinte, após o Text está todo estilizado não tem problema.

Mas o negócio que ao final de tudo eu preciso fazer uma substituição pelo “Replace” de uma String para outra String.

Mas se fizermos utilizarmos o Replace, Todas as imagens e o estilo do JTextPane desaparece, pq eu vou estar dando pane.setText(NovoTextoReplaced);

Alguem sabe alguma forma de dar um replace no JTextPane sem perder o estilo dele?

Obrigado.

6 Respostas

ViniGodoy

Você deve manipular o StyledDocument diretamente. Não é uma tarefa trivial de se explicar. Mas você pode ver como esse editor fez a função de replace:
http://www.guj.com.br/posts/list/48736.java

gregowbr

Cara vc me mostrou outro editor que um amigo fez para o SourceForge?

Isso não foi nada específico.

Mas alguem maiss que tem algo de fato que me ajude, possa ajudar por favor?

Obrigado antecipadamente!

ViniGodoy

gregowbr:
Cara vc me mostrou outro editor que um amigo fez para o SourceForge?

Isso não foi nada específico.

Mas alguem maiss que tem algo de fato que me ajude, possa ajudar por favor?

Obrigado antecipadamente!

Mostrei sim. Nesse editor ele programou um botão de replace. No sourceforge você pode baixar os fontes e ler.
Você tentou fazer isso?

ViniGodoy
Vou postar aqui a classe daquele editor que faz recursos como "find/replace". Como eu te falei, não é um recurso trivial:
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JScrollPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;

import TextUtils;

public class SearchMachine
{
    class Index
    {
        int p1;
        int p2;

        public Index(int p1, int p2)
        {
            this.p1 = p1;
            this.p2 = p2;
        }
    }

    private HighlighterManager hilighter;
    private JTextComponent txtComponent;
    private JScrollPane scrlPane;

    private String word;
    private Pattern pattern;
    
    private boolean isPattern;
    private boolean caseSensitive;

    public SearchMachine(JTextComponent txtComponent, String word)
    {
        setTxtComponent(txtComponent);
        hilighter = new HighlighterManager(txtComponent);

        this.word = word;
        this.isPattern = false;
        this.caseSensitive = false;
    }

    public SearchMachine(JTextComponent txtComponent, Pattern pattern)
    {
        setTxtComponent(txtComponent);
        hilighter = new HighlighterManager(txtComponent);

        this.word    = pattern.pattern();
        this.pattern = pattern;
        this.isPattern = true;
        this.caseSensitive = false;
    }
    
    public void setScrollPane(JScrollPane scrl)
    {
        this.scrlPane = scrl;
    }
    
    public void setCaseSensitive(boolean status)
    {
        this.caseSensitive = status;
    }
    
    public boolean isCaseSensitive()
    {
        return caseSensitive;
    }

    private void setTxtComponent(JTextComponent txtComponent2)
    {
        if (txtComponent2 == null)
            throw new IllegalArgumentException("Text component cannot be null!");

        this.txtComponent = txtComponent2;
    }

    private void addInList(List<Integer> list, int line)
    {
        if (!list.contains(line))
            list.add(line);
    }

    public List<Integer> find()
    {
        return find(0);
    }

    public List<Integer> find(int start)
    {
        if (isPattern)
            return findPattern(start, false);

        return findForward(start);
    }

    private List<Integer> findForward(int start)
    {
        if (word == null)
            throw new IllegalStateException("word is null!");
        
        String targetWord;
        
        if (isCaseSensitive())
            targetWord = word;
        else
            targetWord = word.toUpperCase();
            

        // First remove all old highlights
        hilighter.removeHighlights();

        List<Integer> lines = new ArrayList<Integer>();
        List<Index> indexes = new ArrayList<Index>();

        String text = isCaseSensitive() ? getText(0) : getText(0).toUpperCase();

        boolean firstCicle = true;
        int pos = text.indexOf(targetWord, start);

        // if didn't find from start, search from the begining
        if (pos == -1)
            pos = text.indexOf(targetWord, 0);

        while (pos != -1)
        {
            // Create highlighter using private painter and apply around
            Index i = new Index(pos, pos + word.length());

            hilighter.highlight(i.p1, i.p2);
            indexes.add(i);

            addInList(lines, getLine(i.p1));

            pos = text.indexOf(targetWord, i.p2);

            // if end file has been reached
            if (pos == -1)
            {
                if (firstCicle)
                {
                    // restart in the begin
                    firstCicle = false;
                    pos = text.indexOf(word, 0);
                }
            }
            else if (!firstCicle)
            {
                // when reached the end and so reach where had started.
                if (pos > start)
                    pos = -1;
            }
        }

        if (!indexes.isEmpty())
        {
            select(indexes.get(0));
        }

        return lines;
    }

    private List<Integer> findBackward(int start)
    {
        if (word == null)
            throw new IllegalStateException();

        String targetWord;
        
        if (isCaseSensitive())
            targetWord = word;
        else
            targetWord = word.toUpperCase();
        
        final int realOffset = start - word.length() - 1;

        // First remove all old highlights
        hilighter.removeHighlights();

        List<Integer> lines = new ArrayList<Integer>();
        List<Index> indexes = new ArrayList<Index>();

        String text = isCaseSensitive() ? getText(0) : getText(0).toUpperCase();

        boolean firstCicle = true;
        int pos = text.lastIndexOf(targetWord, realOffset);

        // if didn't find from start, search from the begining
        if (pos == -1)
            pos = text.lastIndexOf(targetWord, text.length());

        while (pos != -1)
        {
            // Create highlighter using private painter and apply around
            Index i = new Index(pos, pos + targetWord.length());

            hilighter.highlight(i.p1, i.p2);
            indexes.add(i);

            addInList(lines, getLine(i.p1));

            pos = text.lastIndexOf(targetWord, pos - targetWord.length() - 1);

            // if end file has been reached
            if (pos == -1)
            {
                if (firstCicle)
                {
                    // restart in the begining
                    firstCicle = false;
                    pos = text.lastIndexOf(targetWord, text.length());
                }
            }
            else if (!firstCicle)
            {
                // when reached the end and so reach where had started.
                if (pos < realOffset)
                    pos = -1;
            }
        }

        if (!indexes.isEmpty())
            select(indexes.get(0));

        return lines;
    }

    public List<Integer> findPattern(int start, boolean back)
    {
        if (pattern == null)
            throw new IllegalStateException();

        // First remove all old highlights
        hilighter.removeHighlights();

        List<Integer> lines = new ArrayList<Integer>();
        List<Index> indexes = new ArrayList<Index>();

        Matcher matcher = pattern.matcher(getText(0));

        while (matcher.find())
        {
            Index i = new Index(matcher.start(), matcher.end());
            hilighter.highlight(i.p1, i.p2);
            indexes.add(i);

            addInList(lines, getLine(i.p1));
        }

        if (!indexes.isEmpty())
        {
            if (back)
                select(getPreviousIndex(indexes, start));
            else
                select(getNextIndex(indexes, start));
        }

        return lines;
    }

    private Index getNextIndex(List<Index> indexes, int start)
    {
        for (Index i : indexes)
        {
            if (i.p1 > start)
                return i;
        }

        return indexes.get(0);
    }

    private Index getPreviousIndex(List<Index> indexes, int start)
    {
        for (int i = indexes.size() - 1; i >= 0; i--)
        {
            Index index = indexes.get(i);

            if (index.p1 < start)
                return index;
        }

        return indexes.get(0);
    }

    protected int getCaretPosition()
    {
        return txtComponent.getCaretPosition();
    }

    public void next()
    {
        if (isPattern)
            findPattern(getCaretPosition(), false);
        else
            findForward(getCaretPosition());
    }

    public void previous()
    {
        if (isPattern)
            findPattern(getCaretPosition(), true);
        else
            findBackward(getCaretPosition());
    }

    private String getText(int start)
    {
        try
        {
            Document doc = txtComponent.getDocument();
            return doc.getText(start, doc.getLength() - start);
        }
        catch (BadLocationException e)
        {
            EditorLog.thrown(getClass(), "getText", e);
        }
        return "";
    }

    private synchronized void select(Index index)
    {
        select(index.p1, index.p2);
    }

    private synchronized void select(int posStart, int posEnd)
    {
        txtComponent.setCaretPosition(posStart);
        txtComponent.moveCaretPosition(posEnd);
        txtComponent.setSelectedTextColor(Color.RED);
        
        if (scrlPane != null)
            TextUtils.scrollToVisible(scrlPane, posStart);
    }

    private void replaceSelected(final String newString)
    {
        if (isPattern)
        {
            replacePattern(newString);
        }
        else
        {
            txtComponent.replaceSelection(newString);
        }
    }
    
    public void replaceNext(String newString)
    {
        replaceSelected(newString);
        find(getCaretPosition());
    }

    public void replaceAll(String newString)
    {
        if (isPattern)
            replaceAllPattern(newString);
        else
            replaceAllSimple(newString);
    }

    private void replaceAllSimple(String newString)
    {
        int pos, end = 0;
        String text = getText(0);

        while ((pos = text.indexOf(word, end)) != -1)
        {
            end = pos + word.length();
            select(pos, end);

            replaceSelected(newString);
            text = getText(0);
        }
    }

    private void replaceAllPattern(String newString)
    {
        Matcher matcher = pattern.matcher(getText(0));

        while (matcher.find())
        {
            select(matcher.start(), matcher.end());
            replacePattern(newString);
            
            matcher = pattern.matcher(getText(0));
        }
    }
    
    private void replacePattern(String newString)
    {
        String strNewText = newString;
        
        String selected = txtComponent.getSelectedText();
        Matcher matcher = pattern.matcher(selected);
        
        if (matcher.find())
        {
            Matcher m = Pattern.compile("\\$[0-9]+").matcher(newString);
            while (m.find())
            {
                int group = Integer.parseInt(m.group().substring(1));
                String text = matcher.group(group);
                if (text != null)
                    strNewText = strNewText.replaceAll("\\$" + group, text);
            }
        }
        
        txtComponent.replaceSelection(strNewText);
    }

    private int getLine(int offset)
    {
        return TextUtils.getLine(txtComponent, offset);
    }

}
Ele também usa essa classe:
/**
 * Util static methods to becomes easy operation with Text Components. All
 * functionalities are available to JTextComponent as well as a Document.
 * 
 * @author Farina, Andre
 */
public class TextUtils
{

    /**
     * Returns the line number of the caret in a text component.
     * 
     * @param txtCmp Text Component
     * @return Line number
     */
    public static int getCaretLine(JTextComponent txtCmp)
    {
        return getLine(txtCmp.getDocument(), txtCmp.getCaretPosition());
    }

    /**
     * Given an offset, returns the line number of a text component.
     * 
     * @param txtCmp Text Component
     * @param offset Offset position
     * @return Line number starting in 0.
     */
    public static int getLine(JTextComponent txtCmp, int offset)
    {
        return getLine(txtCmp.getDocument(), offset);
    }

    /**
     * Given an offset, returns the line number in a text component.
     * 
     * @param doc JTextComponent document
     * @param offset Offset position
     * @return Line number starting in 0.
     */
    public static int getLine(Document doc, int offset)
    {
        Element root = doc.getDefaultRootElement();
        return root.getElementIndex(offset);
    }

    /**
     * Given an offset, returns the column number in a text component.
     * 
     * @param offset Offset position
     * @return Column number starting in 0.
     */
    public static int getCaretColumn(JTextComponent txtCmp)
    {
        return getColumn(txtCmp, txtCmp.getCaretPosition());

    }

    /**
     * Given an offset, returns the column number in a text component.
     * 
     * @param offset Offset position
     * @return Column number starting in 0.
     */
    public static int getColumn(JTextComponent txtCmp, int offset)
    {
        return txtCmp.getCaretPosition() - getOffsetStartLine(txtCmp, offset);
    }

    /**
     * Given an offset, returns the offset where the line begins.
     * 
     * @param offset Offset position.
     * @return Offset where the line begins.
     */
    public static int getOffsetStartLine(JTextComponent txtCmp, int offset)
    {
        return getOffsetStartLine(txtCmp.getDocument(), offset);
    }

    /**
     * Given an offset, returns the offset where the line begins.
     * 
     * @param doc JTextComponent document
     * @param offset Offset position.
     * @return Offset where the line begins.
     */
    public static int getOffsetStartLine(Document doc, int offset)
    {
        int line = getLine(doc, offset);
        return getWhereLineStarts(doc, line);
    }

    /**
     * Given an offset, returns the offset where the line ends.
     * 
     * @param offset Offset position.
     * @return Offset where the line ends.
     */
    public static int getOffsetEndLine(JTextComponent txtCmp, int offset)
    {
        return getOffsetEndLine(txtCmp.getDocument(), offset);
    }

    /**
     * Given an offset, returns the offset where the line ends.
     * 
     * @param doc JTextComponent document
     * @param offset Offset position.
     * @return Offset where the line ends.
     */
    public static int getOffsetEndLine(Document doc, int offset)
    {
        int line = getLine(doc, offset);
        return getWhereLineEnds(doc, line);
    }

    /**
     * Given a line returns the offset number where the line starts.
     * 
     * @param line Line number
     * @return Offset number
     */
    public static int getWhereLineStarts(JTextComponent txtCmp, int line)
    {
        return getWhereLineStarts(txtCmp.getDocument(), line);
    }

    /**
     * Given a line returns the offset number where the line starts.
     * 
     * @param doc JTextComponent document
     * @param line Line number
     * @return Offset number
     */
    public static int getWhereLineStarts(Document doc, int line)
    {
        Element el = doc.getDefaultRootElement().getElement(line);
        if (el != null)
            return el.getStartOffset();

        return doc.getStartPosition().getOffset();
    }

    /**
     * Given a line returns the offset number where the line ends.
     * 
     * @param line Line number
     * @return Offset number
     */
    public static int getWhereLineEnds(JTextComponent txtCmp, int line)
    {
        return getWhereLineEnds(txtCmp.getDocument(), line);
    }

    /**
     * Given a line returns the offset number where the line ends.
     * 
     * @param doc JTextComponent document
     * @param line Line number
     * @return Offset number
     */
    public static int getWhereLineEnds(Document doc, int line)
    {
        Element el = doc.getDefaultRootElement().getElement(line);
        if (el != null)
            return el.getEndOffset();

        return doc.getEndPosition().getOffset();
    }

    /**
     * Count the line number.
     * 
     * @return Amount of lines.
     */
    public static int countLines(JTextComponent txtCmp)
    {
        return countLines(txtCmp.getDocument());
    }

    /**
     * Count the line number.
     * 
     * @param doc JTextComponent document
     * @return Amount of lines.
     */
    public static int countLines(Document doc)
    {
        return doc.getDefaultRootElement().getElementCount();
    }

    /**
     * Get number of rows in the content being shown. Independent of the text's
     * size.
     * 
     * @return Amount of rows.
     */
    public static int getRows(JTextComponent txtCmp)
    {
        FontMetrics fm = txtCmp.getFontMetrics(txtCmp.getFont());
        int fontHeight = fm.getHeight();

        int ybaseline = txtCmp.getY() + fm.getAscent();
        int yend = ybaseline + txtCmp.getHeight();

        int rows = 1;

        while (ybaseline < yend)
        {
            ybaseline += fontHeight;
            rows++;
        }

        return rows;
    }

    /**
     * Get first visible line of the text component in a JScrollPane.
     * 
     * @param txtCmp Text component.
     * @return The line.
     */
    public static int getTopLine(JTextComponent txtCmp)
    {
        FontMetrics fm = txtCmp.getFontMetrics(txtCmp.getFont());
        int fontHeight = fm.getHeight();

        return (Math.abs(txtCmp.getY()) + fm.getAscent()) / fontHeight + 1;
    }

    /**
     * Get last visible line of the text component in a JScrollPane.
     * 
     * @param txtCmp Text component.
     * @return The line.
     */
    public static int getBottomLine(JTextComponent txtCmp)
    {
        FontMetrics fm = txtCmp.getFontMetrics(txtCmp.getFont());
        int fontHeight = fm.getHeight();

        View view = txtCmp.getUI().getRootView(txtCmp).getView(0);
        int height = view.getContainer().getParent().getHeight();

        return (Math.abs(txtCmp.getY()) + fm.getAscent() + height) / fontHeight;
    }

    /**
     * Scroll the pane until offset becomes visible.
     * 
     * @param scroll
     * @param offset
     */
    public static void scrollToVisible(final JScrollPane scroll, int offset)
    {
        if (!(scroll.getViewport().getView() instanceof JTextComponent))
            return;

        JTextComponent txtCmp = (JTextComponent) scroll.getViewport().getView();
        Element root = txtCmp.getDocument().getDefaultRootElement();
        int line = root.getElementIndex(offset);

        scrollLineToVisible(scroll, line);
    }

    /**
     * Scroll the pane until line becomes visible.
     * 
     * @param scroll
     * @param line
     */
    public static void scrollLineToVisible(final JScrollPane scroll, int line)
    {
        if (!(scroll.getViewport().getView() instanceof JTextComponent))
            return;
        
        JTextComponent txtCmp = (JTextComponent) scroll.getViewport().getView();
        FontMetrics fm = txtCmp.getFontMetrics(txtCmp.getFont());
        int fontHeight = fm.getHeight();
        scroll.getViewport().setViewPosition(new Point(0, fontHeight*line));
    }
    
    public static String generateEscapeRegex(String text)
    {
        text = text.replaceAll("\\\\", "\\\\\\\\");
        text = text.replaceAll("\\.", "\\\\.");
        text = text.replaceAll("\\*", "\\\\*");
        text = text.replaceAll("\\?", "\\\\?");
        text = text.replaceAll("\\(", "\\\\(");
        text = text.replaceAll("\\)", "\\\\)");
        text = text.replaceAll("\\[", "\\\\[");
        text = text.replaceAll("\\]", "\\\\]");
        text = text.replaceAll("\\{", "\\\\{");
        text = text.replaceAll("\\}", "\\\\}");
        text = text.replaceAll("\\^", "\\\\^");
        text = text.replaceAll("\\$", "\\\\\\$");
        text = text.replaceAll("\\+", "\\\\+");
        text = text.replaceAll("\\|", "\\\\|");
        return text;
    }
}

Manipular documentos com formatação é uma tarefa longe de ser trivial. Se você quer fazer isso, estude o StyledDocument, e entenda exatamente como funciona.

gregowbr

Vlw aí Vini.

Eu achei uma forma até mais efetiva do que utilizando o Replace. Busqueii a tarde inteira.

Mas vlw mesmo assim! :slight_smile:

F

Olá, eu sei que é um tópico antigo, mas como tenho um exemplo simples que cabe na dúvida e pode ajudar outros que procurar pela solução.

Sei que já tem um exemplo no tópico, mas tenho outro que considero mais simples.

Acabei chegando a este tópico por acaso na busca de outro problema.

Por coincidência tinha feito o substituir com estilo a alguns dias em um projeto, como está fresco na memória segue:

Fonte exemplo completo, a palavra coringa é “texto”

package cnt.doceditor.gui.demo;

import java.awt.Color;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JTextPane;

import java.awt.BorderLayout;

import javax.swing.JButton;
import javax.swing.text.BadLocationException;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

/** 
 * @author Francis Alduan Caetano
 */
public class TextEditorDemo1 {

	private JFrame frame;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					TextEditorDemo1 window = new TextEditorDemo1();
					window.frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public TextEditorDemo1() {
		initialize();
	}

	private JTextPane textPane;
	private JButton btnTextoExemplo;
	private void initialize() {
		frame = new JFrame();
		frame.setBounds(100, 100, 450, 300);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		textPane = new JTextPane();
		frame.getContentPane().add(textPane, BorderLayout.CENTER);

		JButton btnSubst = new JButton("Substituir palavra \"texto\"");
		btnSubst.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {

				String textoAntigo = "texto";
				String textoNovo = "!!!Novo!!!";

				//Atributos do paragrafo
				int posicao = textPane.getCaretPosition();
				MutableAttributeSet attr = (MutableAttributeSet)textPane.getStyledDocument().getCharacterElement(posicao).getAttributes();

				int posicaoSubst = textPane.getCaretPosition() - textoAntigo.length();//posicao do cursor no inicio do texto
				try {//replace
					textPane.getStyledDocument().remove(posicaoSubst, textoAntigo.length());
					textPane.getStyledDocument().insertString(posicaoSubst, textoNovo, attr);
				} catch (BadLocationException e1) {//tratamento caso posicao invalida
					e1.printStackTrace();
				}
			}
		});
		frame.getContentPane().add(btnSubst, BorderLayout.SOUTH);

		btnTextoExemplo = new JButton("Texto Exemplo");
		btnTextoExemplo.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				definirExemplo();
			}
		});
		frame.getContentPane().add(btnTextoExemplo, BorderLayout.NORTH);
		
		definirExemplo();
	}
	
	void definirExemplo(){
		StyledDocument doc = textPane.getStyledDocument();		
		
		try {
			doc.remove(0, doc.getLength());//limpa
		} catch (BadLocationException e) {
			e.printStackTrace();
		}

		Style style = textPane.addStyle("style1", null);
		StyleConstants.setForeground(style, Color.red);

		try { 
			doc.insertString(doc.getLength(), "Exemplo com estilo aplicado",style);//com estilo
		}catch (BadLocationException er){
			er.printStackTrace();
		}

		StyleConstants.setForeground(style, Color.blue);

		try { 
			doc.insertString(doc.getLength(), ", outro exemplo com outra cor", style);//com estilo

			doc.insertString(doc.getLength(), ", agora sem formatacao nenhuma!", null);//sem estilo
		}catch (BadLocationException er){
			er.printStackTrace();
		}
	}
}
Criado 2 de dezembro de 2009
Ultima resposta 21 de ago. de 2015
Respostas 6
Participantes 3