Example usage for javafx.scene.control SelectionMode MULTIPLE

List of usage examples for javafx.scene.control SelectionMode MULTIPLE

Introduction

In this page you can find the example usage for javafx.scene.control SelectionMode MULTIPLE.

Prototype

SelectionMode MULTIPLE

To view the source code for javafx.scene.control SelectionMode MULTIPLE.

Click Source Link

Document

Allows for one or more contiguous range of indices to be selected at a time.

Usage

From source file:javafxapplication2.FXMLDocumentController.java

private void initLclTableView() {

    //  ??/*w  w w .j a v a2 s.com*/
    table1.setEditable(true);
    table1.selectionModelProperty().get().setSelectionMode(SelectionMode.MULTIPLE);

    // ??
    PropertyValueFactory<TableRowDataBean, String> namePropertyValueFactory = new PropertyValueFactory<>(
            "name");
    name.setCellValueFactory(namePropertyValueFactory);

    // 
    PropertyValueFactory<TableRowDataBean, Long> sizePropertyValueFactory = new PropertyValueFactory<>("size");
    size.setCellValueFactory(sizePropertyValueFactory);

    // 
    PropertyValueFactory<TableRowDataBean, String> pathPropertyValueFactory = new PropertyValueFactory<>(
            "path");
    path.setCellValueFactory(pathPropertyValueFactory);

    // 
    PropertyValueFactory<TableRowDataBean, String> updatePropertyValueFactory = new PropertyValueFactory<>(
            "update");
    update.setCellValueFactory(updatePropertyValueFactory);

    toggle();
}

From source file:de.ks.file.FileViewController.java

@Override
public void initialize(URL location, ResourceBundle resources) {
    fileList.setItems(files);//from  w w w.  j  a  v  a  2 s . c  o m

    MultipleSelectionModel<File> selectionModel = fileList.getSelectionModel();
    selectionModel.setSelectionMode(SelectionMode.MULTIPLE);

    ReadOnlyObjectProperty<File> selection = selectionModel.selectedItemProperty();
    selection.addListener((p, o, n) -> {
        folderName.setText(n == null ? "" : n.getParentFile().getAbsolutePath());
        fileNameLabel.setText(n == null ? "" : n.getName());
    });

    BooleanBinding isDirectory = Bindings
            .createBooleanBinding(() -> selection.get() != null && selection.get().isDirectory(), selection);
    edit.disableProperty().bind(isDirectory);

    files.addListener((ListChangeListener<File>) change -> {
        files.forEach(file -> {
            if (!fileReferences.containsKey(file) && file.exists() && !file.isDirectory()) {
                fileReferences.put(file, fileStore.getReference(file));
            }
        });
    });
    BooleanBinding disable = fileList.getSelectionModel().selectedItemProperty().isNull();
    open.disableProperty().bind(disable);
    edit.disableProperty().bind(disable);
    openFolder.disableProperty().bind(disable);
    removeFile.disableProperty().bind(disable);
}

From source file:ca.wumbo.doommanager.client.controller.file.DoomFileController.java

