TDT4100-project/src/main/java/app/controllers/EditorController.java

243 lines
6.7 KiB
Java

package app.controllers;
import java.net.URL;
import java.util.Collection;
import java.util.ResourceBundle;
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.OpenFileEvent;
import app.events.PasteEvent;
import app.events.RedoEvent;
import app.events.SaveFileEvent;
import app.events.ToggleCommentEvent;
import app.events.ToggleWrapTextEvent;
import app.events.UndoEvent;
import app.events.FileSaveStateChangedEvent;
import app.model.Model;
import app.service.FileOperations;
import app.service.LanguageOperations;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.stage.Stage;
/**
* 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);
}
// TODO: document
public CodeArea getEditor() {
return editor;
}
/**
* Applies highlighting to the editor.
*
* @param highlighting highlighting data
*/
private void setHighlighting(StyleSpans<Collection<String>> 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() {
// TODO: This logic might need to be moved to LanguageOperations
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.
*
* @param newContent The String to be inserted into the editor
*/
public void setEditorContent(String newContent) {
editor.clear();
editor.appendText(newContent);
}
/**
* Saving/Writing to the file based on the spesific filepath. Otherwise it will
* open an error dialog to give the user feedback about what has happened.
*/
public void saveCodeArea(boolean isNewFile) {
Stage stage = (Stage) editor.getScene().getWindow();
if (isNewFile && FileOperations.saveFileWithDialog(stage, editor.getText())) {
this.eventBus.post(new OpenFileEvent(Model.getActiveFilePath()));
this.eventBus.post(new FileSaveStateChangedEvent(true));
}
else if (FileOperations.saveFile(Model.getActiveFilePath().orElseThrow(), editor.getText())) {
this.eventBus.post(new FileSaveStateChangedEvent(true));
}
}
/**
* Checking if all is saved before closing the app. The user can either choose
* to exit or go back to the application and save.
*/
/* ------------------------------------------------------------------------ */
/* SUBSCRIPTIONS */
/* ------------------------------------------------------------------------ */
/**
* Updates Code Area (read from file) whenever the FileSelected is changed
*/
@Subscribe
public void handle(OpenFileEvent event) {
String newContent =
event
.getPath()
.map(path -> FileOperations.readFile(path))
.orElse("");
this.setEditorContent(newContent);
}
/**
* Save file (write to file) whenever the save in the menubare is selected
*/
@Subscribe
private void handle(SaveFileEvent event) {
this.saveCodeArea(event.getIsNewFile());
}
@Subscribe
private void handle(LanguageChangedEvent event) {
this.refreshHighlighting();
}
@Subscribe
public 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();
}
}