Advanced   Java   Services Clipboardhandling Back Next Up Home


Grundlagen

Mit Hilfe der Klasse Clipboard kann eine JavaApplikation sich eine eigenes Clipboard einrichten oder auch das Clipboard des Betriebssystems benützen. Die Vorgehensweise ist dabei, so wie es der Philosophie von Java entspricht, unabhängig vom jeweiligen Betriebssystem. Hat man ein Clipboardobjekt instanziiert, so kann man mit der Methode getContents() auf den Inhalt zugreifen und mit der Methode setContents() Daten in das Clipboard übertragen. Mit der Version 1.5 bzw. 5 steht mit der Methode getData(DataFlavor flavor) eine zweite Methode zur Verfügung, mit der man Daten aus dem Clipboard direkt abholen kann, ohne den Weg über ein Transferableobjekt gehen zu müssen. Es folgt ein Auszug aus der API.


Die Klasse java.awt.datatransfer.Clipboard
Attribute
TypName des Attributs
protected Transferable contents
Enthält die Daten des Clipboards.
protected ClipboardOwner owner
Der Eigentümer des Clipboards.
Konstruktor
Clipboard(String name) Creates a clipboard object.
Wichtige Methoden
ReturntypName der Methode
voidaddFlavorListener(FlavorListener listener)
Registers the specified FlavorListener to receive FlavorEvents from this clipboard.
Since 1.5
void removeFlavorListener(FlavorListener listener)
Removes the specified FlavorListener so that it no longer receives FlavorEvents from this Clipboard.
Since 1.5
DataFlavor[]getAvailableDataFlavors()
Returns an array of DataFlavors in which the current contents of this clipboard can be provided.
Since 1.5
TransferablegetContents(Object requestor)
Returns a transferable object representing the current contents of the clipboard. If the clipboard currently has no contents, it returns null. The parameter Object requestor is not currently used.
StringgetName()
Returns the name of this clipboard object.
booleanisDataFlavorAvailable(DataFlavor flavor)
Returns whether or not the current contents of this clipboard can be provided in the specified
Since 1.5
voidsetContents(Transferable contents, ClipboardOwner owner)
Sets the current contents of the clipboard to the specified transferable object and registers the specified clipboard owner as the owner of the new contents.

Anlegen eines Clipboards

Mit dem oben angeführten Konstruktor kann sich eine JavaApplikation ein eigenes Clipboard einrichten. Oft ist man jedoch auch daran interessiert, auf das systemeigene Clipboard zuzugreifen, um Daten aus anderen Anwendungen in eine JavaApplikation zu holen oder umgekehrt. Der Zugriff auf das Systemclipboard erfolgt über die wichtige Klasse Toolkit.


Zugriff auf das Systemclipboard
Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
System.out.println("Clipboard type : " + cb.getClass().getName() );
// Ausgabe : sun.awt.windows.WClipboard  (auf Windowssystemen)
System.out.println("Clipboard name : " + cb.getName() );
// Ausgabe : System

Egal, mit welchem Clipboard man arbeitet, die Vorgehensweise ist immer die gleiche.


Datentransfer mit dem Interface java.awt.datatransfer.Transferable

Der Zugriff auf den Inhalt das Clipboards erfolgt über die Schnittstelle Transferable. Die im Clipboard zwischengespeicherten Daten werden mit Hilfe der Methode getContents() geliefert. Diese Methode liefert die Daten in der Form eines eigenen Typs mit dem Namen Transferable. Wenn man weiß, welcher Typ von Daten anliegt, dann kann man die Daten aus dem Transferableobjekt abholen und auf einen Javatyp casten.