@FXML
private void initialize() {
    // Keep the left window the same size when resizing/maximizing.
    SplitPane.setResizableWithParent(leftBorderPane, false);

    // Allow selection of multiple cells.
    entryTreeTable.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

    // Make the cells update accordingly.
    nameColumn.setCellValueFactory(cellData -> cellData.getValue().getValue().entryProperty());
    sizeColumn.setCellValueFactory(cellData -> cellData.getValue().getValue().dataLengthStringProperty());
    typeColumn.setCellValueFactory(cellData -> cellData.getValue().getValue().entryTypeProperty());

    // Resize the splitter to a reasonable position.
    // Since we can't use Platform.runlater() to do this, we have to use a listener that removes itself.
    // Note: The old way it was done was to pass the tabPane's width to the function after this is initialized.
    InvalidationListener invalidationListener = new InvalidationListener() {
        @Override/*w  ww. j  a va 2s.c  o  m*/
        public void invalidated(Observable observable) {
            setSplitterPosition((int) splitPane.getWidth());
            splitPane.widthProperty().removeListener(this); // Remove itself after the size is set.
        }
    };
    splitPane.widthProperty().addListener(invalidationListener);

    // Handle item selection. This will clean up our GUI and add/remove panes on the right of the splitter.
    entryTreeTable.getSelectionModel().selectedItemProperty().addListener((obsValue, oldValue, newValue) -> {
        updateGUIFromEntrySelection(oldValue, newValue);
    });

    // Support right clicking menus on the rows, source: https://gist.github.com/james-d/7758918
    entryTreeTable.setRowFactory(new Callback<TreeTableView<Entry>, TreeTableRow<Entry>>() {
        @Override
        public TreeTableRow<Entry> call(TreeTableView<Entry> tableView) {
            // Create the row to return.
            TreeTableRow<Entry> row = new TreeTableRow<>();
            ContextMenu contextMenu = new ContextMenu();

            // Whenever this row gets a new item (or is updated), rebuild the right click menu.
            row.itemProperty().addListener((observableValue, oldValue, newValue) -> {
                // TODO - Dynamically generate a new context menu - contextMenu.getItems().add(new MenuItem());
            });

            // Set context menu on row, but use a binding to make it only show for non-empty rows:
            row.contextMenuProperty()
                    .bind(Bindings.when(row.emptyProperty()).then((ContextMenu) null).otherwise(contextMenu));

            return row;
        }
    });

    // Instead of assigning graphics to each node, only do it for the cells.
    // This should help reduce object creation by having it only required for the visible rows.
    nameColumn.setCellFactory(new Callback<TreeTableColumn<Entry, Entry>, TreeTableCell<Entry, Entry>>() {
        @Override
        public TreeTableCell<Entry, Entry> call(TreeTableColumn<Entry, Entry> param) {
            return new TreeTableCell<Entry, Entry>() {
                @Override
                protected void updateItem(Entry item, boolean empty) {
                    super.updateItem(item, empty);
                    if (!empty && item != null) {
                        setText(item.getName());
                        Image img = resources.getImage(item.getClass().getSimpleName().toLowerCase());
                        setGraphic(new ImageView(img));
                    } else {
                        setText(null);
                        setGraphic(null);
                    }
                }
            };
        }
    });
}

From source file:com.esri.geoevent.test.performance.ui.ReportOptionsController.java

@Override
public void initialize(URL location, ResourceBundle resources) {
    reportTypeLabel.setText(UIMessages.getMessage("UI_REPORT_TYPE_LABEL"));
    reportType.setItems(getReportTypes());
    reportType.setValue(report.getType());
    reportFileLocationLabel.setText(UIMessages.getMessage("UI_REPORT_FILE_LOCATION_LABEL"));
    reportFileLocationBtn.setText(UIMessages.getMessage("UI_REPORT_FILE_BROWSE_LABEL"));
    reportFileLocationBtn.setTooltip(new Tooltip(UIMessages.getMessage("UI_REPORT_FILE_BROWSE_DESC")));
    allCoumnsLabel.setText(UIMessages.getMessage("UI_ALL_COLUMNS_LABEL"));
    selectedCoumnsLabel.setText(UIMessages.getMessage("UI_SELECTED_COLUMNS_LABEL"));
    allColumns.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

    selectedColumns.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

    okBtn.setText(UIMessages.getMessage("UI_OK_BTN_LABEL"));
    cancelBtn.setText(UIMessages.getMessage("UI_CANCEL_BTN_LABEL"));

    // lookup current directory and add "/report" to it
    updateSelectedReportFile(report.getReportFile());
    updateSelectedReportColumns(report.getReportColumns());
}

From source file:cz.lbenda.dataman.db.frm.DbConfigFrmController.java

@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
    lvLibraries.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

    btnAddLibrary.setOnAction(event -> {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle(msgLibraryChooseTitle);
        fileChooser.getExtensionFilters().addAll(Constants.librariesFilter);
        List<File> files = fileChooser.showOpenMultipleDialog(btnAddLibrary.getScene().getWindow());
        if (files != null) {
            files.forEach(file -> lvLibraries.getItems().add(file.getAbsolutePath()));
        }/*from   w  ww  .  j a  v a2s.  c  o m*/
    });
    btnRemoveLibrary.setOnAction(
            event -> lvLibraries.getItems().removeAll(lvLibraries.getSelectionModel().getSelectedItems()));
    btnExtendConfigFindPath.setOnAction(event -> {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle(msgExtendConfigChooseTitle);
        fileChooser.getExtensionFilters().addAll(Constants.configFileFilter);
        if (StringUtils.isEmpty(tfExtendConfigPath.getText())) {
            fileChooser.setInitialFileName(tfExtendConfigPath.getText());
        }
        File file = fileChooser.showOpenDialog(btnExtendConfigFindPath.getScene().getWindow());
        if (file != null) {
            tfExtendConfigPath.setText(file.getAbsolutePath());
        }
    });
    lvLibraries.getItems().addListener((ListChangeListener<String>) change -> {
        while (change.next()) {
            if (change.wasAdded() || change.wasRemoved()) {
                findDriverClasses();
            }
        }
    });
}

