summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian P. Hinz <bphinz@users.sf.net>2014-11-19 00:10:46 -0500
committerBrian P. Hinz <bphinz@users.sf.net>2014-11-19 00:10:46 -0500
commitdfbb32446c85fa1d62a593c7ad3fdcf58b77da6f (patch)
treef1576d7d08d72fbacde7fded2b77c6016272ab99
parent85bfb1c2e67f79ddd0c7fcddf8f770543fe263d8 (diff)
downloadtigervnc-dfbb32446c85fa1d62a593c7ad3fdcf58b77da6f.tar.gz
tigervnc-dfbb32446c85fa1d62a593c7ad3fdcf58b77da6f.zip
Major rework of Java viewer clipboard handling
* Clipboard dialog UI significantly improved. - Fixes issue where scrollpane size did not track dialog size - Removed unnecessary JPanel - Adjusted default size of dialog to something more appropriate. * Clipboard dialog is now a pure clipboard viewer - Window title now reflects thati fact. - Eliminates problems with concurrency and updating contents. - Clipboard dialog now updates contents if a clipbard transfer occurs and the dialog is visible. - Prevents possible loop condition when text in the scrollpane is selected (ie: selection buffer triggers clipboard updates). * Custom TransferHandler implemented. - Limits the size of outbound clipboard transfers. - On Windows and Linux this implementation does not appear to provoke huge memory spike issue when the transferable is accessed. Unfortunately, the OSX implemenation still suffers from this problem, but that is a JRE issue. For the time being, this at least minimizes the problem. Additionaly, if an OutOfMemoryError is thrown, it's now caught and an error is logged rather than the viewer bailing out. - Vastly simpler implementation - the copy/paste methods inherent in the JTextArea are utilized for interfacing with the system clipboard. This eliminates the need for checking permissions, as well as streamlining the code quite a bit.
-rw-r--r--java/com/tigervnc/vncviewer/ClipboardDialog.java122
-rw-r--r--java/com/tigervnc/vncviewer/DesktopWindow.java32
2 files changed, 83 insertions, 71 deletions
diff --git a/java/com/tigervnc/vncviewer/ClipboardDialog.java b/java/com/tigervnc/vncviewer/ClipboardDialog.java
index d4cde6e1..fff7dc3e 100644
--- a/java/com/tigervnc/vncviewer/ClipboardDialog.java
+++ b/java/com/tigervnc/vncviewer/ClipboardDialog.java
@@ -1,5 +1,5 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright (C) 2011 Brian P. Hinz
+ * 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
@@ -21,27 +21,83 @@ package com.tigervnc.vncviewer;
import java.awt.*;
import java.awt.event.*;
-import java.awt.datatransfer.Clipboard;
-import java.awt.datatransfer.StringSelection;
+import java.awt.datatransfer.*;
+import java.io.*;
+import java.nio.*;
import javax.swing.*;
import javax.swing.border.*;
+import javax.swing.text.*;
+
import com.tigervnc.rfb.LogWriter;
class ClipboardDialog extends Dialog implements ActionListener {
+ 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 boolean importData(JComponent c, Transferable t) {
+ if (canImport(c, t.getTransferDataFlavors())) {
+ try {
+ DataFlavor VncFlavor = null;
+ for (DataFlavor f : t.getTransferDataFlavors())
+ if (f.isFlavorTextType() && f.isRepresentationClassInputStream())
+ VncFlavor = f;
+ if (VncFlavor == null) return false;
+ Reader reader = (Reader)VncFlavor.getReaderForText(t);
+ CharBuffer cbuf =
+ CharBuffer.allocate(VncViewer.maxCutText.getValue());
+ cbuf.limit(reader.read(cbuf.array(), 0, cbuf.length()));
+ reader.close();
+ if (c instanceof JTextComponent)
+ ((JTextComponent)c).setText(cbuf.toString());
+ return true;
+ } catch (OutOfMemoryError oome) {
+ vlog.error("ERROR: Too much data on local clipboard!");
+ } catch (UnsupportedFlavorException ufe) {
+ // Skip import
+ vlog.info(ufe.toString());
+ } catch (IOException ioe) {
+ // Skip import
+ vlog.info(ioe.toString());
+ }
+ }
+ return false;
+ }
+
+ public boolean canImport(JComponent c, DataFlavor[] flavors) {
+ for (DataFlavor f : flavors)
+ if (f.isFlavorTextType() && f.isRepresentationClassReader())
+ return true;
+ return false;
+ }
+ }
+
public ClipboardDialog(CConn cc_) {
super(false);
+ setTitle("VNC Clipboard Viewer");
+ setPreferredSize(new Dimension(640, 480));
+ addWindowFocusListener(new WindowAdapter() {
+ // Necessary to ensure that updates from the system clipboard
+ // still occur when the ClipboardDialog has the focus.
+ public void WindowGainedFocus(WindowEvent e) {
+ clientCutText();
+ }
+ });
cc = cc_;
- setTitle("VNC clipboard");
- JPanel pt = new JPanel();
- textArea = new JTextArea(5,50);
- textArea.setBorder(BorderFactory.createLineBorder(Color.gray));
- textArea.setLineWrap(true);
+ textArea = new JTextArea();
+ textArea.setTransferHandler(new VncTransferHandler());
+ // 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.
+ textArea.setFocusable(false);
+ textArea.setLineWrap(false);
textArea.setWrapStyleWord(true);
JScrollPane sp = new JScrollPane(textArea);
- pt.add(sp, BorderLayout.CENTER);
- getContentPane().add("North", pt);
-
+ getContentPane().add(sp, BorderLayout.CENTER);
+ // button panel placed below the scrollpane
JPanel pb = new JPanel();
clearButton = new JButton("Clear");
pb.add(clearButton);
@@ -53,40 +109,24 @@ class ClipboardDialog extends Dialog implements ActionListener {
pb.add(cancelButton);
cancelButton.addActionListener(this);
getContentPane().add("South", pb);
-
pack();
}
- public boolean compareContentsTo(String str) {
- return str.equals(textArea.getText());
-
- }
-
- public void setContents(String str) {
+ public void serverCutText(String str, int len) {
textArea.setText(str);
+ textArea.selectAll();
+ textArea.copy();
}
- public String getContents() {
- return textArea.getText();
- }
-
- public void serverCutText(String str, int len) {
- setContents(str);
- SecurityManager sm = System.getSecurityManager();
- try {
- if (sm != null) sm.checkSystemClipboardAccess();
- Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
- if (cb != null) {
- StringSelection ss = new StringSelection(str);
- try {
- cb.setContents(ss, null);
- } catch(Exception e) {
- vlog.debug(e.getMessage());
- }
- }
- } catch(SecurityException e) {
- vlog.debug("Cannot access the system clipboard: "+e.getMessage());
- }
+ public void clientCutText() {
+ int hc = textArea.getText().hashCode();
+ textArea.setText("");
+ textArea.paste();
+ textArea.setCaretPosition(0);
+ String text = textArea.getText();
+ if (cc.viewer.sendClipboard.getValue())
+ if (hc != text.hashCode())
+ cc.writeClientCutText(text, text.length());
}
public void setSendingEnabled(boolean b) {
@@ -98,7 +138,9 @@ class ClipboardDialog extends Dialog implements ActionListener {
if (s instanceof JButton && (JButton)s == clearButton) {
serverCutText(new String(""), 0);
} else if (s instanceof JButton && (JButton)s == sendButton) {
- cc.writeClientCutText(textArea.getText(), textArea.getText().length());
+ String text = textArea.getText();
+ if (cc.viewer.sendClipboard.getValue())
+ cc.writeClientCutText(text, text.length());
endDialog();
} else if (s instanceof JButton && (JButton)s == cancelButton) {
endDialog();
diff --git a/java/com/tigervnc/vncviewer/DesktopWindow.java b/java/com/tigervnc/vncviewer/DesktopWindow.java
index e78ee277..10d158c4 100644
--- a/java/com/tigervnc/vncviewer/DesktopWindow.java
+++ b/java/com/tigervnc/vncviewer/DesktopWindow.java
@@ -85,7 +85,7 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener,
addKeyListener(this);
addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent e) {
- checkClipboard();
+ cc.clipboardDialog.clientCutText();
}
public void focusLost(FocusEvent e) {
cc.releaseDownKeys();
@@ -359,36 +359,6 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener,
g2.dispose();
}
- public synchronized void checkClipboard() {
- SecurityManager sm = System.getSecurityManager();
- try {
- if (sm != null) sm.checkSystemClipboardAccess();
- Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
- if (cb != null) {
- Transferable t = cb.getContents(null);
- if (t == null) return;
- DataFlavor flavor =
- DataFlavor.selectBestTextFlavor(t.getTransferDataFlavors());
- if (flavor == null) return;
- BufferedReader br = new BufferedReader(flavor.getReaderForText(t));
- CharBuffer cbuf =
- CharBuffer.allocate(VncViewer.maxCutText.getValue());
- br.read(cbuf);
- cbuf.flip();
- String newContents = cbuf.toString();
- if (!cc.clipboardDialog.compareContentsTo(newContents)) {
- cc.clipboardDialog.setContents(newContents);
- if (cc.viewer.sendClipboard.getValue())
- cc.writeClientCutText(newContents, newContents.length());
- }
- br.close();
- System.gc();
- }
- } catch(java.lang.Exception e) {
- vlog.debug("Exception getting clipboard data: " + e.getMessage());
- }
- }
-
// Mouse-Motion callback function
private void mouseMotionCB(MouseEvent e) {
if (!cc.viewer.viewOnly.getValue() &&