Создание JEditorPane с HTML поместите правильно отформатированный текст в буфер обмена
У меня есть этот код, чтобы продемонстрировать проблему:
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 или Mail, и текст будет отформатирован правильно. Но если вы вставите его в чистый текстовый редактор, такой как TextEdit, Smultron или окно чата Skype, весь вставленный контент будет в одной строке.
Что я могу сделать, чтобы текст, скопированный в буфер обмена, можно было вставить с сохранением новых строк?
Я запускаю свой код на Mac OS X 10.7
3 ответа
Не получив ответов, я засучил рукава и много изучал и изучал. Решение состоит в том, чтобы создать собственный 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);
}
}
Примечание: это не ответ на вопрос, а комментарий с кодом ответа @Thorn, связанный с ограничениями безопасности
В веб-таблицах с разрешениями по умолчанию (то есть, ни с одним;-) вы можете запросить у 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);