package nsusbloader.Controllers; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.util.Callback; import nsusbloader.MediatorControl; import nsusbloader.NSLDataTypes.EFileStatus; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.ResourceBundle; public class NSTableViewController implements Initializable { @FXML private TableView table; private ObservableList rowsObsLst; @Override public void initialize(URL url, ResourceBundle resourceBundle) { rowsObsLst = FXCollections.observableArrayList(); table.setPlaceholder(new Label()); table.setEditable(false); // At least with hacks it works as expected. Otherwise - null pointer exception table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); table.setOnKeyPressed(new EventHandler() { @Override public void handle(KeyEvent keyEvent) { if (!rowsObsLst.isEmpty()) { if (keyEvent.getCode() == KeyCode.DELETE && !MediatorControl.getInstance().getTransferActive()) { rowsObsLst.removeAll(table.getSelectionModel().getSelectedItems()); if (rowsObsLst.isEmpty()) MediatorControl.getInstance().getContoller().disableUploadStopBtn(true); // TODO: change to something better table.refresh(); } else if (keyEvent.getCode() == KeyCode.SPACE) { for (NSLRowModel item : table.getSelectionModel().getSelectedItems()) { item.setMarkForUpload(!item.isMarkForUpload()); } table.refresh(); } } keyEvent.consume(); } }); TableColumn statusColumn = new TableColumn<>(resourceBundle.getString("tableStatusLbl")); TableColumn fileNameColumn = new TableColumn<>(resourceBundle.getString("tableFileNameLbl")); TableColumn fileSizeColumn = new TableColumn<>(resourceBundle.getString("tableSizeLbl")); TableColumn uploadColumn = new TableColumn<>(resourceBundle.getString("tableUploadLbl")); statusColumn.setEditable(false); fileNameColumn.setEditable(false); fileSizeColumn.setEditable(false); uploadColumn.setEditable(true); // See https://bugs.openjdk.java.net/browse/JDK-8157687 statusColumn.setMinWidth(100.0); statusColumn.setPrefWidth(100.0); statusColumn.setMaxWidth(100.0); statusColumn.setResizable(false); fileNameColumn.setMinWidth(25.0); fileSizeColumn.setMinWidth(120.0); fileSizeColumn.setPrefWidth(120.0); fileSizeColumn.setMaxWidth(120.0); fileSizeColumn.setResizable(false); uploadColumn.setMinWidth(100.0); uploadColumn.setPrefWidth(100.0); uploadColumn.setMaxWidth(100.0); uploadColumn.setResizable(false); statusColumn.setCellValueFactory(new PropertyValueFactory<>("status")); fileNameColumn.setCellValueFactory(new PropertyValueFactory<>("nspFileName")); fileSizeColumn.setCellValueFactory(new PropertyValueFactory<>("nspFileSize")); // >< uploadColumn.setCellValueFactory(new Callback, ObservableValue>() { @Override public ObservableValue call(TableColumn.CellDataFeatures paramFeatures) { NSLRowModel model = paramFeatures.getValue(); SimpleBooleanProperty booleanProperty = new SimpleBooleanProperty(model.isMarkForUpload()); booleanProperty.addListener(new ChangeListener() { @Override public void changed(ObservableValue observableValue, Boolean oldValue, Boolean newValue) { model.setMarkForUpload(newValue); } }); return booleanProperty; } }); uploadColumn.setCellFactory(new Callback, TableCell>() { @Override public TableCell call(TableColumn paramFeatures) { CheckBoxTableCell cell = new CheckBoxTableCell<>(); return cell; } }); table.setRowFactory( // this shit is made to implement context menu. It's such a pain.. new Callback, TableRow>() { @Override public TableRow call(TableView nslRowModelTableView) { final TableRow row = new TableRow<>(); ContextMenu contextMenu = new ContextMenu(); MenuItem deleteMenuItem = new MenuItem(resourceBundle.getString("contextMenuBtnDelete")); deleteMenuItem.setOnAction(new EventHandler() { @Override public void handle(ActionEvent actionEvent) { rowsObsLst.remove(row.getItem()); if (rowsObsLst.isEmpty()) MediatorControl.getInstance().getContoller().disableUploadStopBtn(true); // TODO: change to something better table.refresh(); } }); MenuItem deleteAllMenuItem = new MenuItem(resourceBundle.getString("contextMenuBtnDeleteAll")); deleteAllMenuItem.setOnAction(new EventHandler() { @Override public void handle(ActionEvent actionEvent) { rowsObsLst.clear(); MediatorControl.getInstance().getContoller().disableUploadStopBtn(true); // TODO: change to something better table.refresh(); } }); contextMenu.getItems().addAll(deleteMenuItem, deleteAllMenuItem); row.setContextMenu(contextMenu); row.contextMenuProperty().bind( Bindings.when( Bindings.isNotNull( row.itemProperty())) .then(MediatorControl.getInstance().getTransferActive()?(ContextMenu)null:contextMenu) .otherwise((ContextMenu) null) ); row.setOnMouseClicked(new EventHandler() { // Just.. don't ask.. @Override public void handle(MouseEvent mouseEvent) { if (!row.isEmpty() && mouseEvent.getButton() == MouseButton.PRIMARY){ NSLRowModel thisItem = row.getItem(); thisItem.setMarkForUpload(!thisItem.isMarkForUpload()); table.refresh(); } mouseEvent.consume(); } }); return row; } } ); table.setItems(rowsObsLst); table.getColumns().addAll(statusColumn, fileNameColumn, fileSizeColumn, uploadColumn); } /** * Add files when user selected them * */ public void setFiles(List newFiles){ if (!rowsObsLst.isEmpty()){ List filesAlreayInList = new ArrayList<>(); for (NSLRowModel model : rowsObsLst) filesAlreayInList.add(model.getNspFileName()); for (File file: newFiles) if (!filesAlreayInList.contains(file.getName())) { rowsObsLst.add(new NSLRowModel(file, true)); } } else { for (File file: newFiles) rowsObsLst.add(new NSLRowModel(file, true)); MediatorControl.getInstance().getContoller().disableUploadStopBtn(false); } //rowsObsLst.get(0).setMarkForUpload(true); table.refresh(); } /** * Return files ready for upload. Requested from NSLMainController only -> uploadBtnAction() //TODO: set undefined * @return null if no files marked for upload * List if there are files * */ public List getFilesForUpload(){ List files = new ArrayList<>(); if (rowsObsLst.isEmpty()) return null; else { for (NSLRowModel model: rowsObsLst){ if (model.isMarkForUpload()){ files.add(model.getNspFile()); model.setStatus(EFileStatus.INDETERMINATE); } } if (!files.isEmpty()) { table.refresh(); return files; } else return null; } } public boolean isFilesForUploadListEmpty(){ return rowsObsLst.isEmpty(); } /** * Update files in case something is wrong. Requested from UsbCommunications * */ public void setFileStatus(String fileName, EFileStatus status){ for (NSLRowModel model: rowsObsLst){ if (model.getNspFileName().equals(fileName)) model.setStatus(status); } table.refresh(); } /** * Called if selected different USB protocol * */ public void setNewProtocol(String newProtocol){ if (rowsObsLst.isEmpty()) return; if (! newProtocol.equals("TinFoil")) rowsObsLst.removeIf(current -> current.getNspFileName().toLowerCase().endsWith("xci")); table.refresh(); } }