package org.peerbox.presenter.settings; import java.io.IOException; import java.net.URL; import java.util.Arrays; import java.util.Collections; import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; import javafx.beans.binding.BooleanBinding; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Button; import javafx.scene.control.ListView; import javafx.scene.control.SelectionMode; import javafx.scene.control.TextInputDialog; import org.peerbox.app.config.BootstrappingNodes; import org.peerbox.app.config.BootstrappingNodesFactory; import org.peerbox.utils.DialogUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.Inject; /** * Network settings tab controller. * Allows adding, editing and removing the set of bootstrapping nodes. * * @author albrecht * */ public class Network implements Initializable { private static final Logger logger = LoggerFactory.getLogger(Network.class); @FXML private Button btnAdd; @FXML private Button btnEdit; @FXML private Button btnRemove; @FXML private Button btnUp; @FXML private Button btnDown; @FXML private ListView<String> lwBootstrappingNodes; private BootstrappingNodes bootstrappingNodes; private BootstrappingNodesFactory bootstrappingNodesFactory; private ObservableList<String> addresses; @Inject public Network(BootstrappingNodesFactory bootstrappingNodesFactory) { this.bootstrappingNodesFactory = bootstrappingNodesFactory; } @Override public void initialize(URL location, ResourceBundle resources) { lwBootstrappingNodes.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); addresses = lwBootstrappingNodes.getItems(); // disable buttons if no item selected BooleanBinding isNoItemSelected = lwBootstrappingNodes.getSelectionModel().selectedItemProperty().isNull(); btnEdit.disableProperty().bind(isNoItemSelected); btnRemove.disableProperty().bind(isNoItemSelected); btnUp.disableProperty().bind(isNoItemSelected); btnDown.disableProperty().bind(isNoItemSelected); reset(); } private void reset() { // bootstrapping nodes try { bootstrappingNodes = bootstrappingNodesFactory.create(); bootstrappingNodesFactory.load(); Set<String> nodes = bootstrappingNodes.getBootstrappingNodes(); addresses.clear(); nodes.forEach(item -> addItemIgnoreDuplicate(item)); } catch (IOException ioex) { logger.warn("Could not load settings."); } } @FXML public void addAction(ActionEvent event) { // request node address from user and add TextInputDialog input = new TextInputDialog(); DialogUtils.decorateDialogWithIcon(input); input.getEditor().setPromptText("Enter address"); input.setTitle("New Node Address"); input.setHeaderText("Enter new node address"); Optional<String> result = input.showAndWait(); if (result.isPresent()) { String nodeAddress = result.get().trim(); addItemIgnoreDuplicate(nodeAddress); } } @FXML public void editAction(ActionEvent event) { // load current selection and allow user to change value int index = lwBootstrappingNodes.getSelectionModel().getSelectedIndex(); String nodeAddress = lwBootstrappingNodes.getSelectionModel().getSelectedItem(); TextInputDialog input = new TextInputDialog(); DialogUtils.decorateDialogWithIcon(input); input.getEditor().setText(nodeAddress); input.setTitle("Edit Node Address"); input.setHeaderText("Enter node address"); Optional<String> result = input.showAndWait(); if(result.isPresent()) { String newNodeAddress = result.get().trim(); if (!newNodeAddress.isEmpty() && !containsAddress(newNodeAddress)) { addresses.add(index, newNodeAddress); addresses.remove(nodeAddress); } } } @FXML public void removeAction(ActionEvent event) { // remove the selected indices in reverse order Integer[] indices = lwBootstrappingNodes.getSelectionModel() .getSelectedIndices().toArray(new Integer[0]); Arrays.sort(indices); for(int i = indices.length - 1; i >= 0; --i) { addresses.remove(indices[i].intValue()); } } @FXML public void upAction(ActionEvent event) { int lastSelected = lwBootstrappingNodes.getSelectionModel().getSelectedIndex(); swapBootstrapingNodes(lastSelected, lastSelected-1); } @FXML public void downAction(ActionEvent event) { int lastSelected = lwBootstrappingNodes.getSelectionModel().getSelectedIndex(); swapBootstrapingNodes(lastSelected, lastSelected+1); } private void swapBootstrapingNodes(int indexCurrent, int indexNew) { int size = addresses.size(); if(indexCurrent >= 0 && indexCurrent < size && indexNew >= 0 && indexNew < size) { Collections.swap(addresses, indexCurrent, indexNew); lwBootstrappingNodes.getSelectionModel().clearAndSelect(indexNew); lwBootstrappingNodes.requestFocus(); } } @FXML public void saveAction(ActionEvent event) { try { // update config bootstrappingNodes.clearNodes(); for (String n : addresses) { bootstrappingNodes.addNode(n); } bootstrappingNodesFactory.save(); // reload saved config reset(); logger.debug("Saved network settings."); } catch(IOException ioex) { logger.warn("Could not save settings: {}", ioex); } } @FXML public void resetAction(ActionEvent event) { logger.debug("Reset network settings."); reset(); } /** * Adds an item to the list view collection. * Check that there are no duplicates (ignore case!) * * @param toAdd address */ private void addItemIgnoreDuplicate(String toAdd) { if (!containsAddress(toAdd)) { addresses.add(toAdd); } } /** * Checks whether the given address is in the list of addresses. * Note: ignore case! * * @param address to check * @return true if element is present, otherwise false. */ private boolean containsAddress(String address) { boolean contains = false; for (String item : addresses) { if (item.equalsIgnoreCase(address)) { contains = true; break; } } return contains; } }