Add oving0
This commit is contained in:
16
src/main/java/oving0/HelloWorld.java
Normal file
16
src/main/java/oving0/HelloWorld.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package oving0;
|
||||
|
||||
public class HelloWorld {
|
||||
|
||||
public String getHelloWorld() {
|
||||
return "Hello World!";
|
||||
}
|
||||
|
||||
public int getHelloWorldLength() {
|
||||
return this.getHelloWorld().length();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(new HelloWorld().getHelloWorld());
|
||||
}
|
||||
}
|
||||
43
src/main/java/oving0/todolist/fxui/ITodoFileReading.java
Normal file
43
src/main/java/oving0/todolist/fxui/ITodoFileReading.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package oving0.todolist.fxui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import oving0.todolist.model.TodoList;
|
||||
|
||||
public interface ITodoFileReading {
|
||||
|
||||
/**
|
||||
* Read a TodoList from a given InputStream.
|
||||
*
|
||||
* @param ios The input stream to read from.
|
||||
* @return The TodoList from the InputStream.
|
||||
*/
|
||||
TodoList readTodoList(InputStream is);
|
||||
|
||||
/**
|
||||
* Read a TodoList with a given name, from a default (implementation-specific) location.
|
||||
*
|
||||
* @param name The name of the TodoList
|
||||
* @return The TodoList with the given name from the default location
|
||||
* @throws IOException if the TodoList can't be found.
|
||||
*/
|
||||
TodoList readTodoList(String name) throws IOException;
|
||||
|
||||
/**
|
||||
* Write a TodoList to a given OutputStream
|
||||
*
|
||||
* @param todoList The list to write
|
||||
* @param os The stream to write to
|
||||
*/
|
||||
void writeTodoList(TodoList todoList, OutputStream os);
|
||||
|
||||
/**
|
||||
* Write a TodoList to a file named after the list in a default (implementation specific)
|
||||
* location.
|
||||
*
|
||||
* @param todoList The list to write
|
||||
* @throws IOException If a file at the proper location can't be written to
|
||||
*/
|
||||
void writeTodoList(TodoList todoList) throws IOException;
|
||||
}
|
||||
21
src/main/java/oving0/todolist/fxui/TodoApp.java
Normal file
21
src/main/java/oving0/todolist/fxui/TodoApp.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package oving0.todolist.fxui;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
public class TodoApp extends Application {
|
||||
|
||||
@Override
|
||||
public void start(Stage stage) throws Exception {
|
||||
Parent parent = FXMLLoader.load(getClass().getResource("Todo.fxml"));
|
||||
stage.setScene(new Scene(parent));
|
||||
stage.show();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
launch(TodoApp.class, args);
|
||||
}
|
||||
}
|
||||
238
src/main/java/oving0/todolist/fxui/TodoController.java
Normal file
238
src/main/java/oving0/todolist/fxui/TodoController.java
Normal file
@@ -0,0 +1,238 @@
|
||||
package oving0.todolist.fxui;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.stage.FileChooser;
|
||||
import javafx.stage.Window;
|
||||
import oving0.todolist.model.TodoEntry;
|
||||
import oving0.todolist.model.TodoList;
|
||||
|
||||
public class TodoController {
|
||||
|
||||
private final TodoSettings todoSettings = new TodoSettings();
|
||||
|
||||
public TodoSettings getTodoSettings() {
|
||||
return todoSettings;
|
||||
}
|
||||
|
||||
private TodoList todoList;
|
||||
|
||||
public void setTodoList(final TodoList todoList) {
|
||||
this.todoList = todoList;
|
||||
updateTodoListView();
|
||||
}
|
||||
|
||||
private final ITodoFileReading fileSupport = new TodoFileSupport();
|
||||
|
||||
@FXML
|
||||
private String sampleTodoListResource;
|
||||
|
||||
@FXML
|
||||
private Text todoListNameView;
|
||||
|
||||
@FXML
|
||||
private ListView<String> todoListEntriesView;
|
||||
|
||||
@FXML
|
||||
private TextField todoEntryTextField;
|
||||
|
||||
@FXML
|
||||
private TextField fileLocationNameField;
|
||||
|
||||
@FXML
|
||||
private Button todoEntryButton;
|
||||
|
||||
@FXML
|
||||
private Text statusText;
|
||||
private String defaultStatusText;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@FXML
|
||||
public void initialize() {
|
||||
final var todoList = new TodoList();
|
||||
todoList.setName("todo list");
|
||||
setTodoList(todoList);
|
||||
todoListEntriesView.getSelectionModel().selectedItemProperty()
|
||||
.addListener((prop, oldValue, newValue) -> {
|
||||
handleTodoListEntriesViewSelectedItemChanged();
|
||||
});
|
||||
handleTodoListEntriesViewSelectedItemChanged();
|
||||
defaultStatusText = statusText.getText();
|
||||
}
|
||||
|
||||
private void updateTodoListView() {
|
||||
todoListNameView.setText(todoList.getName());
|
||||
todoListEntriesView.getItems().setAll(getOrderedTodoListItems());
|
||||
}
|
||||
|
||||
protected List<String> getOrderedTodoListItems() {
|
||||
final List<String> listItems = new ArrayList<>();
|
||||
for (final var entry : todoList) {
|
||||
listItems.add(entry.getText());
|
||||
}
|
||||
switch (todoSettings.getTodoListOrder()) {
|
||||
case ADD_ORDER_REVERSED: {
|
||||
Collections.reverse(listItems);
|
||||
break;
|
||||
}
|
||||
case LEXICOGRAPHIC_ORDER: {
|
||||
Collections.sort(listItems);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
return listItems;
|
||||
}
|
||||
|
||||
private void handleTodoListEntriesViewSelectedItemChanged() {
|
||||
todoEntryTextField.setText(todoListEntriesView.getSelectionModel().getSelectedItem());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void handleTodoEntryTextFieldChange(final StringProperty prop, final String oldValue,
|
||||
final String newValue) {
|
||||
if (todoList.hasEntry(newValue)) {
|
||||
todoEntryButton.setText("Remove");
|
||||
} else {
|
||||
todoEntryButton.setText("Add");
|
||||
}
|
||||
todoEntryButton.setDisable(newValue == null || newValue.isBlank());
|
||||
}
|
||||
|
||||
private FileChooser fileChooser;
|
||||
|
||||
private String getFileLocationName(final boolean isSave) {
|
||||
if (fileChooser == null) {
|
||||
fileChooser = new FileChooser();
|
||||
}
|
||||
final Window window = fileLocationNameField.getScene().getWindow();
|
||||
File file = null;
|
||||
if (isSave) {
|
||||
file = fileChooser.showSaveDialog(window);
|
||||
} else {
|
||||
file = fileChooser.showOpenDialog(window);
|
||||
}
|
||||
if (file != null) {
|
||||
String name = file.getName();
|
||||
final int pos = name.lastIndexOf('.');
|
||||
if (pos >= 0) {
|
||||
name = name.substring(0, pos);
|
||||
}
|
||||
if (!name.isBlank()) {
|
||||
return file.getParent() + File.separator + name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void handleBrowseButtonAction() {
|
||||
final String name = getFileLocationName(false);
|
||||
if (name != null) {
|
||||
fileLocationNameField.setText(name);
|
||||
}
|
||||
}
|
||||
|
||||
private String ensureFileLocation(final boolean isSave) {
|
||||
String name = fileLocationNameField.getText();
|
||||
if (name.isBlank()) {
|
||||
name = getFileLocationName(false);
|
||||
if (name != null) {
|
||||
fileLocationNameField.setText(name);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void handleLoadButtonAction() {
|
||||
final String name = ensureFileLocation(false);
|
||||
if (!name.isBlank()) {
|
||||
try {
|
||||
setTodoList(fileSupport.readTodoList(name));
|
||||
statusText.setText(defaultStatusText);
|
||||
} catch (final IOException e) {
|
||||
statusText.setText(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void handleSaveButtonAction() {
|
||||
final String name = ensureFileLocation(true);
|
||||
if (!name.isBlank()) {
|
||||
try {
|
||||
todoList.setName(name);
|
||||
fileSupport.writeTodoList(todoList);
|
||||
todoListNameView.setText(todoList.getName());
|
||||
statusText.setText(defaultStatusText);
|
||||
} catch (final IOException e) {
|
||||
statusText.setText(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void handleTodoEntryButtonAction() {
|
||||
final var text = todoEntryTextField.getText();
|
||||
final var entry = todoList.findEntry(text);
|
||||
if (entry != null) {
|
||||
todoList.removeEntry(entry);
|
||||
} else {
|
||||
todoList.addEntry(new TodoEntry(text));
|
||||
}
|
||||
todoEntryTextField.clear();
|
||||
updateTodoListView();
|
||||
}
|
||||
|
||||
private TodoSettingsController settingsController;
|
||||
|
||||
private Scene scene;
|
||||
private Parent oldSceneRoot, settingsSceneRoot;
|
||||
|
||||
@FXML
|
||||
private void handleSettingsAction() {
|
||||
// check if we need to load settings ui from fxml
|
||||
if (settingsController == null) {
|
||||
final FXMLLoader fxmlLoader =
|
||||
new FXMLLoader(getClass().getResource("TodoSettings.fxml"));
|
||||
try {
|
||||
// load settings ui
|
||||
settingsSceneRoot = fxmlLoader.load();
|
||||
// remember old ui
|
||||
scene = todoListEntriesView.getScene();
|
||||
oldSceneRoot = scene.getRoot();
|
||||
// get the settings controller, so we can set its todoSettings property
|
||||
settingsController = fxmlLoader.getController();
|
||||
settingsController.setTodoController(this);
|
||||
} catch (final IOException e) {
|
||||
}
|
||||
}
|
||||
if (settingsController != null) {
|
||||
todoListEntriesView.getScene().setRoot(settingsSceneRoot);
|
||||
settingsController.setTodoSettings(todoSettings);
|
||||
}
|
||||
}
|
||||
|
||||
public void applyTodoSettings(final TodoSettings settings) {
|
||||
if (settings != null) {
|
||||
settings.copyInto(this.todoSettings);
|
||||
}
|
||||
scene.setRoot(oldSceneRoot);
|
||||
if (settings != null) {
|
||||
updateTodoListView();
|
||||
}
|
||||
}
|
||||
}
|
||||
80
src/main/java/oving0/todolist/fxui/TodoFileSupport.java
Normal file
80
src/main/java/oving0/todolist/fxui/TodoFileSupport.java
Normal file
@@ -0,0 +1,80 @@
|
||||
package oving0.todolist.fxui;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Scanner;
|
||||
import oving0.todolist.model.TodoEntry;
|
||||
import oving0.todolist.model.TodoList;
|
||||
|
||||
public class TodoFileSupport implements ITodoFileReading {
|
||||
|
||||
public static final String TODO_EXTENSION = "todo";
|
||||
|
||||
private Path getTodoUserFolderPath() {
|
||||
return Path.of(System.getProperty("user.home"), "tdt4100", "todo");
|
||||
}
|
||||
|
||||
private boolean ensureTodoUserFolder() {
|
||||
try {
|
||||
Files.createDirectories(getTodoUserFolderPath());
|
||||
return true;
|
||||
} catch (IOException ioe) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Path getTodoListPath(String name) {
|
||||
return getTodoUserFolderPath().resolve(name + "." + TODO_EXTENSION);
|
||||
}
|
||||
|
||||
public TodoList readTodoList(InputStream input) {
|
||||
TodoList todoList = null;
|
||||
try (var scanner = new Scanner(input)) {
|
||||
todoList = new TodoList();
|
||||
while (scanner.hasNextLine()) {
|
||||
var line = scanner.nextLine().stripTrailing();
|
||||
if (line.isEmpty() || line.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
if (todoList.getName() == null) {
|
||||
todoList.setName(line);
|
||||
} else {
|
||||
todoList.addEntry(new TodoEntry(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
return todoList;
|
||||
}
|
||||
|
||||
public TodoList readTodoList(String name) throws IOException {
|
||||
var todoListPath = getTodoListPath(name);
|
||||
try (var input = new FileInputStream(todoListPath.toFile())) {
|
||||
return readTodoList(input);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTodoList(TodoList todoList, OutputStream output) {
|
||||
try (var writer = new PrintWriter(output)) {
|
||||
writer.println("# name");
|
||||
writer.println(todoList.getName());
|
||||
writer.println("# entries");
|
||||
for (var entry : todoList) {
|
||||
writer.println(entry.getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void writeTodoList(TodoList todoList) throws IOException {
|
||||
var todoListPath = getTodoListPath(todoList.getName());
|
||||
ensureTodoUserFolder();
|
||||
try (var output = new FileOutputStream(todoListPath.toFile())) {
|
||||
writeTodoList(todoList, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/main/java/oving0/todolist/fxui/TodoLauncher.java
Normal file
11
src/main/java/oving0/todolist/fxui/TodoLauncher.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package oving0.todolist.fxui;
|
||||
|
||||
/**
|
||||
* Class needed in a shaded über jar to run a JavaFX app
|
||||
*/
|
||||
public class TodoLauncher {
|
||||
|
||||
public static void main(String[] args) {
|
||||
TodoApp.main(args);
|
||||
}
|
||||
}
|
||||
38
src/main/java/oving0/todolist/fxui/TodoSettings.java
Normal file
38
src/main/java/oving0/todolist/fxui/TodoSettings.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package oving0.todolist.fxui;
|
||||
|
||||
public class TodoSettings {
|
||||
|
||||
public enum TodoListOrder {
|
||||
ADD_ORDER, ADD_ORDER_REVERSED, LEXICOGRAPHIC_ORDER;
|
||||
}
|
||||
|
||||
private TodoListOrder todoListOrder = TodoListOrder.ADD_ORDER;
|
||||
|
||||
public TodoSettings() {}
|
||||
|
||||
/**
|
||||
* Initialises this TodoSettings from the provided argument
|
||||
*
|
||||
* @param target the target TodoSettings
|
||||
*/
|
||||
public TodoSettings(final TodoSettings todoSettings) {
|
||||
todoSettings.copyInto(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all properties in this TodoSettings into target
|
||||
*
|
||||
* @param target the target TodoSettings
|
||||
*/
|
||||
public void copyInto(final TodoSettings target) {
|
||||
target.setTodoListOrder(this.getTodoListOrder());
|
||||
}
|
||||
|
||||
public TodoListOrder getTodoListOrder() {
|
||||
return todoListOrder;
|
||||
}
|
||||
|
||||
public void setTodoListOrder(final TodoListOrder todoListOrder) {
|
||||
this.todoListOrder = todoListOrder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package oving0.todolist.fxui;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.ChoiceBox;
|
||||
|
||||
public class TodoSettingsController {
|
||||
|
||||
private TodoController todoController;
|
||||
|
||||
public void setTodoController(final TodoController todoController) {
|
||||
this.todoController = todoController;
|
||||
}
|
||||
|
||||
private TodoSettings todoSettings = new TodoSettings();
|
||||
|
||||
public void setTodoSettings(final TodoSettings todoSettings) {
|
||||
this.todoSettings = todoSettings;
|
||||
updateView();
|
||||
}
|
||||
|
||||
@FXML
|
||||
private ChoiceBox<String> listOrderSelector;
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
// same order as TodoSettings.ListOrder
|
||||
listOrderSelector.getItems().setAll("Add order", "Add order reversed", "Alphabetic");
|
||||
updateView();
|
||||
}
|
||||
|
||||
private void updateView() {
|
||||
listOrderSelector.getSelectionModel().select(todoSettings.getTodoListOrder().ordinal());
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void handleApplySettings() {
|
||||
todoController.applyTodoSettings(todoSettings);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void handleCancelSettings() {
|
||||
todoController.applyTodoSettings(null);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void handleListOrderSelection() {
|
||||
this.todoSettings.setTodoListOrder(TodoSettings.TodoListOrder.values()[listOrderSelector
|
||||
.getSelectionModel().getSelectedIndex()]);
|
||||
}
|
||||
}
|
||||
31
src/main/java/oving0/todolist/model/TodoEntry.java
Normal file
31
src/main/java/oving0/todolist/model/TodoEntry.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package oving0.todolist.model;
|
||||
|
||||
/**
|
||||
* Class representing a single entry in a To-Do-list.
|
||||
*
|
||||
* The class contains a text-value representing the label of the entry, and has a getText-method to
|
||||
* retrieve said label.
|
||||
*
|
||||
*/
|
||||
public class TodoEntry {
|
||||
|
||||
private final String text;
|
||||
|
||||
/**
|
||||
* Create a new entry with a given label.
|
||||
*
|
||||
* @param text The label to assign to this entry
|
||||
*/
|
||||
public TodoEntry(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[TodoEntry \"%s\"]", getText());
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
81
src/main/java/oving0/todolist/model/TodoList.java
Normal file
81
src/main/java/oving0/todolist/model/TodoList.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package oving0.todolist.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A TodoList, which is an iterable of {@link TodoEntry}s.
|
||||
*
|
||||
* The class has a name, along with methods for adding and removing entries, searching through them,
|
||||
* and checking if one exists in the list.
|
||||
*/
|
||||
public class TodoList implements Iterable<TodoEntry> {
|
||||
|
||||
private String name;
|
||||
|
||||
private List<TodoEntry> entries = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[TodoList \"%s\" with %s entries]", getName(), entries.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<TodoEntry> iterator() {
|
||||
return entries.iterator();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an entry given its text label.
|
||||
*
|
||||
* @param text The label to look for.
|
||||
* @return An entry with the given text label if it exists, otherwise null.
|
||||
*/
|
||||
public TodoEntry findEntry(String text) {
|
||||
for (var entry : entries) {
|
||||
if (entry.getText().equals(text)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this TodoList has an entry with a given text label.
|
||||
*
|
||||
* @param text The text label of the entry
|
||||
* @return true if an entry with the given text label is in this TodoList, otherwise false
|
||||
*/
|
||||
public boolean hasEntry(String text) {
|
||||
return findEntry(text) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an entry to this TodoList.
|
||||
*
|
||||
* @param entry The entry to add.
|
||||
*/
|
||||
public void addEntry(TodoEntry entry) {
|
||||
if (!entries.contains(entry)) {
|
||||
entries.add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove and entry from this TodoList.
|
||||
*
|
||||
* @param entry The entry to remove.
|
||||
*/
|
||||
public void removeEntry(TodoEntry entry) {
|
||||
entries.remove(entry);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user