0

I have a TableView which accepts data by individual cell editing. Now I also want to enter data by pasting from a file as shown below.

11.12    23.32   15.43
22.23    24.45   26.65

I want to paste data using say CTRL+V. I have already seen posts like post in StackOverflow or the GIT repo. I could not paste data for more than one row.

Here I am giving the main code.

package testmatrix;

import javafx.application.Application;
import javafx.collections.*;
import javafx.event.*;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.*;
import javafx.scene.control.cell.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class TestMATRIX extends Application {
TableView<CommonDataClass> matrixData = new TableView<CommonDataClass>();
final ObservableList<CommonDataClass> matData = FXCollections.
        observableArrayList();
TableColumn[] matrixDataColumns = new TableColumn[6];
int numColVal = 0;
int theNDX = 0;
int maxRowNum = 0;
TextField TC1 = new TextField();
TextField TC2 = new TextField();
TextField TC3 = new TextField();
TextField TC4 = new TextField();
TextField TC5 = new TextField();
TextField TC6 = new TextField();
boolean numColStatus = false;
int oldRowVal = 0;
int oldColVal = 0;
boolean newRow = false;
boolean newCol = false;

@Override
public void start(Stage primaryStage) {
    final BorderPane root = new BorderPane();

    TextField myTextField = new TextField();
    Label colL = new Label("Column Number->");
    TextField colNumT = new TextField();
    Button getNum = new Button("SET");
    colNumT.setMaxWidth(40);
    TableUtils.doCopyPasteHandler (matrixData, matData);
    matrixData.setVisible(true);
    matrixData.setEditable(true);
    matrixData.getSelectionModel().setCellSelectionEnabled(true);
    matrixData.getSelectionModel ().setSelectionMode (SelectionMode.MULTIPLE);

    HBox hb1 = new HBox();
    HBox hb2 = new HBox();
    hb1.getChildren().add(matrixData);
    hb2.getChildren().addAll(colL, colNumT, getNum);

    root.setCenter(hb1);
    root.setRight(hb2);
    getNum.addEventHandler(ActionEvent.ACTION, e -> {
        numColStatus = (colNumT.getText().isEmpty()) ? false : true;
        numColVal = (numColStatus) ? Integer.parseInt(colNumT.getText()): 0;

        if (numColStatus) {
            addRowBelow();
            matrixData.getColumns ().clear ();
            for (int ii = 0; ii < numColVal; ii++) {
                matrixDataColumns[ii] = createCol(ii);
                editCommit(ii);
            }
            matrixData.setItems(matData);
            if (numColStatus) {
                for (int ii = 0; ii < numColVal; ii++) {
                    matrixData.getColumns().add(matrixDataColumns[ii]);
                }
            }
            matrixData.refresh();
        }
    });
    Scene scene = new Scene(root, 1200, 400);
    primaryStage.setTitle("Hello World!");
    primaryStage.setScene(scene);
    primaryStage.show();
}

TableColumn<CommonDataClass, String> createCol(int icol) {
    TableColumn<CommonDataClass, String> column = new TableColumn<>();
    column.setMinWidth(30);
    column.setStyle(
            "-fx-alignment: CENTER-RIGHT;-fx-font-family: monospace; -fx-font-size: 10px; ");
    String nameC = "myD" + (icol + 1);
    System.out.println("colName->" + nameC);
    column.setCellValueFactory(
            new PropertyValueFactory<CommonDataClass, String>(nameC));

    column.setCellFactory(
            new DragSelectionCellFactory<CommonDataClass, String>(TextFieldTableCell.forTableColumn()
            )
    );
    column.setMinWidth(120);
    return column;
}

private void editCommit(int ii) {
    System.out.println("Command came here");
    matrixDataColumns[ii].setOnEditCommit(
            new EventHandler<CellEditEvent<CommonDataClass, String>>() {
        @Override
        public void handle(CellEditEvent<CommonDataClass, String> event) {
            int colNum = event.getTablePosition().getColumn();
            switch (colNum) {
                case 0:
                    event.getTableView().getItems().get(event.getTablePosition().getRow()).setMyD1(event.getNewValue());
                    break;
                case 1:
                    event.getTableView().getItems().get(event.getTablePosition().getRow()).setMyD2(event.getNewValue());
                    break;
                case 2:
                    event.getTableView().getItems().get(event.getTablePosition().getRow()).setMyD3(event.getNewValue());
                    break;
                case 3:
                    event.getTableView().getItems().get(event.getTablePosition().getRow()).setMyD4(event.getNewValue());
                    break;
                case 4:
                    event.getTableView().getItems().get(event.getTablePosition().getRow()).setMyD5(event.getNewValue());
                    break;
                case 5:
                    event.getTableView().getItems().get(event.getTablePosition().getRow()).setMyD6(event.getNewValue());
                    break;
            }
            matrixData.setItems(matData);
            matrixData.refresh();               
            if (!event.getNewValue().isEmpty()) {
                addRowBelow();
            }
        }
    });
}

void addRowBelow() {
    matData.add(new CommonDataClass(
            TC1.getText(), TC2.getText(), TC3.getText(),
            TC4.getText(), TC5.getText(), TC6.getText()
    ));
}

/**
 * @param args the command line arguments
 */
public static void main(String[] args) {
    launch(args);
}

}

