Search code examples
javajavafxdata-structuresdoubly-linked-listkeyevent

Edit text from a specific line at text area when a key is pressed using javafx?


So I'm working on building a text editor using java, javaFx, I have to open a file in the (Editor VBox) and put the lines in a double linked list, then edit, delete, overWrite a line etc.., I made a textArea called textAr in the Editor VBox, but I have a problem in reaching a text in specific line from the textAr, so I can edit, or insert a text in the double linked list and the position that new text should be inserted in,I have to use (I) to insert so I can't use getSelectedText().., here is a picture for what is required as user interface..

interface pic

here is what I'm trying to do to insert a text when I is pressed on the keyboard, I put getText from the textAr (text Area in editor VBox ), but it takes the whole text in the area, not in a specific line or position..In addition to crash when I press (I) on keyboard and black screen appears in the java Application program


Solution

  • You could look at this code and see if it helps.

    It is not designed to be a really usable, fully functional text editor, just to implement the main features outlined in your question.

    Also, if the design wasn't as provided, I'd do things differently, e.g. instead of having an edit command to manually initiate editing a row, automatically start editing the selected row on change of selection.

    The code provided does not use a custom linked list structure, it just uses array lists.

    To initiate operations in the editor, it is necessary to use a system specific shortcut key in combination with the command to be executed. See the KeyCombination documentation for more info if required.

    If you wished to adapt the code to use a custom list implementation:

    1. Have your custom linked list structure implement the List interface.

      • You don't need to implement all of the interface, only the pieces you need to get your application working, the rest of the operations can just throw UnsupportedOperationException.
    2. Pass an instance of your custom list to FXCollections to wrap it in an observable list the UI can use: FXCollections.observableList(yourDoubleLinkedList)

      • Do this instead of places I use an ArrayList via FXCollections.observableArrayList().

    I won't provide any further assistance on adaption of the implementation to use a custom list, if you have difficulties with that, those difficulties are yours to solve.

    not true

    import javafx.application.*;
    import javafx.beans.binding.Bindings;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.collections.*;
    import javafx.geometry.Insets;
    import javafx.geometry.Orientation;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.input.KeyCombination;
    import javafx.scene.layout.*;
    import javafx.stage.*;
    
    import java.io.*;
    import java.nio.file.*;
    import java.util.Map;
    
    public class LineEditor extends Application {
        private Document document;
    
        @Override
        public void init() throws Exception {
            Path path = Path.of(getParameters().getRaw().get(0));
            document = new Document(
                    path.getFileName().toString(),
                    FXCollections.observableArrayList(
                            Files.readAllLines(path)
                    )
            );
        }
    
        @Override
        public void start(Stage stage) throws IOException {
            Editor editor = new Editor(document);
            Scene scene = new Scene(editor.getUI());
            stage.setScene(scene);
    
            scene.getAccelerators().putAll(
                    Map.of(
                            KeyCombination.valueOf("SHORTCUT+I"),
                            editor::insert,
                            KeyCombination.valueOf("SHORTCUT+O"),
                            editor::overwrite,
                            KeyCombination.valueOf("SHORTCUT+D"),
                            editor::delete,
                            KeyCombination.valueOf("SHORTCUT+E"),
                            editor::edit,
                            KeyCombination.valueOf("SHORTCUT+C"),
                            editor::changeTitle,
                            KeyCombination.valueOf("SHORTCUT+S"),
                            () -> editor.save(scene.getWindow()),
                            KeyCombination.valueOf("SHORTCUT+Q"),
                            editor::quit
                    )
            );
    
            stage.titleProperty().bind(
                    Bindings.concat("Editing: ", document.titleProperty())
            );
    
            stage.show();
        }
    
        public static void main(String[] args) throws IOException {
            if (args.length == 0) {
                Path dirPath = Files.createTempDirectory(TEMP_DIR_PREFIX);
                Path filePath = dirPath.resolve(DEFAULT_TITLE);
                Files.writeString(filePath, DEFAULT_TEXT);
                args = new String[]{filePath.toString()};
            } else {
                if (!Files.isReadable(Path.of(args[0]))) {
                    System.out.println("File " + args[0] + " is not readable.");
                    System.exit(1);
                }
            }
    
            launch(args);
        }
    
        private static final String TEMP_DIR_PREFIX = "line-editor";
        private static final String TEXT_EXTENSION = "txt";
        private static final String DEFAULT_TITLE = "sonnet-148." + TEXT_EXTENSION;
        private static final String DEFAULT_TEXT = """
                O me, what eyes hath Love put in my head,
                Which have no correspondence with true sight!
                Or, if they have, where is my judgment fled,
                That censures falsely what they see aright?
                If that be fair whereon my false eyes dote,
                What means the world to say it is not so?
                If it be not, then love doth well denote
                Love's eye is not so true as all men's 'No.'
                How can it? O, how can Love's eye be true,
                That is so vex'd with watching and with tears?
                No marvel then, though I mistake my view;
                The sun itself sees not till heaven clears.
                   O cunning Love! with tears thou keep'st me blind,
                   Lest eyes well-seeing thy foul faults should find.
                """;
    }
    
    class Document {
        private final SimpleStringProperty title;
        private final ObservableList<String> lines;
    
        public Document(String title, ObservableList<String> lines) {
            this.title = new SimpleStringProperty(title);
            this.lines = lines;
        }
    
        public SimpleStringProperty titleProperty() {
            return title;
        }
    
        public ObservableList<String> getLines() {
            return lines;
        }
    }
    
    class Editor {
        private final Pane layout;
        private final ListView<String> display;
        private final TextArea input;
        private final Document document;
        
        private final static String INSTRUCTION_TEXT = """
                Shortcut
                  + Key   Command
                    I     Insert
                    O     Overwrite
                    D     Delete
                    E     Edit
                    C     Change Title
                    S     Save
                    Q     Quit
                """;
    
        private final static String UNTITLED_DOCUMENT_TITLE = "Untitled.txt";
    
        public Editor(Document document) {
            this.document =
                    (document != null)
                        ? document
                        : new Document(
                                UNTITLED_DOCUMENT_TITLE,
                                FXCollections.observableArrayList()
                        );
    
            assert document != null;
            display = new ListView<>(document.getLines());
            display.setPrefSize(350, 300);
            input = new TextArea();
    
            Label instructions = new Label(INSTRUCTION_TEXT);
            instructions.setStyle("-fx-font-family: monospace;");
    
            HBox displayWithInstructions = new HBox(10,
                    instructions,
                    display
            );
    
            SplitPane splitPane = new SplitPane(
                    displayWithInstructions,
                    input
            );
            splitPane.setOrientation(Orientation.VERTICAL);
            splitPane.setDividerPositions(.8);
    
            layout = new StackPane(splitPane);
            layout.setPadding(new Insets(10));
        }
    
        public Pane getUI() {
            return layout;
        }
    
        public void insert() {
            int selectedIndex = display.getSelectionModel().getSelectedIndex();
            document.getLines().add(selectedIndex + 1, input.getText());
        }
    
        public void overwrite() {
            int selectedIndex = display.getSelectionModel().getSelectedIndex();
            if (selectedIndex != -1) {
                document.getLines().set(selectedIndex, input.getText());
            }
        }
    
        public void delete() {
            int selectedIndex = display.getSelectionModel().getSelectedIndex();
            if (selectedIndex != -1) {
                document.getLines().remove(selectedIndex);
            }
        }
    
        public void edit() {
            int selectedIndex = display.getSelectionModel().getSelectedIndex();
            if (selectedIndex != -1) {
                input.setText(document.getLines().get(selectedIndex));
            }
        }
    
        public void changeTitle() {
            TextInputDialog titleDialog = new TextInputDialog(document.titleProperty().get());
            titleDialog.setTitle("Set Title");
            titleDialog.showAndWait()
                    .ifPresent(result -> document.titleProperty().set(result));
        }
    
        public void save(Window owner) {
            FileChooser fileChooser = new FileChooser();
            fileChooser.getExtensionFilters().setAll(
                    new FileChooser.ExtensionFilter("Text Files", "txt")
            );
            fileChooser.setTitle("Save Document");
            fileChooser.setInitialFileName(document.titleProperty().get());
    
            File saveFile = fileChooser.showSaveDialog(owner);
    
            if (saveFile != null && Files.isWritable(saveFile.toPath())) {
                document.titleProperty().set(saveFile.getName());
    
                try {
                    Files.writeString(
                            saveFile.toPath(),
                            String.join("\n", document.getLines())
                    );
                } catch (IOException e) {
                    e.printStackTrace();
    
                    Alert alert = new Alert(
                            Alert.AlertType.ERROR,
                            "Unable to save " + document.titleProperty().get()
                    );
                    alert.initOwner(owner);
                    alert.showAndWait();
                }
            }
        }
    
        public void quit() {
            Platform.exit();
        }
    }