Will man umgekehrt Daten in das Clipboard bringen, muß man die Daten in ein Transferableobjekt übertragen, da ein Clipboard nur mit einem Transferableobjekt kommunizieren kann. Für Textdaten bietet Java dazu die Klasse StringSelection an. Für andere Datenformen gibt es keine entsprechenden Klassen. In diesen Fällen muß man sich eigene klassen schreiben, die das Interface Transferable implementieren. Wir werden dies hier zeigen und sowohl für Imagedaten als auch für Dateien eigene Klassen entwickeln. Weiter unten werden solche Klassen entwickelt. Mit dieser Klasse TransferableImage gelingt es, Bilddaten in das Systemclipboard (oder auch ein eigenes) zu bringen. Mit der Klasse TransferableFile wird es uns gelingen Dateien über das Clipboard zu verschieben. Zunächst stellen wir das Interface Transferable vor.

Wichtige Methoden
ReturntypName der Methode
ObjectgetTransferData(DataFlavor flavor)
Returns an object which represents the data to be transferred. throws UnsupportedFlavorException, IOException
DataFlavor[]getTransferDataFlavors()
Returns an array of DataFlavor objects indicating the flavors the data can be provided in.
booleanisDataFlavorSupported(DataFlavor flavor)
Returns whether or not the specified data flavor is supported for this object.

Ein Objekt vom Typ Transferable hält die Daten aus dem Clipboard. getTransferData() liefert dann diese Daten, aber nur, wenn der richtige Datentyp in Form eines DataFlavorobjekts übergeben wird. Ansonsten wirft die Methode eine UnsupportedFlavorException.


Die Klasse java.awt.datatransfer.DataFlavor
Konstanten
TypName der Konstante
static DataFlavorimageFlavor
The DataFlavor representing a Java Image class (representationClass = java.awt.Image. mimeType = "image/x-java-image").
static DataFlavorjavaFileListFlavor
To transfer a list of files to/from Java (and the underlying platform) a DataFlavor of this type/subtype and representation class of java.util.List is used.
static DataFlavorstringFlavor
The DataFlavor representing a Java Unicode String class (representationClass = java.lang.String, mimeType = "application/x-java-serialized-object")
Konstruktoren
DataFlavor(Class<?> representationClass, String humanPresentableName) Constructs a DataFlavor that represents a Java class.
DataFlavor(String mimeType) Constructs a DataFlavor from a mimeType string.
DataFlavor(String mimeType, String humanPresentableName) Constructs a DataFlavor that represents a MimeType.

In unseren Beispielen werden wir nur Konstanten vom Typ DataFlavor verwenden. Aber auch für selbst kreierte Objekte ist das Vorgehen nicht anders, denn : DataFlavor objects are constant and never change once instantiated. (API)


Die Klasse java.awt.datatransfer.StringSelection

Als letzte Klasse der API stellen wir noch Stringselection vor. StringSelection implementiert das Interface Transferable. Wir werden diese Klasse brauchen, um Strings in die Zwischenablage zu transportieren.

Konstruktoren
StringSelection(String data) Creates a Transferable capable of transferring the specified String.
Methoden
ReturntypName der Methode
ObjectgetTransferData(DataFlavor flavor)
Returns the Transferable's data in the requested DataFlavor if possible.
DataFlavor[]getTransferDataFlavors()
Returns an array of flavors in which this Transferable can provide the data.
boolean isDataFlavorSupported(DataFlavor flavor)
Returns whether the requested flavor is supported by this Transferable.
voidlostOwnership(Clipboard clipboard, Transferable contents)
Notifies this object that it is no longer the clipboard owner.

Im folgenden Teil werden wir den Copy/Paste-mechansimus für die Datentypen String, Image und File erläutern. Alle Beispiele sollen für eigene Clipboards und das Systemclipboard brauchbar sein. Am einfachsten ist Vorgehen für Strings, da hier die Klasse StringSelection zur Verfügung steht. Im Falle des Datentyps Image wird es möglich sein beliebige ImageObjekte in das Systemclipboard zu bringen bzw. abzuholen. Dazu entwickeln wir die Klasse TransferableImage. Im letzten Beispiel wollen wir FileObjekte in einer JList speichern. Dabei sollen die Fileobjekte in der Liste reale Dateien auf einem externen Speichermedium darstellen. JList soll so gestaltet werden, daß der Datenaustausch sowhl von einer JList zur anderen als auch zum Betriebssystem funktioniert.

