add export search results as csv functionality

This commit is contained in:
Craig Raw 2024-04-17 14:01:55 +02:00
parent d3752a856b
commit f245b57022
2 changed files with 83 additions and 2 deletions

View file

@ -1,19 +1,27 @@
package com.sparrowwallet.sparrow.control; package com.sparrowwallet.sparrow.control;
import com.csvreader.CsvWriter;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.drongo.Utils;
import com.sparrowwallet.drongo.address.Address; import com.sparrowwallet.drongo.address.Address;
import com.sparrowwallet.drongo.address.InvalidAddressException; import com.sparrowwallet.drongo.address.InvalidAddressException;
import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.glyphfont.GlyphUtils;
import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.wallet.*; import com.sparrowwallet.sparrow.wallet.*;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.event.ActionEvent;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.controlsfx.control.textfield.TextFields; import org.controlsfx.control.textfield.TextFields;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -21,6 +29,10 @@ import tornadofx.control.Field;
import tornadofx.control.Fieldset; import tornadofx.control.Fieldset;
import tornadofx.control.Form; import tornadofx.control.Form;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
public class SearchWalletDialog extends Dialog<Entry> { public class SearchWalletDialog extends Dialog<Entry> {
@ -129,10 +141,18 @@ public class SearchWalletDialog extends Dialog<Entry> {
vBox.getChildren().addAll(form, results); vBox.getChildren().addAll(form, results);
dialogPane.setContent(vBox); dialogPane.setContent(vBox);
ButtonType exportButtonType = new ButtonType("Export CSV", ButtonBar.ButtonData.LEFT);
ButtonType showButtonType = new javafx.scene.control.ButtonType("Show", ButtonBar.ButtonData.APPLY); ButtonType showButtonType = new javafx.scene.control.ButtonType("Show", ButtonBar.ButtonData.APPLY);
ButtonType cancelButtonType = new javafx.scene.control.ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE); ButtonType cancelButtonType = new javafx.scene.control.ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE);
dialogPane.getButtonTypes().addAll(cancelButtonType, showButtonType); dialogPane.getButtonTypes().addAll(exportButtonType, cancelButtonType, showButtonType);
Button exportButton = (Button)dialogPane.lookupButton(exportButtonType);
exportButton.setGraphic(GlyphUtils.getDownArrowGlyph());
exportButton.addEventFilter(ActionEvent.ACTION, event -> {
event.consume();
exportResults(showWallet);
});
Button showButton = (Button)dialogPane.lookupButton(showButtonType); Button showButton = (Button)dialogPane.lookupButton(showButtonType);
showButton.setDefaultButton(true); showButton.setDefaultButton(true);
@ -288,6 +308,60 @@ public class SearchWalletDialog extends Dialog<Entry> {
return inputString; return inputString;
} }
public void exportResults(boolean showWallet) {
Stage window = new Stage();
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Export search results to CSV");
fileChooser.setInitialFileName(getDialogPane().getHeaderText() + ".csv");
AppServices.moveToActiveWindowScreen(window, 800, 450);
File file = fileChooser.showSaveDialog(window);
if(file != null) {
try(FileOutputStream outputStream = new FileOutputStream(file)) {
CsvWriter writer = new CsvWriter(outputStream, ',', StandardCharsets.UTF_8);
List<String> headers = new ArrayList<>(List.of("Wallet", "Account", "Type", "Date", "Txid / Address / Output", "Label", "Value"));
if(!showWallet) {
headers.remove(0);
}
writer.writeRecord(headers.toArray(new String[0]));
for(TreeItem<Entry> item : results.getRoot().getChildren()) {
Entry entry = item.getValue();
if(showWallet) {
writer.write(entry.getWallet().getMasterName());
}
writer.write(entry.getWallet().getDisplayName());
writer.write(entry.getEntryType());
if(entry instanceof TransactionEntry transactionEntry) {
writer.write(transactionEntry.getBlockTransaction().getDate() == null ? "Unconfirmed" : EntryCell.DATE_FORMAT.format(transactionEntry.getBlockTransaction().getDate()));
writer.write(transactionEntry.getBlockTransaction().getHash().toString());
} else if(entry instanceof NodeEntry nodeEntry) {
writer.write("");
writer.write(nodeEntry.getAddress().toString());
} else if(entry instanceof HashIndexEntry hashIndexEntry) {
writer.write(hashIndexEntry.getBlockTransaction().getDate() == null ? "Unconfirmed" : EntryCell.DATE_FORMAT.format(hashIndexEntry.getBlockTransaction().getDate()));
writer.write(hashIndexEntry.getHashIndex().toString());
} else {
writer.write("");
writer.write("");
}
writer.write(entry.getLabel());
writer.write(getCoinValue(entry.getValue() == null ? 0 : entry.getValue()));
writer.endRecord();
}
writer.close();
} catch(IOException e) {
log.error("Error exporting search results as CSV", e);
AppServices.showErrorDialog("Error exporting search results as CSV", e.getMessage());
}
}
}
private String getCoinValue(Long value) {
UnitFormat format = Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat();
return BitcoinUnit.BTC.equals(results.getBitcoinUnit()) ? format.tableFormatBtcValue(value) : String.format(Locale.ENGLISH, "%d", value);
}
private static class SearchWalletEntry extends Entry { private static class SearchWalletEntry extends Entry {
public SearchWalletEntry(Wallet wallet, List<Entry> entries) { public SearchWalletEntry(Wallet wallet, List<Entry> entries) {
super(wallet, wallet.getName(), entries); super(wallet, wallet.getName(), entries);

View file

@ -203,4 +203,11 @@ public class GlyphUtils {
busyGlyph.setFontSize(12); busyGlyph.setFontSize(12);
return busyGlyph; return busyGlyph;
} }
public static Glyph getDownArrowGlyph() {
Glyph downGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.ARROW_DOWN);
downGlyph.getStyleClass().add("arrow-down");
downGlyph.setFontSize(12);
return downGlyph;
}
} }