package org.jftclient;
import com.google.common.base.Strings;
import com.jcraft.jsch.JSchException;
import com.sun.javafx.scene.control.skin.TreeViewSkin;
import com.sun.javafx.scene.control.skin.VirtualFlow;
import org.jftclient.config.dao.ConfigDao;
import org.jftclient.config.dao.HostDao;
import org.jftclient.config.domain.Config;
import org.jftclient.config.domain.Host;
import org.jftclient.ssh.Connection;
import org.jftclient.ssh.ConnectionState;
import org.jftclient.terminal.LocalTerminal;
import org.jftclient.terminal.RemoteTerminal;
import org.jftclient.terminal.TerminalPanel;
import org.jftclient.tree.CommonTree;
import org.jftclient.tree.CustomTreeViewSkin;
import org.jftclient.tree.LocalTree;
import org.jftclient.tree.Node;
import org.jftclient.tree.NodeTreeCell;
import org.jftclient.tree.RemoteTree;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.awt.*;
import java.io.IOException;
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.ComboBox;
import javafx.scene.control.IndexedCell;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.PasswordField;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.Skin;
import javafx.scene.control.SplitPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextField;
import javafx.scene.control.TitledPane;
import javafx.scene.control.ToolBar;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Modality;
import javafx.stage.Stage;
/**
* @author sergei.malafeev
*/
public class JFTClient extends Application {
private static final Logger logger = LoggerFactory.getLogger(JFTClient.class);
private ComboBox<String> hostField = new ComboBox<>();
private TextField userField = new TextField();
private PasswordField passwordField = new PasswordField();
private NodeTreeCell cellLocal;
private NodeTreeCell cellRemote;
private TitledPane remotePane = new TitledPane();
private Stage primaryStage;
private ConfigDao configDao;
private HostDao hostDao;
private Connection connection;
private LocalTree localTree;
private RemoteTree remoteTree;
private CommonTree commonTree;
private TerminalPanel localTerminalPanel = new TerminalPanel();
private TerminalPanel remoteTerminalPanel = new TerminalPanel();
private AnnotationConfigApplicationContext context;
private TabPane tabPane;
private Tab remoteTerminalTab;
private RemoteTerminal remoteTerminal;
private LocalTerminal localTerminal;
@Override
public void start(Stage primaryStage) throws IOException, AWTException {
this.primaryStage = primaryStage;
primaryStage.setTitle("JFTClient");
initSpring();
TitledPane localPane = new TitledPane("Local", createLocalTree());
localPane.setPrefHeight(1000d);
localPane.setCollapsible(false);
remotePane.setText("Remote");
remotePane.setPrefHeight(1000d);
remotePane.setCollapsible(false);
SplitPane splitTrees = new SplitPane();
splitTrees.getItems().addAll(localPane, remotePane);
splitTrees.setDividerPositions(0.5);
tabPane = new TabPane();
Tab tabOutput = new Tab();
tabOutput.setText("Output");
tabOutput.setContent(OutputPanel.getInstance().getScrollPane());
tabOutput.setClosable(false);
Tab tabTerminal = new Tab("Terminal");
tabTerminal.setContent(localTerminalPanel.getTextArea());
tabTerminal.setClosable(false);
remoteTerminalTab = new Tab("Remote");
remoteTerminalTab.setContent(remoteTerminalPanel.getTextArea());
remoteTerminalTab.setClosable(false);
tabPane.getTabs().addAll(tabOutput, tabTerminal);
SplitPane splitHorizontal = new SplitPane();
splitHorizontal.getItems().addAll(splitTrees, tabPane);
splitHorizontal.setDividerPositions(0.7);
splitHorizontal.setOrientation(Orientation.VERTICAL);
MenuBar menuBar = createMenu();
ToolBar toolBar = createToolbar();
VBox topBox = new VBox();
topBox.getChildren().addAll(menuBar, toolBar);
BorderPane borderPane = new BorderPane();
borderPane.setTop(topBox);
borderPane.setCenter(splitHorizontal);
Scene scene = new Scene(borderPane, 950d, 700d, Color.WHITE);
primaryStage.setScene(scene);
primaryStage.getIcons().add(new Image("java.png"));
tabTerminal.selectedProperty().addListener((observable, oldValue, newValue) -> {
if (newValue) {
try {
localTerminal.connect();
} catch (IOException e) {
logger.warn("failed to open local terminal", e);
}
}
});
remoteTerminalTab.selectedProperty().addListener((observable, oldValue, newValue) -> {
if (newValue) {
try {
remoteTerminal.connect(connection, remoteTerminalPanel);
} catch (JSchException | IOException e) {
logger.warn("failed to open remote terminal", e);
}
}
});
primaryStage.show();
}
@Override
public void stop() {
context.close();
}
private void initSpring() {
context = new AnnotationConfigApplicationContext(JFTConfiguration.class);
configDao = context.getBean(ConfigDao.class);
if (configDao.get() == null) {
Config config = new Config();
configDao.save(config);
}
hostDao = context.getBean(HostDao.class);
connection = context.getBean(Connection.class);
localTree = context.getBean(LocalTree.class);
remoteTree = context.getBean(RemoteTree.class);
commonTree = context.getBean(CommonTree.class);
remoteTerminal = context.getBean(RemoteTerminal.class);
localTerminal = context.getBean(LocalTerminal.class);
localTerminal.setLocalTerminalPanel(localTerminalPanel);
}
private MenuBar createMenu() {
MenuBar menuBar = new MenuBar();
Menu menuEdit = new Menu("Edit");
MenuItem editHosts = new MenuItem("Hosts");
editHosts.setOnAction(event -> createEditHostsDialog());
menuEdit.getItems().add(editHosts);
Menu menuView = new Menu("View");
CheckMenuItem cmShowHiddenFiles = new CheckMenuItem("Show hidden files");
cmShowHiddenFiles.setSelected(configDao.get().isShowHiddenFiles());
cmShowHiddenFiles.selectedProperty().addListener((observable, oldValue, newValue) -> {
Config config = configDao.get();
config.setShowHiddenFiles(newValue);
configDao.save(config);
cellLocal.refreshTree();
if (cellRemote != null) {
cellRemote.refreshTree();
}
});
menuView.getItems().addAll(cmShowHiddenFiles);
Menu menuSettings = new Menu("Settings");
CheckMenuItem cmSavePasswords = new CheckMenuItem("Save passwords");
cmSavePasswords.setSelected(configDao.get().isSavePasswords());
cmSavePasswords.selectedProperty().addListener((observable, oldValue, newValue) -> {
Config config = configDao.get();
config.setSavePasswords(newValue);
configDao.save(config);
});
menuSettings.getItems().addAll(cmSavePasswords);
menuBar.getMenus().addAll(menuEdit, menuView, menuSettings);
return menuBar;
}
private void createEditHostsDialog() {
Stage dialog = new Stage();
dialog.initModality(Modality.WINDOW_MODAL);
dialog.initOwner(primaryStage);
ListView<String> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
listView.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.DELETE) {
ObservableList<String> selectedItems = listView.getSelectionModel().getSelectedItems();
if (selectedItems.isEmpty()) {
return;
}
for (String host : selectedItems) {
Host h = hostDao.getHostByName(host);
hostDao.delete(h);
}
listView.getItems().setAll(hostDao.getHostNames());
hostField.getItems().setAll(hostDao.getHostNames());
}
});
listView.getItems().addAll(hostDao.getHostNames());
listView.setCellFactory(param -> new HostCell(hostDao, hostField));
StackPane root = new StackPane();
root.getChildren().add(listView);
Scene dialogScene = new Scene(root);
dialog.setScene(dialogScene);
dialog.setHeight(150d);
dialog.setWidth(300d);
dialog.setTitle("Edit Hosts");
double x = primaryStage.getX() + primaryStage.getWidth() / 2. - dialog.getWidth() / 2.;
double y = primaryStage.getY() + primaryStage.getHeight() / 2. - dialog.getHeight() / 2.;
dialog.setX(x);
dialog.setY(y);
dialog.showAndWait();
}
private TreeView<Node> createTree(TreeItem<Node> root) {
return new TreeView<Node>(root) {
@Override
protected Skin createDefaultSkin() {
return new CustomTreeViewSkin<>(this);
}
};
}
private TreeView<Node> createLocalTree() {
TreeItem<Node> root = localTree.createRootNode();
root.setExpanded(true);
TreeView<Node> treeView = createTree(root);
treeView.setEditable(true);
treeView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
treeView.setCellFactory(param -> {
cellLocal = new NodeTreeCell(primaryStage, connection, localTree, commonTree);
commonTree.setDragDropEvent(cellLocal, localTree);
return cellLocal;
});
treeView.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.DELETE) {
commonTree.deleteSelectedItems(treeView, localTree);
} else if (event.getCode() == KeyCode.F5) {
commonTree.refresh(treeView, localTree);
} else if (event.getCode() == KeyCode.ENTER) {
commonTree.expand(treeView);
}
});
return treeView;
}
private void connect() {
connection.disconnect();
remoteTerminalPanel.getTextArea().clear();
String host = hostField.getValue();
if (Strings.isNullOrEmpty(host)) {
OutputPanel.getInstance().printRed("host is empty\n");
return;
}
String user = userField.getText();
if (user.isEmpty()) {
OutputPanel.getInstance().printRed("user is empty\n");
return;
}
String password = passwordField.getText();
if (password.isEmpty()) {
OutputPanel.getInstance().printRed("password is empty\n");
return;
}
ConnectionState connectionState = connection.connect(host, user, password);
if (!connectionState.isSuccess()) {
logger.debug("failed to connect to host {}", host);
OutputPanel.getInstance().printRed(connectionState.getMsg());
return;
}
Config config = configDao.get();
Host host1 = hostDao.getHostByName(host);
if (host1 == null) {
host1 = new Host();
host1.setHostname(host);
}
host1.setUsername(user);
host1.setPassword(config.isSavePasswords() ? password : "");
hostDao.save(host1);
TreeItem<Node> root = remoteTree.createRootNote();
root.setExpanded(true);
TreeView<Node> treeView = createTree(root);
treeView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
treeView.setCellFactory(param -> {
cellRemote = new NodeTreeCell(primaryStage, connection, remoteTree, commonTree);
commonTree.setDragDropEvent(cellRemote, localTree);
return cellRemote;
});
treeView.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.DELETE) {
commonTree.deleteSelectedItems(treeView, remoteTree);
} else if (event.getCode() == KeyCode.F5) {
commonTree.refresh(treeView, remoteTree);
} else if (event.getCode() == KeyCode.ENTER) {
commonTree.expand(treeView);
}
});
remotePane.setContent(treeView);
if (tabPane.getTabs().size() == 2) {
tabPane.getTabs().add(remoteTerminalTab);
}
if (remoteTerminalTab.isSelected()) {
try {
remoteTerminal.connect(connection, remoteTerminalPanel);
} catch (JSchException | IOException e) {
logger.warn("failed to open remote terminal", e);
}
}
}
private ToolBar createToolbar() {
Label hostLabel = new Label("Host:");
Label userLabel = new Label("User:");
Label passwordLabel = new Label("Password:");
userField.setText(System.getProperty("user.name"));
hostField.setEditable(true);
hostField.setPrefWidth(220d);
hostField.getItems().addAll(hostDao.getHostNames());
hostField.valueProperty().addListener((observable, oldValue, newValue) -> {
Host host = hostDao.getHostByName(newValue);
if (host == null) {
return;
}
userField.setText(host.getUsername());
if (!Strings.isNullOrEmpty(host.getPassword())) {
passwordField.setText(host.getPassword());
}
});
Button button = new Button("Connect");
button.setOnAction(event -> connect());
passwordField.setOnKeyPressed(event -> {
if (event.getCode() == KeyCode.ENTER) {
connect();
}
});
Region region = new Region();
HBox.setHgrow(region, Priority.ALWAYS);
return new ToolBar(
hostLabel,
hostField,
new Label(" "),
userLabel,
userField,
new Label(" "),
passwordLabel,
passwordField,
new Label(" "),
button,
region
);
}
public static void main(String[] args) {
launch(args);
}
}