Uso del Clipboard del sistema

De ChuWiki

Desde java podemos usar el portapapeles (Clipboard) del sistema o bien crear uno propio interno a nuestra aplicación java. En este ejemplo vamos a ver cómo usar el del sistema. Entre todos los posibles tippos de datos, nos centraremos en los String.


Acceder al Clipboard[editar]

Para acceder al portapapeles Clipboard del sistema, debemos realizar la siguiente llamada

Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();

y ya lo tenemos accesible. Ahora sólo tenemos que meter o sacar contenido de él. Y he ahí el problema. Puesto que el contenido que metamos podrá ser leido por cualquier otra aplicación que corra en el ordenador y el contenido que extraigamos puede haber sido introducido por cualquier otra aplicación, tenemos que meter o extraer los datos en un determinado formato, de forma que todas las aplicaciones lo sigan y así puedan entenderse e intercambiar datos.


Meter datos en el Clipboard[editar]

Para meter el dato en el Clipboard debemos usar el método setContents(Transferable, ClipboardOwner). El Transferable es el dato que queremos meter y que para que todas las aplicaciones lo entiendan debe implementar la interface Transferable, de esta forma incluye información sobre qué tipo de datos es. El ClipboardOwner puede ser cualquier cosa que queramos meter ahí que cumpla la interface ClipboardOwner.

Puesto que la interface Transferable es compleja, con muchos métodos, es mejor usar alguna de las que ya tiene java implementadas:

  • Para texto normal, podemos usar java.awt.datatransfer.SelectionText
  • Para otros tipos de datos, podemos usar javax.activation.DataHandler

El primero es una opción adecuada para meter texto en el Clipboard. La misma clase SelectionText implementa Transferable y ClipboardOwner, por lo que podemos usarla para el método setContents(Transferable, ClipboardOwner)

Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
StringSelection ss = new StringSelection("Este texto va al Clipboard");
cb.setContents(ss, ss);

ClipboardOwner es a la clase a la que se avisará cuando este dato deje de estar en el Clipboard y sea reemplazado por otro. Habitualmente no querremos hacer nada en este caso, pero se nos da opción a ello.

cb.setContents(ss, new ClipboardOwner() {
   public void lostOwnership(Clipboard clipboard, Transferable contents) {
      System.out.println("Alquien ha reemplazado lo que meti en el Clipboard");
      // el dato que meti es contents
   }
});

Extraer datos del Clipboard[editar]

Para recoger datos del Clipboard debemos llamar al método getContents(Object). El parámetro puede ser cualquier cosa y actualmente java no lo utiliza para nada.

Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable dato = cb.getContents(this);

El problema ahora es interpretar ese dato Transferable que recibimos para ver si somos capaces de hacer algo con él. Para ello, podemos pedirle al Transferable dato los DataFlavor que tiene. Estos DataFlavor son las posibles interpretaciones para ese dato (si es un texto html, si es un texto xml, si es un texto plano, si es una imagen, etc, etc). De esta forma, analizando los DataFlavor obtenidos, sabremos mejor cómo podemos tratar el dato.

En el caso de que queramos, por ejemplo, ver si lo que hay es un texto que podamos conseguir dentro de un String de java, podemos preguntar al dato si soporta un DataFlavor cuyo MimeType sea "application/x-java-serialized-object; class=java.lang.String".

Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable t = cb.getContents(this);

// Construimos el DataFlavor correspondiente al String java
DataFlavor dataFlavorStringJava = new DataFlavor("application/x-java-serialized-object; class=java.lang.String");

// Y si el dato se puede conseguir como String java, lo sacamos por pantalla
if (t.isDataFlavorSupported(dataFlavorStringJava)) {
   String texto = (String) t.getTransferData(dataFlavorStringJava);
   System.out.println(texto);
}


Un ejemplo completo[editar]

El siguiente ejemplo mete un String en el Clipboard y luego saca una ventana con un botón. Cada vez que pulsamos el botón, sacará por la estándar out el contenido del Clipboard si es un String. Una vez arrancado, puedes jugar a hacer "copy" de textos de otras aplicaciones, para ver si al pulsar el botón sale dicho texto.

package com.chuidiang.ejemplos.dataflavor;

import java.awt.FlowLayout;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.WindowConstants;

/**
 * Ejemplo de como meter y sacar String del Clipboard.
 * Una vez visualizada la ventana, prueba a hacer "copy" de textos de otras 
 * aplicaciones y pulsa el botón para ver si sale por pantalla dicho texto.
 * @author chuidiang
 *
 */