From source file:Main.java

private void initializeListView(ListView<Student> listView) {
    listView.setPrefSize(250, 290);//  www  .j a v  a2 s  .  c om
    listView.setEditable(false);
    listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
    listView.setCellFactory(new StringListCellFactory());
}

From source file:io.bitsquare.gui.main.funds.withdrawal.WithdrawalView.java

@Override
public void initialize() {
    tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
    tableView.setPlaceholder(new Label("No funds are available for withdrawal"));
    tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

    setAddressColumnCellFactory();//w w  w. j  av  a2s .c o  m
    setBalanceColumnCellFactory();
    setSelectColumnCellFactory();

    addressColumn.setComparator((o1, o2) -> o1.getAddressString().compareTo(o2.getAddressString()));
    balanceColumn.setComparator((o1, o2) -> o1.getBalance().compareTo(o2.getBalance()));
    balanceColumn.setSortType(TableColumn.SortType.DESCENDING);
    tableView.getSortOrder().add(balanceColumn);

    balanceListener = new BalanceListener() {
        @Override
        public void onBalanceChanged(Coin balance, Transaction tx) {
            updateList();
        }
    };
    amountListener = (observable, oldValue, newValue) -> {
        if (amountTextField.focusedProperty().get()) {
            try {
                senderAmountAsCoinProperty.set(formatter.parseToCoin(amountTextField.getText()));
            } catch (Throwable t) {
                log.error("Error at amountTextField input. " + t.toString());
            }
        }
    };
    amountFocusListener = (observable, oldValue, newValue) -> {
        if (oldValue && !newValue) {
            if (senderAmountAsCoinProperty.get().isPositive())
                amountTextField.setText(formatter.formatCoin(senderAmountAsCoinProperty.get()));
            else
                amountTextField.setText("");
        }
    };
}

From source file:com.ggvaidya.scinames.dataset.DatasetSceneController.java

