mirror of
https://github.com/frosch95/SmartCSV.fx.git
synced 2026-04-11 13:38:23 +02:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 55ed382203 | |||
| 81d75a995b | |||
| f5ab096452 | |||
| 18335b9280 | |||
| 4e216b4792 | |||
| 3980e2951f | |||
| 1b39c9a08e | |||
| 0db16c0328 | |||
| bec55b6119 | |||
| 7dac3ce011 | |||
| 449f929f08 | |||
| 9e3561fedb |
4
.github/workflows/build-and-test.yml
vendored
4
.github/workflows/build-and-test.yml
vendored
@@ -9,10 +9,10 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 25
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 25
|
||||||
- name: Grant execute permission for gradlew
|
- name: Grant execute permission for gradlew
|
||||||
run: chmod +x gradlew
|
run: chmod +x gradlew
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ even in a "normal" CSV editor. So I decided to write this simple JavaFX applicat
|
|||||||
|
|
||||||
[Wiki & Documentation](https://github.com/frosch95/SmartCSV.fx/wiki)
|
[Wiki & Documentation](https://github.com/frosch95/SmartCSV.fx/wiki)
|
||||||
|
|
||||||
binary distribution of the [latest release (1.1.0)](https://github.com/frosch95/SmartCSV.fx/releases/download/1.1.0/SmartCSV.fx-1.0.1.zip)
|
binary distribution of the [latest release (1.5.0)](https://github.com/frosch95/SmartCSV.fx/releases/download/1.5.0/SmartCSV.fx-1.5.0.zip)
|
||||||
|
|
||||||
## Talks
|
## Talks
|
||||||
[Introduction](http://javafx.ninja/talks/introduction/)
|
[Introduction](http://javafx.ninja/talks/introduction/)
|
||||||
|
|||||||
63
build.gradle
63
build.gradle
@@ -3,43 +3,50 @@ plugins {
|
|||||||
id 'java'
|
id 'java'
|
||||||
id 'groovy'
|
id 'groovy'
|
||||||
id 'application'
|
id 'application'
|
||||||
id 'org.openjfx.javafxplugin' version '0.0.10'
|
id 'org.openjfx.javafxplugin' version '0.0.14'
|
||||||
|
id 'com.github.ben-manes.versions' version '0.48.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
jcenter()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
sourceCompatibility = 17
|
|
||||||
targetCompatibility = 17
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(25)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
javafx {
|
javafx {
|
||||||
version = "17"
|
version = "25"
|
||||||
modules = [ 'javafx.controls', 'javafx.fxml' ]
|
modules = [ 'javafx.controls', 'javafx.fxml' ]
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api
|
// updated testing libraries
|
||||||
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.8.2'
|
testImplementation group: 'org.junit.platform', name: 'junit-platform-launcher', version: '6.0.0'
|
||||||
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.8.2'
|
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '6.0.0'
|
||||||
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.8.2'
|
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '6.0.0'
|
||||||
testImplementation group: 'org.hamcrest', name: 'hamcrest-library', version: '2.2'
|
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '6.0.0'
|
||||||
testImplementation group: 'org.mockito', name: 'mockito-core', version:'3.1.0'
|
testImplementation group: 'org.hamcrest', name: 'hamcrest', version: '3.0'
|
||||||
implementation group: 'org.apache.groovy', name: 'groovy', version: '4.0.0-rc-1'
|
testImplementation group: 'org.mockito', name: 'mockito-core', version:'5.20.0'
|
||||||
implementation group: 'org.springframework', name:'spring-context', version: '5.3.13'
|
|
||||||
implementation group: 'de.siegmar', name: 'fastcsv', version: '2.1.0'
|
// runtime libraries - bumped to recent stable versions where compatible
|
||||||
implementation group: 'commons-validator', name: 'commons-validator', version: '1.7'
|
implementation group: 'org.apache.groovy', name: 'groovy', version: '5.0.2'
|
||||||
implementation group: 'de.jensd', name: 'fontawesomefx-commons', version: '11.0'
|
implementation group: 'org.springframework', name: 'spring-context', version: '6.2.12'
|
||||||
implementation group: 'de.jensd', name: 'fontawesomefx-fontawesome', version: '4.7.0-11'
|
implementation group: 'de.siegmar', name: 'fastcsv', version: '4.1.0'
|
||||||
implementation group: 'de.jensd', name: 'fontawesomefx-materialdesignfont', version: '1.7.22-11'
|
implementation group: 'commons-validator', name: 'commons-validator', version: '1.10.0'
|
||||||
implementation group: 'de.jensd', name: 'fontawesomefx-materialicons', version: '2.2.0-11'
|
implementation group: 'de.jensd', name: 'fontawesomefx-commons', version: '9.1.2'
|
||||||
implementation group: 'org.controlsfx', name: 'controlsfx', version: '11.1.0'
|
implementation group: 'de.jensd', name: 'fontawesomefx-fontawesome', version: '4.7.0-9.1.2'
|
||||||
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.9'
|
implementation group: 'de.jensd', name: 'fontawesomefx-materialdesignfont', version: '2.0.26-9.1.2'
|
||||||
implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.14.1'
|
implementation group: 'de.jensd', name: 'fontawesomefx-materialicons', version: '2.2.0-9.1.2'
|
||||||
implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.14.1'
|
implementation group: 'org.controlsfx', name: 'controlsfx', version: '11.2.2'
|
||||||
implementation group: 'org.fxmisc.richtext', name: 'richtextfx', version: '0.10.7'
|
implementation group: 'com.google.code.gson', name: 'gson', version: '2.13.2'
|
||||||
|
implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.25.2'
|
||||||
|
implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.25.2'
|
||||||
|
implementation group: 'org.fxmisc.richtext', name: 'richtextfx', version: '0.11.6'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,5 +55,9 @@ test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group 'ninja.javafx'
|
group 'ninja.javafx'
|
||||||
version '1.1.0'
|
version '1.5.0'
|
||||||
mainClassName = 'ninja.javafx.smartcsv.Main'
|
|
||||||
|
application {
|
||||||
|
mainClass = 'ninja.javafx.smartcsv.Main'
|
||||||
|
applicationDefaultJvmArgs = ["--enable-native-access=javafx.graphics"]
|
||||||
|
}
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
|
|
||||||
package ninja.javafx.smartcsv.csv;
|
package ninja.javafx.smartcsv.csv;
|
||||||
|
|
||||||
import de.siegmar.fastcsv.reader.NamedCsvReader;
|
import de.siegmar.fastcsv.reader.CsvReader;
|
||||||
|
import de.siegmar.fastcsv.reader.NamedCsvRecord;
|
||||||
import ninja.javafx.smartcsv.FileReader;
|
import ninja.javafx.smartcsv.FileReader;
|
||||||
import ninja.javafx.smartcsv.fx.table.model.CSVModel;
|
import ninja.javafx.smartcsv.fx.table.model.CSVModel;
|
||||||
|
|
||||||
@@ -49,7 +50,13 @@ public class CSVFileReader extends CSVConfigurable implements FileReader<CSVMode
|
|||||||
model = new CSVModel();
|
model = new CSVModel();
|
||||||
|
|
||||||
// the header columns are used as the keys to the Map
|
// the header columns are used as the keys to the Map
|
||||||
var header = csv.getHeader().toArray(new String[csv.getHeader().size()]);
|
var firstRow = csv.stream().findFirst();
|
||||||
|
|
||||||
|
if (firstRow.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var header = firstRow.get().getHeader().toArray(String[]::new);
|
||||||
model.setHeader(header);
|
model.setHeader(header);
|
||||||
|
|
||||||
csv.forEach(csvRow -> {
|
csv.forEach(csvRow -> {
|
||||||
@@ -66,14 +73,14 @@ public class CSVFileReader extends CSVConfigurable implements FileReader<CSVMode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private NamedCsvReader getNamedCsvReader(File file) throws IOException {
|
private CsvReader<NamedCsvRecord> getNamedCsvReader(File file) throws IOException {
|
||||||
var builder = NamedCsvReader.builder()
|
var builder = CsvReader.builder()
|
||||||
.fieldSeparator(csvPreference.delimiterChar());
|
.fieldSeparator(csvPreference.delimiterChar());
|
||||||
if (csvPreference.quoteChar() != null) {
|
if (csvPreference.quoteChar() != null) {
|
||||||
builder.quoteCharacter(csvPreference.quoteChar());
|
builder.quoteCharacter(csvPreference.quoteChar());
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.build(file.toPath(), Charset.forName(fileEncoding));
|
return builder.ofNamedCsvRecord(file.toPath(), Charset.forName(fileEncoding));
|
||||||
}
|
}
|
||||||
|
|
||||||
public CSVModel getContent() {
|
public CSVModel getContent() {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
package ninja.javafx.smartcsv.csv;
|
package ninja.javafx.smartcsv.csv;
|
||||||
|
|
||||||
import de.siegmar.fastcsv.writer.CsvWriter;
|
import de.siegmar.fastcsv.writer.CsvWriter;
|
||||||
import de.siegmar.fastcsv.writer.QuoteStrategy;
|
import de.siegmar.fastcsv.writer.QuoteStrategies;
|
||||||
import ninja.javafx.smartcsv.fx.table.model.CSVModel;
|
import ninja.javafx.smartcsv.fx.table.model.CSVModel;
|
||||||
import ninja.javafx.smartcsv.fx.table.model.CSVRow;
|
import ninja.javafx.smartcsv.fx.table.model.CSVRow;
|
||||||
|
|
||||||
@@ -52,9 +52,9 @@ public class CSVFileWriter extends CSVConfigurable implements ninja.javafx.smart
|
|||||||
@Override
|
@Override
|
||||||
public void write(File filename) throws IOException {
|
public void write(File filename) throws IOException {
|
||||||
try (var writer = getCsvWriter(filename)){
|
try (var writer = getCsvWriter(filename)){
|
||||||
writer.writeRow(model.getHeader());
|
writer.writeRecord(model.getHeader());
|
||||||
for(CSVRow row: model.getRows()) {
|
for(CSVRow row: model.getRows()) {
|
||||||
writer.writeRow(convertMapFromModel(row));
|
writer.writeRecord(convertMapFromModel(row));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,7 @@ public class CSVFileWriter extends CSVConfigurable implements ninja.javafx.smart
|
|||||||
var writer = CsvWriter.builder().fieldSeparator(csvPreference.delimiterChar());
|
var writer = CsvWriter.builder().fieldSeparator(csvPreference.delimiterChar());
|
||||||
if (csvPreference.quoteChar() != null) {
|
if (csvPreference.quoteChar() != null) {
|
||||||
writer.quoteCharacter(csvPreference.quoteChar());
|
writer.quoteCharacter(csvPreference.quoteChar());
|
||||||
writer.quoteStrategy(QuoteStrategy.ALWAYS);
|
writer.quoteStrategy(QuoteStrategies.ALWAYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
return writer.build(filename.toPath(), Charset.forName(fileEncoding));
|
return writer.build(filename.toPath(), Charset.forName(fileEncoding));
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ import javafx.event.EventHandler;
|
|||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.input.Dragboard;
|
||||||
|
import javafx.scene.input.TransferMode;
|
||||||
import javafx.scene.layout.AnchorPane;
|
import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
@@ -255,6 +257,34 @@ public class SmartCSVController extends FXMLController {
|
|||||||
fileEncodingFile.setFile(ENCODING_FILE);
|
fileEncodingFile.setFile(ENCODING_FILE);
|
||||||
|
|
||||||
loadCsvPreferencesFromFile();
|
loadCsvPreferencesFromFile();
|
||||||
|
|
||||||
|
initDragAndDrop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDragAndDrop() {
|
||||||
|
tableWrapper.setOnDragOver(event -> {
|
||||||
|
Dragboard db = event.getDragboard();
|
||||||
|
if (event.getGestureSource() != tableWrapper
|
||||||
|
&& db.hasFiles()
|
||||||
|
&& db.getFiles().size() == 1
|
||||||
|
&& db.getFiles().get(0).getName().endsWith(".csv")) {
|
||||||
|
event.acceptTransferModes(TransferMode.COPY);
|
||||||
|
}
|
||||||
|
event.consume();
|
||||||
|
});
|
||||||
|
|
||||||
|
tableWrapper.setOnDragDropped(event -> {
|
||||||
|
Dragboard db = event.getDragboard();
|
||||||
|
boolean success = false;
|
||||||
|
if (db.hasFiles() && db.getFiles().size() == 1 && canOpen()) {
|
||||||
|
File file = db.getFiles().get(0);
|
||||||
|
openFile(currentCsvFile, file);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
/* let the source know whether the string was successfully
|
||||||
|
* transferred and used */
|
||||||
|
event.setDropCompleted(success);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadEncodingFromFile() {
|
private void loadEncodingFromFile() {
|
||||||
@@ -473,6 +503,22 @@ public class SmartCSVController extends FXMLController {
|
|||||||
return canExit;
|
return canExit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean canOpen() {
|
||||||
|
boolean canOpen = true;
|
||||||
|
if (currentCsvFile.getContent() != null && currentCsvFile.isFileChanged()) {
|
||||||
|
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
|
||||||
|
alert.setTitle(resourceBundle.getString("dialog.changes.title"));
|
||||||
|
alert.setHeaderText(resourceBundle.getString("dialog.changes.header.text"));
|
||||||
|
alert.setContentText(resourceBundle.getString("dialog.changes.text"));
|
||||||
|
|
||||||
|
Optional<ButtonType> result = alert.showAndWait();
|
||||||
|
if (result.get() != ButtonType.OK){
|
||||||
|
canOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return canOpen;
|
||||||
|
}
|
||||||
|
|
||||||
public void showValidationEditor(String column) {
|
public void showValidationEditor(String column) {
|
||||||
validationEditorController.setSelectedColumn(column);
|
validationEditorController.setSelectedColumn(column);
|
||||||
validationEditorController.updateForm();
|
validationEditorController.updateForm();
|
||||||
@@ -599,6 +645,10 @@ public class SmartCSVController extends FXMLController {
|
|||||||
|
|
||||||
//Show open file dialog
|
//Show open file dialog
|
||||||
File file = fileChooser.showOpenDialog(applicationPane.getScene().getWindow());
|
File file = fileChooser.showOpenDialog(applicationPane.getScene().getWindow());
|
||||||
|
openFile(storageFile, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openFile(FileStorage storageFile, File file) {
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
File previousFile = storageFile.getFile();
|
File previousFile = storageFile.getFile();
|
||||||
storageFile.setFile(file);
|
storageFile.setFile(file);
|
||||||
|
|||||||
@@ -65,8 +65,8 @@ public class EditableValidationCell extends TableCell<CSVRow, CSVValue> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cancelEdit() {
|
public void cancelEdit() {
|
||||||
super.cancelEdit();
|
|
||||||
setText(getItem().getValue());
|
setText(getItem().getValue());
|
||||||
|
super.cancelEdit();
|
||||||
setContentDisplay(ContentDisplay.TEXT_ONLY);
|
setContentDisplay(ContentDisplay.TEXT_ONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,16 +113,11 @@ public class EditableValidationCell extends TableCell<CSVRow, CSVValue> {
|
|||||||
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
|
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
|
||||||
textField.setOnKeyPressed(t -> {
|
textField.setOnKeyPressed(t -> {
|
||||||
if (t.getCode() == KeyCode.ENTER) {
|
if (t.getCode() == KeyCode.ENTER) {
|
||||||
commitEdit(textField.getValue());
|
runLater(() -> commitEdit(textField.getValue()));
|
||||||
} else if (t.getCode() == KeyCode.ESCAPE) {
|
} else if (t.getCode() == KeyCode.ESCAPE) {
|
||||||
cancelEdit();
|
cancelEdit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
textField.focusedProperty().addListener((observable, oldValue, newValue) -> {
|
|
||||||
if (!newValue && textField != null) {
|
|
||||||
commitEdit(textField.getValue());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ValueTextField extends TextField {
|
private static class ValueTextField extends TextField {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
application.name = SmartCSV.fx
|
application.name = SmartCSV.fx
|
||||||
application.version = 1.0.0
|
application.version = 1.4.0
|
||||||
|
|
||||||
# fxml views
|
# fxml views
|
||||||
fxml.smartcvs.view = /ninja/javafx/smartcsv/fx/smartcsv.fxml
|
fxml.smartcvs.view = /ninja/javafx/smartcsv/fx/smartcsv.fxml
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ dialog.exit.title = Close Application
|
|||||||
dialog.exit.header.text = Do you want to close application?
|
dialog.exit.header.text = Do you want to close application?
|
||||||
dialog.exit.text = There are changes made to the csv file. If you close now, the changes are lost!
|
dialog.exit.text = There are changes made to the csv file. If you close now, the changes are lost!
|
||||||
|
|
||||||
|
dialog.changes.title = Discard changes
|
||||||
|
dialog.changes.header.text = Do you want to discard changes?
|
||||||
|
dialog.changes.text = There are changes made to the csv file. If you open a new file, the changes are lost!
|
||||||
|
|
||||||
dialog.preferences.title = Preferences
|
dialog.preferences.title = Preferences
|
||||||
dialog.preferences.header.text = Preferences
|
dialog.preferences.header.text = Preferences
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ dialog.exit.title = Anwendung beenden
|
|||||||
dialog.exit.header.text = M\u00f6chten Sie wirklich die Anwendung beenden?
|
dialog.exit.header.text = M\u00f6chten Sie wirklich die Anwendung beenden?
|
||||||
dialog.exit.text = Es gibt noch ungespeicherte \u00c4nderungen, die verloren gehen, wenn Sie die Anwendung jetzt beenden.
|
dialog.exit.text = Es gibt noch ungespeicherte \u00c4nderungen, die verloren gehen, wenn Sie die Anwendung jetzt beenden.
|
||||||
|
|
||||||
|
dialog.changes.title = Änderungen verwerfen
|
||||||
|
dialog.changes.header.text = Wollen Sie Ihre Änderungen verwerfen?
|
||||||
|
dialog.changes.text = Es gibt noch ungespeicherte \u00c4nderungen, die verloren gehen, wenn Sie die Datei laden.
|
||||||
|
|
||||||
preferences.quoteChar = Einfassungszeichen:
|
preferences.quoteChar = Einfassungszeichen:
|
||||||
preferences.delimiterChar = Trennzeichen:
|
preferences.delimiterChar = Trennzeichen:
|
||||||
preferences.ignoreEmptyLines = Leere Zeilen ignorieren:
|
preferences.ignoreEmptyLines = Leere Zeilen ignorieren:
|
||||||
|
|||||||
Reference in New Issue
Block a user