web-dev-qa-db-fra.com

Comment changer la couleur de mots spécifiques dans un JTextPane?

Comment changer la couleur de certains mots dans un JTextPane juste pendant que l'utilisateur tape? Dois-je remplacer la méthode JTextPanepaintComponent?

24
Soheil

Le remplacement de paintComponent ne vous aidera pas.

Ce n'est pas facile, mais pas impossible non plus. Quelque chose comme ça vous aidera:

DefaultStyledDocument document = new DefaultStyledDocument();
JTextPane textpane = new JTextPane(document);
StyleContext context = new StyleContext();
// build a style
Style style = context.addStyle("test", null);
// set some style properties
StyleConstants.setForeground(style, Color.BLUE);
// add some data to the document
document.insertString(0, "", style);

Vous devrez peut-être modifier cela, mais au moins cela vous montre par où commencer.

14
Dan D.

Non. Vous n'êtes pas censé remplacer la méthode paintComponent (). À la place, vous devez utiliser StyledDocument. Vous devez également délimiter les mots par vous-même.

Voici la démo, qui devient "publique", "protégée" et "privée" en rouge lors de la frappe, comme un simple éditeur de code:

enter image description here

import javax.swing.*;
import Java.awt.*;
import javax.swing.text.*;

public class Test extends JFrame {
    private int findLastNonWordChar (String text, int index) {
        while (--index >= 0) {
            if (String.valueOf(text.charAt(index)).matches("\\W")) {
                break;
            }
        }
        return index;
    }

    private int findFirstNonWordChar (String text, int index) {
        while (index < text.length()) {
            if (String.valueOf(text.charAt(index)).matches("\\W")) {
                break;
            }
            index++;
        }
        return index;
    }

    public Test () {
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(400, 400);
        setLocationRelativeTo(null);

        final StyleContext cont = StyleContext.getDefaultStyleContext();
        final AttributeSet attr = cont.addAttribute(cont.getEmptySet(), StyleConstants.Foreground, Color.RED);
        final AttributeSet attrBlack = cont.addAttribute(cont.getEmptySet(), StyleConstants.Foreground, Color.BLACK);
        DefaultStyledDocument doc = new DefaultStyledDocument() {
            public void insertString (int offset, String str, AttributeSet a) throws BadLocationException {
                super.insertString(offset, str, a);

                String text = getText(0, getLength());
                int before = findLastNonWordChar(text, offset);
                if (before < 0) before = 0;
                int after = findFirstNonWordChar(text, offset + str.length());
                int wordL = before;
                int wordR = before;

                while (wordR <= after) {
                    if (wordR == after || String.valueOf(text.charAt(wordR)).matches("\\W")) {
                        if (text.substring(wordL, wordR).matches("(\\W)*(private|public|protected)"))
                            setCharacterAttributes(wordL, wordR - wordL, attr, false);
                        else
                            setCharacterAttributes(wordL, wordR - wordL, attrBlack, false);
                        wordL = wordR;
                    }
                    wordR++;
                }
            }

            public void remove (int offs, int len) throws BadLocationException {
                super.remove(offs, len);

                String text = getText(0, getLength());
                int before = findLastNonWordChar(text, offs);
                if (before < 0) before = 0;
                int after = findFirstNonWordChar(text, offs);

                if (text.substring(before, after).matches("(\\W)*(private|public|protected)")) {
                    setCharacterAttributes(before, after - before, attr, false);
                } else {
                    setCharacterAttributes(before, after - before, attrBlack, false);
                }
            }
        };
        JTextPane txt = new JTextPane(doc);
        txt.setText("public class Hi {}");
        add(new JScrollPane(txt));
        setVisible(true);
    }

    public static void main (String args[]) {
        new Test();
    }
}

Le code n'est pas si beau car je l'ai tapé rapidement mais ça marche. Et j'espère que cela vous donnera un indice.

32
shuangwhywhy

Une autre solution consiste à utiliser un DocumentFilter.

Voici un exemple:

Créez une classe qui étend DocumentFilter:

private final class CustomDocumentFilter extends DocumentFilter
{
        private final StyledDocument styledDocument = yourTextPane.getStyledDocument();

        private final StyleContext styleContext = StyleContext.getDefaultStyleContext();
        private final AttributeSet greenAttributeSet = styleContext.addAttribute(styleContext.getEmptySet(), StyleConstants.Foreground, Color.GREEN);
        private final AttributeSet blackAttributeSet = styleContext.addAttribute(styleContext.getEmptySet(), StyleConstants.Foreground, Color.BLACK);

