How to implement the Copy&Paste for a JTable


The standard implementation of JTable allows to copy from the clipboard, via Ctrl+C, but not to paste to the clipboard, via Ctrl+V. Also we can copy only a full row, not a single cell, in a tab-separated or in a HTML flavour (The format depends by the target application, if it supports a rich format like HTML or less).

But how we can force JTable to act like a spreadsheet? Where the Ctrl+V was supported and the Ctrl+C copy the value of the single selected cell?

The copy action

First of all we have to override the standard behaviour:

final JTable table;
...
ActionListener listener = new ActionListener() {
 public void actionPerformed(ActionEvent event) {
 doCopy();
 }//end actionPerformed(ActionEvent)
};

final KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK, false);

table.registerKeyboardAction(listener, "Copy", stroke, JComponent.WHEN_FOCUSED);

So, when we press Ctrl+C we call the doCopy() method of the parent class of the anonymous listener.

private void doCopy() {
    int col = table.getSelectedColumn();
    int row = table.getSelectedRow();
    if (col != -1 && row != -1) {
        Object value = table.getValueAt(row, col);
        String data;
        if (value == null) {
            data = "";
        } else {
            data = value.toString();
        }//end if

        final StringSelection selection = new StringSelection(data);     

        final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        clipboard.setContents(selection, selection);
    }//end if
}//end doCopy()

The clipboard

The clipboard, java.awt.datatransfer.Clipboard, is the component that allows us to interact with the System Clipboard. In Java there is the concept of Data Flavor. The Data Flavor[1] permit to handle different format, ie if a client may understand a rich format like HTML or RTF it may use the content as is, otherwise it may use it as a bare text.
Usually we may access to the system clipboard via the AWT system toolkit, Toolkit.getDefaultToolkit().getSystemClipboard(), but we may create our own clipboard due to limit the copy&paste visibility only for the local VM or to extend it between some remote machines.

The paste action

As for the copy action we have to register[2] the keyboard action:

final JTable table;     
...      
ActionListener listener = new ActionListener(){
    public void actionPerformed(ActionEvent event) {
        doPaste();
    }//end actionPerformed(ActionEvent)
};      

final KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_V, ActionEvent.CTRL_MASK, false);
table.registerKeyboardAction(listener, "Paste", stroke, JComponent.WHEN_FOCUSED);

In the table we suppose that the data are stored as String, is trivial handle Number, Date, etcetera etcetera.

private void doPaste() {
    final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
    final Transferable content = clipboard.getContents(this);
    if (content != null) {
        try {
            final String value = content.getTransferData(DataFlavor.stringFlavor).toString();     

            final int col = table.getSelectedColumn();
            final int row = table.getSelectedRow();
            if (table.isCellEditable(row, col)) {
                table.setValueAt(value, row, col);
                if (table.getEditingRow() == row && table.getEditingColumn() == col) {
                    final CellEditor editor = table.getCellEditor();
                    editor.cancelCellEditing();
                    table.editCellAt(row, col);
                }//end if
            }//end if
            table.repaint();
        } catch (UnsupportedFlavorException e) {
            // String have to be the standard flavor
            System.err.println("UNSUPPORTED FLAVOR EXCEPTION " + e.getLocalizedMessage());
        } catch (IOException e) {
            // The data is consumed?
            System.err.println("DATA CONSUMED EXCEPTION " + e.getLocalizedMessage());
        }//end try
    }//end if
}//end doPaste()

The first step is to get the clipboard content as a Transferableobject. If this contents is null the clipboard is empty and we have no data to copy. Now we have to translate the content in a format that we can understand. Because we handle String, we ask the data in a String Flavor. Almost every content may be translated into String, but we may ask at the Transferable object the supported flavor in order to obtain a more rich one.

As last step we may copy the clipboard value into the first editable selected cell.

[1] In UK English Flavour, in US English Flavor. This may be the source of some typo errors.
[2] The registerKeyboardAction of JComponent is described as an obsolete method, even it is not marked by a @obsolete tag. This method is a shorthand for:

public void registerKeyboardAction(ActionListener anAction,String aCommand,KeyStroke aKeyStroke,int aCondition) {
    final InputMap inputMap = getInputMap(aCondition, true);
    if (inputMap != null) {
        final ActionMap actionMap = getActionMap(true);
        final ActionStandin action = new ActionStandin(anAction, aCommand);
        inputMap.put(aKeyStroke, action);
        if (actionMap != null) {
            actionMap.put(action, action);
        }
    }
}
Advertisements

3 thoughts on “How to implement the Copy&Paste for a JTable

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s