/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. * Copyright (C) 2011-2014 Brian P. Hinz * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package com.tigervnc.vncviewer; import java.awt.*; import java.awt.event.*; import java.awt.datatransfer.*; import java.io.*; import java.nio.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.text.*; import com.tigervnc.rfb.LogWriter; import static com.tigervnc.vncviewer.Parameters.*; class ClipboardDialog extends Dialog { protected static class MyJTextArea extends JTextArea { private class VncTransferHandler extends TransferHandler { // Custom TransferHandler designed to limit the size of outbound // clipboard transfers to VncViewer.maxCutText.getValue() bytes. private LogWriter vlog = new LogWriter("VncTransferHandler"); public void exportToClipboard(JComponent c, Clipboard clip, int a) throws IllegalStateException { if (!(c instanceof JTextComponent)) return; String text = ((JTextComponent)c).getText(); try { if (text.equals((String)clip.getData(DataFlavor.stringFlavor))) return; } catch (IOException e) { // worst case we set the clipboard contents unnecessarily vlog.info(e.toString()); } catch (UnsupportedFlavorException e) { // worst case we set the clipboard contents unnecessarily vlog.info(e.toString()); } StringSelection selection = new StringSelection(text); clip.setContents(selection, null); } public boolean importData(JComponent c, Transferable t) { if (canImport(c, t.getTransferDataFlavors())) { try { DataFlavor VncFlavor = null; for (DataFlavor f : t.getTransferDataFlavors()) { if (f.isMimeTypeEqual("text/plain") && f.isRepresentationClassInputStream()) { VncFlavor = f; break; } } if (VncFlavor == null) return false; CharBuffer cbuf = CharBuffer.allocate(maxCutText.getValue()); Reader reader = (Reader)VncFlavor.getReaderForText(t); int n = reader.read(cbuf.array(), 0, cbuf.length()); reader.close(); // reader returns -1 (EOF) for empty clipboard cbuf.limit(n < 0 ? 0 : n); if (c instanceof JTextComponent) if (!cbuf.toString().equals(((JTextComponent)c).getText())) ((JTextComponent)c).setText(cbuf.toString()); return true; } catch (OutOfMemoryError e) { vlog.error("ERROR: Too much data on local clipboard!"); } catch (UnsupportedFlavorException e) { // Skip import vlog.info(e.toString()); } catch (IOException e) { // Skip import vlog.info(e.toString()); } } return false; } public boolean canImport(JComponent c, DataFlavor[] flavors) { for (DataFlavor f : flavors) if (f.isMimeTypeEqual("text/plain") && f.isRepresentationClassReader()) return true; return false; } } private class MyTextListener implements DocumentListener { public MyTextListener() { } public void changedUpdate(DocumentEvent e) { } public void insertUpdate(DocumentEvent e) { if (!listen) return; String text = textArea.getText(); if (sendClipboard.getValue()) VncViewer.cc.writeClientCutText(text, text.length()); } public void removeUpdate(DocumentEvent e) { } } public MyJTextArea() { super(); setTransferHandler(new VncTransferHandler()); getDocument().addDocumentListener(new MyTextListener()); // If the textArea can receive the focus, then text within the textArea // can be selected. On platforms that don't support separate selection // and clipboard buffers, this triggers a replacement of the textAra's // contents with the selected text. setFocusable(false); setLineWrap(false); setWrapStyleWord(true); } } public ClipboardDialog() { super(false); setTitle("VNC Clipboard Viewer"); setPreferredSize(new Dimension(640, 480)); addWindowFocusListener(new WindowFocusListener() { // Necessary to ensure that updates from the system clipboard // are propagated to the textArea when the dialog is visible. public void windowGainedFocus(WindowEvent e) { clientCutText(); } public void windowLostFocus(WindowEvent e) { } }); JScrollPane sp = new JScrollPane(textArea); getContentPane().add(sp, BorderLayout.CENTER); // button panel placed below the scrollpane JPanel pb = new JPanel(); clearButton = new JButton("Clear"); pb.add(clearButton); sendButton = new JButton("Send to VNC server"); pb.add(sendButton); cancelButton = new JButton("Cancel"); pb.add(cancelButton); getContentPane().add("South", pb); addListeners(this); pack(); } public static void showDialog(Container c) { if (dialog == null) dialog = new ClipboardDialog(); dialog.show(c); } public void show(Container c) { super.showDialog(c); } public void endDialog() { super.endDialog(); dialog.dispose(); } public static void serverCutText(String str) { if (textArea.getText().equals(str)) return; // Update the text area with incoming serverCutText. We need to diable // the DocumentListener temporarily to prevent an clientCutText msg from // being sent back to the server when the textArea is updated. listen = false; textArea.setText(str); textArea.copy(); listen = true; } public static void clientCutText() { // Update the textArea with the current contents of the system clipboard. // The TransferHandler ensures that the textArea's contents are only // changed when they differ from the clipboard's. If the textArea is // updated, the DocumentListener will trigger an RFB clientCutText msg. textArea.paste(); textArea.setCaretPosition(0); } public void setSendingEnabled(boolean b) { sendButton.setEnabled(b); } public void actionPerformed(ActionEvent e) { Object s = e.getSource(); if (s instanceof JButton && (JButton)s == clearButton) { serverCutText(new String("")); } else if (s instanceof JButton && (JButton)s == sendButton) { String text = textArea.getText(); VncViewer.cc.writeClientCutText(text, text.length()); endDialog(); } else if (s instanceof JButton && (JButton)s == cancelButton) { endDialog(); } } private JButton clearButton, sendButton, cancelButton; private static boolean listen = true; static ClipboardDialog dialog; static MyJTextArea textArea = new MyJTextArea(); static Toolkit tk = Toolkit.getDefaultToolkit(); static LogWriter vlog = new LogWriter("ClipboardDialog"); }