    // Use a regular expression to find the words you are looking for
    Pattern pattern = buildPattern();

    @Override
    public void insertString(FilterBypass fb, int offset, String text, AttributeSet attributeSet) throws BadLocationException {
        super.insertString(fb, offset, text, attributeSet);

        handleTextChanged();
    }

    @Override
    public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
        super.remove(fb, offset, length);

        handleTextChanged();
    }

    @Override
    public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attributeSet) throws BadLocationException {
        super.replace(fb, offset, length, text, attributeSet);

        handleTextChanged();
    }

    /**
     * Runs your updates later, not during the event notification.
     */
    private void handleTextChanged()
    {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                updateTextStyles();
            }
        });
    }

    /**
     * Build the regular expression that looks for the whole Word of each Word that you wish to find.  The "\\b" is the beginning or end of a Word boundary.  The "|" is a regex "or" operator.
     * @return
     */
    private Pattern buildPattern()
    {
        StringBuilder sb = new StringBuilder();
        for (String token : ALL_WORDS_THAT_YOU_WANT_TO_FIND) {
            sb.append("\\b"); // Start of Word boundary
            sb.append(token);
            sb.append("\\b|"); // End of Word boundary and an or for the next Word
        }
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1); // Remove the trailing "|"
        }

        Pattern p = Pattern.compile(sb.toString());

        return p;
    }


    private void updateTextStyles()
    {
        // Clear existing styles
        styledDocument.setCharacterAttributes(0, yourTextPane.getText().length(), blackAttributeSet, true);

        // Look for tokens and highlight them
        Matcher matcher = pattern.matcher(yourTextPane.getText());
        while (matcher.find()) {
            // Change the color of recognized tokens
            styledDocument.setCharacterAttributes(matcher.start(), matcher.end() - matcher.start(), greenAttributeSet, false);
        }
    }
}

Il vous suffit alors d'appliquer le DocumentFilter que vous avez créé à votre JTextPane comme suit:

((AbstractDocument) yourTextPane.getDocument()).setDocumentFilter(new CustomDocumentFilter());
9
diadyne

