package app.controllers; import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.net.URL; import java.util.Collection; import java.util.ResourceBundle; import java.util.Scanner; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import org.fxmisc.richtext.CodeArea; import org.fxmisc.richtext.LineNumberFactory; import org.fxmisc.richtext.model.StyleSpans; import org.fxmisc.richtext.model.TwoDimensional.Bias; import org.fxmisc.richtext.model.TwoDimensional.Position; import app.events.CopyEvent; import app.events.CutEvent; import app.events.EditorChangedEvent; import app.events.LanguageChangedEvent; import app.events.PasteEvent; import app.events.RedoEvent; import app.events.SaveFile; import app.events.ToggleCommentEvent; import app.events.ToggleWrapTextEvent; import app.events.UndoEvent; import app.events.FileSaveStateChangedEvent; import app.events.FileSelectedEvent; import app.model.Model; import app.service.LanguageOperations; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; /** * A FXML controller that controls the editor component of the UI */ public class EditorController implements Initializable, Controller { @FXML private CodeArea editor; private EventBus eventBus; /** * Initializes the controller, and binds the event of change in editor content * to {@link #editorChanged() editorChanged} */ @Override public void initialize(URL url, ResourceBundle resourceBundle) { editor.setParagraphGraphicFactory(LineNumberFactory.get(editor)); editor.textProperty().addListener((obs, oldV, newV) -> this.editorChanged()); } @Override public void setEventBus(EventBus eventBus) { this.eventBus = eventBus; this.eventBus.register(this); } /** * Applies highlighting to the editor. * * @param highlighting highlighting data */ private void setHighlighting(StyleSpans> highlighting) { this.editor.setStyleSpans(0, highlighting); } /** * Recalculates and refreshes the syntax highlighting of the editor. */ private void refreshHighlighting() { this.setHighlighting(LanguageOperations.syntaxHighlight(this.editor.getText(), Model.getLanguage())); } /** * Uses the {@link app.model.ProgrammingLanguage ProgrammingLanguage} in * {@link app.model.Model Model} to determine whether the current line/selection * is commented or not, and toggles the comment. * * @see app.model.ProgrammingLanguage#commentLine(String) * ProgrammingLanguage.commentLine(line) */ private void toggleComment() { if (editor.getSelectedText().equals("")) { String currentLine = editor.getText(editor.getCurrentParagraph()); String newText; if (Model.getLanguage().isCommentedLine(currentLine)) newText = Model.getLanguage().unCommentLine(currentLine); else newText = Model.getLanguage().commentLine(currentLine); editor.replaceText(editor.getCurrentParagraph(), 0, editor.getCurrentParagraph(), currentLine.length(), newText); } else { // Comment selection String newText; if (Model.getLanguage().isCommentedSelection(editor.getSelectedText())) newText = Model.getLanguage().unCommentSelection(editor.getSelectedText()); else newText = Model.getLanguage().commentSelection(editor.getSelectedText()); editor.replaceSelection(newText); } } /** * Updates the wraptext setting of the code area * * @param isWrapText The new value for the setting */ private void setWrapText(boolean isWrapText) { this.editor.setWrapText(isWrapText); } /** * Handles the event whenever the content of the editor is changed. */ private void editorChanged() { int offset = this.editor.getCaretPosition(); Position pos = this.editor.offsetToPosition(offset, Bias.Forward); this.eventBus.post(new EditorChangedEvent(pos.getMajor() + 1, pos.getMinor())); if (Model.getFileIsSaved()) this.eventBus.post(new FileSaveStateChangedEvent(false)); this.refreshHighlighting(); } /** * Updates the content of the editor to a specific filepath * * @param filePath The path of the file * @throws FileNotFoundException */ private void setEditorContent(String filePath) { if (filePath == null) { editor.clear(); return; } try (Scanner sc = new Scanner(new File(filePath))) { if (filePath.endsWith(".java") || filePath.endsWith(".md")) { editor.clear(); while (sc.hasNextLine()) { editor.appendText(sc.nextLine()); editor.appendText("\n"); } } else { throw new FileNotFoundException(); } } catch (FileNotFoundException ex) { Alert error = new Alert(AlertType.ERROR); error.setContentText("Could not be opened!\nMust be a java or md file. Try again."); error.showAndWait(); System.err.println(filePath); } } private void saveCodeArea(String filePath) { try (PrintWriter writer = new PrintWriter(new File(filePath))) { if (filePath.endsWith(".java") || filePath.endsWith(".md")) { writer.println(editor.getText()); } else { throw new FileNotFoundException(); } } catch (FileNotFoundException ex) { Alert error = new Alert(AlertType.ERROR); error.setContentText("Could not save file!\nMust be a java or md file. Try again."); error.showAndWait(); System.err.println(filePath); } } @Subscribe private void handle(FileSelectedEvent event) { this.setEditorContent(event.getPath()); } @Subscribe private void handle(SaveFile event) { this.saveCodeArea(event.getPath()); } @Subscribe private void handle(LanguageChangedEvent event) { this.refreshHighlighting(); } @Subscribe private void handle(ToggleCommentEvent event) { this.toggleComment(); } @Subscribe private void handle(ToggleWrapTextEvent event) { this.setWrapText(event.getIsWrapped()); } @Subscribe private void handle(UndoEvent event) { if (this.editor.isFocused()) this.editor.undo(); } @Subscribe private void handle(RedoEvent event) { if (this.editor.isFocused()) this.editor.redo(); } @Subscribe private void handle(CopyEvent event) { if (this.editor.isFocused()) this.editor.copy(); } @Subscribe private void handle(CutEvent event) { if (this.editor.isFocused()) this.editor.cut(); } @Subscribe private void handle(PasteEvent event) { if (this.editor.isFocused()) this.editor.paste(); } }