Make JTable cell editor value be selectable, but not editable?
Asked Answered
C

2

7

I have tried to keep my JTable's tight and secure, making only editable columns editable via isCellEditable(). However, my clients are insisting that they want to double click on a cell so they can copy its contents, even if it is read only. I could let the cell be editable and not do anything with any edits they could make in the setValueAt() (so it reverts back to original value when editor exits). But I don't want this application to feel so freeform. Is there an easy effective way to make the JTextField used as the cell editor to allow selecting of text in the editor, but not editable?

I tried this override on my JTable below, but I don't think I'm looking for the right "instanceof" object.

@Override
public TableCellEditor getDefaultEditor(Class<?> columnClass) {
    if (super.getDefaultEditor(columnClass) instanceof JTextField) {
        JTextField jTextField = new JTextField();
        jTextField.setEditable(false);
        return (TableCellEditor) jTextField;
    }
    if (columnClass == null) {
        return null;
    }
    else {
        Object editor = defaultEditorsByColumnClass.get(columnClass);
        if (editor != null) {
            return (TableCellEditor)editor;
        }
        else {
            return getDefaultEditor(columnClass.getSuperclass());
        }
    }
}
Casserole answered 18/9, 2014 at 16:11 Comment(2)
maybe you should check the CellEditor#stopCellEditingAloes
haven't use them but you could check also CellEditor#cancelCellEditing()Aloes
L
10

However, my clients are insisting that they want to double click on a cell so they can copy its contents, even if it is read only.

Create a custom editor that uses a readonly text field:

JTextField tf = new JTextField();
tf.setEditable(false);
DefaultCellEditor editor = new DefaultCellEditor( tf );
table.setDefaultEditor(Object.class, editor);

Use the keyboard or mouse to select the text you want to copy. Then you would then use Ctrl+C to copy the selected text. Or you could even add a popup menu to the text field and add a Copy menu item.

Lewiss answered 18/9, 2014 at 17:34 Comment(1)
Wish I could accept answers. Although answer below is a very original solution, this was technically what I asked for although I will experiment with double-click solution.Casserole
R
7

"However, my clients are insisting that they want to double click on a cell so they can copy its contents, even if it is read only"

You could just use a MouseListener, and on a double click, programmatically copy the contents of the cell to the clipboard. Something like:

table.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent e) {
        if (e.getClickCount() == 2) {
            System.out.println("double click");
            Point p = e.getPoint();
            int row = table.rowAtPoint(p);
            int col = table.columnAtPoint(p);
            Object value = table.getValueAt(row, col);
            StringSelection stringSelection = new StringSelection(value.toString());
            Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
            clipboard.setContents(stringSelection, ProgrammaticCopyDemo.this);
        }
    }
});

Here's a full example:

enter image description here

import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.io.IOException;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.JTextComponent;

public class ProgrammaticCopyDemo implements ClipboardOwner {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new ProgrammaticCopyDemo();
            }
        });
    }

    public ProgrammaticCopyDemo() {
        JTable table = getTable();
        addCopylistenerToTable(table);

        JTextArea area = new JTextArea(3, 20);
        addPasteListenerToArea(area);

        JFrame frame = new JFrame();
        frame.add(new JScrollPane(table));
        frame.add(area, BorderLayout.PAGE_END);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private void addPasteListenerToArea(final JTextComponent component) {
        JPopupMenu menu = new JPopupMenu();
        menu.add(new AbstractAction("Paste") {
            public void actionPerformed(ActionEvent e) {
                String copiedContent = getClipboardContents();
                int caretPosition = component.getCaretPosition();
                try {
                    component.getDocument().insertString(caretPosition, copiedContent, null);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }

            }

            private String getClipboardContents() {
                String result = "";
                Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
                Transferable contents = clipboard.getContents(null);
                boolean hasTransferableText
                        = (contents != null)
                        && contents.isDataFlavorSupported(DataFlavor.stringFlavor);
                if (hasTransferableText) {
                    try {
                        result = (String) contents.getTransferData(DataFlavor.stringFlavor);
                    } catch (UnsupportedFlavorException | IOException ex) {
                        System.out.println(ex);
                        ex.printStackTrace();
                    }
                }
                return result;
            }
        });
        component.setComponentPopupMenu(menu);
    }

    private void addCopylistenerToTable(final JTable table) {
        table.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    System.out.println("double click");
                    Point p = e.getPoint();
                    int row = table.rowAtPoint(p);
                    int col = table.columnAtPoint(p);
                    Object value = table.getValueAt(row, col);
                    StringSelection stringSelection = new StringSelection(value.toString());
                    Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
                    clipboard.setContents(stringSelection, ProgrammaticCopyDemo.this);
                }
            }
        });
    }

    private JTable getTable() {
        String[][] data = {
            {"Hello", "World"},
            {"Stack", "Overflow"},
            {"Foo", "Bar"}
        };
        String[] cols = {"Col", "Col"};
        DefaultTableModel model = new DefaultTableModel(data, cols) {
            @Override
            public boolean isCellEditable(int row, int col) {
                return false;
            }
        };
        return new JTable(model) {
            @Override
            public Dimension getPreferredScrollableViewportSize() {
                return getPreferredSize();
            }
        };
    }

    @Override
    public void lostOwnership(Clipboard clipboard, Transferable contents) {
    }
}
Rabinowitz answered 18/9, 2014 at 17:2 Comment(6)
I was just looking for a "premade" function but never thought about this. Actually I also learned from this answer about the copy to clipboard thing. :) +1Aloes
@Aloes Sometime magic functions evade me. Didn't really go searching for one. Who know's maybe there is one :-)Rabinowitz
I'm at school and can't program right now so I was just doing my best looking for that magic function. I'll learn more about swing so I can create functions like yours :-)Aloes
I kind of like this idea... rather than overriding cell editors and all that overhead this is much cleaner. It may require user training though because functionality is so subtle but its simple and requires less clicks and drags.Casserole
@ThomasN. Yea I kinda kinda misread your question. I originally read it as "double click == copy". I overlooked the "so they can copy" :-) Anyway you got two working solutions out of it. Enjoy!Rabinowitz
Thanks but I may very easily go with the double click solution anyway, seems more efficientCasserole

© 2022 - 2024 — McMap. All rights reserved.