Vous pouvez étendre DefaultStyledDocument comme je l'ai fait ici pour un éditeur SQL que je construis avec la coloration du texte des mots clés ...

    import Java.util.ArrayList;
    import Java.util.List;
    import javax.swing.text.AttributeSet;
    import javax.swing.text.BadLocationException;
    import javax.swing.text.DefaultStyledDocument;
    import javax.swing.text.Style;

    public class KeywordStyledDocument extends DefaultStyledDocument  {
        private static final long serialVersionUID = 1L;
        private Style _defaultStyle;
        private Style _cwStyle;

        public KeywordStyledDocument(Style defaultStyle, Style cwStyle) {
            _defaultStyle =  defaultStyle;
            _cwStyle = cwStyle;
        }

         public void insertString (int offset, String str, AttributeSet a) throws BadLocationException {
             super.insertString(offset, str, a);
             refreshDocument();
         }

         public void remove (int offs, int len) throws BadLocationException {
             super.remove(offs, len);
             refreshDocument();
         }

         private synchronized void refreshDocument() throws BadLocationException {
             String text = getText(0, getLength());
             final List<HiliteWord> list = processWords(text);

             setCharacterAttributes(0, text.length(), _defaultStyle, true);   
             for(HiliteWord Word : list) {
                 int p0 = Word._position;
                 setCharacterAttributes(p0, Word._Word.length(), _cwStyle, true);
             }
         }       

         private static  List<HiliteWord> processWords(String content) {
             content += " ";
             List<HiliteWord> hiliteWords = new ArrayList<HiliteWord>();
             int lastWhitespacePosition = 0;
             String Word = "";
             char[] data = content.toCharArray();

             for(int index=0; index < data.length; index++) {
                 char ch = data[index];
                 if(!(Character.isLetter(ch) || Character.isDigit(ch) || ch == '_')) {
                     lastWhitespacePosition = index;
                     if(Word.length() > 0) {
                         if(isReservedWord(Word)) {
                             hiliteWords.add(new HiliteWord(Word,(lastWhitespacePosition - Word.length())));
                         }
                         Word="";
                     }
                 }
                 else {
                     Word += ch;
                 }
            }
            return hiliteWords;
         }

         private static final boolean isReservedWord(String Word) {
             return(Word.toUpperCase().trim().equals("CROSS") || 
                            Word.toUpperCase().trim().equals("CURRENT_DATE") ||
                            Word.toUpperCase().trim().equals("CURRENT_TIME") ||
                            Word.toUpperCase().trim().equals("CURRENT_TIMESTAMP") ||
                            Word.toUpperCase().trim().equals("DISTINCT") ||
                            Word.toUpperCase().trim().equals("EXCEPT") ||
                            Word.toUpperCase().trim().equals("EXISTS") ||
                            Word.toUpperCase().trim().equals("FALSE") ||
                            Word.toUpperCase().trim().equals("FETCH") ||
                            Word.toUpperCase().trim().equals("FOR") ||
                            Word.toUpperCase().trim().equals("FROM") ||
                            Word.toUpperCase().trim().equals("FULL") ||
                            Word.toUpperCase().trim().equals("GROUP") ||
                            Word.toUpperCase().trim().equals("HAVING") ||
                            Word.toUpperCase().trim().equals("INNER") ||
                            Word.toUpperCase().trim().equals("INTERSECT") ||
                            Word.toUpperCase().trim().equals("IS") ||
                            Word.toUpperCase().trim().equals("JOIN") ||
                            Word.toUpperCase().trim().equals("LIKE") ||
                            Word.toUpperCase().trim().equals("LIMIT") ||
                            Word.toUpperCase().trim().equals("MINUS") ||
                            Word.toUpperCase().trim().equals("NATURAL") ||
                            Word.toUpperCase().trim().equals("NOT") ||
                            Word.toUpperCase().trim().equals("NULL") ||
                            Word.toUpperCase().trim().equals("OFFSET") ||
                            Word.toUpperCase().trim().equals("ON") ||
                            Word.toUpperCase().trim().equals("ORDER") ||
                            Word.toUpperCase().trim().equals("PRIMARY") ||
                            Word.toUpperCase().trim().equals("ROWNUM") ||
                            Word.toUpperCase().trim().equals("SELECT") ||
                            Word.toUpperCase().trim().equals("SYSDATE") ||
                            Word.toUpperCase().trim().equals("SYSTIME") ||
                            Word.toUpperCase().trim().equals("SYSTIMESTAMP") ||
                            Word.toUpperCase().trim().equals("TODAY") ||
                            Word.toUpperCase().trim().equals("TRUE") ||
                            Word.toUpperCase().trim().equals("UNION") ||
                            Word.toUpperCase().trim().equals("UNIQUE") ||
                            Word.toUpperCase().trim().equals("WHERE"));
        }
    }

Ajoutez-le simplement à votre classe comme ceci:

    import Java.awt.BorderLayout;
    import Java.awt.Color;
    import Java.awt.Font;
    import javax.swing.JFrame;
    import javax.swing.JScrollPane;
    import javax.swing.JTextPane;
    import javax.swing.text.BadLocationException;
    import javax.swing.text.Style;
    import javax.swing.text.StyleConstants;
    import javax.swing.text.StyleContext;

    public class SQLEditor extends JFrame {
        private static final long serialVersionUID = 1L;

        public SQLEditor() {
            StyleContext styleContext = new StyleContext();
            Style defaultStyle = styleContext.getStyle(StyleContext.DEFAULT_STYLE);
            Style cwStyle = styleContext.addStyle("ConstantWidth", null);
            StyleConstants.setForeground(cwStyle, Color.BLUE);
            StyleConstants.setBold(cwStyle, true);

            final JTextPane pane = new JTextPane(new KeywordStyledDocument(defaultStyle, cwStyle));
            pane.setFont(new Font("Courier New", Font.PLAIN, 12));

            JScrollPane scrollPane = new JScrollPane(pane);
            getContentPane().add(scrollPane, BorderLayout.CENTER);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setSize(375, 400);      
        }

        public static void main(String[] args) throws BadLocationException {
            SQLEditor app = new SQLEditor();
            app.setVisible(true);
        }
    }

Voici la classe HiliteWord manquante ...

public class HiliteWord {

    int _position;  
    String _Word;

    public HiliteWord(String Word, int position) {
        _position = position;   
        _Word = Word;
    }
}
3
Constantin