Wir beginnen jedesmal mit Paste, da das Abholen der Daten aus einem Clipboard generell der einfachere Vorgang ist.


Paste für Strings

Da es einfacher ist, Daten aus dem Clipboard herauszuholen, fangen wir damit an.


Zugriff auf die Daten des Clipboards (Paste)

Die Daten des Clipboards befinden sich im Attribut contents. Die Methode getContents(Object requestor) liefert die Daten zunächst in der Form eines Types Transferable. Transferable ist ein InterfaceTyp. Die Methode liefert also eine Referenz auf ein Objekt, dessen Klasse das Interface implementiert hat. Der Parameter requestor wird zur Zeit noch nicht verwendet.

Transferable trans = cb.getContents(null);  // Parameter wird nicht benützt
System.out.println("Transferable type : " + trans.getClass().getName() );
// Ausgabe : sun.awt.datatransfer.ClipboardTransferable

Welche Daten liegen an ?

Liegen keine Daten an, so liefert getContents() null. Das wird eher selten der Fall sein. Irgendetwas liegt meistens in der Zwischenablage. Mit der Methode isDataFlavorSupported(DataFlavor flavor) von Transferable erfährt man, welche Art von Daten anliegen. Die Daten werden also nach verschiedenen "Geschmacksrichtungen" unterschieden. Dafür gibt es die Klasse Dataflavor. Sie hält statische Konstanten bereit, mit denen man die Daten im Transferableobjekt untersuchen kann.

System.out.println("StringFlavor is supported : " +
                    trans.isDataFlavorSupported(DataFlavor.stringFlavor));
// true, falls ein String anliegt, false sonst

Den String aus dem Transferableobjekt holen

Die Methode getTransferData(DataFlavor flavor) liefert die Daten in einer lesbaren Form, falls der flavor zu den Daten passt. Wenn nicht, so wird eine Exception mit dem schönen Namen UnsupportedFlavorException geworfen.

try
{
   Object data = trans.getTransferData(DataFlavor.stringFlavor) ;
   // data kann nun auf String heruntergecastet werden
}
catch(UnsupportedFlavorException ex)
{
   System.out.println("Daten enthalten keinen Text");
}
catch(IOException ex)
{
   System.out.println(ex);
}

Den String z.Bsp in eine (J)TextArea einfügen
textarea.replaceRange( (String)data, tear.getSelectionStart(), tear.getSelectionEnd());
//Falls kein Text markiert ist, wird an der aktuellen Cursorposition eingefügt

Copy/Cut für Strings

Will man Daten aus der eigenen Anwendung exportieren, muß man genau den umgekehrten Weg gehen. Also muß man es schaffen, einen String in ein Transferableobjekt zu verwandeln. Für Strings ist das keine Arbeit, da es die Klasse StringSelection gibt. StringSelection implementiert das Interface Transferable.


Einen String in ein Transferableobjekt verwandeln

Der folgende Codeausschnitt holt den markierten Text aus eine (J)TextArea und legt damit ein Objekt vom Typ StringSelection an.

String selected = tear.getSelectedText();
if (selected!=null && selected.length()!=0)
{
   StringSelection clipText = new StringSelection(selected);
}

Das Transferableobjekt ins Clipboard übertragen

Clipboard bietet dazu die Methode setContents(Transferable contents, ClipboardOwner owner) an. Da die Klasse StringSelection auch das Interface ClipboardOwner implementiert, kann man für beide Parameter das soeben erzeugte StringSelectionobjekt hernehmen.

cb.setContents(clipText, clipText);