Here I am showing the data structure for the TableView.

package testmatrix;
import javafx.beans.property.*;

public class CommonDataClass {

private final SimpleStringProperty myD1;
private final SimpleStringProperty myD2;
private final SimpleStringProperty myD3;
private final SimpleStringProperty myD4;
private final SimpleStringProperty myD5;
private final SimpleStringProperty myD6;

public CommonDataClass(String myStr1, String myStr2, String myStr3, String myStr4, String myStr5, String myStr6) {
    this.myD1 = new SimpleStringProperty(myStr1);
    this.myD2 = new SimpleStringProperty(myStr2);
    this.myD3 = new SimpleStringProperty(myStr3);
    this.myD4 = new SimpleStringProperty(myStr4);
    this.myD5 = new SimpleStringProperty(myStr5);
    this.myD6 = new SimpleStringProperty(myStr6);
}

public String getMyD1(){
    return myD1.get();
}
public String getMyD2(){
    return myD2.get();
}
public String getMyD3(){
    return myD3.get();
}
public String getMyD4(){
    return myD4.get();
}
public String getMyD5(){
    return myD5.get();
}
public String getMyD6(){
    return myD6.get();
}
public void setMyD1(String myStr){
    myD1.set(myStr);
}
public void setMyD2(String myStr){
    myD2.set(myStr);
}
public void setMyD3(String myStr){
    myD3.set(myStr);
}
public void setMyD4(String myStr){
    myD4.set(myStr);
}
public void setMyD5(String myStr){
    myD5.set(myStr);
}
public void setMyD6(String myStr){
    myD6.set(myStr);
}


public StringProperty dataNameProperty(int index) {
    StringProperty strProp = null;

    switch (index) {
        case 0:
            strProp = myD1;
            break;
        case 1:
            strProp = myD2;
            break;
        case 2:
            strProp = myD3;
            break;
        case 3:
            strProp = myD4;
            break;
        case 4:
            strProp = myD5;
            break;
        case 5:
            strProp = myD6;
            break;
    }
    return strProp;
}
}

Here I am giving the code for the DrageSelection for cell.

package testmatrix;
import javafx.scene.control.TableCell;
import javafx.scene.input.*;

public class DragSelectionCell extends TableCell<CommonDataClass, String> {

public DragSelectionCell() {
    setOnDragDetected ((MouseEvent event) -> {
        startFullDrag ();
        getTableColumn ().getTableView ().getSelectionModel ().select (
                getIndex (), getTableColumn ());
    });
    setOnMouseDragEntered ((MouseDragEvent event) -> {
        getTableColumn ().getTableView ().getSelectionModel ().select (
                getIndex (), getTableColumn ());
    });
}

@Override
public void updateItem(String item, boolean empty) {
    super.updateItem (item, empty);
    if ( empty ) {
        setText (null);
    } else {
        setText (item);
    }
}
}

Here I am giving the DragSelectionCellfactory code.

package testmatrix;
import javafx.scene.control.*;
import javafx.util.Callback;


public class DragSelectionCellFactory<CommonDataClass, String> implements
    Callback<TableColumn<CommonDataClass, String>, TableCell<CommonDataClass, String>> {

private final Callback<TableColumn<CommonDataClass, String>, TableCell<CommonDataClass, String>> factory;

public DragSelectionCellFactory(
        Callback<TableColumn<CommonDataClass, String>, TableCell<CommonDataClass, String>> factory) {
    this.factory = factory;
}

public DragSelectionCellFactory() {
    this (col -> new TableCell<CommonDataClass, String> () {
        @Override
        protected void updateItem(String item, boolean empty) {
            super.updateItem (item, empty);
            if ( empty || item == null ) {
                setText (null);
            } else {
                setText (item.toString ());
            }
        }
    });
}

@Override
public TableCell<CommonDataClass, String> call(
        final TableColumn<CommonDataClass, String> col) {
    TableCell<CommonDataClass, String> cell = factory.call (col);
    cell.setOnDragDetected (event -> {
        cell.startFullDrag ();
        col.getTableView ().getSelectionModel ().select (cell.getIndex (),
                col);
    });
    cell.setOnMouseDragEntered (event -> {
        col.getTableView ().getSelectionModel ().select (cell.getIndex (),
                col);
    });

    cell.setOnMouseEntered (e -> {
        String item = cell.getItem ();
        if ( item != null ) {
        }
    });

    return cell;
}
}

