Создание JEditorPane с помощью html помещает правильно отформатированный текст в буфер обмена

У меня есть этот код, чтобы продемонстрировать проблему:

public static void main(String[] args) { JFrame frame = new JFrame(); frame.getContentPane().add(new JEditorPane("text/html", "Hello cruel world
\nGoodbye cruel world
\n
\nHello again
\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

Получив ответы, я свернул armва и много исследований и обучения. Решение заключается в создании пользовательского 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", "Hello
\u2663
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 list = new ArrayList(); 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); } }

Примечание: это не ответ на вопрос, просто комментарий с кодом к ответу @Thorn, связанный с ограничениями безопасности

В webstartables с разрешениями по умолчанию (т. Е. None 😉 вы можете спросить SecurityManager во время выполнения для ClipboardService: появится диалоговое окно с просьбой разрешить одному пользователю (или запретить) копирование. При этом вы можете заменить действие копирования по умолчанию в textComponent. В демонстрации SwingX мы поддерживаем вставку кода из исходной области:

 /** * 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; } 

Спасибо за ваш пост! Я работаю над созданием и запуском приложения под 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);