Will man cut statt copy realisieren muß man noch den markierten Text, etwa in einer (J)TextArea löschen:

textarea.replaceRange("", tear.getSelectionStart(), tear.getSelectionEnd());

Das war's.


Paste für Images

Seit der Version 1.4 ist es ziemlich einfach, auch Bilder aus der Zwischenablage zu holen. Die systemabhängige Implementierung liefert die Bilder in einem systemunabhängigen Javaformat. Entlockt man dem mit getTransferData(DataFlavor.imageFlavor) erhaltenem Objekt mit getClass().getName() seinen eigentlichen Typ, so erhält man den Typ BufferedImage. BufferedImage ist der komfortabelste Imagetyp, den Java zu bieten hat. Ein BufferedImage kann man überall einsetzen, wo man ein Image braucht. Man kann es daher problemlos auch in ein ImageIcon umwandeln. Zudem ist es in verschiedenen Formaten auf Platte speicherbar.


Welche Daten liegen an ?

Die Konstante DataFlavor.imageFlavor ist eine der nützlichen Errungenschaften der Version 1.4. Ab dieser Version kann man Bilder ohne großen Aufwand aus der Zwischenablage holen, bzw. sie auch dort ablegen.

System.out.println("ImageFlavor is supported : " +
                    trans.isDataFlavorSupported(DataFlavor.imageFlavor));
// true, falls ein Image anliegt, false sonst

Hat man sich davon überzeugt, daß Bilddaten anliegen, kann man sie genauso abholen wie einen String.

try
{
   Object data = trans.getTransferData(DataFlavor.imageFlavor) ;
   // data kann nun auf BufferedImage heruntergecastet werden
}
catch(UnsupportedFlavorException ex)
{
   System.out.println("Daten enthalten kein Image");
}
catch(IOException ex)
{
   System.out.println(ex);
}

Das BufferedImage z.Bsp auf ein JLabel legen
BufferedImage img = (BufferedImage)data;
imageLabel.setIcon( new ImageIcon(img) );

Copy/Cut für Images (ab 1.4)

Um Bilder in das Clipboard zu bringen muß man sich etwas mehr anstrengen. Es gibt nämlich zu StringSelection kein Äquivalent für Images. Also schreiben wir es selbst. Das ist nach einiger Überlegung garnicht so schwer. Sun's Implementierung sun.awt.datatransfer.ClipboardTransferable des Interfaces Transferable liefert beim Aufruf der Methode getTransferData(DataFlavor.imageFlavor) im Erfolgsfall ein Bild vom Typ BufferedImage. Wir legen uns also eine Klasse an, die ein BufferedImage als Attribut enthält und dieses mit der Methode getTransferData(DataFlavor flavor) herausrückt. Die Konstruktoren haben die Aufgabe, dieses BufferedImage aus anderen Imagetypen zu erzeugen. Ich habe die Klasse TransferableImage genannt.


Die Klasse TransferableImage
public class TransferableImage implements Transferable
{
   private BufferedImage bufImg;

   public TransferableImage(ImageIcon ic)
   {
      this( ic.getImage() );
   }

   public TransferableImage(Image img)
   {
      int w = img.getWidth(null); // es muß keinen ImageObserver geben
      int h = img.getHeight(null);

      bufImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
      // einfachster Fall : BufferedImage.TYPE_INT_RGB
      // mit drawImage das Image auf ein BufferedImage zeichnen
      Graphics g = bufImg.createGraphics();
      g.drawImage(img, 0, 0, null);
      g.dispose();
   }

   public TransferableImage(BufferedImage bImg)
   {
      bufImg = bImg;
   }

   //Returns an object which represents the data to be transferred.
   public Object getTransferData(DataFlavor flavor)
      throws UnsupportedFlavorException
   {
      if( flavor.equals(DataFlavor.imageFlavor) )
         return bufImg;

      throw new UnsupportedFlavorException(flavor);
   }