The following code blocks describe similar to the Table Utility for copy/paste routine as mentioned in the beginning. However, paste part was not working and so I tried to modify. Now I am totally confused. The copy part was copying, while paste part as per code shown in the reference above could not paste with all the rows and columns.

package testmatrix;
import java.util.StringTokenizer;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.*;
import javafx.event.EventHandler;
import javafx.scene.control.*;
import javafx.scene.input.*;

public class TableUtils extends TableCell<CommonDataClass, String> {
ObservableList<CommonDataClass> datTab = FXCollections.
        observableArrayList ();

public static void doCopyPasteHandler(TableView<?> table,
        ObservableList<CommonDataClass> myData) {

    table.setOnKeyPressed (new TableKeyEventHandler ());
}

public static class TableKeyEventHandler implements EventHandler<KeyEvent> {
    KeyCodeCombination copyCode = new KeyCodeCombination (KeyCode.C,
            KeyCodeCombination.CONTROL_ANY);
    KeyCodeCombination pasteCode = new KeyCodeCombination (KeyCode.V,
            KeyCodeCombination.CONTROL_ANY);

    //@Override
    public void handle(KeyEvent event) {
        if ( copyCode.match (event) ) {
            if ( event.getSource () instanceof TableView ) {
                copySelection2Clipboard ((TableView<?>) event.getSource ());  // this will copy to clipboard
                event.consume (); // After using event please consume
            }
        } else if ( pasteCode.match (event) ) {
            if ( event.getSource () instanceof TableView ) {
                pasteFromClipboard ((TableView<?>) event.getSource ());
                event.consume ();
            }
        }
    }
}

public static void copySelection2Clipboard(TableView<?> table) {
    StringBuilder clipboardString = new StringBuilder ();
    ObservableList<TablePosition> positionList = table.getSelectionModel ().
            getSelectedCells ();
    int prevRow = -1;
    for ( TablePosition pos : positionList ) {
        int row = pos.getRow ();
        int col = pos.getColumn ();
        if ( prevRow == row ) {    // determine whether we advance in a row or col (newline)
            clipboardString.append ('\t');
        } else if ( prevRow != -1 ) {
            clipboardString.append ('\n');
        }
        String text = "";
        Object obsValue = (Object) table.getColumns ().get (col).
                getCellObservableValue (row);
        if ( obsValue == null ) {
            text = "";
        } else if ( obsValue instanceof StringProperty ) {
            text = ((StringProperty) obsValue).get ();
        } else {
            System.out.println ("Unsupported observable value: " + obsValue);
        }
        clipboardString.append (text);
        prevRow = row;
    }
    ClipboardContent clipboardContent = new ClipboardContent ();
    clipboardContent.putString (clipboardString.toString ());
}


public static void pasteFromClipboard(TableView<?> table) {
    if ( table.getSelectionModel ().getSelectedCells ().size () == 0 ) {
        return;
    }
    TablePosition pasteCellPosition = table.getSelectionModel ().
            getSelectedCells ().get (0);     // get cell position at start
    String pasteString = Clipboard.getSystemClipboard ().getString ();
    StringTokenizer rowTokenizer = new StringTokenizer (pasteString, "\n");
    int rowNum = rowTokenizer.countTokens () + 1;
    int rowCB = -1;
    while (rowTokenizer.hasMoreTokens ()) {
        rowCB++;
        String rowString = rowTokenizer.nextToken ();
        StringTokenizer colTokenizer = new StringTokenizer (rowString, "\t");
        int colCB = -1;
        while (colTokenizer.hasMoreTokens ()) {
            colCB++;
            String clpCellCont = colTokenizer.nextToken ();
            int rowTable = pasteCellPosition.getRow () + rowCB;
            int colTable = pasteCellPosition.getColumn () + colCB;
            if ( rowTable >= table.getItems ().size () ) {
                continue;
            }
            if ( colTable >= table.getColumns ().size () ) {
                continue;
            }

            TableColumn tabCol = table.getVisibleLeafColumn (colTable);
            ObservableValue obsVal = tabCol.
                    getCellObservableValue (rowTable);               
        }

    }
}

}

It would be of great help, if I get any help. Thanks and Regards

Community
  • 1
  • 1
vegaonline
  • 43
  • 10

1 Answers1

1

