Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,13 @@
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.io.IOException;
import java.nio.file.Paths;

public class CommanderApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(CommanderApplication.class.getResource("commander-ui.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 400, 400);
MainController ctrl = fxmlLoader.getController();
String home = System.getProperty("user.home");
if (ctrl != null && home != null) {
ctrl.setInitialDirs(Paths.get(home), Paths.get(home));
}
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("commander-ui.fxml"));
Scene scene = new Scene(loader.load());
stage.setTitle("Commander");
stage.setScene(scene);
stage.show();
System.out.println("Hello world");
}
}
}
214 changes: 192 additions & 22 deletions commander/src/main/java/hse/java/commander/MainController.java
Original file line number Diff line number Diff line change
@@ -1,45 +1,215 @@
package hse.java.commander;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.*;
import javafx.scene.input.MouseButton;
import javafx.util.Callback;

import java.nio.file.Path;
import java.io.IOException;
import java.nio.file.*;
import java.util.Comparator;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class MainController {

@FXML
public ListView<String> left;

private ListView<Path> leftPane;
@FXML
public ListView<String> right;

private ListView<Path> rightPane;
@FXML
private Label leftPathLabel;
@FXML
private Label rightPathLabel;
@FXML
private Button btnCopy;
@FXML
private Button btnMove;
@FXML
public Button move;
private Button btnDelete;

private Path leftDir;
private Path rightDir;
private ListView<Path> focusedPane;

// for testing
public void setInitialDirs(Path leftStart, Path rightStart) {
this.leftDir = leftStart;
this.rightDir = rightStart;
if (leftStart != null) this.leftDir = leftStart;
if (rightStart != null) this.rightDir = rightStart;
refreshBoth();
}

@FXML
public void initialize() {
move.setOnMouseClicked(event -> {
if (leftDir == null) leftDir = Paths.get(System.getProperty("user.home"));
if (rightDir == null) rightDir = Paths.get(System.getProperty("user.home"));

});
System.out.println(System.getProperty("user.home"));
left.getItems().add("Kek");

left.setOnMouseClicked(event -> {
if (event.getClickCount() == 2) {
int index = left.getSelectionModel().getSelectedIndex();
if (index >= 0) {
left.getItems().set(index, "clicked");
Callback<ListView<Path>, ListCell<Path>> cellFactory = lv -> new ListCell<>() {
@Override
protected void updateItem(Path item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setStyle("");
} else {
if (item.getFileName() == null) {
setText(item.toString());
} else {
setText(item.getFileName().toString());
}
if (Files.isDirectory(item)) {
setStyle("-fx-font-weight: bold;");
} else {
setStyle("");
}
}
}
};
leftPane.setCellFactory(cellFactory);
rightPane.setCellFactory(cellFactory);

setupPaneInteraction(leftPane, true);
setupPaneInteraction(rightPane, false);

btnCopy.setOnAction(e -> transferSelected(TransferOp.COPY));
btnMove.setOnAction(e -> transferSelected(TransferOp.MOVE));
btnDelete.setOnAction(e -> transferSelected(TransferOp.DELETE));

refreshBoth();
}

private void setupPaneInteraction(ListView<Path> pane, boolean isLeft) {
pane.setOnMouseClicked(event -> {
focusedPane = pane;
if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) {
Path selected = pane.getSelectionModel().getSelectedItem();
if (selected == null) return;

Path current = isLeft ? leftDir : rightDir;
if (selected.equals(current.getParent())) {
Path parent = current.getParent();
if (parent != null) {
if (isLeft) leftDir = parent;
else rightDir = parent;
refreshBoth();
}
return;
}
if (Files.isDirectory(selected)) {
if (isLeft) leftDir = selected;
else rightDir = selected;
refreshBoth();
}
}
});
}
}

private enum TransferOp { COPY, MOVE, DELETE }

private void transferSelected(TransferOp op) {
if (focusedPane == null) return;
Path selected = focusedPane.getSelectionModel().getSelectedItem();
if (selected == null) return;

Path sourceDir = (focusedPane == leftPane) ? leftDir : rightDir;
Path targetDir = (focusedPane == leftPane) ? rightDir : leftDir;

if (selected.equals(sourceDir.getParent())) return;

Path src = sourceDir.resolve(selected.getFileName());

if (op == TransferOp.DELETE) {
try {
if (Files.isDirectory(src)) {
deleteDirectory(src);
} else {
Files.deleteIfExists(src);
}
} catch (IOException e) {
showError("Delete failed", e.getMessage());
}
refreshBoth();
return;
}

Path dst = targetDir.resolve(selected.getFileName());
try {
if (op == TransferOp.COPY) {
if (Files.isDirectory(src)) {
copyDirectory(src, dst);
} else {
Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
}
} else if (op == TransferOp.MOVE) {
Files.move(src, dst, StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
showError(op == TransferOp.COPY ? "Copy failed" : "Move failed", e.getMessage());
}
refreshBoth();
}

private void copyDirectory(Path source, Path target) throws IOException {
try (Stream<Path> walk = Files.walk(source)) {
walk.forEach(sourcePath -> {
Path targetPath = target.resolve(source.relativize(sourcePath));
try {
if (Files.isDirectory(sourcePath)) {
Files.createDirectories(targetPath);
} else {
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
System.err.println("Failed to copy " + sourcePath + ": " + e.getMessage());
}
});
}
}

private void deleteDirectory(Path dir) throws IOException {
try (Stream<Path> walk = Files.walk(dir)) {
walk.sorted(Comparator.reverseOrder())
.forEach(path -> {
try {
Files.deleteIfExists(path);
} catch (IOException e) {
System.err.println("Failed to delete " + path + ": " + e.getMessage());
}
});
}
}

private void refreshBoth() {
safeRefresh(leftPane, leftDir, leftPathLabel);
safeRefresh(rightPane, rightDir, rightPathLabel);
}

private void safeRefresh(ListView<Path> pane, Path dir, Label label) {
if (dir == null) {
pane.setItems(FXCollections.observableArrayList());
if (label != null) label.setText("");
return;
}

ObservableList<Path> items = FXCollections.observableArrayList();
Path parent = dir.getParent();
if (parent != null) items.add(parent);

try (Stream<Path> stream = Files.list(dir)) {
stream.sorted(Comparator.comparing(p -> p.getFileName().toString().toLowerCase()))
.forEach(items::add);
} catch (IOException e) {
}
pane.setItems(items);
if (label != null) label.setText(dir.toString());
}

private void showError(String title, String message) {
Alert alert = new Alert(Alert.AlertType.ERROR);
alert.setTitle(title);
alert.setHeaderText(null);
alert.setContentText(message);
alert.showAndWait();
}
}
24 changes: 17 additions & 7 deletions commander/src/main/resources/hse/java/commander/commander-ui.fxml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,24 @@
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="421.0" prefWidth="627.0" xmlns="http://javafx.com/javafx/17.0.12" xmlns:fx="http://javafx.com/fxml/1" fx:controller="hse.java.commander.MainController">
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="421.0" prefWidth="627.0"
xmlns="http://javafx.com/javafx/17.0.12"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="hse.java.commander.MainController">
<HBox prefHeight="370.0" prefWidth="627.0">
<ListView fx:id="left" id="left" prefHeight="318.0" prefWidth="311.0" />
<ListView fx:id="right" id="right" prefHeight="318.0" prefWidth="321.0" />
<VBox>
<Label fx:id="leftPathLabel"/>
<ListView fx:id="leftPane" prefHeight="318.0" prefWidth="311.0"/>
</VBox>
<VBox>
<Label fx:id="rightPathLabel"/>
<ListView fx:id="rightPane" prefHeight="318.0" prefWidth="321.0"/>
</VBox>
</HBox>
<HBox spacing="10.0">
<Button fx:id="copy" mnemonicParsing="false" text="Copy" />
<Button fx:id="move" mnemonicParsing="false" text="Move" />
<Button fx:id="delete" mnemonicParsing="false" text="Delete" />
<Button fx:id="btnCopy" text="Copy"/>
<Button fx:id="btnMove" text="Move"/>
<Button fx:id="btnDelete" text="Delete"/>
</HBox>
</VBox>
</VBox>
Loading