   //Returns an array of DataFlavor objects indicating the flavors
   //the data can be provided in.
   public DataFlavor[] getTransferDataFlavors()
   {
      return new DataFlavor[] {DataFlavor.imageFlavor} ;
   }

   //Returns whether or not the specified data flavor is supported
   //for this object.
   public boolean isDataFlavorSupported(DataFlavor flavor)
   {
      return flavor.equals(DataFlavor.imageFlavor) ;
   }
}

Die Klasse verfügt über drei Konstruktoren, die alle Ansprüche abdecken sollten. Man kann ein ImageIcon, ein Image oder ein BufferedImage übergeben. Zur Funktionsweise der Konstruktoren verweise ich auf Image / Icon / BufferedImage / Screenshot und die Abschnitte "Vom Image zum ImageIcon und umgekehrt" und "Ein Image in ein BufferedImage verwandeln".

Die Methode getTransferData() liefert das BufferedImage, falls die Konstante DataFlavor.imageFlavor übergeben wird.

Die Methode getTransferDataFlavors() liefert ein Array von flavors der Länge 1, das die Konstante DataFlavor.imageFlavor enthält. Die Klasse ist eben nur für diesen flavor zuständig.

Auch die Implementierung der dritten Methode ist offensichtlich.


Der ClipboardOwner

Um analog wie bei Strings die Methode setContents(Transferable contents, ClipboardOwner owner) anwenden zu können brauchen wir noch einen ClipboardOwner. Ja, aber nur formal. Ein Blick in den Quellcode von Clipboard zeigt, daß man problemlos als owner null setzen kann.


Ausschnitt aus dem Quelltext von Clipboard
public synchronized void setContents(Transferable contents, ClipboardOwner owner)
{
   final ClipboardOwner oldOwner = this.owner;
   final Transferable oldContents = this.contents;

   this.owner = owner;
   this.contents = contents;

   if (oldOwner != null && oldOwner != owner)
   {
      oldOwner.lostOwnership(this, oldContents);
   }
}

Ist hier owner null, so wird die Methode lostOwnership() nicht gerufen. Schaut man sich zudem die Implementierung des Interfaces ClipboardOwner von Stringselection an, so sieht man, daß die Implementierung leer ist.


Ausschnitt aus dem Quelltext von StringSelection
public void lostOwnership(Clipboard clipboard, Transferable contents)
{
}

Um für zukünftige Erweiterungen gerüstet zu sein, verfahren wir ebenso und implementieren in der Klasse TransferableImage auch das Interface ClipboardOwner :


Die endgültige Fassung der Klasse TransferableImage
public class TransferableImage implements Transferable, ClipboardOwner
{
   private BufferedImage bufImg;

   public TransferableImage(ImageIcon ic)
   {
      this( ic.getImage() );
   }

   public TransferableImage(Image img)
   {
      int w = img.getWidth(null); // es muß keinen ImageObserver geben
      int h = img.getHeight(null);

      bufImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
      // einfachster Fall : BufferedImage.TYPE_INT_RGB
      // mit drawImage das Image auf ein BufferedImage zeichnen
      Graphics g = bufImg.createGraphics();
      g.drawImage(img, 0, 0, null);
      g.dispose();
   }

   public TransferableImage(BufferedImage bImg)
   {
      bufImg = bImg;
   }

   //Returns an object which represents the data to be transferred.
   public Object getTransferData(DataFlavor flavor)
      throws UnsupportedFlavorException
   {
      if( flavor.equals(DataFlavor.imageFlavor) )
         return bufImg;

      throw new UnsupportedFlavorException(flavor);
   }

   //Returns an array of DataFlavor objects indicating the flavors
   //the data can be provided in.
   public DataFlavor[] getTransferDataFlavors()
   {
      return new DataFlavor[] {DataFlavor.imageFlavor} ;
   }

