1
0

Arkistointi

This commit is contained in:
2026-04-13 00:33:23 +03:00
parent 1d8cda35f8
commit 17b53a38cf
93 changed files with 8476 additions and 1 deletions
@@ -0,0 +1,332 @@
package com.example.harjoitustyo;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Predicate;
/**
* Ohjelman käyttöliittymä
* Luokasta ei tarkoitus tehdä olioita
*/
public class Paaikkuna extends Application {
/**
* Ohjelman entrypoint
* @param args komentoriviargumentit, ei käytössä
*/
public static void main(String[] args) {
launch(args);
}
//Ohjelman globaaleja asioita
private Tehtavat tehtavat = new Tehtavat();
private DatePicker dpHaunAlkupvm;
private DatePicker dpHaunLoppupvm;
private TextField tfHakuteksti;
private ComboBox<Tehtava.Status> cbValittuStatus;
private TableView<Tehtava> tvTehtavat = new TableView<>();
private TextField tfOtsikko;
private TextArea taKuvaus;
private DatePicker dpDeadline;
private ComboBox<Tehtava.Status> cbStatus;
private TextField tfValmistumisPaiva;
//Tyylittelyvalintoja ohjelman käyttöön
private final Insets oletusPadding = new Insets(10);
private final ButtonType bttKylla = new ButtonType("Kyllä");
private final ButtonType bttEi = new ButtonType("Ei");
private final Border borderMusta = new Border(new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, new CornerRadii(10), BorderWidths.DEFAULT));
/**
* Päivittää tvTehtavat valittujen suodatusvalintojen mukaisesti
*/
void paivitaTehtavanakyma() {
ObservableList<Tehtava> olTehtavat = FXCollections.observableList(tehtavat.get());
Tehtava.Status valittuStatus = cbValittuStatus.getValue();
String hakuteksti = tfHakuteksti.getText().toLowerCase();
LocalDate alkupvm = dpHaunAlkupvm.getValue();
LocalDate loppupvm = dpHaunLoppupvm.getValue();
Predicate<Tehtava> tehtavaFiltteri = new Predicate<Tehtava>() {
@Override
public boolean test(Tehtava tehtava) {
if (valittuStatus != null && valittuStatus != tehtava.getTila()) {
return false;
}
if (!tehtava.getOtsikko().toLowerCase().contains(hakuteksti) && !tehtava.getKuvaus().toLowerCase().contains(hakuteksti)) {
return false;
}
if ((alkupvm != null && tehtava.getDeadline().isBefore(alkupvm)) || (loppupvm != null && tehtava.getDeadline().isAfter(loppupvm))) {
return false;
}
return true;
}
};
FilteredList<Tehtava> flTehtavat = new FilteredList<>(olTehtavat, tehtavaFiltteri);
tvTehtavat.setItems(flTehtavat);
}
/**
* Asettaa oletusarvot GUI:n tietokenttiin
*/
void nollaaKentat() {
tfOtsikko.clear();
taKuvaus.clear();
dpDeadline.getEditor().clear();
cbStatus.getSelectionModel().select(Tehtava.Status.Luotu);
tfValmistumisPaiva.clear();
}
/**
* Nollaa yläpalkin hakukentät
*/
void nollaaHakukentat() {
dpHaunAlkupvm.setValue(null);
dpHaunLoppupvm.setValue(null);
tfHakuteksti.clear();
cbValittuStatus.getSelectionModel().clearSelection();
paivitaTehtavanakyma();
}
/**
* Asettaa valitun tehtävän tiedot kenttiin
*/
void naytaTehtava() {
Tehtava valittu = tvTehtavat.getSelectionModel().getSelectedItem();
if (valittu == null) return;
tfOtsikko.setText(valittu.getOtsikko());
taKuvaus.setText(valittu.getKuvaus());
dpDeadline.setValue(valittu.getDeadline());
cbStatus.getSelectionModel().select(valittu.getTila());
if (valittu.getTila() == Tehtava.Status.Valmis) {
tfValmistumisPaiva.setText(valittu.getValmistumisPaiva().format(DateTimeFormatter.ofPattern("dd.MM.yyyy")));
} else {
tfValmistumisPaiva.setText("");
}
}
/**
* Tallentaa kenttien tiedot valittuun tehtävään, käytetään tehtävän muutoksien tallentamiseen
*/
void tallennaTehtava() {
Tehtava valittu = tvTehtavat.getSelectionModel().getSelectedItem();
if (valittu == null) return;
valittu.setOtsikko(tfOtsikko.getText());
valittu.setKuvaus(taKuvaus.getText());
valittu.setDeadline(dpDeadline.getValue());
valittu.setTila(cbStatus.getSelectionModel().getSelectedItem());
tvTehtavat.refresh();
}
/**
* Poistaa valitun tehtävän
*/
void poistaTehtava() {
Tehtava valittu = tvTehtavat.getSelectionModel().getSelectedItem();
if (valittu == null) return;
Alert vahvistus = new Alert(Alert.AlertType.CONFIRMATION, "Jatketaanko?", bttKylla, bttEi);
vahvistus.setTitle("Poiston vahvistaminen");
vahvistus.setHeaderText("Olet poistamassa tehtävää\n" + valittu.getOtsikko());
Optional<ButtonType> vastaus = vahvistus.showAndWait();
if (vastaus.isPresent() && vastaus.get() == bttKylla) {
tehtavat.poista(valittu);
paivitaTehtavanakyma();
nollaaKentat();
}
}
/**
* Luo ikkunan, jossa käyttäjä voi luoda uuden tehtävän
*/
void lisaaTehtava() {
Stage stage = new Stage();
stage.setTitle("Tehtävän lisääminen");
stage.setResizable(false);
GridPane rt = new GridPane();
rt.setPadding(oletusPadding);
rt.setVgap(10);
rt.setHgap(10);
rt.setAlignment(Pos.CENTER);
TextField otsikko = new TextField();
rt.addRow(0, new Label("Anna otsikko"), otsikko);
TextArea kuvaus = new TextArea();
kuvaus.setPrefRowCount(2);
rt.addRow(1, new Label("Anna kuvaus"), kuvaus);
DatePicker deadline = new DatePicker(LocalDate.now().minusDays(1));
rt.addRow(2, new Label("Anna deadline"), deadline);
Button lisaaTehtava = new Button("Lisää tehtävä");
Label info = new Label();
info.setStyle("-fx-font-size: 15; -fx-font-weight: bold;");
lisaaTehtava.setMinWidth(80);
lisaaTehtava.setOnAction(e -> {
if (otsikko.getText().isEmpty()) {
info.setText("Lisää otsikko");
}
else if (deadline.getValue().isBefore(LocalDate.now())) {
info.setText("Deadline ei voi olla menneisyydessä");
}
else {
tehtavat.lisaa(new Tehtava(otsikko.getText(), kuvaus.getText(), deadline.getValue()));
stage.close();
paivitaTehtavanakyma();
nollaaHakukentat();
}
});
rt.addRow(3, lisaaTehtava, info);
Scene sc = new Scene(rt);
stage.setScene(sc);
stage.show();
}
/**
* Piirtää ohjelman pääikkunan
* @param primaryStage ohjelman päästage
*/
@Override
public void start(Stage primaryStage) {
//Ikkunamäärittelyt
primaryStage.setTitle("TODO");
primaryStage.setResizable(false);
primaryStage.setWidth(800);
primaryStage.setHeight(600);
primaryStage.setOnCloseRequest(e -> tehtavat.tallenna());
//Ikkunan asettelun pohja
BorderPane root = new BorderPane();
//Yläreunan suodatusvalinnat
HBox ylaPalkki = new HBox();
ylaPalkki.setAlignment(Pos.CENTER_RIGHT);
ylaPalkki.setPadding(oletusPadding);
ylaPalkki.setBorder(borderMusta);
ylaPalkki.setSpacing(5);
//Haku deadlinella
dpHaunAlkupvm = new DatePicker();
dpHaunAlkupvm.setMaxWidth(100);
dpHaunAlkupvm.valueProperty().addListener(e -> paivitaTehtavanakyma());
dpHaunLoppupvm = new DatePicker();
dpHaunLoppupvm.setMaxWidth(100);
dpHaunLoppupvm.valueProperty().addListener(e -> paivitaTehtavanakyma());
ylaPalkki.getChildren().addAll(new Label("Deadline välillä"), dpHaunAlkupvm, new Label("-"), dpHaunLoppupvm);
//Haku otsikosta ja kuvauksesta
tfHakuteksti = new TextField();
tfHakuteksti.textProperty().addListener(e-> paivitaTehtavanakyma());
ylaPalkki.getChildren().addAll(new Label("Haettava teksti:"), tfHakuteksti);
//Statuksella hakemista
cbValittuStatus = new ComboBox<>(FXCollections.observableList(Arrays.asList(Tehtava.Status.values())));
cbValittuStatus.setBackground(new Background(new BackgroundFill(Color.WHITE, new CornerRadii(5), null)));
cbValittuStatus.setBorder(new Border(new BorderStroke(Color.LIGHTGRAY, BorderStrokeStyle.SOLID, null, BorderWidths.DEFAULT)));
cbValittuStatus.getSelectionModel().selectedIndexProperty().addListener(e-> paivitaTehtavanakyma());
Button bResetValittuStatus = new Button("Nollaa");
bResetValittuStatus.setOnAction(e-> nollaaHakukentat());
ylaPalkki.getChildren().addAll(new Label("Status"), cbValittuStatus, bResetValittuStatus);
root.setTop(ylaPalkki);
//ListView keskelle
VBox tehtavaNakyma = new VBox();
TableColumn<Tehtava, LocalDate> tcLuontipaiva = new TableColumn<>("Luontipäivä");
tcLuontipaiva.setMinWidth(100);
tcLuontipaiva.setCellFactory(_ -> new PaivamaaraTableCell());
tcLuontipaiva.setCellValueFactory(new PropertyValueFactory<>("luontiPaiva"));
tcLuontipaiva.setResizable(false);
TableColumn<Tehtava, String> tcOtsikko = new TableColumn<>("Otsikko");
tcOtsikko.setMinWidth(449);
tcOtsikko.setCellValueFactory(new PropertyValueFactory<>("otsikko"));
tcOtsikko.setResizable(false);
TableColumn<Tehtava, LocalDate> tcDeadline = new TableColumn<>("Deadline");
tcDeadline.setMinWidth(100);
tcDeadline.setCellFactory(_ -> new PaivamaaraTableCell());
tcDeadline.setCellValueFactory(new PropertyValueFactory<>("deadline"));
tcDeadline.setResizable(false);
TableColumn<Tehtava, String> tcValmis = new TableColumn<>("Tehtävän tila");
tcValmis.setMinWidth(100);
tcValmis.setCellValueFactory(new PropertyValueFactory<>("tila"));
tcValmis.setResizable(false);
tvTehtavat.getColumns().addAll(tcLuontipaiva, tcOtsikko, tcDeadline, tcValmis);
tvTehtavat.getSelectionModel().selectedIndexProperty().addListener(e->naytaTehtava());
tvTehtavat.setPlaceholder(new Label("Ei tehtäviä\nKokeile lisätä uusia!"));
tehtavaNakyma.getChildren().add(tvTehtavat);
tehtavaNakyma.setPadding(oletusPadding);
root.setCenter(tehtavaNakyma);
paivitaTehtavanakyma();
//Alapuolen toiminnallisuus
int btnLeveys = 100;
GridPane gpAlapalkki = new GridPane();
gpAlapalkki.setHgap(28);
gpAlapalkki.setVgap(5);
Label lbOtsikko = new Label("Otsikko:");
tfOtsikko = new TextField();
tfOtsikko.setMinWidth(450);
Button btPoista = new Button("Poista");
btPoista.setMinWidth(btnLeveys);
btPoista.setOnAction(e->poistaTehtava());
gpAlapalkki.addRow(0, lbOtsikko, tfOtsikko, btPoista);
Label lbKuvaus = new Label("Kuvaus:");
taKuvaus = new TextArea();
taKuvaus.setMinWidth(450);
taKuvaus.setPrefRowCount(2);
Button btLisaa = new Button("Lisää uusi");
btLisaa.setMinWidth(btnLeveys);
btLisaa.setOnAction(e->lisaaTehtava());
gpAlapalkki.addRow(1, lbKuvaus, taKuvaus, btLisaa);
Label lbDeadline = new Label("Deadline");
HBox hbDeadlineJaTila = new HBox();
dpDeadline = new DatePicker();
cbStatus = new ComboBox<>(FXCollections.observableList(Arrays.asList(Tehtava.Status.values())));
tfValmistumisPaiva = new TextField();
tfValmistumisPaiva.setEditable(false);
hbDeadlineJaTila.getChildren().addAll(dpDeadline, new Label("Valmistuspäivä"), tfValmistumisPaiva, new Label("Status"), cbStatus);
hbDeadlineJaTila.setSpacing(5);
hbDeadlineJaTila.setAlignment(Pos.CENTER_LEFT);
Button btTallenna = new Button("Tallenna");
btTallenna.setMinWidth(btnLeveys);
btTallenna.setOnAction(e->tallennaTehtava());
gpAlapalkki.addRow(2, lbDeadline, hbDeadlineJaTila, btTallenna);
gpAlapalkki.setPadding(oletusPadding);
root.setBottom(gpAlapalkki);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
nollaaKentat();
}
}
@@ -0,0 +1,22 @@
package com.example.harjoitustyo;
import javafx.scene.control.TableCell;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
/**
* Kustomoitu TableCell, joka muokkaa sen sisältämän LocalDaten muotoon dd.MM.yyyy
*/
public class PaivamaaraTableCell extends TableCell<Tehtava, LocalDate> {
public PaivamaaraTableCell() {}
@Override
protected void updateItem(LocalDate aika, boolean empty) {
super.updateItem(aika, empty);
if (empty || aika == null) return;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
String muokattuAika = aika.format(formatter);
setText(muokattuAika);
}
}
@@ -0,0 +1,112 @@
package com.example.harjoitustyo;
import java.io.Serializable;
import java.time.LocalDate;
/**
* Ohjelman perustietoluokka
*/
public class Tehtava implements Serializable {
/**
* Enum tehtävän tilan seuraamiseen
*/
public enum Status {
Luotu,
Vaiheessa,
Valmis
}
private static int idCounter = 1;
private final int id;
private String otsikko;
private String kuvaus;
private final LocalDate luontiPaiva;
private LocalDate deadline;
private LocalDate valmistumisPaiva;
private Status tila;
/**
* Luo uuden tehtävän
* @param otsikko Tehtävän otsikko
* @param kuvaus Tehtävän kuvaus
* @param deadline Tehtävän deadline
*/
public Tehtava(String otsikko, String kuvaus, LocalDate deadline) {
this.id = idCounter++;
this.otsikko = otsikko;
this.kuvaus = kuvaus;
this.luontiPaiva = LocalDate.now();
this.deadline = deadline;
this.tila = Status.Luotu;
}
/**
* Asettaa luokan staattisen muuttujan tiettyyn arvoon
* @param idCounter kentän uusi arvo
*/
protected static void setIdCounter(int idCounter) {
Tehtava.idCounter = idCounter;
}
/**
* Asettaa tehtävän tilan ja valmistumisPaiva kentän valmistumispäivään
* @param tila tehtävän uusi tila
*/
public void setTila(Status tila) {
switch (tila) {
case Luotu:
this.tila = Status.Luotu;
this.valmistumisPaiva = null;
break;
case Vaiheessa:
this.tila = Status.Vaiheessa;
this.valmistumisPaiva = null;
break;
case Valmis:
if (this.tila == Status.Valmis) return; //Jos on jo valmis, ei tehdä mitään
this.tila = Status.Valmis;
this.valmistumisPaiva = LocalDate.now();
}
}
public void setOtsikko(String otsikko) {
this.otsikko = otsikko;
}
public void setKuvaus(String kuvaus) {
this.kuvaus = kuvaus;
}
public void setDeadline(LocalDate deadline) {
this.deadline = deadline;
}
public int getId() {
return id;
}
public String getOtsikko() {
return otsikko;
}
public String getKuvaus() {
return kuvaus;
}
public LocalDate getLuontiPaiva() {
return luontiPaiva;
}
public LocalDate getDeadline() {
return deadline;
}
public LocalDate getValmistumisPaiva() {
return valmistumisPaiva;
}
public Status getTila() {
return tila;
}
}
@@ -0,0 +1,90 @@
package com.example.harjoitustyo;
import java.io.*;
import java.util.ArrayList;
/**
* Luokka, joka hoitaa tehtävien lukemisen ja kirjoittamisen tiedostoon
*/
public class Tehtavat {
//Polku datatiedostoon
private final String tiedostoPolku = "tehtavat.dat";
//Tehtävälista
private ArrayList<Tehtava> tehtavat = new ArrayList<>();
/**
* Luo Tehtavat olion, joka sisältää ohjelmassa käsiteltävät tehtävät
* Hakee automaattisesti tiedostosta tehtävät, mikäli niitä on.
*/
public Tehtavat() {
try (FileInputStream fis = new FileInputStream(tiedostoPolku);
ObjectInputStream ois = new ObjectInputStream(fis)) {
Object luettu = ois.readObject();
if (luettu instanceof ArrayList<?>) {
tehtavat = (ArrayList<Tehtava>) luettu;
System.out.println(tehtavat.size() + " tehtävää ladattu tiedostosta");
Tehtava.setIdCounter(getMaxId() + 1);
}
} catch (FileNotFoundException e) {
System.out.println("Tiedostoa ei löytynyt");
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* Palauttaa tehtävälistan
* @return tehtävälistan
*/
public ArrayList<Tehtava> get() {
return tehtavat;
}
/**
* Lisää tehtävän listaan
* @param tehtava lisättävä tehtävä
*/
public void lisaa(Tehtava tehtava) {
tehtavat.add(tehtava);
}
/**
* Poistaa tehtävän
* @param tehtava poistettava tehtava
*/
public void poista(Tehtava tehtava) {
tehtavat.remove(tehtava);
}
/**
* Tallentaa tehtävälistan luokkaan määriteltyyn tiedostoon
*/
public void tallenna() {
try (FileOutputStream fos = new FileOutputStream(tiedostoPolku);
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(this.tehtavat);
System.out.println("Tehtävät tallennettu");
} catch (FileNotFoundException e) {
System.out.println("Tiedostoa ei löytynyt");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Metodi tehtävien suurimman id:n etsimiseen
* @return max(Tehtava.getId())
*/
private int getMaxId() {
int max = 0;
for (Tehtava tehtava : tehtavat) {
if (tehtava.getId() > max) {
max = tehtava.getId();
}
}
return max;
}
}