0

メインのJTextPaneでマークダウンフォーマットをライブでレンダリングできるSwingを使用して、シンプルなマークダウンテキストエディターを作成しています。つまり、マークダウン形式のテキスト like*hello*は、検出されるとイタリック体で表示されますが、プレーン テキストで保存されます。

この目的のために、マークダウン トークンを取得するための正規表現を思いつきました (*italics*ここでは例として使用します)。コードは数秒ごとに JTextPane のテキストを検索し、JTextPane#setCharacterAttributes を使用して問題の領域の書式設定を変更します。 .

// init
PLAIN = Document.addStyle("plain", null);
StyleConstants.setFontSize(PLAIN, 12);

ITALIC = Document.addStyle("italic", null);
StyleConstants.setItalic(ITALIC, true);
...
// every few seconds
// remove all formatting
Document.setCharacterAttributes(0, Document.getLength(), PLAIN, true);

// italicize parts that the regex matches
m = Pattern.compile("\\*([^\\n*]+)\\*").matcher(temp);
while (m.find()) {
    Document.setCharacterAttributes(m.start(), m.group().length(), ITALIC, false);
}

問題は「活性」です。しばらくすると、JTextPaneは単語ではなく文字で折り返しを開始し、単語の折り返しが完全に失われ、単に折り返しのない行が表示されることがあります。

これを修正する/JTextPaneを拡張して修正する方法はありますか、それともJTextPaneはそのようなライブ更新には適していませんか? かなり長い間グーグルで検索しましたが、何も見つかりませんでした。何を検索すればよいかわかりません。

package test;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.lang.reflect.Field;
import java.util.logging.*;
import java.util.regex.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;

public class Test {

    private JFrame frame = new JFrame();
    private JTextPane jtp;
    private StyledDocument doc;

    // NEW LINES
    private Timer T;
    private boolean update = true;
    MarkdownRenderer m;
    // END OF NEW LINES

    public Test() {
        jtp = new JTextPane();
        jtp.setEditorKit(new MyStyledEditorKit());
        jtp.setText("\ntype some text in the above empty line and check the wrapping behavior");
        doc = jtp.getStyledDocument();
        // NEW LINES
        m = new MarkdownRenderer(jtp);
        Timer T = new Timer(2000, new java.awt.event.ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (update) {
                    update = false;
                    m.render();
                }
            }
        });
        T.start();
        // END OF NEW LINES
        doc.addDocumentListener(new DocumentListener() {

            private boolean doUpdate = true;
            public void insertUpdate(DocumentEvent e) {
                insert();
            }

            public void removeUpdate(DocumentEvent e) {
                insert();
            }

            public void changedUpdate(DocumentEvent e) {
//                triggers every time formatting is changed
//                 insert();
            }

            public void insert() {
                SwingUtilities.invokeLater(new Runnable() {

                    public void run() {
                        Style defaultStyle = jtp.getStyle(StyleContext.DEFAULT_STYLE);
                        doc.setCharacterAttributes(0, doc.getLength(), defaultStyle, false);
                        update = true;
                    }
                });
            }
        });
        JScrollPane scroll = new JScrollPane(jtp);
        scroll.setPreferredSize(new Dimension(200, 200));
        frame.add(scroll);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                Test bugWrapJava7 = new Test();
            }
        });
    }
}

class MyStyledEditorKit extends StyledEditorKit {
    private MyFactory factory;

    public ViewFactory getViewFactory() {
        if (factory == null) {
            factory = new MyFactory();
        }
        return factory;
    }
}

class MyFactory implements ViewFactory {
    public View create(Element elem) {
        String kind = elem.getName();
        if (kind != null) {
            if (kind.equals(AbstractDocument.ContentElementName)) {
                return new MyLabelView(elem);
            } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
                return new ParagraphView(elem);
            } else if (kind.equals(AbstractDocument.SectionElementName)) {
                return new BoxView(elem, View.Y_AXIS);
            } else if (kind.equals(StyleConstants.ComponentElementName)) {
                return new ComponentView(elem);
            } else if (kind.equals(StyleConstants.IconElementName)) {
                return new IconView(elem);
            }
        }

        // default to text display
        return new LabelView(elem);
    }
}

class MyLabelView extends LabelView {
    public MyLabelView(Element elem) {
        super(elem);
    }
    public View breakView(int axis, int p0, float pos, float len) {
        if (axis == View.X_AXIS) {
            resetBreakSpots();
        }
        return super.breakView(axis, p0, pos, len);
    }

    private void resetBreakSpots() {
        try {
            // HACK the breakSpots private fields
            Field f=GlyphView.class.getDeclaredField("breakSpots");
            f.setAccessible(true);
            f.set(this, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}


class MarkdownRenderer {    
    private static final MutableAttributeSet PLAIN = new SimpleAttributeSet();
    private static final MutableAttributeSet BOLD = new SimpleAttributeSet();
    private static final MutableAttributeSet ITALIC = new SimpleAttributeSet();
    private static final MutableAttributeSet UNDERLINE = new SimpleAttributeSet();

    private StyledDocument Document = null;

    public MarkdownRenderer(JTextPane editor) {
        Document = (StyledDocument) editor.getDocument();

        StyleConstants.setBold(BOLD, true);
        StyleConstants.setItalic(ITALIC, true);
        StyleConstants.setUnderline(UNDERLINE, true);
    }

    void render() {
        String s = "";
        try {
            s = Document.getText(0, Document.getLength());
        } catch (BadLocationException ex) {
            Logger.getLogger(MarkdownRenderer.class.getName()).log(Level.SEVERE, null, ex);
        }
//        Document.setCharacterAttributes(0, Document.getLength(), PLAIN, true);

        String temp = s.replaceAll("\\*\\*([^\\n*]+)\\*\\*", "|`$1`|"); // can also use lazy quantifier: (.+?)

        Matcher m = Pattern.compile("\\|`.+?`\\|").matcher(temp);
        while (m.find()) {
            Document.setCharacterAttributes(m.start(), m.group().length(), BOLD, false);
        }
        m = Pattern.compile("\\*([^\\n*]+)\\*").matcher(temp);
        while (m.find()) {
            Document.setCharacterAttributes(m.start(), m.group().length(), ITALIC, false);
        }
        m = Pattern.compile("_+([^\\n*]+)_+").matcher(temp);
        while (m.find()) {
            Document.setCharacterAttributes(m.start(), m.group().length(), UNDERLINE, false);
        }
    }
}
4

0 に答える 0