   //Returns whether or not the specified data flavor is supported
   //for this object.
   public boolean isDataFlavorSupported(DataFlavor flavor)
   {
      return flavor.equals(DataFlavor.imageFlavor) ;
   }

   // Implementierung des Interfaces ClipboardOwner
   public void lostOwnership(Clipboard clipboard, Transferable contents)
   {
   }
}

Ein Image mit der Klasse TransferableImage in ein Transferableobjekt verwandeln und dieses in die Zwischenablage befördern

Der folgende Codeausschnitt holt ein Bild aus einem JLabel, verwandelt es in ein TransferableImage und schiebt dieses in die Zwischenablage.

Icon icon = imageLabel.getIcon() ;
if ( icon instanceof ImageIcon )
{
   TransferableImage ti = new TransferableImage( (ImageIcon)icon );
   clipboard.setContents(ti, ti);
}

Nun kann man mit irgendeiner Bildbearbeitungssoftware (z.B. PhotoEditor) das Bild aus der Zwischenablage holen und einfügen.


Paste für Dateien mit JList

Wir skizzieren zunächst die Konfiguration einer JList, die Dateien anzeigen soll.


JList konfigurieren
DefaultListModel defaultListModel = new DefaultListModel();
JList fileList = new JList(defaultListModel);
fileList.addKeyListener( new PasteHandler() );
fileList.setCellRenderer( new FileListCellRenderer() );
JScrollPane sp = new JScrollPane(fileList);

Wir verwenden ein DefaultListModel. Es arbeitet mit einem Vector und kann beliebige Objekte aufnehmen. Wir werden File-Objekte aufnehmen. Mit CTRL+V wollen wir Dateien aus dem Clipboard holen. Dazu brauchen wir einen KeyListener. natürlich wollen wir, daß mit Copy/Paste wiklich Dateien kopiert werden und nicht nur die Namen der Dateien.
Da der DefaultRenderer entweder Text oder Icons anzeigen kann, aber nicht beides, wollen wir einen eigenen Renderer verwenden. Dies ist ein schönes GUI-feature, aber für Copy/Paste unerheblich.


JList füllen
File root = new File("c:/"); // oder new File("/")
File[] contents = root.listFiles() ;
for(int i=0; i<contents.length; i++)
   defaultListModel.addElement(contents[i]) ;

Reaktion auf CTRL+V

Wir nehmen nun an, daß im Clipboard eine Datei abgelegt worden ist (z.Bsp. dadurch, daß in einem Arbeitsplatzfenster eine Datei mit CTRL-C ins Clipboard befördert wurde) und wollen diese Datei sowohl in die JListe aufnehmen als auch physikalisch kopieren.

class PasteHandler extends KeyAdapter
{
   public void keyPressed(KeyEvent e)
   {
      int kCode = e.getKeyCode();
      int mod = e.getModifiers();

      if(kCode == KeyEvent.VK_V && mod == KeyEvent.CTRL_MASK)
      {
         //System.out.println("control+v"); // ok
         Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
         paste(cb);
      }
   }
}

Wir überprüfen in keyPressed() die Tastenkombination, holen uns das SystemClipboard (oder ein anderes) und delegieren die Aufgabe an die Methode paste():

private void paste(Clipboard cb)
{
   Transferable tr = cb.getContents(null);  // Parameter wird nicht benützt
   try
   {
      java.util.List files = (java.util.List)tr.getTransferData(DataFlavor.javaFileListFlavor);
      int len = files.size();
      File targetDir = getTargetDir();
      File file;
      for(int i=0; i<len; i++)
      {
         file = (File)files.get(i);
         copyFiles(file, targetDir);  // wirft IOException
      }
      updateList(targetDir);
   }
   catch(UnsupportedFlavorException ex)
   {
      System.out.println ("DataFlavor.javaFileListFlavor is not supported, rejected");
   }
   catch(IOException ex)
   {
      System.out.println ("IOException " +ex);
      //ex.printStackTrace();
   }
}

