mirror of
https://codeberg.org/qg-info-unterricht/zpg-graphentester.git
synced 2026-03-24 20:48:26 +01:00
587 lines
19 KiB
Java
587 lines
19 KiB
Java
package control;
|
|
|
|
import imp.*;
|
|
import graph.*;
|
|
import algorithmen.*;
|
|
import java.io.FileReader;
|
|
import java.io.BufferedReader;
|
|
import java.io.IOException;
|
|
import java.io.PrintWriter;
|
|
import java.io.FileWriter;
|
|
|
|
import javafx.fxml.*;
|
|
import javafx.scene.control.*;
|
|
import javafx.scene.control.Alert.AlertType;
|
|
import javafx.event.*;
|
|
import javafx.scene.input.MouseEvent;
|
|
import javafx.scene.layout.*;
|
|
import javafx.scene.Node;
|
|
import javafx.scene.text.*;
|
|
import javafx.geometry.Pos;
|
|
import javafx.stage.*; // Dateiöffnen / Speichern-Dialog
|
|
import java.io.File;
|
|
import java.nio.file.*;
|
|
import javafx.stage.FileChooser.ExtensionFilter;
|
|
import javafx.scene.image.Image;
|
|
import javafx.geometry.Rectangle2D;
|
|
|
|
import java.util.List;
|
|
import java.util.ArrayList;
|
|
import javafx.collections.ObservableList;
|
|
/**
|
|
* Die Klasse Controller stellt den Controller des Hauptfensters / Menu dar.
|
|
*
|
|
* @author Thomas Schaller
|
|
* @version 12.02.2025 (v7.4)
|
|
* v7.0: Die aktuelle Bildschirmposition und der angezeigte Graph werden in config.csv abgelegt.
|
|
* v7.1: Verzeichnisauswahl für Laden/Speichern verbessert
|
|
* v7.4: Unterbrechen von Simulieren-Thread neu geregelt.
|
|
*/
|
|
|
|
public class Controller {
|
|
private String version = "7.4 (Januar 2025)";
|
|
private String pfad; // Pfad der aktuell angezeigten Datei
|
|
|
|
@FXML
|
|
private TabPane tpRekursionen;
|
|
|
|
@FXML
|
|
private CheckMenuItem mOptionKnotenwerte;
|
|
|
|
@FXML
|
|
private CheckMenuItem mOptionKnotenname;
|
|
|
|
@FXML
|
|
private CheckMenuItem mOptionKantengewichte;
|
|
|
|
@FXML
|
|
private CheckMenuItem mOptionKnoteninfo;
|
|
|
|
@FXML
|
|
private CheckMenuItem mOptionBild;
|
|
|
|
@FXML
|
|
private CheckMenuItem mHilfefenster;
|
|
|
|
@FXML
|
|
private MenuItem mmSpeichern;
|
|
|
|
@FXML
|
|
private Menu mmBearbeiten;
|
|
|
|
@FXML
|
|
private Menu mmExperimentieren;
|
|
|
|
@FXML
|
|
private Menu mmSimulieren;
|
|
|
|
@FXML
|
|
private Menu mmZuruecksetzen;
|
|
|
|
@FXML
|
|
private Menu mmAnsicht;
|
|
|
|
private FileChooser dateidialog;
|
|
private Graph graph;
|
|
private GraphOptions options;
|
|
private Stage stage;
|
|
|
|
public void initialize() {
|
|
|
|
dateidialog = new FileChooser();
|
|
dateidialog.setInitialDirectory(new File("beispielgraphen"));
|
|
|
|
oeffneHauptTab();
|
|
mNeuerGraph(null);
|
|
|
|
tpRekursionen.getSelectionModel().selectedItemProperty().
|
|
addListener((value, tabOld, tabNew) -> changeTab(tabOld, tabNew));
|
|
|
|
BufferedReader in =null;
|
|
try{
|
|
in = new BufferedReader(new FileReader("config.csv"));
|
|
String fullScreen = in.readLine();
|
|
String posSize = in.readLine();
|
|
String[] ps = posSize.split(",");
|
|
Rectangle2D ss = Screen.getPrimary().getBounds();
|
|
|
|
stage.setX(Double.parseDouble(ps[0]));
|
|
stage.setY(Double.parseDouble(ps[1]));
|
|
stage.setWidth(Math.min(Double.parseDouble(ps[2]), ss.getWidth()-Double.parseDouble(ps[0])));
|
|
stage.setHeight(Math.min(Double.parseDouble(ps[3]), ss.getHeight()-Double.parseDouble(ps[1])));
|
|
String[] fs = fullScreen.split(",");
|
|
if(fs[0].equals("true")) stage.setFullScreen(true);
|
|
if(fs[1].equals("true")) stage.setMaximized(true);
|
|
pfad = in.readLine();
|
|
File f = new File(pfad);
|
|
f.getCanonicalPath();
|
|
if(!pfad.isBlank() && f.exists()){
|
|
graphLaden(pfad);
|
|
dateidialog.setInitialDirectory((f.getAbsoluteFile()).getParentFile());
|
|
} else {
|
|
pfad = "";
|
|
}
|
|
}
|
|
catch(Exception e) {
|
|
pfad = "";
|
|
dateidialog.setInitialDirectory(new File("beispielgraphen"));
|
|
}
|
|
finally{
|
|
try{if(in != null) in.close();} catch(IOException e) {}
|
|
showTitle();
|
|
}
|
|
|
|
}
|
|
|
|
public void saveAktConfig() {
|
|
PrintWriter pWriter = null;
|
|
String s = "config.csv";
|
|
try {
|
|
pWriter = new PrintWriter(new FileWriter(s));
|
|
pWriter.println(stage.isFullScreen()+","+stage.isMaximized());
|
|
stage.setFullScreen(false);
|
|
stage.setMaximized(false);
|
|
pWriter.println(stage.getX()+","+stage.getY()+","+stage.getWidth()+","+ stage.getHeight());
|
|
pWriter.println(pfad);
|
|
} catch (IOException ioe) {
|
|
ioe.printStackTrace();
|
|
} finally {
|
|
if (pWriter != null) {
|
|
pWriter.flush();
|
|
pWriter.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void changeTab(Tab tabOld, Tab tabNew) {
|
|
if(tpRekursionen.getTabs().indexOf(tabNew)<tpRekursionen.getTabs().size()-2) {
|
|
tpRekursionen.getSelectionModel().select(tabOld);
|
|
} else {
|
|
|
|
if(tpRekursionen.getTabs().indexOf(tabNew)==tpRekursionen.getTabs().size()-2) {
|
|
tpRekursionen.getTabs().remove(tpRekursionen.getTabs().size()-1);
|
|
}
|
|
((TabMitController) tabNew).update();
|
|
this.menuChangeAnsicht();
|
|
}
|
|
}
|
|
|
|
private void oeffneHauptTab() {
|
|
try { // try-catch ist notwendig, um Fehler durch fehlende Dateien abzufangen.
|
|
|
|
HauptTabMitController newtab = new HauptTabMitController(graph, options);
|
|
newtab.setText("Graph");
|
|
tpRekursionen.getTabs().add(newtab);
|
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/haupttab.fxml"));
|
|
loader.setController(newtab);
|
|
newtab.setContent((Node) loader.load());
|
|
tpRekursionen.getSelectionModel().select(newtab);
|
|
|
|
}
|
|
catch(Exception e) {
|
|
// System.out.println(e);
|
|
}
|
|
}
|
|
|
|
public void setStage(Stage s){
|
|
stage = s;
|
|
}
|
|
|
|
@FXML
|
|
void mNeuerGraph(ActionEvent event) {
|
|
while(tpRekursionen.getTabs().size()>1) tpRekursionen.getTabs().remove(1);
|
|
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getTabs().get(0));
|
|
|
|
graph = new Graph();
|
|
options = new GraphOptions(graph);
|
|
tc.setGraph(graph, options);
|
|
pfad = "";
|
|
|
|
showTitle();
|
|
menuChangeAnsicht();
|
|
}
|
|
|
|
void schliesseTabs() {
|
|
while(tpRekursionen.getTabs().size()>1) {
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getTabs().get(1));
|
|
if(tc instanceof SimulationTabMitController) {
|
|
((SimulationTabMitController) tc).showHilfe(false);
|
|
}
|
|
tpRekursionen.getTabs().remove(1);
|
|
}
|
|
}
|
|
|
|
@FXML
|
|
void mBearbeiten(MouseEvent event) {
|
|
|
|
try { // try-catch ist notwendig, um Fehler durch fehlende Dateien abzufangen.
|
|
schliesseTabs();
|
|
EditTabMitController newtab = new EditTabMitController(graph, options);
|
|
newtab.setText("Bearbeiten");
|
|
tpRekursionen.getTabs().add(newtab);
|
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/edittab.fxml"));
|
|
loader.setController(newtab);
|
|
newtab.setContent((Node) loader.load());
|
|
tpRekursionen.getSelectionModel().select(newtab);
|
|
}
|
|
catch(Exception e) {
|
|
//System.out.println(e);
|
|
}
|
|
}
|
|
|
|
@FXML
|
|
void mSimuliere(MouseEvent event) {
|
|
|
|
try { // try-catch ist notwendig, um Fehler durch fehlende Dateien abzufangen.
|
|
schliesseTabs();
|
|
|
|
SimulationTabMitController newtab = new SimulationTabMitController(graph, options);
|
|
newtab.setText("Algorithmen-Simulation");
|
|
tpRekursionen.getTabs().add(newtab);
|
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/simulationstab.fxml"));
|
|
loader.setController(newtab);
|
|
newtab.setContent((Node) loader.load());
|
|
tpRekursionen.getSelectionModel().select(newtab);
|
|
}
|
|
catch(Exception e) {
|
|
// System.out.println(e);
|
|
}
|
|
}
|
|
|
|
@FXML
|
|
void mExperimentiereKanten(ActionEvent event) {
|
|
schliesseTabs();
|
|
|
|
GraphOptions neu = options.copy();
|
|
neu.bildAnzeigen = false;
|
|
neu.auswahl = 0;
|
|
neu.fokusArt = 1;
|
|
neu.parent = null;
|
|
// neu.markiert = Auswahl.BELIEBIG;
|
|
// neu.geloescht = Auswahl.BELIEBIG;
|
|
// neu.besucht = Auswahl.BELIEBIG;
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
tc.tabOeffnen(neu);
|
|
}
|
|
|
|
@FXML
|
|
void mExperimentiereKnoten(ActionEvent event) {
|
|
schliesseTabs();
|
|
|
|
GraphOptions neu = options.copy();
|
|
neu.bildAnzeigen = false;
|
|
neu.auswahl = 0;
|
|
neu.fokusArt = 0;
|
|
neu.parent = null;
|
|
if(neu.farbenKanten[0].equals("invisible")) neu.farbenKanten[0]="808080";
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
tc.tabOeffnen(neu);
|
|
}
|
|
|
|
@FXML
|
|
void mExperimentiereAuswahl(ActionEvent event) {
|
|
schliesseTabs();
|
|
|
|
GraphOptions neu = options.copy();
|
|
neu.bildAnzeigen = false;
|
|
neu.auswahl = 2;
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
|
|
if(tc.viewer.getSelectedKnoten() != null) {
|
|
neu.fokusArt = 0; // Knoten
|
|
neu.parent = tc.viewer.getSelectedKnoten();
|
|
tc.tabOeffnen(neu);
|
|
|
|
} else {
|
|
if(tc.viewer.getSelectedKante() != null) {
|
|
neu.fokusArt = 1; //Kante
|
|
neu.parent = tc.viewer.getSelectedKante();
|
|
tc.tabOeffnen(neu);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
@FXML
|
|
public void mBeenden(ActionEvent event) {
|
|
saveAktConfig();
|
|
schliesseTabs();
|
|
((Stage)tpRekursionen.getScene().getWindow()).close();
|
|
System.exit(0);
|
|
}
|
|
|
|
void menuChangeAnsicht() {
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
GraphOptions options = tc.getGraphOptions();
|
|
mOptionBild.setSelected(options.bildAnzeigen);
|
|
mOptionKantengewichte.setSelected(options.showEdgeWeights);
|
|
mOptionKnotenname.setSelected(options.showVertexText);
|
|
mOptionKnotenwerte.setSelected(options.showVertexValue);
|
|
mOptionKnoteninfo.setSelected(options.showVertexInfo);
|
|
|
|
mmSpeichern.setDisable(!tc.getText().equals("Bearbeiten"));
|
|
/* mmBearbeiten.setDisabled(!tc.getText().equals("Graph"));
|
|
mmExperimentieren.setVisible(tc.getText().equals("Graph"));
|
|
mmSimulieren.setVisible(tc.getText().equals("Graph"));
|
|
mmZuruecksetzen.setVisible(!tc.getText().equals("Bearbeiten"));
|
|
*/
|
|
mmAnsicht.setDisable(tc.getText().equals("Bearbeiten"));
|
|
if(tc.getText().equals("Algorithmen-Simulation")) {
|
|
mHilfefenster.setDisable(false);
|
|
} else
|
|
{
|
|
mHilfefenster.setSelected(false);
|
|
mHilfefenster.setDisable(true);
|
|
}
|
|
|
|
}
|
|
|
|
@FXML
|
|
void mChangeOptionBild(ActionEvent event) {
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
GraphOptions options = tc.getGraphOptions();
|
|
options.bildAnzeigen = mOptionBild.isSelected();
|
|
tc.update();
|
|
}
|
|
|
|
@FXML
|
|
void mChangeOptionKantengewichte(ActionEvent event) {
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
GraphOptions options = tc.getGraphOptions();
|
|
options.showEdgeWeights = mOptionKantengewichte.isSelected();
|
|
tc.update();
|
|
}
|
|
|
|
@FXML
|
|
void mChangeOptionKnoteninfo(ActionEvent event) {
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
GraphOptions options = tc.getGraphOptions();
|
|
options.showVertexInfo = mOptionKnoteninfo.isSelected();
|
|
tc.update();
|
|
}
|
|
|
|
@FXML
|
|
void mChangeOptionKnotenname(ActionEvent event) {
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
GraphOptions options = tc.getGraphOptions();
|
|
options.showVertexText = mOptionKnotenname.isSelected();
|
|
if(options.showVertexText) {
|
|
options.showVertexValue = false;
|
|
mOptionKnotenwerte.setSelected(false);
|
|
}
|
|
tc.update();
|
|
}
|
|
|
|
@FXML
|
|
void mChangeOptionKnotenwerte(ActionEvent event) {
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
GraphOptions options = tc.getGraphOptions();
|
|
options.showVertexValue = mOptionKnotenwerte.isSelected();
|
|
if(options.showVertexValue) {
|
|
options.showVertexText = false;
|
|
mOptionKnotenname.setSelected(false);
|
|
}
|
|
tc.update();
|
|
}
|
|
|
|
@FXML
|
|
void mChangeHilfefenster(ActionEvent event) {
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getTabs().get(tpRekursionen.getTabs().size()-1));
|
|
if(tc instanceof SimulationTabMitController) {
|
|
((SimulationTabMitController) tc).showHilfe(mHilfefenster.isSelected());
|
|
}
|
|
}
|
|
|
|
@FXML
|
|
void mOeffnen(ActionEvent event) {
|
|
dateidialog.getExtensionFilters().clear();
|
|
dateidialog.getExtensionFilters().add(new ExtensionFilter("Graph-Datei (*.csv)", "*.csv"));
|
|
|
|
File file = dateidialog.showOpenDialog(null);
|
|
if (file != null) {
|
|
graphLaden(file.getAbsolutePath());
|
|
dateidialog.setInitialDirectory(file.getAbsoluteFile().getParentFile());
|
|
}
|
|
}
|
|
|
|
void graphLaden(String p) {
|
|
|
|
while(tpRekursionen.getTabs().size()>2) tpRekursionen.getTabs().remove(1);
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getTabs().get(0));
|
|
|
|
File f = new File(p);
|
|
if(f.exists() ){
|
|
pfad = p;
|
|
|
|
Table csvParser = new Table(pfad,"",',','"');
|
|
|
|
graph = new Graph();
|
|
graph.ladeGraph(csvParser);
|
|
|
|
options = new GraphOptions(graph);
|
|
options.ladeGraph(csvParser);
|
|
|
|
tc.setGraph(graph, options);
|
|
|
|
if(tpRekursionen.getTabs().size()>1){
|
|
tc = (TabMitController) (tpRekursionen.getTabs().get(1));
|
|
tc.setGraph(graph, options);
|
|
}
|
|
}
|
|
|
|
menuChangeAnsicht();
|
|
showTitle();
|
|
|
|
}
|
|
|
|
public void showTitle() {
|
|
|
|
if(stage!=null) {
|
|
if(pfad == null || pfad.equals("")) {
|
|
stage.setTitle("GraphTester by Thomas Schaller - Version "+version);
|
|
} else {
|
|
String[] arr = pfad.split("[/\\\\]");
|
|
String dateiname = arr[arr.length-1];
|
|
stage.setTitle("GraphTester by Thomas Schaller - Version "+version+" - "+dateiname);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
@FXML
|
|
void mSchliessen(ActionEvent event) {
|
|
mNeuerGraph(event);
|
|
}
|
|
|
|
@FXML
|
|
void mSpeichern(ActionEvent event) {
|
|
dateidialog.getExtensionFilters().clear();
|
|
dateidialog.getExtensionFilters().add(new ExtensionFilter("Graph-Datei (*.csv)", "*.csv"));
|
|
if(!pfad.isBlank())
|
|
dateidialog.setInitialFileName(new File(pfad).getName());
|
|
else
|
|
dateidialog.setInitialFileName("");
|
|
|
|
File file = dateidialog.showSaveDialog(null);
|
|
if (file != null) {
|
|
try{
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
GraphOptions options = tc.getGraphOptions();
|
|
String text = options.getText();
|
|
text += "#\n# Graph:\n";
|
|
text +=graph.toCSVString(options.saveAsMatrix);
|
|
String dateiName = file.getAbsolutePath();
|
|
String name = dateiName.substring(dateiName.lastIndexOf("\\")+1);
|
|
if(name.contains(".")) dateiName = dateiName.substring(0, dateiName.lastIndexOf("."));
|
|
Files.write(Paths.get(file.getAbsolutePath()), text.getBytes());
|
|
pfad = file.getAbsolutePath();
|
|
dateidialog.setInitialDirectory(file.getAbsoluteFile().getParentFile());
|
|
showTitle();
|
|
} catch(Exception e) {
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
@FXML
|
|
void mBildExportieren(ActionEvent event) {
|
|
dateidialog.getExtensionFilters().clear();
|
|
dateidialog.getExtensionFilters().add(new ExtensionFilter("Bild des Graphen", "*.png","*.gif"));
|
|
File file = dateidialog.showSaveDialog(null);
|
|
if (file != null) {
|
|
try{
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
Picture p = tc.getViewer().updateImage();
|
|
String dateiName = file.getAbsolutePath();
|
|
p.save(dateiName);
|
|
|
|
} catch(Exception e) {
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
@FXML
|
|
void mUeber(ActionEvent event) {
|
|
Alert alert = new Alert(AlertType.INFORMATION);
|
|
alert.setTitle("Graphtester");
|
|
alert.setHeaderText(null);
|
|
alert.setContentText("Mit diesem Programm können Sie Graphen modellieren, Graphenalgorithmen von Hand durchspielen oder implementierte Algorithmen schrittweise ausführen.\n\n"+
|
|
"Version: "+version+"\nThomas Schaller\nLandesfachgruppe Informatik (ZSL BW)\n"+
|
|
"Lizenz: CC BY-NC 4.0 (https://creativecommons.org/licenses/by-nc/4.0/deed.de)\n\n"+
|
|
"3rd Party:\n CommonsIO.jar, csv.jar, JDom.jar\n Lizenzinfo: siehe Ordner '+libs'" );
|
|
Image icon = new Image("view/icon.png");
|
|
Stage stage = (Stage) alert.getDialogPane().getScene().getWindow();
|
|
stage.getIcons().add(icon);
|
|
alert.showAndWait();
|
|
}
|
|
|
|
@FXML
|
|
void mResetAlles(ActionEvent event) {
|
|
graph.initialisiereAlleKanten();
|
|
graph.initialisiereAlleKnoten();
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
tc.update();
|
|
}
|
|
|
|
@FXML
|
|
void mResetBesucht(ActionEvent event) {
|
|
for(Knoten k : graph.getAlleKnoten()) {
|
|
k.setBesucht(false);
|
|
}
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
tc.update();
|
|
}
|
|
|
|
@FXML
|
|
void mResetFarbe(ActionEvent event) {
|
|
for(Knoten k : graph.getAlleKnoten()) {
|
|
k.setFarbeAutomatisch(true);
|
|
}
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
tc.update();
|
|
}
|
|
|
|
@FXML
|
|
void mResetGeloescht(ActionEvent event) {
|
|
for(Kante k : graph.getAlleKanten()) {
|
|
k.setGeloescht(false);
|
|
}
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
tc.update();
|
|
|
|
}
|
|
|
|
@FXML
|
|
void mResetKantenmarkierung(ActionEvent event) {
|
|
for(Kante k : graph.getAlleKanten()) {
|
|
k.setMarkiert(false);
|
|
}
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
tc.update();
|
|
|
|
}
|
|
|
|
@FXML
|
|
void mResetMarkierung(ActionEvent event) {
|
|
for(Knoten k : graph.getAlleKnoten()) {
|
|
k.setMarkiert(false);
|
|
}
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
tc.update();
|
|
|
|
}
|
|
|
|
@FXML
|
|
void mResetWert(ActionEvent event) {
|
|
for(Knoten k : graph.getAlleKnoten()) {
|
|
k.setWert(0.0);
|
|
}
|
|
TabMitController tc = (TabMitController) (tpRekursionen.getSelectionModel().getSelectedItem());
|
|
tc.update();
|
|
|
|
}
|
|
|
|
}
|