public class EjemploDataFlavor {
    public static void main(String[] args) {

        // Metemos "hola" en el Clipboard
        Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
        StringSelection texto = new StringSelection("hola");
        cb.setContents(texto, texto);

        // Construimos una ventana con un boton para leer el Clipboard
        JFrame v = new JFrame("Ejemplo data flavor");
        v.getContentPane().setLayout(new FlowLayout());
        JButton boton = new JButton("escribe data flavors");

        // Añadimos al boton la accion de leer el Clipboard y sacarlo por
        // la estandar out si es un String.
        boton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent arg0) {
                try {
                    // Se obtiene el Clipboard y su contenido
                    Clipboard cb = Toolkit.getDefaultToolkit()
                            .getSystemClipboard();
                    Transferable t = cb.getContents(this);

                    // Construimos el DataFlavor correspondiente a String.
                    DataFlavor dataFlavorStringJava = new DataFlavor(
                            "application/x-java-serialized-object; class=java.lang.String");

                    // Si el dato se puede obtener como String, lo obtenemos y
                    // lo
                    // sacamos por la estándar out.
                    if (t.isDataFlavorSupported(dataFlavorStringJava)) {
                        String texto = (String) t
                                .getTransferData(dataFlavorStringJava);
                        System.out.println(texto);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        });

        // Construcción y visualización del panel.
        v.getContentPane().add(boton);
        v.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        v.pack();
        v.setVisible(true);
    }
}


Obtener los MimeTypes de DataFlavor[editar]

La siguiente mini aplicación saca por pantalla todos los posibles MimeTypes soportados por el dato actual en el Clipboard. Para ver un tipo de dato concreto qué posibles MimeTypes tiene, simplemente hazle un "copy" y luego ejecuta la aplicación.

package com.chuidiang.ejemplos.dataflavor;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;

/**
 * Ejemplo de como obtener los MimeTypes del dato actual en el Clipboard
 * @author chuidiang
 *
 */
public class ObtenerDataFlavors {

    /**
     * Obtiene todos los posibles MimeTypes del dato actual en el Clipboard.
     * @param args
     */
    public static void main(String[] args) {
        // Se obtiene el portapapeles
        Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
        
        // Se obtiene el dato
        Transferable t = cb.getContents(null);
        if (t==null) return;
        
        // Se obtienen los posibles DataFlavor y se escribe en pantalla el
        // MimeType de cada uno de ellos.
        DataFlavor[] dfs = t.getTransferDataFlavors();
        for (DataFlavor df : dfs)
            System.out.println(df.getMimeType());
    }
}

Para un texto normal, este programa saca todo esto

application/rtf; class=java.io.InputStream
text/rtf; class=java.io.InputStream
text/rtf; class=java.nio.ByteBuffer
text/rtf; class="[B"
application/x-java-serialized-object; class=java.lang.String
text/plain; class=java.io.Reader; charset=Unicode
text/plain; class=java.lang.String; charset=Unicode
text/plain; class=java.nio.CharBuffer; charset=Unicode
text/plain; class="[C"; charset=Unicode
text/plain; class=java.io.InputStream; charset=unicode
text/plain; class=java.nio.ByteBuffer; charset=UTF-16
text/plain; class="[B"; charset=UTF-16
text/plain; class=java.io.InputStream; charset=UTF-8
text/plain; class=java.nio.ByteBuffer; charset=UTF-8
text/plain; class="[B"; charset=UTF-8
text/plain; class=java.io.InputStream; charset=UTF-16BE
text/plain; class=java.nio.ByteBuffer; charset=UTF-16BE
text/plain; class="[B"; charset=UTF-16BE
text/plain; class=java.io.InputStream; charset=UTF-16LE
text/plain; class=java.nio.ByteBuffer; charset=UTF-16LE
text/plain; class="[B"; charset=UTF-16LE
text/plain; class=java.io.InputStream; charset=ISO-8859-1
text/plain; class=java.nio.ByteBuffer; charset=ISO-8859-1
text/plain; class="[B"; charset=ISO-8859-1
text/plain; class=java.io.InputStream; charset=US-ASCII
text/plain; class=java.nio.ByteBuffer; charset=US-ASCII
text/plain; class="[B"; charset=US-ASCII

mientras que para una imagen, saca esto

image/jpeg; class=java.io.InputStream
image/tiff; class=java.io.InputStream
image/x-ico; class=java.io.InputStream
image/x-icon; class=java.io.InputStream
image/x-ms-bmp; class=java.io.InputStream
image/x-bmp; class=java.io.InputStream
image/bmp; class=java.io.InputStream
image/png; class=java.io.InputStream
image/x-java-image; class=java.awt.Image


Artículos relacionados[editar]