Wesentlich ist die erste Zeile. Wir übergeben der Methode getTransferData() die Konstante DataFlavor.javaFileListFlavor. Liegen Dateien im Clipboard, so liefert diese Methode eine Liste von File-Objekten, andernfalls wird eine UnsupportedFlavorException geworfen. Hat man die Quelldateinamen und das Zielverzeichnis, so kann man den Kopiervorgang an die Methode copyFiles() delegieren. Diese wird hier nicht implementiert. Danach müssen die kopierten Dateien noch in die Liste aufgenommen werden. Dies ist wiederum nur eine Skizze. Eine saubere Implementierung muß selbstverständlich auch jeden einzelnen Kopiervorgang überprüfen.


Copy/Cut für Dateien mit JList

Um Dateien ins Clipboard zu transferieren gehen wir ähnlich wie im letzten Abschnitt vor und schreiben uns eine Klasse TransferableFile. Aus der Beschreibung der Konstanten DataFlavor.javaFileListFlavor in der API erkennt man, daß man dem Clipboard die Daten in Form einer Liste von Fileobjekten übergeben muß. Genau diesem Zweck dient die Klasse TransferableFile


Die Klasse TransferableFile
import java.io.*;
import java.util.*;
import java.awt.datatransfer.*;

public class TransferableFile implements Transferable
{
   private Vector fileList;

   public TransferableFile(String fileName)
   {
      fileList = new Vector();
      fileList.add( new File(fileName) ) ;
   }

   public TransferableFile(File file)
   {
      fileList = new Vector();
      fileList.add( file ) ;
   }

   public TransferableFile(String[] fileNames)
   {
      fileList = new Vector();
      for(int i=0; i<fileNames.length; i++)
         fileList.add( new File(fileNames[i]) ) ;
   }

   public TransferableFile(File[] files)
   {
      fileList = new Vector();
      for(int i=0; i<files.length; i++)
         fileList.add( files[i] ) ;
   }

   public TransferableFile(Vector files)
   {
      // we had to make a copy, because the elements of files can change !
      fileList = new Vector(files);
   }

   //Returns an object which represents the data to be transferred.
   public Object getTransferData(DataFlavor flavor)
      throws UnsupportedFlavorException
   {
      if( flavor.equals(DataFlavor.javaFileListFlavor) )
         return fileList ;

      throw new UnsupportedFlavorException(flavor);
   }

   //Returns an array of DataFlavor objects indicating the flavors
   //the data can be provided in.
   public DataFlavor[] getTransferDataFlavors()
   {
      return new DataFlavor[] {DataFlavor.javaFileListFlavor} ;
   }

   //Returns whether or not the specified data flavor is supported for this object.
   public boolean isDataFlavorSupported(DataFlavor flavor)
   {
      return flavor.equals(DataFlavor.javaFileListFlavor) ;
   }
}

Die Konstruktorausstattung ist üppig und sollte alle Fälle abdecken. Zentral ist die Methode getTransferData(), über die das Clipboard die Liste erhält.


Die Daten ins Clipboard bringen (Variante 1)

Wie bei Images könnten wir wieder mit clipboard.setContents() arbeiten. Im KeyListener lauscht man dazu auf die Tastenkombination CTRL+C oder CTRL+X und setzt dann clipboard.setContents() ab.




Die Daten über den Transferhandler ins Clipboard bringen (Variante 2  Teil 1)

Interessanter ist der Weg über einen TransferHandler, der in der Version 1.4 für Swingkomponenten eingeführt wurde. TransferHandler vereinfachen den Drag-teil von Drag und Drop und exportieren nebenbei die Daten auch noch ins Clipboard (Drag and Drop wird ausführlich im nächsten Kapitel behandelt). JList hat einen TransferHandler vorinstalliert, der allerdings nur für Strings geeignet ist. Für unsere Bedürfnissse brauchen wir einen eigenen TransferHandler. Der Vorteil dieser Variante ist, daß wir hier den Datenexport in das Clipboard nicht über einen Listener überwachen müssen, weil er automatisch initiert wird. Zudem hat man mit dieser Variante auch schon die erste Hälfte von Drag und Drop codiert.

