Gestire la clipboard in Java Swing

di  Antonio Coschignano, mercoledì 16 dicembre 2009
La cosiddetta clipboard è in generale un area di memoria temporanea dove vengono memorizzati alcuni dati che poi possono essere trasferiti da una parta all'altra. E' molto usata nel filesystem quando copiamo e incolliamo un file, oppure un intera cartella. Mentre negli ambienti di video scrittura come Word o semplici editor di testo è usata per copiare ed incollare parti di testo, immagini etc.

In quest'articolo vedremo come gestire la clipboard di sistema in ambiente java, nei componenti di testo Swing, come JTextArea, JTextField, cioè tutti quegli oggetti che estendono JTextComponent. Tengo presente che verrà gestita la clipboard di sistema, quindi i dati potranno interagire con tutto l'ambiente del sistema operativo, cioè ad esempio dal Notepad, ad un componente java e viceversa.

Toolkit e Clipboard

Innanzitutto bisogna ottenere un accesso alla clipboard del sistema. In java abbiamo la classe java.awt.Toolkit che attraverso alcuni metodi implementa delle funzinalità native che interagiscono con il sistema operativo. Vediamo come ottenere il Toolkit:
Toolkit toolkit = Toolkit.getDefaultToolkit();
L' oggetto toolKit ritornato implementa ad esempio delle funzionalitè come l'accesso alla stampante, riprodurre un segnale acustico, le dimensioni e la risoluzione dello schermo etc.. Mentre per ottenere un riferimento alla clipboard bisogna utlizzare il metodo statico getSystemClipboard():
java.awt.datatransfer.Clipboard clipboard = toolkit.getSystemClipboard();
La classe java.awt.datatransfer.Clipboard implementa un meccanismo di trasferimento dei dati da un componente java alla clipboard del sistema operativo e viceversa. Il tutto avviene tramite l'ausilio di un oggetto di tipo java.awt.datatransfer.Transferable che si occupa appunto di gestire le operazioni di trasferimento, indicando il tipo di dato che si vuole trasferire (DataFlavor). Ci occuperemo in questo articolo solo ed esclusivamente di dati di tipo stringa che viene identificato dalla costante DataFlavor.stringFlavor. Vediamo un piccolo esempio di come recuperare il testo dalla clipboard:
Transferable clipboardContent = clipBoard.getContents(null);
if(clipboardContent!=null && (clipboardContent.isDataFlavorSupported (DataFlavor.stringFlavor))) {
  try {
	String text = clipboardContent.getTransferData(DataFlavor.stringFlavor));
	System.out.println(text);
  }catch(Exception ex) {
    text.printStackTrace();
  }
}
In questo caso abbiamo recuperato il testo contenuto nella clipboard, controllando prima se effettivamente ci sia qualcosa al suo interno e se corrisponde al tipo String:
...
clipboardContent.isDataFlavorSupported (DataFlavor.stringFlavor));
...
Vorrei introdurre adesso un altro oggetto indispensabile invece per trasferire il testo selezionato generalmente in un JTextComponent alla clipboard, nella modalità di CUT (taglia) oppure COPY (copia). L 'oggetto in questione è il TransferHandler ed è associato automaticamente ad ogni JComponent e quindi per ereditarietà a tutti i JTextComponent. Ad esempio:
...
JTextArea textArea = new JTextArea();
...
TransferHandler handler = textArea.getTransferHandler();
...
Adesso vediamo il caso un cui essendoci un testo selezionato vogliamo trasferirlo nella clipboard:
handler.exportToClipboard(textArea, clipBoard, TransferHandler.COPY);
Il parametro textArea è il riferimento al componente in questione, da cui stiamo copiando qualcosa, clipBoard è un riferimento all'oggetto Clipboard e TransferHandler.COPY indica invece il tipo di operazione che vogliamo effettuare. Infatti se volevamo 'tagliare' il testo bisognava utilizzare invece TransferHandler.MOVE che oltre a inserire il testo selezionato nella clipboard automaticamente elimina il testo dal componente.

Un PopupMenu per la clipboard

Fatta questa breve panoramica sugli oggetti coinvolti nella gestione della clipboard, propongo di realizzare una classe che gestisce in maniera automatica le diverse operazioni di copia/incolla/taglia/cancella su ogni oggetto di tipo JTextComponent, usando un semplice JPopupMenu. Per intenderci meglio ecco un'immagine di quello che sarà la nostra opera:

Popup Menu

In pratica questa classe implementa un listener tramite il MouseAdapter estendedola ed ridefinendo i metodi mousePressed() e mouseReleased() im modo tale da visualizzare un popupmenu alla pressione del tasto del mouse. Il menu contiene dei pulsanti (copia, taglia, incolla, cancella) che a loro volta lavorano sull'oggetto Clipboard eseguendo le azioni opportune. Tutto questo meccanismo viene collegato al JTextComponent dove settiamo come MouseListener (addMouseListener()) la classe che abbiamo definito.

La classe ClipboardPopupMenu.java

