【问题标题】:Making a JEditorPane with html put correctly formatted text in clipboard使用 html 制作 JEditorPane 将格式正确的文本放入剪贴板
【发布时间】:2011-12-06 09:39:12
【问题描述】:

我有这段代码来演示这个问题:

public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.getContentPane().add(new JEditorPane("text/html", "Hello cruel world<br>\n<font color=red>Goodbye cruel world</font><br>\n<br>\nHello again<br>\n"));
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
}

如果您选择应用程序启动后出现在框架中的所有文本,您可以将其复制并粘贴到 MS Word、Apple 的 Pages 或 Mail 中,并且文本格式正确。但是,如果您将其粘贴到纯文本编辑器(例如 TextEdit、Smultron 或 Skype 聊天窗口)中,则所有粘贴的内容都在一行中。

如何使复制到剪贴板的文本能够在保留换行符的情况下粘贴?

我在 Mac OS X 10.7 上运行我的代码

【问题讨论】:

  • 可能是 TextEdit 根本不呈现这样的东西吗?将其粘贴到另一个文本编辑器时会发生什么?
  • @Shakedown,问题出在其他纯文本编辑器上,例如 Smultron
  • +1 好问题,回答

标签: java macos swing clipboard jeditorpane


【解决方案1】:

在没有得到答案之后,我卷起袖子,进行了大量的研究和学习。解决方案是为组件制作一个自定义的 TransferHandler,并手动处理 HTML 文本。解决这一切并不容易,这可能是我得到零答案的原因。

这是一个可行的解决方案:

import javax.swing.*;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.parser.ParserDelegator;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;

public class ScratchSpace {

    public static void main(String[] args) {
        final JFrame frame = new JFrame();
        final JEditorPane pane = new JEditorPane("text/html", "<html><font color=red>Hello</font><br>\u2663<br>World");
        pane.setTransferHandler(new MyTransferHandler());
        frame.getContentPane().add(pane);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

}

class MyTransferHandler extends TransferHandler {

    protected Transferable createTransferable(JComponent c) {
        final JEditorPane pane = (JEditorPane) c;
        final String htmlText = pane.getText();
        final String plainText = extractText(new StringReader(htmlText));
        return new MyTransferable(plainText, htmlText);
    }

    public String extractText(Reader reader) {
        final ArrayList<String> list = new ArrayList<String>();

        HTMLEditorKit.ParserCallback parserCallback = new HTMLEditorKit.ParserCallback() {
            public void handleText(final char[] data, final int pos) {
                list.add(new String(data));
            }

            public void handleStartTag(HTML.Tag tag, MutableAttributeSet attribute, int pos) {
            }

            public void handleEndTag(HTML.Tag t, final int pos) {
            }

            public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, final int pos) {
                if (t.equals(HTML.Tag.BR)) {
                    list.add("\n");
                }
            }

            public void handleComment(final char[] data, final int pos) {
            }

            public void handleError(final String errMsg, final int pos) {
            }
        };
        try {
            new ParserDelegator().parse(reader, parserCallback, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        String result = "";
        for (String s : list) {
            result += s;
        }
        return result;
    }


    @Override
    public void exportToClipboard(JComponent comp, Clipboard clip, int action) throws IllegalStateException {
        if (action == COPY) {
            clip.setContents(this.createTransferable(comp), null);
        }
    }

    @Override
    public int getSourceActions(JComponent c) {
        return COPY;
    }

}

class MyTransferable implements Transferable {

    private static final DataFlavor[] supportedFlavors;

    static {
        try {
            supportedFlavors = new DataFlavor[]{
                    new DataFlavor("text/html;class=java.lang.String"),
                    new DataFlavor("text/plain;class=java.lang.String")
            };
        } catch (ClassNotFoundException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    private final String plainData;
    private final String htmlData;

    public MyTransferable(String plainData, String htmlData) {
        this.plainData = plainData;
        this.htmlData = htmlData;
    }

    public DataFlavor[] getTransferDataFlavors() {
        return supportedFlavors;
    }

    public boolean isDataFlavorSupported(DataFlavor flavor) {
        for (DataFlavor supportedFlavor : supportedFlavors) {
            if (supportedFlavor == flavor) {
                return true;
            }
        }
        return false;
    }

    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        if (flavor.equals(supportedFlavors[0])) {
            return htmlData;
        }
        if (flavor.equals(supportedFlavors[1])) {
            return plainData;
        }
        throw new UnsupportedFlavorException(flavor);
    }
}

【讨论】:

    【解决方案2】:

    注意:这不是问题的答案,只是@Thorn 对答案的代码的评论,与安全限制有关

    在具有默认权限(即无 ;-)的 webstartables 中,您可以在运行时向 SecurityManager 询问 ClipboardService:它会弹出一个对话框,询问用户一次是否允许(或禁止)复制。这样,您可以替换 textComponent 中的默认复制操作。在SwingX demo 中,我们支持通过以下方式粘贴来自源区域的代码:

    /**
     * Replaces the editor's default copy action in security restricted
     * environments with one messaging the ClipboardService. Does nothing 
     * if not restricted.
     * 
     * @param editor the editor to replace 
     */
    public static void replaceCopyAction(final JEditorPane editor) {
        if (!isRestricted()) return;
        Action safeCopy = new AbstractAction() {
    
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    ClipboardService cs = (ClipboardService)ServiceManager.lookup
                        ("javax.jnlp.ClipboardService");
                    StringSelection transferable = new StringSelection(editor.getSelectedText());
                    cs.setContents(transferable);
                } catch (Exception e1) {
                    // do nothing
                }
            }
        };
        editor.getActionMap().put(DefaultEditorKit.copyAction, safeCopy);
    }
    
    private static boolean isRestricted() {
        SecurityManager manager = System.getSecurityManager();
        if (manager == null) return false;
        try {
            manager.checkSystemClipboardAccess();
            return false;
        } catch (SecurityException e) {
            // nothing to do - not allowed to access
        }
        return true;
    }
    

    【讨论】:

      【解决方案3】:

      感谢您发布代码!我正在努力让一个应用程序在 JNLP 下启动并运行,它允许用户创建 MLA 引文,然后将它们复制/粘贴到文字处理器中。所以需要保留格式。

      http://proctinator.com/citation/

      有一种更简单的方法,但我认为我需要您在上面演示的那种方法才能让我的应用程序使用 jnlp。

      以下代码适用于在不受限制的环境中运行的 JEditorPane。但是,当您的应用位于沙箱中时,复制/粘贴不直接可用(例如,未请求完全权限的小程序或 JNLP 文件就是这种情况。)

      JEditorPane citEditorPane;
      //user fills pane with MLA citations.
      citEditorPane.selectAll();
      citEditorPane.copy();
      citEditorPane.select(0, 0);
      

      【讨论】:

      • 要在沙箱中复制 webstartable 的权限,请参阅我的 not-an-answer-but-comment :-)
      猜你喜欢
      • 2022-06-18
      • 1970-01-01
      • 1970-01-01
      • 2014-01-24
      • 2016-04-23
      • 2020-10-07
      • 2014-02-03
      • 2023-04-08
      相关资源
      最近更新 更多