private void setupTableWithChanges(TableView<Change> tv, Dataset tp) {
    tv.setEditable(true);/*from  w w w .  j  av a  2  s. c om*/
    tv.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
    tv.getColumns().clear();

    TableColumn<Change, ChangeType> colChangeType = new TableColumn<>("Type");
    colChangeType.setCellFactory(ComboBoxTableCell.forTableColumn(new ChangeTypeStringConverter(),
            ChangeType.ADDITION, ChangeType.DELETION, ChangeType.RENAME, ChangeType.LUMP, ChangeType.SPLIT,
            ChangeType.COMPLEX, ChangeType.ERROR));
    colChangeType.setCellValueFactory(new PropertyValueFactory<>("type"));
    colChangeType.setPrefWidth(100.0);
    colChangeType.setEditable(true);
    tv.getColumns().add(colChangeType);

    TableColumn<Change, ObservableSet<Name>> colChangeFrom = new TableColumn<>("From");
    colChangeFrom.setCellFactory(TextFieldTableCell.forTableColumn(new NameSetStringConverter()));
    colChangeFrom.setCellValueFactory(new PropertyValueFactory<>("from"));
    colChangeFrom.setPrefWidth(200.0);
    colChangeFrom.setEditable(true);
    tv.getColumns().add(colChangeFrom);

    TableColumn<Change, ObservableSet<Name>> colChangeTo = new TableColumn<>("To");
    colChangeTo.setCellFactory(TextFieldTableCell.forTableColumn(new NameSetStringConverter()));
    colChangeTo.setCellValueFactory(new PropertyValueFactory<>("to"));
    colChangeTo.setPrefWidth(200.0);
    colChangeTo.setEditable(true);
    tv.getColumns().add(colChangeTo);

    TableColumn<Change, String> colExplicit = new TableColumn<>("Explicit or implicit?");
    colExplicit.setCellValueFactory(
            (TableColumn.CellDataFeatures<Change, String> features) -> new ReadOnlyStringWrapper(
                    features.getValue().getDataset().isChangeImplicit(features.getValue()) ? "Implicit"
                            : "Explicit"));
    tv.getColumns().add(colExplicit);

    ChangeFilter cf = datasetView.getProjectView().getProject().getChangeFilter();
    TableColumn<Change, String> colFiltered = new TableColumn<>("Eliminated by filter?");
    colFiltered.setCellValueFactory(
            (TableColumn.CellDataFeatures<Change, String> features) -> new ReadOnlyStringWrapper(
                    cf.test(features.getValue()) ? "Allowed" : "Eliminated"));
    tv.getColumns().add(colFiltered);

    TableColumn<Change, String> colNote = new TableColumn<>("Note");
    colNote.setCellFactory(TextFieldTableCell.forTableColumn());
    colNote.setCellValueFactory(new PropertyValueFactory<>("note"));
    colNote.setPrefWidth(100.0);
    colNote.setEditable(true);
    tv.getColumns().add(colNote);

    TableColumn<Change, String> colCitations = new TableColumn<>("Citations");
    colCitations.setCellValueFactory(
            (TableColumn.CellDataFeatures<Change, String> features) -> new ReadOnlyStringWrapper(
                    features.getValue().getCitationStream().map(citation -> citation.getCitation()).sorted()
                            .collect(Collectors.joining("; "))));
    tv.getColumns().add(colCitations);

    TableColumn<Change, String> colGenera = new TableColumn<>("Genera");
    colGenera.setCellValueFactory(
            (TableColumn.CellDataFeatures<Change, String> features) -> new ReadOnlyStringWrapper(
                    String.join(", ", features.getValue().getAllNames().stream().map(n -> n.getGenus())
                            .distinct().sorted().collect(Collectors.toList()))));
    tv.getColumns().add(colGenera);

    TableColumn<Change, String> colSpecificEpithet = new TableColumn<>("Specific epithets");
    colSpecificEpithet.setCellValueFactory(
            (TableColumn.CellDataFeatures<Change, String> features) -> new ReadOnlyStringWrapper(String
                    .join(", ", features.getValue().getAllNames().stream().map(n -> n.getSpecificEpithet())
                            .filter(s -> s != null).distinct().sorted().collect(Collectors.toList()))));
    tv.getColumns().add(colSpecificEpithet);

    // The infraspecific string.
    TableColumn<Change, String> colInfraspecificEpithet = new TableColumn<>("Infraspecific epithets");
    colInfraspecificEpithet.setCellValueFactory(
            (TableColumn.CellDataFeatures<Change, String> features) -> new ReadOnlyStringWrapper(
                    String.join(", ",
                            features.getValue().getAllNames().stream()
                                    .map(n -> n.getInfraspecificEpithetsAsString()).filter(s -> s != null)
                                    .distinct().sorted().collect(Collectors.toList()))));
    tv.getColumns().add(colInfraspecificEpithet);

    // The very last epithet of all
    TableColumn<Change, String> colTerminalEpithet = new TableColumn<>("Terminal epithet");
    colTerminalEpithet.setCellValueFactory(
            (TableColumn.CellDataFeatures<Change, String> features) -> new ReadOnlyStringWrapper(
                    String.join(", ", features.getValue().getAllNames().stream().map(n -> {
                        List<Name.InfraspecificEpithet> infraspecificEpithets = n.getInfraspecificEpithets();
                        if (!infraspecificEpithets.isEmpty()) {
                            return infraspecificEpithets.get(infraspecificEpithets.size() - 1).getValue();
                        } else {
                            return n.getSpecificEpithet();
                        }
                    }).filter(s -> s != null).distinct().sorted().collect(Collectors.toList()))));
    tv.getColumns().add(colTerminalEpithet);

    // Properties
    TableColumn<Change, String> colProperties = new TableColumn<>("Properties");
    colProperties.setCellValueFactory(
            (TableColumn.CellDataFeatures<Change, String> features) -> new ReadOnlyStringWrapper(
                    features.getValue().getProperties().entrySet().stream()
                            .map(entry -> entry.getKey() + ": " + entry.getValue()).sorted()
                            .collect(Collectors.joining("; "))));
    tv.getColumns().add(colProperties);

    fillTableWithChanges(tv, tp);

    // When someone selects a cell in the Table, try to select the appropriate data in the
    // additional data view.
    tv.getSelectionModel().getSelectedItems().addListener((ListChangeListener<Change>) lcl -> {
        AdditionalData aData = additionalDataCombobox.getSelectionModel().getSelectedItem();

        if (aData != null) {
            aData.onSelectChange(tv.getSelectionModel().getSelectedItems());
        }
    });

    // Create a right-click menu for table rows.
    changesTableView.setRowFactory(table -> {
        TableRow<Change> row = new TableRow<>();

        row.setOnContextMenuRequested(event -> {
            if (row.isEmpty())
                return;

            // We don't currently use the clicked change, since currently all options
            // change *all* the selected changes, but this may change in the future.
            Change change = row.getItem();

            ContextMenu changeMenu = new ContextMenu();

            Menu searchForName = new Menu("Search for name");
            searchForName.getItems().addAll(
                    change.getAllNames().stream().sorted().map(n -> createMenuItem(n.getFullName(), action -> {
                        datasetView.getProjectView().openDetailedView(n);
                    })).collect(Collectors.toList()));
            changeMenu.getItems().add(searchForName);
            changeMenu.getItems().add(new SeparatorMenuItem());

            changeMenu.getItems().add(createMenuItem("Edit note", action -> {
                List<Change> changes = new ArrayList<>(changesTableView.getSelectionModel().getSelectedItems());

                String combinedNotes = changes.stream().map(ch -> ch.getNote().orElse("").trim()).distinct()
                        .collect(Collectors.joining("\n")).trim();

                Optional<String> result = askUserForTextArea(
                        "Modify the note for these " + changes.size() + " changes:", combinedNotes);

                if (result.isPresent()) {
                    String note = result.get().trim();
                    LOGGER.info("Using 'Edit note' to set note to '" + note + "' on changes " + changes);
                    changes.forEach(ch -> ch.noteProperty().set(note));
                }
            }));
            changeMenu.getItems().add(new SeparatorMenuItem());

            // Create a submenu for tags and urls.
            String note = change.noteProperty().get();

            Menu removeTags = new Menu("Tags");
            removeTags.getItems().addAll(change.getTags().stream().sorted()
                    .map(tag -> new MenuItem(tag.getName())).collect(Collectors.toList()));

            Menu lookupURLs = new Menu("Lookup URL");
            change.getURIs().stream().sorted().map(uri -> {
                return createMenuItem(uri.toString(), evt -> {
                    try {
                        Desktop.getDesktop().browse(uri);
                    } catch (IOException ex) {
                        LOGGER.warning("Could not open URL '" + uri + "': " + ex);
                    }
                });
            }).forEach(mi -> lookupURLs.getItems().add(mi));
            changeMenu.getItems().add(lookupURLs);

            changeMenu.getItems().add(new SeparatorMenuItem());
            changeMenu.getItems().add(createMenuItem("Prepend text to all notes", action -> {
                List<Change> changes = new ArrayList<>(changesTableView.getSelectionModel().getSelectedItems());

                Optional<String> result = askUserForTextField(
                        "Enter tags to prepend to notes in " + changes.size() + " changes:");

                if (result.isPresent()) {
                    String tags = result.get().trim();
                    changes.forEach(ch -> {
                        String prevValue = change.getNote().orElse("").trim();

                        LOGGER.info("Prepending tags '" + tags + "' to previous value '" + prevValue
                                + "' for change " + ch);

                        ch.noteProperty().set((tags + " " + prevValue).trim());
                    });
                }
            }));
            changeMenu.getItems().add(createMenuItem("Append text to all notes", action -> {
                List<Change> changes = new ArrayList<>(changesTableView.getSelectionModel().getSelectedItems());
                Optional<String> result = askUserForTextField(
                        "Enter tags to append to notes in " + changes.size() + " changes:");

                if (result.isPresent()) {
                    String tags = result.get().trim();
                    changes.forEach(ch -> {
                        String prevValue = ch.getNote().orElse("").trim();

                        LOGGER.info("Appending tags '" + tags + "' to previous value '" + prevValue
                                + "' for change " + ch);

                        ch.noteProperty().setValue((prevValue + " " + tags).trim());
                    });
                }
            }));

            changeMenu.show(datasetView.getScene().getWindow(), event.getScreenX(), event.getScreenY());

        });

        return row;
    });

    LOGGER.info("setupTableWithChanges() completed");
}