Per installare la clipboard su una JTextArea procediamo in questo modo:
...
JTextArea textArea = new JTextArea();
ClipboardPopupMenu clipBoard = ClipboardPopupMenu.installForComponent(textArea);
...
Una volta installata, otteniamo un riferimento all'oggetto dove possiamo richiamare i metodi per comporre il menu con le funzioni a nostra scelta. Ad esempio se vogliamo solo implementare Copia/Incolla, basta procedere in questo modo:
...
 clipBoard.addCopyFunction("Copia");
 clipBoard.addPasteFunction("Incolla");
 ...
Questo è il codice completo dell'intera classe da copiare ed incollare direttamente nel vostro editor di sviluppo preferito:
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.text.*;

public class ClipboardPopupMenu extends MouseAdapter {

  private Clipboard clipBoard;
  private JPopupMenu popup;
  private JTextComponent textComponent;
  private JMenuItem copy;
  private JMenuItem paste;
  private JMenuItem clear;
  private JMenuItem cut;
  private TransferHandler handler;

  public ClipboardPopupMenu(JTextComponent textComponent) {
    popup = new JPopupMenu();
    Toolkit toolkit = Toolkit.getDefaultToolkit();
    clipBoard = Toolkit.getDefaultToolkit().getSystemClipboard();
    this.textComponent = textComponent;
    handler = textComponent.getTransferHandler();
    createMenu();
  }

  protected void createMenu() {
    copy = new JMenuItem("Copy");
    paste = new JMenuItem("Paste");
    cut = new JMenuItem("Cut");
    clear = new JMenuItem("Clear");
  }

  public void addPasteFunction() {
    paste.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        Transferable clipboardContent = clipBoard.getContents(this);
        if(clipboardContent!=null &&
          (clipboardContent.isDataFlavorSupported (DataFlavor.stringFlavor))) {
            try {
              textComponent.setText(textComponent.getText()+clipboardContent.getTransferData(DataFlavor.stringFlavor));
            }catch(Exception ex) {}
          }
        }
    });
    popup.add(paste);
  }

  public void addCopyFunction() {
    copy.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        handler.exportToClipboard(textComponent, clipBoard, TransferHandler.COPY);
      }
    });
    popup.add(copy);
  }
  public void addClearFunction() {
    popup.addSeparator();
    popup.add(clear);
  }

  public void addCutFunction() {
    cut.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        handler.exportToClipboard(textComponent, clipBoard, TransferHandler.MOVE);
      }
    });
    popup.add(cut);
  }

  public void addPasteFunction(String nameItem) {
   paste.setText(nameItem);
   addPasteFunction();
  }

  public void addCopyFunction(String nameItem) {
    copy.setText(nameItem);
    addCopyFunction();
  }

  public void addClearFunction(String nameItem) {
    clear.setText(nameItem);
    addClearFunction();
  }

  public void addCutFunction(String menuItem) {
    cut.setText(menuItem);
    addCutFunction();
  }

  public void mousePressed(MouseEvent e) {
    showPopup(e);
  }

  public void mouseReleased(MouseEvent e) {
    showPopup(e);
  }

  private void showPopup(MouseEvent e) {
     if (e.isPopupTrigger()) {
        if(textComponent.getText().length()==0) {
          copy.setEnabled(false);
          cut.setEnabled(false);
        } else {
          copy.setEnabled(true);
          copy.setEnabled(true);
        }
        popup.show(e.getComponent(), e.getX(), e.getY());
     }
  }

  public JPopupMenu getPopup() {
    return popup;
  }

  public static ClipboardPoupMenu installForComponent(JTextComponent c) {
     ClipboardPopupMenu cpb = new ClipboardPopupMenu(c);
     c.addMouseListener(cpb);
     return cpb;
  }
}

Un esempio

Per concludere vediamo un semplice esempio attraverso un JFrame che contiene un'area di testo ed un campo di testo dove viene installata in entrambi i casi la clipboard che abbiamo definito:
public static void main(String [] argv) {
  JTextArea textArea = new JTextArea();
  JTextField field = new JTextField(20);
  ClipboardPopupMenu cb=  ClipboardPopupMenu.installForComponent(textArea);
  cb.addCopyFunction("Copia");
  cb.addCutFunction("Taglia");
  cb.addPasteFunction("Incolla");
  cb.addClearFunction("Cancella");
  ClipboardPopupMenu cbField = ClipboardPopupMenu.installForComponent(field);
  cbField.addCopyFunction("Copia");
  cbField.addCutFunction("Taglia");
  cbField.addPasteFunction("Incolla");
  cbField.addClearFunction("Cancella");
  JFrame frame = new JFrame("Clipboard Demo");
  textArea.setBorder(new TitledBorder("TextArea"));
  frame.getContentPane().add(field, BorderLayout.NORTH);
  frame.getContentPane().add(new JScrollPane(textArea), BorderLayout.CENTER);
  frame.setSize(500,400);
  frame.setVisible(true);
}
Questa è una sequenza di output dell'esempio:

Esempio taglia

Esempio incolla

Altri link che potrebbero interessarti
  • » Una lista concatenata in java
  • » Tutorial JFileChooser in Java Swing
  • » La gestione delle Stringhe in java
  • » La gestione degli eventi in java Swing
  • » La classe Collections
  • » Java Generics
  • » Gestire la System Tray in Java
  • » Connessioni HTTP in Java