Das folgende ist zu tun:


Die Klasse javax.swing.TransferHandler erweitern
import java.io.*;
import java.util.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.datatransfer.*;

public class ListTransferHandler extends TransferHandler
{
   private Vector files ;

   public ListTransferHandler()
   {
      files = new Vector();
   }

   public ListTransferHandler(String text)
   {
      super(text);
      files = new Vector();
   }

   /**
   * Create a Transferable to use as the source for a data transfer.
   * @param c  The component holding the data to be transfered.
   * @return  The representation of the data to be transfered.
   */
   public Transferable createTransferable(JComponent c)
   {
      if (c instanceof JList)
      {
         JList list = (JList) c;
         Object[] values = list.getSelectedValues();
         if (values == null || values.length == 0)
         {
            return null;
         }

         for(int i=0; i<values.length; i++)
         {
            files.add( values[i] );
         }
         return new TransferableFile(files);
      }
      else
         return null;
   }

   public int getSourceActions(JComponent c)
   {
      System.out.println("getSourceActions");
      return COPY;  // or return COPY_OR_MOVE;
   }

   public void exportToClipboard(JComponent comp, Clipboard clip, int action)
      throws IllegalStateException
   {
      System.out.println("exportToClipboard");
      super.exportToClipboard(comp,clip,action);
   }

   // Causes the Swing drag support to be initiated.
   public void exportAsDrag(JComponent comp, java.awt.event.InputEvent e, int action)
   {
      System.out.println("exportAsDrag");
      super.exportAsDrag(comp, e, action);
   }

   //Invoked after data has been exported.
   public void exportDone(JComponent source, Transferable data, int action)
   {
      super.exportDone(source, data, action) ;
      files.removeAllElements(); // empty the vector for the next operation
   }

} // end class ListTransferHandler

Wie man sieht, arbeitet unser ListTransferHandler mit der Klasse TransferableFile zusammen. Die Methoden exportToClipboard() und exportAsDrag() brauchen nicht (und sollen in der Regel auch nicht) überschrieben werden. Mit Hilfe der Meldungen kann man jedoch die automatische Initiierung erkennen. In der Methode exportDone() kann man Aufräumungsarbeiten erledigen.


Den Transferhandler der JList bekanntmachen (Variante 2  Teil 2)

Dazu braucht man zwei Zeilen.

fileList.setDragEnabled(true);
fileList.setTransferHandler( new ListTransferHandler() );

Im nächsten Kapitel über Drag und Drop werden wir die Klassen ListTransferHandler und TransferableFile noch einmal brauchen.


Betriebssystemabhängige FileIcons in der JList darstellen

Zum Schluß noch eine kleine Verzierung. Es wäre schön, wenn unsere JList Icons und Text anzeigen könnte. Dieses Problem wird durch einen eigenen Renderer gelöst im Kapitel über JList im Abschnitt "Listen mit Text und Bildern (Icons)". Ein Beispiel für einen passenden Renderer steht dort im Quelltext. Hier zeigen wir, wie man über die Klasse JFileChooser an die Icons herankommt. Die folgende Skizze zeigt, wie man das zu einer Datei passende Icon erhält.

JFileChooser choo = new JFileChooser();
FileChooserUI chooUI = choo.getUI();
FileView fileView = chooUI.getFileView(choo);
Icon  icon = fileView.getIcon(file) ;

Und so sehen die Icons aus (Java Version 1.5, W2K)

fileviewicons.jpg

Wer schon mit dem JFileChooser gearbeitet hat, der kennt sie.

Valid XHTML 1.0 Strict top Back Next Up Home