From source file:caillou.company.clonemanager.gui.customComponent.results.ResultController.java

@Override
public void initialize(URL location, ResourceBundle resources) {
    this.guiApplicationFileListFiltered = new FilteredList<>(guiApplicationFileList);
    this.initializePhaseAutomaticResizing();
    this.initializeContextDependant();

    SortedList<GUIApplicationFile> sortedList = new SortedList<>(guiApplicationFileListFiltered);
    // Bind the SortedList comparator to the TableView comparator.
    sortedList.comparatorProperty().bind(resultViewId.comparatorProperty());

    resultViewId.setItems(sortedList);/*  ww w  . ja  v  a  2s .c  o  m*/
    resultViewId.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

    this.filterList(guiApplicationFileListFiltered);
    this.initializeFilter();
    this.initializeRowFactory();

    this.initializeStatistic();

    accordionPaneId.setExpandedPane(informationPaneId);

    groupId.getItems().add(Group.GROUPA);
    groupId.getItems().add(Group.GROUPB);
    groupId.setValue(Group.GROUPA);

    /**
     * Due to the bug
     * "https://bitbucket.org/controlsfx/controlsfx/issue/185/nullpointerexception-when-using-popover"
     */
    MainApp.getInstance().getStage().setOnCloseRequest(new EventHandler<WindowEvent>() {
        @Override
        public void handle(WindowEvent event) {
            if (popOver != null) {
                popOver.hide(Duration.millis(0));
            }
        }
    });
    /**
     * End *
     */
}

