]> source.dussan.org Git - tigervnc.git/commitdiff
Major rework of Java viewer clipboard handling 73/head
authorBrian P. Hinz <bphinz@users.sf.net>
Wed, 19 Nov 2014 05:10:46 +0000 (00:10 -0500)
committerBrian P. Hinz <bphinz@users.sf.net>
Wed, 19 Nov 2014 05:10:46 +0000 (00:10 -0500)
* 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.

java/com/tigervnc/vncviewer/ClipboardDialog.java
java/com/tigervnc/vncviewer/DesktopWindow.java

index d4cde6e1c9c7aaf0206460e9e606562432bb5bea..fff7dc3e2cacd6824e802d4f96c09027c42a6ebd 100644 (file)
@@ -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();
index e78ee277eff8a778bce68c9d6b79c88bbf4f7fb6..10d158c47b722acaabae946a58e7eb15304f2264 100644 (file)
@@ -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() &&