You couldn't insert a value since there is no code in your event handler, that assigns a value.

Furthermore you do not use the properties, which makes things more complicated.

If you do use the properties

TableColumn<CommonDataClass, String> createCol(int icol) {
    ...
    column.setCellValueFactory(
            cd -> cd.getValue().dataNameProperty(icol));

you can use the properties to assign the new values:

public static <T> void pasteFromClipboard(TableView<T> table) {
    if (table.getSelectionModel().getSelectedCells().isEmpty()) {
        return;
    }
    TablePosition pasteCellPosition = table.getSelectionModel().
            getSelectedCells().get(0);     // get cell position at start

    Clipboard cb = Clipboard.getSystemClipboard();

    // check, if clipboard contains a string
    if (!cb.hasString()) {
        return;
    }

    String pasteString = cb.getString();
    String[][] values = Stream.of(pasteString.split("\r?\n"))
            .map(line -> line.split("\t")).toArray(String[][]::new);

    final int offsetY = pasteCellPosition.getRow();
    final int offsetX = pasteCellPosition.getColumn();
    final int maxY = Math.min(table.getItems().size() - offsetY, values.length);
    final int colMax = table.getColumns().size() - offsetX;

    for (int y = 0; y < maxY; y++) {
        String[] r = values[y];
        final int maxX = Math.min(colMax, r.length);
        T rowObject = table.getItems().get(y+offsetY);
        for (int x = 0; x < maxX; x++) {
            Object property = table.getColumns().get(x + offsetX).getCellObservableValue(rowObject);
            if (property instanceof StringProperty) {
                // write value using the property
                ((StringProperty) property).set(r[x]);
            }
        }
    }
}

This only inserts values into existing rows...


Note though, that you should really use the naming conventions in your DragSelectionCellFactory class, i.e. use a single letter as name for type parameters. In your case the type parameters are named CommonDataClass and String, i.e. the names of existing classes, which will almost surely lead to confusion...
Probably you should remove the type parameters though:

public class DragSelectionCellFactory implements
    Callback<TableColumn<CommonDataClass, String>, TableCell<CommonDataClass, String>> {

Furthermore copying the content will not work, since you forgot to set the clipboard content:

ClipboardContent clipboardContent = new ClipboardContent();
clipboardContent.putString(clipboardString.toString());
Clipboard.getSystemClipboard().setContent(clipboardContent);

Furthermore there is no guarantee that the selected cells are in a rectangular area. Cells can be selected arbitrarily by holding down Ctrl.

Also

getNum.addEventHandler(ActionEvent.ACTION, e -> {...});

could (and in this case IMHO should) be replaced with

getNum.setOnAction(e -> {...});
fabian
  • 80,457
  • 12
  • 86
  • 114
  • At first, I thank you very much for your precious time and kind patience. I followed your input. I wish to enter data as say `11.12 23.32 15.43 \n 22.23 24.45 26.65`. Now the code pastes the first row `11.12 23.32 15.43` and does not insert new row for pasting the rest. Can you kindly check? – vegaonline Apr 20 '17 at 08:12
  • @vegaonline How many rows have you added to the table? If you click the button only a single time, there simply is no second row for the items to be inserted... – fabian Apr 20 '17 at 17:59
  • I copied two rows in clipboard first. The I chose 3 columns first so that columns appear. Then on the first row of first column I tried to use `CTRL+V` so that pasting happens. It pastes, first row only. I want that it must automatically add rows as per copied data. – vegaonline Apr 21 '17 at 05:27
  • Actually I see issue in ` for ( int y = 0; y < maxY; y++ ) { String[] r = values[y]; final int maxX = Math.min (colMax, r.length); System.out.println(y+offsetY); S rowObject = (S) table.getItems ().get (y + offsetY);` code part. Now for two rows, maxY becomes 1. If I change `for ( int y = 0; y < maxY; y++ )` into `for ( int y = 0; y <= maxY; y++ )` then I see problem in the code part `S rowObject = (S) table.getItems ().get (y + offsetY);`. I thought `maxY=1` could be `for row = 0 and row=1`. Here `offsetY` is 0. – vegaonline Apr 21 '17 at 07:25
  • I'm not sure how you get `maxY==1`; assuming you paste a 2-line text to a table with 2 rows and select the top-left cell get `maxY == Math.min(table.getItems().size() - offsetY, values.length) == Math.min(2 - 0, 2) == 2`. Please try selecting something from the second row before trying to paste 2 lines to a table that contains only a single row... (I've tested your code with my modifications and pasting 2 lines works for me...) Furthermore I'm not sure why you changed the name of the type parameter of the `pasteFromClipboard` method in your code. – fabian Apr 21 '17 at 12:40