From source file:com.ggvaidya.scinames.dataset.BinomialChangesSceneController.java

private void setupTableWithBinomialChanges() {
    changesTableView.setEditable(false);
    changesTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
    changesTableView.setItems(potentialChanges);

    changesTableView.getColumns().clear();

    TableColumn<PotentialChange, ChangeType> colChangeType = new TableColumn<>("Type");
    colChangeType.setCellFactory(ComboBoxTableCell.forTableColumn(new ChangeTypeStringConverter(),
            ChangeType.ADDITION, ChangeType.DELETION, ChangeType.RENAME, ChangeType.LUMP, ChangeType.SPLIT,
            ChangeType.COMPLEX, ChangeType.ERROR));
    colChangeType.setCellValueFactory(new PropertyValueFactory<>("type"));
    colChangeType.setPrefWidth(100.0);//w w  w  .  j a v a 2s  . c o  m
    colChangeType.setEditable(true);
    changesTableView.getColumns().add(colChangeType);

    TableColumn<PotentialChange, ObservableSet<Name>> colChangeFrom = new TableColumn<>("From");
    colChangeFrom.setCellFactory(TextFieldTableCell.forTableColumn(new NameSetStringConverter()));
    colChangeFrom.setCellValueFactory(new PropertyValueFactory<>("from"));
    colChangeFrom.setPrefWidth(200.0);
    colChangeFrom.setEditable(true);
    changesTableView.getColumns().add(colChangeFrom);

    TableColumn<PotentialChange, ObservableSet<Name>> colChangeTo = new TableColumn<>("To");
    colChangeTo.setCellFactory(TextFieldTableCell.forTableColumn(new NameSetStringConverter()));
    colChangeTo.setCellValueFactory(new PropertyValueFactory<>("to"));
    colChangeTo.setPrefWidth(200.0);
    colChangeTo.setEditable(true);
    changesTableView.getColumns().add(colChangeTo);

    TableColumn<PotentialChange, String> colDataset = new TableColumn<>("Dataset");
    colDataset.setCellValueFactory(cvf -> {
        return new ReadOnlyStringWrapper(cvf.getValue().getDataset().toString());
    });
    colDataset.setPrefWidth(150.0);
    changesTableView.getColumns().add(colDataset);

    TableColumn<PotentialChange, SimplifiedDate> dateCol = new TableColumn<>("Date");
    dateCol.setCellFactory(
            TextFieldTableCell.forTableColumn(new SimplifiedDate.SimplifiedDateStringConverter()));
    dateCol.setCellValueFactory(cvf -> new ReadOnlyObjectWrapper<>(cvf.getValue().getDataset().getDate()));
    dateCol.setPrefWidth(150);
    dateCol.setSortable(true);
    dateCol.setSortType(SortType.ASCENDING);
    changesTableView.getColumns().add(dateCol);
    changesTableView.getSortOrder().add(dateCol);

    TableColumn<PotentialChange, String> colChangeSummary = new TableColumn<>("Changes summary");
    colChangeSummary.setCellValueFactory(cvf -> {
        Set<Change> changes = changesByPotentialChange.get(cvf.getValue());
        return new ReadOnlyStringWrapper(changes.size() + ": "
                + changes.stream().map(ch -> ch.toString()).collect(Collectors.joining("; ")));
    });
    colChangeSummary.setPrefWidth(200.0);
    changesTableView.getColumns().add(colChangeSummary);

    /*
    TableColumn<PotentialChange, String> colExplicit = new TableColumn<>("Explicit or implicit?");
    colExplicit.setCellValueFactory(
       (TableColumn.CellDataFeatures<Change, String> features) -> 
    new ReadOnlyStringWrapper(
       features.getValue().getDataset().isChangeImplicit(features.getValue()) ? "Implicit" : "Explicit"
    )
    );
    tv.getColumns().add(colExplicit);
            
    ChangeFilter cf = binomialChangesView.getProjectView().getProject().getChangeFilter();
    TableColumn<Change, String> colFiltered = new TableColumn<>("Eliminated by filter?");
    colFiltered.setCellValueFactory(
       (TableColumn.CellDataFeatures<Change, String> features) -> 
    new ReadOnlyStringWrapper(
       cf.test(features.getValue()) ? "Allowed" : "Eliminated"
    )
    );
    tv.getColumns().add(colFiltered);
    */

    TableColumn<PotentialChange, String> colNote = new TableColumn<>("Note");
    colNote.setCellFactory(TextFieldTableCell.forTableColumn());
    colNote.setCellValueFactory(new PropertyValueFactory<>("note"));
    colNote.setPrefWidth(100.0);
    changesTableView.getColumns().add(colNote);

    TableColumn<PotentialChange, String> colReason = new TableColumn<>("Reason");
    colReason.setCellValueFactory(cvf -> new ReadOnlyStringWrapper(calculateReason(cvf.getValue())));
    colReason.setPrefWidth(100.0);
    changesTableView.getColumns().add(colReason);

    TableColumn<PotentialChange, String> colReasonDate = new TableColumn<>("ReasonDate");
    colReasonDate.setCellValueFactory(cvf -> {
        String result;
        Set<SimplifiedDate> dates = calculateReasonDate(cvf.getValue());

        if (dates.size() > 1) {
            result = "(" + dates.size() + ") " + dates.stream().distinct().sorted()
                    .map(sd -> sd.asYYYYmmDD("-")).collect(Collectors.joining("|"));

        } else if (dates.size() == 1) {
            result = dates.iterator().next().asYYYYmmDD("-");

        } else {
            result = "NA";
        }

        return new ReadOnlyStringWrapper(result);
    });
    colReasonDate.setPrefWidth(100.0);
    changesTableView.getColumns().add(colReasonDate);

    TableColumn<PotentialChange, String> colCitations = new TableColumn<>("Citations");
    colCitations.setCellValueFactory(
            (TableColumn.CellDataFeatures<PotentialChange, String> features) -> new ReadOnlyStringWrapper(
                    features.getValue().getCitationStream().map(citation -> citation.getCitation()).sorted()
                            .collect(Collectors.joining("; "))));
    changesTableView.getColumns().add(colCitations);

    TableColumn<PotentialChange, String> colGenera = new TableColumn<>("Genera");
    colGenera.setCellValueFactory(
            (TableColumn.CellDataFeatures<PotentialChange, String> features) -> new ReadOnlyStringWrapper(
                    String.join(", ", features.getValue().getAllNames().stream().map(n -> n.getGenus())
                            .distinct().sorted().collect(Collectors.toList()))));
    changesTableView.getColumns().add(colGenera);

    TableColumn<PotentialChange, String> colSpecificEpithet = new TableColumn<>("Specific epithets");
    colSpecificEpithet.setCellValueFactory(
            (TableColumn.CellDataFeatures<PotentialChange, String> features) -> new ReadOnlyStringWrapper(String
                    .join(", ", features.getValue().getAllNames().stream().map(n -> n.getSpecificEpithet())
                            .filter(s -> s != null).distinct().sorted().collect(Collectors.toList()))));
    changesTableView.getColumns().add(colSpecificEpithet);

    // The infraspecific string.
    TableColumn<PotentialChange, String> colInfraspecificEpithet = new TableColumn<>("Infraspecific epithets");
    colInfraspecificEpithet.setCellValueFactory(
            (TableColumn.CellDataFeatures<PotentialChange, String> features) -> new ReadOnlyStringWrapper(
                    String.join(", ",
                            features.getValue().getAllNames().stream()
                                    .map(n -> n.getInfraspecificEpithetsAsString()).filter(s -> s != null)
                                    .distinct().sorted().collect(Collectors.toList()))));
    changesTableView.getColumns().add(colInfraspecificEpithet);

    // The very last epithet of all
    TableColumn<PotentialChange, String> colTerminalEpithet = new TableColumn<>("Terminal epithet");
    colTerminalEpithet.setCellValueFactory(
            (TableColumn.CellDataFeatures<PotentialChange, String> features) -> new ReadOnlyStringWrapper(
                    String.join(", ", features.getValue().getAllNames().stream().map(n -> {
                        List<Name.InfraspecificEpithet> infraspecificEpithets = n.getInfraspecificEpithets();
                        if (!infraspecificEpithets.isEmpty()) {
                            return infraspecificEpithets.get(infraspecificEpithets.size() - 1).getValue();
                        } else {
                            return n.getSpecificEpithet();
                        }
                    }).filter(s -> s != null).distinct().sorted().collect(Collectors.toList()))));
    changesTableView.getColumns().add(colTerminalEpithet);

    TableColumn<PotentialChange, String> dateForRCol = new TableColumn<>("DateYMD");
    dateForRCol.setCellValueFactory(
            cvf -> new ReadOnlyObjectWrapper<>(cvf.getValue().getDataset().getDate().asYYYYmmDD("-")));
    changesTableView.getColumns().add(dateForRCol);

    // Properties
    TableColumn<PotentialChange, String> colProperties = new TableColumn<>("Properties");
    colProperties.setCellValueFactory(
            (TableColumn.CellDataFeatures<PotentialChange, String> features) -> new ReadOnlyStringWrapper(
                    features.getValue().getProperties().entrySet().stream()
                            .map(entry -> entry.getKey() + ": " + entry.getValue()).sorted()
                            .collect(Collectors.joining("; "))));
    changesTableView.getColumns().add(colProperties);

    fillTableWithBinomialChanges();

    // When someone selects a cell in the Table, try to select the appropriate data in the
    // additional data view.
    changesTableView.getSelectionModel().getSelectedItems()
            .addListener((ListChangeListener<PotentialChange>) lcl -> {
                AdditionalData aData = additionalDataCombobox.getSelectionModel().getSelectedItem();

                if (aData != null) {
                    aData.onSelectChange(changesTableView.getSelectionModel().getSelectedItems());
                }
            });

    // Create a right-click menu for table rows.
    changesTableView.setRowFactory(table -> {
        TableRow<PotentialChange> row = new TableRow<>();

        row.setOnContextMenuRequested(event -> {
            if (row.isEmpty())
                return;

            // We don't currently use the clicked change, since currently all options
            // change *all* the selected changes, but this may change in the future.
            PotentialChange change = row.getItem();

            ContextMenu changeMenu = new ContextMenu();

            Menu lookupChange = new Menu("Look up change");
            lookupChange.getItems().addAll(changesByPotentialChange.getOrDefault(change, new HashSet<>())
                    .stream()
                    .map(ch -> createMenuItem(ch.toString() + " in " + ch.getDataset().toString(), action -> {
                        binomialChangesView.getProjectView().openDetailedView(ch);
                    })).collect(Collectors.toList()));
            changeMenu.getItems().add(lookupChange);

            changeMenu.getItems().add(new SeparatorMenuItem());

            Menu searchForName = new Menu("Search for name");
            searchForName.getItems().addAll(
                    change.getAllNames().stream().sorted().map(n -> createMenuItem(n.getFullName(), action -> {
                        binomialChangesView.getProjectView().openDetailedView(n);
                    })).collect(Collectors.toList()));
            changeMenu.getItems().add(searchForName);

            changeMenu.getItems().add(new SeparatorMenuItem());

            // Create a submenu for tags and urls.
            String note = change.noteProperty().get();

            Menu removeTags = new Menu("Tags");
            removeTags.getItems().addAll(change.getTags().stream().sorted()
                    .map(tag -> new MenuItem(tag.getName())).collect(Collectors.toList()));

            Menu lookupURLs = new Menu("Lookup URL");
            change.getURIs().stream().sorted().map(uri -> {
                return createMenuItem(uri.toString(), evt -> {
                    try {
                        Desktop.getDesktop().browse(uri);
                    } catch (IOException ex) {
                        LOGGER.warning("Could not open URL '" + uri + "': " + ex);
                    }
                });
            }).forEach(mi -> lookupURLs.getItems().add(mi));
            changeMenu.getItems().add(lookupURLs);

            changeMenu.show(binomialChangesView.getScene().getWindow(), event.getScreenX(), event.getScreenY());

        });

        return row;
    });

    LOGGER.info("setupTableWithChanges() completed");
}