package org.simantics.spreadsheet.ui; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.ClipboardOwner; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.io.IOException; import java.util.ArrayList; import java.util.regex.Pattern; import javax.swing.JComponent; import javax.swing.JOptionPane; import javax.swing.JTable; import javax.swing.KeyStroke; import org.simantics.databoard.Bindings; import org.simantics.databoard.binding.mutable.MutableVariant; import org.simantics.databoard.binding.mutable.Variant; import org.simantics.spreadsheet.Adaptable; import org.simantics.spreadsheet.CellEditor; import org.simantics.spreadsheet.CellEditor.Transaction; import org.simantics.spreadsheet.ClientModel.OperationMode; import org.simantics.spreadsheet.ClientModel; import org.simantics.spreadsheet.Range; import org.simantics.spreadsheet.common.cell.StringCellParser; import org.simantics.spreadsheet.util.SpreadsheetUtils; import org.simantics.utils.threads.logger.ITask; import org.simantics.utils.threads.logger.ThreadLogger; /** * ExcelAdapter enables Copy-Paste Clipboard functionality on JTables. * The clipboard data format used by the adapter is compatible with * the clipboard format used by Excel. This provides for clipboard * interoperability between enabled JTables and Excel. */ public class ExcelAdapter implements ActionListener { final private JTable table; final private ClientModel model; //final private SheetManipulator manager; final private CellEditor editor; final static private Pattern newline = Pattern.compile("\n"); final static private Pattern tab = Pattern.compile("\t"); final private StringCellParser[] parsers; // public boolean ownClipboard = false; private Object clipboard = null; private ClipboardOwner clipboardOwner = null; private String rowstring, value; public ExcelAdapter(JTable table, ClientModel model, Adaptable serverInterface, StringCellParser[] parsers) { this.table = table; this.model = model; //this.manager = serverInterface.getAdapter(SheetManipulator.class); this.editor = serverInterface.getAdapter(CellEditor.class); this.parsers = parsers; // system = Toolkit.getDefaultToolkit().getSystemClipboard(); KeyStroke copy = KeyStroke.getKeyStroke(KeyEvent.VK_C,ActionEvent.CTRL_MASK,false); // Identifying the copy KeyStroke user can modify this // to copy on some other Key combination. KeyStroke paste = KeyStroke.getKeyStroke(KeyEvent.VK_V,ActionEvent.CTRL_MASK,false); // Identifying the Paste KeyStroke user can modify this //to copy on some other Key combination. table.registerKeyboardAction(this,"Copy",copy,JComponent.WHEN_FOCUSED); table.registerKeyboardAction(this,"Paste",paste,JComponent.WHEN_FOCUSED); } // /** // * Public Accessor methods for the Table on which this adapter acts. // */ // public JTable getJTable() {return jTable1;} // public void setJTable(JTable jTable1) {this.jTable1=jTable1;} /** * This method is activated on the Keystrokes we are listening to * in this implementation. Here it listens for Copy and Paste ActionCommands. * Selections comprising non-adjacent cells result in invalid selection and * then copy action cannot be performed. * Paste is done by aligning the upper left corner of the selection with the * 1st element in the current selection of the JTable. */ public void actionPerformed(ActionEvent e) { Clipboard system = Toolkit.getDefaultToolkit().getSystemClipboard(); if (e.getActionCommand().compareTo("Copy")==0) { // Check to ensure we have selected only a contiguous block of // cells int numcols=table.getSelectedColumnCount(); int numrows=table.getSelectedRowCount(); if (numcols == 0 || numrows == 0) return; int[] rowsselected=table.getSelectedRows(); int[] colsselected=table.getSelectedColumns(); if (!((numrows-1==rowsselected[rowsselected.length-1]-rowsselected[0] && numrows==rowsselected.length) && (numcols-1==colsselected[colsselected.length-1]-colsselected[0] && numcols==colsselected.length))) { JOptionPane.showMessageDialog(null, "Invalid Copy Selection", "Invalid Copy Selection", JOptionPane.ERROR_MESSAGE); return; } Object[] rows = new Object[numrows]; for (int i=0;i0) builder.append("\t"); Object value=(Object)cols[j]; if(value != null) { builder.append(value.toString()); } } builder.append("\n"); } clipboard = new Range(rowsselected[0],rowsselected[0]+numrows-1,colsselected[0],colsselected[0]+numcols-1); clipboardOwner = new ClipboardOwner() { @Override public void lostOwnership(Clipboard arg0, Transferable arg1) { if(clipboardOwner == this) { clipboardOwner = null; clipboard = null; } } }; system.setContents(new StringSelection(builder.toString()), clipboardOwner); } if (e.getActionCommand().compareTo("Paste")==0) { int[] selectedRows = table.getSelectedRows(); int[] selectedColumns = table.getSelectedColumns(); if (selectedRows.length == 0 || selectedColumns.length == 0) return; int startRow = selectedRows[0]; int startCol = selectedColumns[0]; if(clipboardOwner == null) { //if(manager == null) return; if(editor == null) return; String trstring = null; try { trstring = (String)(system.getContents(this).getTransferData(DataFlavor.stringFlavor)); } catch (UnsupportedFlavorException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } if(trstring == null || trstring.isEmpty()) return; ITask task = ThreadLogger.getInstance().begin("Spreadsheet.paste"); /* * Analyse the target area. No computed cells are allowed. Collect cells to remove. */ ArrayList removals = new ArrayList(); String[] rows = newline.split(trstring); for(int i=0;i 0 && startRow+i< table.getRowCount() && startCol+j< table.getColumnCount()) { CellValue cell = (CellValue)table.getValueAt(startRow+i, startCol+j); if(cell.label != null) { String location = SpreadsheetUtils.cellName(startRow+i, startCol+j); Boolean computed = model.getPropertyAt(location, ClientModel.COMPUTED); if(computed != null && computed) return; removals.add(location); } } } } /* * Create the cell data * */ Transaction tr = editor.startTransaction(OperationMode.OPERATION); for(int i=0;i 0 && startRow+i< table.getRowCount() && startCol+j< table.getColumnCount()) { if (value.startsWith("=")) { editor.edit(tr, SpreadsheetUtils.cellName(startRow+i, startCol+j), ClientModel.CONTENT_EXPRESSION, value, Bindings.STRING, null); } else { editor.edit(tr, SpreadsheetUtils.cellName(startRow+i, startCol+j), Variant.ofInstance(value), null); } } } } tr.commit(); task.finish(); } else { Range from = (Range)clipboard; Range to = new Range(startRow, startRow+from.height()-1, startCol, startCol+from.width()-1); Transaction tr = editor.startTransaction(OperationMode.OPERATION); for(int i=0;i