add unit format menu selection for alternative grouping and decimal separators

This commit is contained in:
Craig Raw 2022-09-21 11:36:13 +02:00
parent 8270eb71db
commit 8fb6de85f1
33 changed files with 408 additions and 165 deletions

View file

@ -132,6 +132,9 @@ public class AppController implements Initializable {
@FXML
private ToggleGroup bitcoinUnit;
@FXML
private ToggleGroup unitFormat;
@FXML
private ToggleGroup theme;
@ -325,6 +328,15 @@ public class AppController implements Initializable {
Optional<Toggle> selectedUnitToggle = bitcoinUnit.getToggles().stream().filter(toggle -> selectedUnit.equals(toggle.getUserData())).findFirst();
selectedUnitToggle.ifPresent(toggle -> bitcoinUnit.selectToggle(toggle));
UnitFormat format = Config.get().getUnitFormat();
if(format == null) {
format = UnitFormat.DOT;
Config.get().setUnitFormat(format);
}
final UnitFormat selectedFormat = format;
Optional<Toggle> selectedFormatToggle = unitFormat.getToggles().stream().filter(toggle -> selectedFormat.equals(toggle.getUserData())).findFirst();
selectedFormatToggle.ifPresent(toggle -> unitFormat.selectToggle(toggle));
Theme configTheme = Config.get().getTheme();
if(configTheme == null) {
configTheme = Theme.LIGHT;
@ -858,6 +870,13 @@ public class AppController implements Initializable {
EventManager.get().post(new BitcoinUnitChangedEvent(unit));
}
public void setUnitFormat(ActionEvent event) {
MenuItem item = (MenuItem)event.getSource();
UnitFormat format = (UnitFormat)item.getUserData();
Config.get().setUnitFormat(format);
EventManager.get().post(new UnitFormatChangedEvent(format));
}
public void preventSleep(ActionEvent event) {
CheckMenuItem item = (CheckMenuItem)event.getSource();
Config.get().setPreventSleep(item.isSelected());
@ -2698,6 +2717,12 @@ public class AppController implements Initializable {
selectedToggle.ifPresent(toggle -> bitcoinUnit.selectToggle(toggle));
}
@Subscribe
public void unitFormatChanged(UnitFormatChangedEvent event) {
Optional<Toggle> selectedToggle = unitFormat.getToggles().stream().filter(toggle -> event.getUnitFormat().equals(toggle.getUserData())).findFirst();
selectedToggle.ifPresent(toggle -> unitFormat.selectToggle(toggle));
}
@Subscribe
public void openWalletsInNewWindowsStatusChanged(OpenWalletsNewWindowsStatusEvent event) {
openWalletsInNewWindows.setSelected(event.isOpenWalletsInNewWindows());

View file

@ -0,0 +1,86 @@
package com.sparrowwallet.sparrow;
import com.sparrowwallet.drongo.protocol.Transaction;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
public enum UnitFormat {
DOT {
private final DecimalFormat btcFormat = new DecimalFormat("0", DecimalFormatSymbols.getInstance(getLocale()));
private final DecimalFormat tableBtcFormat = new DecimalFormat("0.00000000", DecimalFormatSymbols.getInstance(getLocale()));
private final DecimalFormat currencyFormat = new DecimalFormat("#,##0.00", DecimalFormatSymbols.getInstance(getLocale()));
public DecimalFormat getBtcFormat() {
btcFormat.setMaximumFractionDigits(8);
return btcFormat;
}
public DecimalFormat getTableBtcFormat() {
return tableBtcFormat;
}
public DecimalFormat getCurrencyFormat() {
return currencyFormat;
}
public Locale getLocale() {
return Locale.ENGLISH;
}
},
COMMA {
private final DecimalFormat btcFormat = new DecimalFormat("0", DecimalFormatSymbols.getInstance(getLocale()));
private final DecimalFormat tableBtcFormat = new DecimalFormat("0.00000000", DecimalFormatSymbols.getInstance(getLocale()));
private final DecimalFormat currencyFormat = new DecimalFormat("#,##0.00", DecimalFormatSymbols.getInstance(getLocale()));
public DecimalFormat getBtcFormat() {
btcFormat.setMaximumFractionDigits(8);
return btcFormat;
}
public DecimalFormat getTableBtcFormat() {
return tableBtcFormat;
}
public DecimalFormat getCurrencyFormat() {
return currencyFormat;
}
public Locale getLocale() {
return Locale.GERMAN;
}
};
public abstract Locale getLocale();
public abstract DecimalFormat getBtcFormat();
public abstract DecimalFormat getTableBtcFormat();
public abstract DecimalFormat getCurrencyFormat();
public String formatBtcValue(Long amount) {
return getBtcFormat().format(amount.doubleValue() / Transaction.SATOSHIS_PER_BITCOIN);
}
public String formatSatsValue(Long amount) {
return String.format(getLocale(), "%,d", amount);
}
public String formatCurrencyValue(double amount) {
return getCurrencyFormat().format(amount);
}
public DecimalFormatSymbols getDecimalFormatSymbols() {
return DecimalFormatSymbols.getInstance(getLocale());
}
public String getGroupingSeparator() {
return Character.toString(getDecimalFormatSymbols().getGroupingSeparator());
}
public String getDecimalSeparator() {
return Character.toString(getDecimalFormatSymbols().getDecimalSeparator());
}
}

View file

@ -20,7 +20,7 @@ import java.util.*;
public class AddressTreeTable extends CoinTreeTable {
public void initialize(NodeEntry rootEntry) {
getStyleClass().add("address-treetable");
setBitcoinUnit(rootEntry.getWallet());
setUnitFormat(rootEntry.getWallet());
String address = rootEntry.getAddress().toString();
updateAll(rootEntry);
@ -114,7 +114,7 @@ public class AddressTreeTable extends CoinTreeTable {
}
public void updateAll(NodeEntry rootEntry) {
setBitcoinUnit(rootEntry.getWallet());
setUnitFormat(rootEntry.getWallet());
RecursiveTreeItem<Entry> rootItem = new RecursiveTreeItem<>(rootEntry, Entry::getChildren);
setRoot(rootItem);

View file

@ -3,6 +3,7 @@ package com.sparrowwallet.sparrow.control;
import com.google.common.collect.Lists;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.wallet.Entry;
import com.sparrowwallet.sparrow.wallet.TransactionEntry;
@ -35,8 +36,7 @@ public class BalanceChart extends LineChart<Number, Number> {
getData().add(balanceSeries);
update(walletTransactionsEntry);
BitcoinUnit unit = Config.get().getBitcoinUnit();
setBitcoinUnit(walletTransactionsEntry.getWallet(), unit);
setUnitFormat(walletTransactionsEntry.getWallet(), Config.get().getUnitFormat(), Config.get().getBitcoinUnit());
}
public void update(WalletTransactionsEntry walletTransactionsEntry) {
@ -116,12 +116,16 @@ public class BalanceChart extends LineChart<Number, Number> {
}
}
public void setBitcoinUnit(Wallet wallet, BitcoinUnit unit) {
public void setUnitFormat(Wallet wallet, UnitFormat format, BitcoinUnit unit) {
if(format == null) {
format = UnitFormat.DOT;
}
if(unit == null || unit.equals(BitcoinUnit.AUTO)) {
unit = wallet.getAutoUnit();
}
NumberAxis yaxis = (NumberAxis)getYAxis();
yaxis.setTickLabelFormatter(new CoinAxisFormatter(yaxis, unit));
yaxis.setTickLabelFormatter(new CoinAxisFormatter(yaxis, format, unit));
}
}

View file

@ -1,28 +1,31 @@
package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.sparrow.UnitFormat;
import javafx.scene.chart.NumberAxis;
import javafx.util.StringConverter;
import java.text.ParseException;
final class CoinAxisFormatter extends StringConverter<Number> {
private final UnitFormat unitFormat;
private final BitcoinUnit bitcoinUnit;
public CoinAxisFormatter(NumberAxis axis, BitcoinUnit unit) {
public CoinAxisFormatter(NumberAxis axis, UnitFormat format, BitcoinUnit unit) {
this.unitFormat = format;
this.bitcoinUnit = unit;
}
@Override
public String toString(Number object) {
Double value = bitcoinUnit.getValue(object.longValue());
return CoinTextFormatter.COIN_FORMAT.format(value);
return new CoinTextFormatter(unitFormat).getCoinFormat().format(value);
}
@Override
public Number fromString(String string) {
try {
Number number = CoinTextFormatter.COIN_FORMAT.parse(string);
Number number = new CoinTextFormatter(unitFormat).getCoinFormat().parse(string);
return bitcoinUnit.getSatsValue(number.doubleValue());
} catch (ParseException e) {
throw new RuntimeException(e);

View file

@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.wallet.Entry;
import com.sparrowwallet.sparrow.wallet.HashIndexEntry;
import com.sparrowwallet.sparrow.wallet.TransactionEntry;
@ -14,12 +15,8 @@ import javafx.util.Duration;
import org.controlsfx.tools.Platform;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
class CoinCell extends TreeTableCell<Entry, Number> {
public static final DecimalFormat TABLE_BTC_FORMAT = new DecimalFormat("0.00000000", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
private final Tooltip tooltip;
public CoinCell() {
@ -45,10 +42,11 @@ class CoinCell extends TreeTableCell<Entry, Number> {
EntryCell.applyRowStyles(this, entry);
CoinTreeTable coinTreeTable = (CoinTreeTable)getTreeTableView();
UnitFormat format = coinTreeTable.getUnitFormat();
BitcoinUnit unit = coinTreeTable.getBitcoinUnit();
String satsValue = String.format(Locale.ENGLISH, "%,d", amount.longValue());
DecimalFormat decimalFormat = (amount.longValue() == 0L ? CoinLabel.getBTCFormat() : TABLE_BTC_FORMAT);
String satsValue = format.formatSatsValue(amount.longValue());
DecimalFormat decimalFormat = (amount.longValue() == 0L ? format.getBtcFormat() : format.getTableBtcFormat());
final String btcValue = decimalFormat.format(amount.doubleValue() / Transaction.SATOSHIS_PER_BITCOIN);
if(unit.equals(BitcoinUnit.BTC)) {
@ -61,8 +59,7 @@ class CoinCell extends TreeTableCell<Entry, Number> {
setTooltip(tooltip);
String tooltipValue = tooltip.getText();
if(entry instanceof TransactionEntry) {
TransactionEntry transactionEntry = (TransactionEntry)entry;
if(entry instanceof TransactionEntry transactionEntry) {
tooltip.setText(tooltipValue + " (" + transactionEntry.getConfirmationsDescription() + ")");
transactionEntry.confirmationsProperty().addListener((observable, oldValue, newValue) -> {

View file

@ -1,7 +1,7 @@
package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.io.Config;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
@ -12,13 +12,7 @@ import javafx.scene.control.Tooltip;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
public class CoinLabel extends Label {
public static final DecimalFormat BTC_FORMAT = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
private final LongProperty valueProperty = new SimpleLongProperty(-1);
private final Tooltip tooltip;
private final CoinContextMenu contextMenu;
@ -58,8 +52,9 @@ public class CoinLabel extends Label {
setTooltip(tooltip);
setContextMenu(contextMenu);
String satsValue = String.format(Locale.ENGLISH, "%,d", value) + " sats";
String btcValue = BTC_FORMAT.format(value.doubleValue() / Transaction.SATOSHIS_PER_BITCOIN) + " BTC";
UnitFormat format = Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat();
String satsValue = format.formatSatsValue(value) + " sats";
String btcValue = format.formatBtcValue(value) + " BTC";
BitcoinUnit unit = bitcoinUnit;
if(unit == null || unit.equals(BitcoinUnit.AUTO)) {
@ -89,16 +84,12 @@ public class CoinLabel extends Label {
copyBtcValue.setOnAction(AE -> {
hide();
ClipboardContent content = new ClipboardContent();
content.putString(BTC_FORMAT.format((double)getValue() / Transaction.SATOSHIS_PER_BITCOIN));
UnitFormat format = Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat();
content.putString(format.formatBtcValue(getValue()));
Clipboard.getSystemClipboard().setContent(content);
});
getItems().addAll(copySatsValue, copyBtcValue);
}
}
public static DecimalFormat getBTCFormat() {
BTC_FORMAT.setMaximumFractionDigits(8);
return BTC_FORMAT;
}
}

View file

@ -1,24 +1,39 @@
package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.sparrow.UnitFormat;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextInputControl;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.ParseException;
import java.util.Locale;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
public class CoinTextFormatter extends TextFormatter<String> {
private static final Pattern COIN_VALIDATION = Pattern.compile("[\\d,]*(\\.\\d{0,8})?");
public static final DecimalFormat COIN_FORMAT = new DecimalFormat("###,###.########", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
public CoinTextFormatter(UnitFormat unitFormat) {
super(new CoinFilter(unitFormat));
}
public CoinTextFormatter() {
super(new CoinFilter());
public UnitFormat getUnitFormat() {
return ((CoinFilter)getFilter()).unitFormat;
}
public DecimalFormat getCoinFormat() {
return ((CoinFilter)getFilter()).coinFormat;
}
private static class CoinFilter implements UnaryOperator<Change> {
private final UnitFormat unitFormat;
private final DecimalFormat coinFormat;
private final Pattern coinValidation;
public CoinFilter(UnitFormat unitFormat) {
this.unitFormat = unitFormat == null ? UnitFormat.DOT : unitFormat;
this.coinFormat = new DecimalFormat("###,###.########", DecimalFormatSymbols.getInstance(unitFormat.getLocale()));
this.coinValidation = Pattern.compile("[\\d" + Pattern.quote(unitFormat.getGroupingSeparator()) + "]*(" + Pattern.quote(unitFormat.getDecimalSeparator()) + "\\d{0,8})?");
}
@Override
public Change apply(Change change) {
String oldText = change.getControlText();
@ -30,17 +45,17 @@ public class CoinTextFormatter extends TextFormatter<String> {
String noFractionCommaText = newText;
int commasRemoved = 0;
int dotIndex = newText.indexOf(".");
int dotIndex = newText.indexOf(unitFormat.getDecimalSeparator());
if(dotIndex > -1) {
noFractionCommaText = newText.substring(0, dotIndex) + newText.substring(dotIndex).replaceAll(",", "");
noFractionCommaText = newText.substring(0, dotIndex) + newText.substring(dotIndex).replaceAll(Pattern.quote(unitFormat.getGroupingSeparator()), "");
commasRemoved = newText.length() - noFractionCommaText.length();
}
if(!COIN_VALIDATION.matcher(noFractionCommaText).matches()) {
if(!coinValidation.matcher(noFractionCommaText).matches()) {
return null;
}
if(",".equals(change.getText())) {
if(unitFormat.getGroupingSeparator().equals(change.getText())) {
return null;
}
@ -48,20 +63,20 @@ public class CoinTextFormatter extends TextFormatter<String> {
return change;
}
if(change.isDeleted() && ",".equals(deleted) && change.getRangeStart() > 0) {
if(change.isDeleted() && unitFormat.getGroupingSeparator().equals(deleted) && change.getRangeStart() > 0) {
noFractionCommaText = noFractionCommaText.substring(0, change.getRangeStart() - 1) + noFractionCommaText.substring(change.getRangeEnd() - 1);
}
try {
Number value = COIN_FORMAT.parse(noFractionCommaText);
String correct = COIN_FORMAT.format(value.doubleValue());
Number value = coinFormat.parse(noFractionCommaText);
String correct = coinFormat.format(value.doubleValue());
String compare = newText;
if(compare.contains(".") && compare.endsWith("0")) {
if(compare.contains(unitFormat.getDecimalSeparator()) && compare.endsWith("0")) {
compare = compare.replaceAll("0*$", "");
}
if(compare.endsWith(".")) {
if(compare.endsWith(unitFormat.getDecimalSeparator())) {
compare = compare.substring(0, compare.length() - 1);
}
@ -79,11 +94,11 @@ public class CoinTextFormatter extends TextFormatter<String> {
if(correct.length() != newText.length()) {
String postCorrect = correct.substring(Math.min(change.getCaretPosition(), correct.length()));
int commasAfter = postCorrect.length() - postCorrect.replace(",", "").length();
int caretShift = change.isDeleted() && ".".equals(deleted) ? commasAfter : 0;
int commasAfter = postCorrect.length() - postCorrect.replace(unitFormat.getGroupingSeparator(), "").length();
int caretShift = change.isDeleted() && unitFormat.getDecimalSeparator().equals(deleted) ? commasAfter : 0;
int caret = change.getCaretPosition() + (correct.length() - newText.length() - caretShift) + commasRemoved;
if(caret >= 0) {
if(caret >= 0 && caret <= change.getControlNewText().length()) {
change.setCaretPosition(caret);
change.setAnchor(caret);
}

View file

@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.WalletAddressesChangedEvent;
@ -26,25 +27,36 @@ import java.util.Optional;
public class CoinTreeTable extends TreeTableView<Entry> {
private BitcoinUnit bitcoinUnit;
private UnitFormat unitFormat;
public BitcoinUnit getBitcoinUnit() {
return bitcoinUnit;
}
public void setBitcoinUnit(BitcoinUnit bitcoinUnit) {
this.bitcoinUnit = bitcoinUnit;
public UnitFormat getUnitFormat() {
return unitFormat;
}
public void setBitcoinUnit(Wallet wallet) {
setBitcoinUnit(wallet, Config.get().getBitcoinUnit());
public void setUnitFormat(Wallet wallet) {
setUnitFormat(wallet, Config.get().getUnitFormat(), Config.get().getBitcoinUnit());
}
public void setBitcoinUnit(Wallet wallet, BitcoinUnit unit) {
public void setUnitFormat(Wallet wallet, UnitFormat format) {
setUnitFormat(wallet, format, Config.get().getBitcoinUnit());
}
public void setUnitFormat(Wallet wallet, UnitFormat format, BitcoinUnit unit) {
if(format == null) {
format = UnitFormat.DOT;
}
if(unit == null || unit.equals(BitcoinUnit.AUTO)) {
unit = wallet.getAutoUnit();
}
boolean changed = (bitcoinUnit != unit);
boolean changed = (unitFormat != format);
changed |= (bitcoinUnit != unit);
this.unitFormat = format;
this.bitcoinUnit = unit;
if(changed && !getChildren().isEmpty()) {

View file

@ -1,7 +1,7 @@
package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.io.Config;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
@ -11,8 +11,6 @@ import javafx.scene.control.Tooltip;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import java.util.Locale;
public class CopyableCoinLabel extends CopyableLabel {
private final LongProperty valueProperty = new SimpleLongProperty(-1);
private final Tooltip tooltip;
@ -24,7 +22,7 @@ public class CopyableCoinLabel extends CopyableLabel {
public CopyableCoinLabel(String text) {
super(text);
valueProperty().addListener((observable, oldValue, newValue) -> setValueAsText((Long)newValue, Config.get().getBitcoinUnit()));
valueProperty().addListener((observable, oldValue, newValue) -> setValueAsText((Long)newValue, Config.get().getUnitFormat(), Config.get().getBitcoinUnit()));
tooltip = new Tooltip();
contextMenu = new CoinContextMenu();
}
@ -42,19 +40,23 @@ public class CopyableCoinLabel extends CopyableLabel {
}
public void refresh() {
refresh(Config.get().getBitcoinUnit());
refresh(Config.get().getUnitFormat(), Config.get().getBitcoinUnit());
}
public void refresh(BitcoinUnit bitcoinUnit) {
setValueAsText(getValue(), bitcoinUnit);
public void refresh(UnitFormat unitFormat, BitcoinUnit bitcoinUnit) {
setValueAsText(getValue(), unitFormat, bitcoinUnit);
}
private void setValueAsText(Long value, BitcoinUnit bitcoinUnit) {
private void setValueAsText(Long value, UnitFormat unitFormat, BitcoinUnit bitcoinUnit) {
setTooltip(tooltip);
setContextMenu(contextMenu);
String satsValue = String.format(Locale.ENGLISH, "%,d", value) + " sats";
String btcValue = CoinLabel.getBTCFormat().format(value.doubleValue() / Transaction.SATOSHIS_PER_BITCOIN) + " BTC";
if(unitFormat == null) {
unitFormat = UnitFormat.DOT;
}
String satsValue = unitFormat.formatSatsValue(value) + " sats";
String btcValue = unitFormat.formatBtcValue(value) + " BTC";
BitcoinUnit unit = bitcoinUnit;
if(unit == null || unit.equals(BitcoinUnit.AUTO)) {
@ -84,7 +86,8 @@ public class CopyableCoinLabel extends CopyableLabel {
copyBtcValue.setOnAction(AE -> {
hide();
ClipboardContent content = new ClipboardContent();
content.putString(CoinLabel.getBTCFormat().format((double)getValue() / Transaction.SATOSHIS_PER_BITCOIN));
UnitFormat format = Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat();
content.putString(format.formatBtcValue(getValue()));
Clipboard.getSystemClipboard().setContent(content);
});

View file

@ -1,7 +1,9 @@
package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.CurrencyRate;
import com.sparrowwallet.sparrow.io.Config;
import javafx.beans.property.*;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
@ -10,14 +12,9 @@ import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Currency;
import java.util.Locale;
public class FiatLabel extends CopyableLabel {
private static final DecimalFormat CURRENCY_FORMAT = new DecimalFormat("#,##0.00", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
private final LongProperty valueProperty = new SimpleLongProperty(-1);
private final DoubleProperty btcRateProperty = new SimpleDoubleProperty(0.0);
private final ObjectProperty<Currency> currencyProperty = new SimpleObjectProperty<>(null);
@ -30,9 +27,9 @@ public class FiatLabel extends CopyableLabel {
public FiatLabel(String text) {
super(text);
valueProperty().addListener((observable, oldValue, newValue) -> setValueAsText((Long)newValue));
btcRateProperty().addListener((observable, oldValue, newValue) -> setValueAsText(getValue()));
currencyProperty().addListener((observable, oldValue, newValue) -> setValueAsText(getValue()));
valueProperty().addListener((observable, oldValue, newValue) -> setValueAsText((Long)newValue, Config.get().getUnitFormat()));
btcRateProperty().addListener((observable, oldValue, newValue) -> setValueAsText(getValue(), Config.get().getUnitFormat()));
currencyProperty().addListener((observable, oldValue, newValue) -> setValueAsText(getValue(), Config.get().getUnitFormat()));
tooltip = new Tooltip();
contextMenu = new FiatContextMenu();
}
@ -83,14 +80,22 @@ public class FiatLabel extends CopyableLabel {
setCurrency(currency);
}
private void setValueAsText(long balance) {
public void refresh() {
refresh(Config.get().getUnitFormat());
}
public void refresh(UnitFormat unitFormat) {
setValueAsText(getValue(), unitFormat);
}
private void setValueAsText(long balance, UnitFormat unitFormat) {
if(getCurrency() != null && getBtcRate() > 0.0) {
BigDecimal satsBalance = BigDecimal.valueOf(balance);
BigDecimal btcBalance = satsBalance.divide(BigDecimal.valueOf(Transaction.SATOSHIS_PER_BITCOIN));
BigDecimal fiatBalance = btcBalance.multiply(BigDecimal.valueOf(getBtcRate()));
String label = getCurrency().getSymbol() + " " + CURRENCY_FORMAT.format(fiatBalance.doubleValue());
tooltip.setText("1 BTC = " + getCurrency().getSymbol() + " " + CURRENCY_FORMAT.format(getBtcRate()));
String label = getCurrency().getSymbol() + " " + unitFormat.formatCurrencyValue(fiatBalance.doubleValue());
tooltip.setText("1 BTC = " + getCurrency().getSymbol() + " " + unitFormat.formatCurrencyValue(getBtcRate()));
setText(label);
setTooltip(tooltip);

View file

@ -74,7 +74,7 @@ public class SearchWalletDialog extends Dialog<Entry> {
results = new CoinTreeTable();
results.setShowRoot(false);
results.setPrefWidth(showWallet ? 950 : 850);
results.setBitcoinUnit(walletForms.iterator().next().getWallet());
results.setUnitFormat(walletForms.iterator().next().getWallet());
results.setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY);
results.setPlaceholder(new Label("No results"));

View file

@ -3,10 +3,10 @@ package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.KeyPurpose;
import com.sparrowwallet.drongo.address.Address;
import com.sparrowwallet.drongo.protocol.Sha256Hash;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.protocol.TransactionOutput;
import com.sparrowwallet.drongo.uri.BitcoinURI;
import com.sparrowwallet.drongo.wallet.*;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.Theme;
@ -56,8 +56,6 @@ import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
import static com.sparrowwallet.sparrow.control.CoinLabel.BTC_FORMAT;
public class TransactionDiagram extends GridPane {
private static final int MAX_UTXOS = 8;
private static final int REDUCED_MAX_UTXOS = MAX_UTXOS - 2;
@ -1334,7 +1332,8 @@ public class TransactionDiagram extends GridPane {
copyBtcValue.setOnAction(event -> {
hide();
ClipboardContent content = new ClipboardContent();
content.putString(BTC_FORMAT.format((double)value / Transaction.SATOSHIS_PER_BITCOIN));
UnitFormat format = Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat();
content.putString(format.formatBtcValue(value));
Clipboard.getSystemClipboard().setContent(content);
});
getItems().addAll(copySatsValue, copyBtcValue);

View file

@ -1,6 +1,5 @@
package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.wallet.WalletNode;
import com.sparrowwallet.sparrow.wallet.Entry;
import com.sparrowwallet.sparrow.wallet.TransactionEntry;
import com.sparrowwallet.sparrow.wallet.WalletTransactionsEntry;
@ -8,12 +7,10 @@ import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import java.util.List;
public class TransactionsTreeTable extends CoinTreeTable {
public void initialize(WalletTransactionsEntry rootEntry) {
getStyleClass().add("transactions-treetable");
setBitcoinUnit(rootEntry.getWallet());
setUnitFormat(rootEntry.getWallet());
updateAll(rootEntry);
setShowRoot(false);
@ -58,7 +55,7 @@ public class TransactionsTreeTable extends CoinTreeTable {
}
public void updateAll(WalletTransactionsEntry rootEntry) {
setBitcoinUnit(rootEntry.getWallet());
setUnitFormat(rootEntry.getWallet());
RecursiveTreeItem<Entry> rootItem = new RecursiveTreeItem<>(rootEntry, Entry::getChildren);
setRoot(rootItem);

View file

@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.wallet.Entry;
import com.sparrowwallet.sparrow.wallet.UtxoEntry;
@ -36,8 +37,7 @@ public class UtxosChart extends BarChart<String, Number> {
getData().add(utxoSeries);
update(walletUtxosEntry);
BitcoinUnit unit = Config.get().getBitcoinUnit();
setBitcoinUnit(walletUtxosEntry.getWallet(), unit);
setUnitFormat(walletUtxosEntry.getWallet(), Config.get().getUnitFormat(), Config.get().getBitcoinUnit());
}
public void update(WalletUtxosEntry walletUtxosEntry) {
@ -117,12 +117,16 @@ public class UtxosChart extends BarChart<String, Number> {
this.selectedEntries = entries;
}
public void setBitcoinUnit(Wallet wallet, BitcoinUnit unit) {
public void setUnitFormat(Wallet wallet, UnitFormat format, BitcoinUnit unit) {
if(format == null) {
format = UnitFormat.DOT;
}
if(unit == null || unit.equals(BitcoinUnit.AUTO)) {
unit = wallet.getAutoUnit();
}
NumberAxis yaxis = (NumberAxis)getYAxis();
yaxis.setTickLabelFormatter(new CoinAxisFormatter(yaxis, unit));
yaxis.setTickLabelFormatter(new CoinAxisFormatter(yaxis, format, unit));
}
}

View file

@ -1,20 +1,17 @@
package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.wallet.WalletNode;
import com.sparrowwallet.sparrow.wallet.*;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumnBase;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import java.util.Comparator;
import java.util.List;
public class UtxosTreeTable extends CoinTreeTable {
public void initialize(WalletUtxosEntry rootEntry) {
getStyleClass().add("utxos-treetable");
setBitcoinUnit(rootEntry.getWallet());
setUnitFormat(rootEntry.getWallet());
updateAll(rootEntry);
setShowRoot(false);
@ -93,7 +90,7 @@ public class UtxosTreeTable extends CoinTreeTable {
}
public void updateAll(WalletUtxosEntry rootEntry) {
setBitcoinUnit(rootEntry.getWallet());
setUnitFormat(rootEntry.getWallet());
RecursiveTreeItem<Entry> rootItem = new RecursiveTreeItem<>(rootEntry, Entry::getChildren);
setRoot(rootItem);

View file

@ -1,11 +1,19 @@
package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.io.Config;
public class BitcoinUnitChangedEvent {
public class BitcoinUnitChangedEvent extends UnitFormatChangedEvent {
private final BitcoinUnit bitcoinUnit;
public BitcoinUnitChangedEvent(BitcoinUnit bitcoinUnit) {
super(Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat());
this.bitcoinUnit = bitcoinUnit;
}
public BitcoinUnitChangedEvent(UnitFormat unitFormat, BitcoinUnit bitcoinUnit) {
super(unitFormat);
this.bitcoinUnit = bitcoinUnit;
}

View file

@ -1,10 +1,9 @@
package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.wallet.BlockTransaction;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.control.CoinLabel;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.io.Config;
import com.sparrowwallet.sparrow.wallet.Entry;
import com.sparrowwallet.sparrow.wallet.HashIndexEntry;
@ -13,7 +12,6 @@ import com.sparrowwallet.sparrow.wallet.TransactionHashIndexEntry;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
public class NewWalletTransactionsEvent {
@ -50,16 +48,21 @@ public class NewWalletTransactionsEvent {
}
public String getValueAsText(long value) {
UnitFormat format = Config.get().getUnitFormat();
if(format == null) {
format = UnitFormat.DOT;
}
BitcoinUnit unit = Config.get().getBitcoinUnit();
if(unit == null || unit.equals(BitcoinUnit.AUTO)) {
unit = (value >= BitcoinUnit.getAutoThreshold() ? BitcoinUnit.BTC : BitcoinUnit.SATOSHIS);
}
if(unit == BitcoinUnit.BTC) {
return CoinLabel.getBTCFormat().format((double) value / Transaction.SATOSHIS_PER_BITCOIN) + " BTC";
return format.formatBtcValue(value) + " BTC";
}
return String.format(Locale.ENGLISH, "%,d", value) + " sats";
return format.formatSatsValue(value) + " sats";
}
public List<BlockTransaction> getUnspentConfirmingWhirlpoolMixTransactions() {

View file

@ -0,0 +1,21 @@
package com.sparrowwallet.sparrow.event;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.io.Config;
public class UnitFormatChangedEvent {
private final UnitFormat unitFormat;
public UnitFormatChangedEvent(UnitFormat unitFormat) {
this.unitFormat = unitFormat;
}
public UnitFormat getUnitFormat() {
return unitFormat;
}
public BitcoinUnit getBitcoinUnit() {
return Config.get().getBitcoinUnit();
}
}

View file

@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow.io;
import com.google.gson.*;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.Mode;
import com.sparrowwallet.sparrow.Theme;
import com.sparrowwallet.sparrow.net.*;
@ -27,6 +28,7 @@ public class Config {
private Mode mode;
private BitcoinUnit bitcoinUnit;
private UnitFormat unitFormat;
private FeeRatesSource feeRatesSource;
private FeeRatesSelection feeRatesSelection;
private OptimizationStrategy sendOptimizationStrategy;
@ -133,6 +135,15 @@ public class Config {
flush();
}
public UnitFormat getUnitFormat() {
return unitFormat;
}
public void setUnitFormat(UnitFormat unitFormat) {
this.unitFormat = unitFormat;
flush();
}
public FeeRatesSource getFeeRatesSource() {
return feeRatesSource;
}

View file

@ -1270,8 +1270,8 @@ public class HeadersController extends TransactionFormController implements Init
}
@Subscribe
public void bitcoinUnitChanged(BitcoinUnitChangedEvent event) {
fee.refresh(event.getBitcoinUnit());
public void unitFormatChanged(UnitFormatChangedEvent event) {
fee.refresh(event.getUnitFormat(), event.getBitcoinUnit());
}
@Subscribe

View file

@ -529,8 +529,8 @@ public class InputController extends TransactionFormController implements Initia
}
@Subscribe
public void bitcoinUnitChanged(BitcoinUnitChangedEvent event) {
spends.refresh(event.getBitcoinUnit());
public void unitFormatChanged(UnitFormatChangedEvent event) {
spends.refresh(event.getUnitFormat(), event.getBitcoinUnit());
}
@Subscribe

View file

@ -9,10 +9,7 @@ import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.CopyableCoinLabel;
import com.sparrowwallet.sparrow.control.CopyableLabel;
import com.sparrowwallet.sparrow.event.BitcoinUnitChangedEvent;
import com.sparrowwallet.sparrow.event.BlockTransactionFetchedEvent;
import com.sparrowwallet.sparrow.event.PSBTCombinedEvent;
import com.sparrowwallet.sparrow.event.PSBTFinalizedEvent;
import com.sparrowwallet.sparrow.event.*;
import javafx.collections.MapChangeListener;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
@ -170,8 +167,8 @@ public class InputsController extends TransactionFormController implements Initi
}
@Subscribe
public void bitcoinUnitChanged(BitcoinUnitChangedEvent event) {
total.refresh(event.getBitcoinUnit());
public void unitFormatChanged(UnitFormatChangedEvent event) {
total.refresh(event.getUnitFormat(), event.getBitcoinUnit());
}
@Subscribe

View file

@ -9,7 +9,7 @@ import com.sparrowwallet.drongo.wallet.BlockTransaction;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.*;
import com.sparrowwallet.sparrow.event.BitcoinUnitChangedEvent;
import com.sparrowwallet.sparrow.event.UnitFormatChangedEvent;
import com.sparrowwallet.sparrow.event.BlockTransactionOutputsFetchedEvent;
import com.sparrowwallet.sparrow.event.ViewTransactionEvent;
import com.sparrowwallet.sparrow.net.ElectrumServer;
@ -175,7 +175,7 @@ public class OutputController extends TransactionFormController implements Initi
}
@Subscribe
public void bitcoinUnitChanged(BitcoinUnitChangedEvent event) {
value.refresh(event.getBitcoinUnit());
public void unitFormatChanged(UnitFormatChangedEvent event) {
value.refresh(event.getUnitFormat(), event.getBitcoinUnit());
}
}

View file

@ -6,7 +6,7 @@ import com.sparrowwallet.drongo.protocol.TransactionOutput;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.CopyableCoinLabel;
import com.sparrowwallet.sparrow.control.CopyableLabel;
import com.sparrowwallet.sparrow.event.BitcoinUnitChangedEvent;
import com.sparrowwallet.sparrow.event.UnitFormatChangedEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.chart.PieChart;
@ -57,7 +57,7 @@ public class OutputsController extends TransactionFormController implements Init
}
@Subscribe
public void bitcoinUnitChanged(BitcoinUnitChangedEvent event) {
total.refresh(event.getBitcoinUnit());
public void unitFormatChanged(UnitFormatChangedEvent event) {
total.refresh(event.getUnitFormat(), event.getBitcoinUnit());
}
}

View file

@ -4,12 +4,11 @@ import com.google.common.eventbus.Subscribe;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.address.Address;
import com.sparrowwallet.drongo.protocol.NonStandardScriptException;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.protocol.TransactionOutput;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.BaseController;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.TransactionTabData;
import com.sparrowwallet.sparrow.control.CoinLabel;
import com.sparrowwallet.sparrow.event.TransactionTabsClosedEvent;
import com.sparrowwallet.sparrow.io.Config;
import javafx.collections.FXCollections;
@ -22,7 +21,6 @@ import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import java.util.List;
import java.util.Locale;
public abstract class TransactionFormController extends BaseController {
private static final int MAX_PIE_SEGMENTS = 200;
@ -64,13 +62,14 @@ public abstract class TransactionFormController extends BaseController {
return;
}
UnitFormat format = Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat();
pie.setData(outputsPieData);
final double totalSum = outputsPieData.stream().map(PieChart.Data::getPieValue).mapToDouble(Double::doubleValue).sum();
pie.getData().forEach(data -> {
Tooltip tooltip = new Tooltip();
double percent = 100.0 * (data.getPieValue() / totalSum);
String satsValue = String.format(Locale.ENGLISH, "%,d", (long)data.getPieValue()) + " sats";
String btcValue = CoinLabel.BTC_FORMAT.format(data.getPieValue() / Transaction.SATOSHIS_PER_BITCOIN) + " BTC";
String satsValue = format.formatSatsValue((long)data.getPieValue()) + " sats";
String btcValue = format.formatBtcValue((long)data.getPieValue()) + " BTC";
tooltip.setText(data.getName() + "\n" + (Config.get().getBitcoinUnit() == BitcoinUnit.BTC ? btcValue : satsValue) + " (" + String.format("%.1f", percent) + "%)");
Tooltip.install(data.getNode(), tooltip);
data.pieValueProperty().addListener((observable, oldValue, newValue) -> tooltip.setText(newValue + "%"));

View file

@ -88,9 +88,9 @@ public class AddressesController extends WalletFormController implements Initial
}
@Subscribe
public void bitcoinUnitChanged(BitcoinUnitChangedEvent event) {
receiveTable.setBitcoinUnit(getWalletForm().getWallet(), event.getBitcoinUnit());
changeTable.setBitcoinUnit(getWalletForm().getWallet(), event.getBitcoinUnit());
public void unitFormatChanged(UnitFormatChangedEvent event) {
receiveTable.setUnitFormat(getWalletForm().getWallet(), event.getUnitFormat(), event.getBitcoinUnit());
changeTable.setUnitFormat(getWalletForm().getWallet(), event.getUnitFormat(), event.getBitcoinUnit());
}
@Subscribe

View file

@ -14,6 +14,7 @@ import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.protocol.TransactionOutput;
import com.sparrowwallet.drongo.uri.BitcoinURI;
import com.sparrowwallet.drongo.wallet.*;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.CurrencyRate;
import com.sparrowwallet.sparrow.EventManager;
@ -50,6 +51,7 @@ import java.net.URL;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static com.sparrowwallet.sparrow.AppServices.showErrorDialog;
@ -240,7 +242,7 @@ public class PaymentController extends WalletFormController implements Initializ
sendController.updateTransaction();
});
amount.setTextFormatter(new CoinTextFormatter());
amount.setTextFormatter(new CoinTextFormatter(Config.get().getUnitFormat()));
amount.textProperty().addListener(amountListener);
amountUnit.getSelectionModel().select(BitcoinUnit.BTC.equals(sendController.getBitcoinUnit(Config.get().getBitcoinUnit())) ? 0 : 1);
@ -390,8 +392,13 @@ public class PaymentController extends WalletFormController implements Initializ
}
private Long getRecipientValueSats(BitcoinUnit bitcoinUnit) {
return getRecipientValueSats(Config.get().getUnitFormat(), bitcoinUnit);
}
private Long getRecipientValueSats(UnitFormat unitFormat, BitcoinUnit bitcoinUnit) {
if(amount.getText() != null && !amount.getText().isEmpty()) {
double fieldValue = Double.parseDouble(amount.getText().replaceAll(",", ""));
UnitFormat format = unitFormat == null ? UnitFormat.DOT : unitFormat;
double fieldValue = Double.parseDouble(amount.getText().replaceAll(Pattern.quote(format.getGroupingSeparator()), "").replaceAll(",", "."));
return bitcoinUnit.getSatsValue(fieldValue);
}
@ -400,7 +407,8 @@ public class PaymentController extends WalletFormController implements Initializ
private void setRecipientValueSats(long recipientValue) {
amount.textProperty().removeListener(amountListener);
DecimalFormat df = new DecimalFormat("#.#", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
UnitFormat unitFormat = Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat();
DecimalFormat df = new DecimalFormat("#.#", unitFormat.getDecimalFormatSymbols());
df.setMaximumFractionDigits(8);
amount.setText(df.format(amountUnit.getValue().getValue(recipientValue)));
amount.textProperty().addListener(amountListener);
@ -621,6 +629,19 @@ public class PaymentController extends WalletFormController implements Initializ
amountUnit.getSelectionModel().select(BitcoinUnit.BTC.equals(unit) ? 0 : 1);
}
@Subscribe
public void unitFormatChanged(UnitFormatChangedEvent event) {
if(amount.getTextFormatter() instanceof CoinTextFormatter coinTextFormatter && coinTextFormatter.getUnitFormat() != event.getUnitFormat()) {
Long value = getRecipientValueSats(coinTextFormatter.getUnitFormat(), event.getBitcoinUnit());
amount.setTextFormatter(new CoinTextFormatter(event.getUnitFormat()));
if(value != null) {
setRecipientValueSats(value);
}
}
fiatAmount.refresh(event.getUnitFormat());
}
@Subscribe
public void fiatCurrencySelected(FiatCurrencySelectedEvent event) {
if(event.getExchangeSource() == ExchangeSource.NONE) {

View file

@ -13,6 +13,7 @@ import com.sparrowwallet.drongo.crypto.ECKey;
import com.sparrowwallet.drongo.protocol.*;
import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.drongo.wallet.*;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.CurrencyRate;
import com.sparrowwallet.sparrow.EventManager;
@ -61,8 +62,8 @@ import tornadofx.control.Field;
import java.io.IOException;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static com.sparrowwallet.sparrow.AppServices.*;
@ -354,7 +355,7 @@ public class SendController extends WalletFormController implements Initializabl
};
});
fee.setTextFormatter(new CoinTextFormatter());
fee.setTextFormatter(new CoinTextFormatter(Config.get().getUnitFormat()));
fee.textProperty().addListener(feeListener);
BitcoinUnit unit = getBitcoinUnit(Config.get().getBitcoinUnit());
@ -737,8 +738,13 @@ public class SendController extends WalletFormController implements Initializabl
}
private Long getFeeValueSats(BitcoinUnit bitcoinUnit) {
return getFeeValueSats(Config.get().getUnitFormat(), bitcoinUnit);
}
private Long getFeeValueSats(UnitFormat unitFormat, BitcoinUnit bitcoinUnit) {
if(fee.getText() != null && !fee.getText().isEmpty()) {
double fieldValue = Double.parseDouble(fee.getText().replaceAll(",", ""));
UnitFormat format = unitFormat == null ? UnitFormat.DOT : unitFormat;
double fieldValue = Double.parseDouble(fee.getText().replaceAll(Pattern.quote(format.getGroupingSeparator()), "").replaceAll(",", "."));
return bitcoinUnit.getSatsValue(fieldValue);
}
@ -747,7 +753,8 @@ public class SendController extends WalletFormController implements Initializabl
private void setFeeValueSats(long feeValue) {
fee.textProperty().removeListener(feeListener);
DecimalFormat df = new DecimalFormat("#.#", DecimalFormatSymbols.getInstance(Locale.ENGLISH));
UnitFormat unitFormat = Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat();
DecimalFormat df = new DecimalFormat("#.#", unitFormat.getDecimalFormatSymbols());
df.setMaximumFractionDigits(8);
fee.setText(df.format(feeAmountUnit.getValue().getValue(feeValue)));
fee.textProperty().addListener(feeListener);
@ -1533,6 +1540,19 @@ public class SendController extends WalletFormController implements Initializabl
feeAmountUnit.getSelectionModel().select(BitcoinUnit.BTC.equals(unit) ? 0 : 1);
}
@Subscribe
public void unitFormatChanged(UnitFormatChangedEvent event) {
if(fee.getTextFormatter() instanceof CoinTextFormatter coinTextFormatter && coinTextFormatter.getUnitFormat() != event.getUnitFormat()) {
Long value = getFeeValueSats(coinTextFormatter.getUnitFormat(), event.getBitcoinUnit());
fee.setTextFormatter(new CoinTextFormatter(event.getUnitFormat()));
if(value != null) {
setFeeValueSats(value);
}
}
fiatFeeAmount.refresh(event.getUnitFormat());
}
@Subscribe
public void fiatCurrencySelected(FiatCurrencySelectedEvent event) {
if(event.getExchangeSource() == ExchangeSource.NONE) {

View file

@ -3,7 +3,7 @@ package com.sparrowwallet.sparrow.wallet;
import com.csvreader.CsvWriter;
import com.google.common.eventbus.Subscribe;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.*;
@ -141,9 +141,8 @@ public class TransactionsController extends WalletFormController implements Init
}
private String getCoinValue(Long value) {
return BitcoinUnit.BTC.equals(transactionsTable.getBitcoinUnit()) ?
CoinLabel.getBTCFormat().format(value.doubleValue() / Transaction.SATOSHIS_PER_BITCOIN) :
String.format(Locale.ENGLISH, "%d", value);
UnitFormat format = Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat();
return BitcoinUnit.BTC.equals(transactionsTable.getBitcoinUnit()) ? format.formatBtcValue(value) : String.format(Locale.ENGLISH, "%d", value);
}
private void logMessage(String logMessage) {
@ -201,11 +200,13 @@ public class TransactionsController extends WalletFormController implements Init
}
@Subscribe
public void bitcoinUnitChanged(BitcoinUnitChangedEvent event) {
transactionsTable.setBitcoinUnit(getWalletForm().getWallet(), event.getBitcoinUnit());
balanceChart.setBitcoinUnit(getWalletForm().getWallet(), event.getBitcoinUnit());
balance.refresh(event.getBitcoinUnit());
mempoolBalance.refresh(event.getBitcoinUnit());
public void unitFormatChanged(UnitFormatChangedEvent event) {
transactionsTable.setUnitFormat(getWalletForm().getWallet(), event.getUnitFormat(), event.getBitcoinUnit());
balanceChart.setUnitFormat(getWalletForm().getWallet(), event.getUnitFormat(), event.getBitcoinUnit());
balance.refresh(event.getUnitFormat(), event.getBitcoinUnit());
mempoolBalance.refresh(event.getUnitFormat(), event.getBitcoinUnit());
fiatBalance.refresh(event.getUnitFormat());
fiatMempoolBalance.refresh(event.getUnitFormat());
}
@Subscribe

View file

@ -9,8 +9,8 @@ import com.sparrowwallet.drongo.SecureString;
import com.sparrowwallet.drongo.address.Address;
import com.sparrowwallet.drongo.address.InvalidAddressException;
import com.sparrowwallet.drongo.crypto.*;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.wallet.*;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.*;
@ -189,7 +189,7 @@ public class UtxosController extends WalletFormController implements Initializab
utxosTable.getSelectionModel().getSelectedIndices().addListener((ListChangeListener<Integer>) c -> {
List<Entry> selectedEntries = utxosTable.getSelectionModel().getSelectedCells().stream().filter(tp -> tp.getTreeItem() != null).map(tp -> tp.getTreeItem().getValue()).collect(Collectors.toList());
utxosChart.select(selectedEntries);
updateButtons(Config.get().getBitcoinUnit());
updateButtons(Config.get().getUnitFormat(), Config.get().getBitcoinUnit());
updateUtxoCount(getWalletForm().getWalletUtxosEntry());
});
}
@ -210,7 +210,7 @@ public class UtxosController extends WalletFormController implements Initializab
return WhirlpoolServices.canWalletMix(getWalletForm().getWallet());
}
private void updateButtons(BitcoinUnit unit) {
private void updateButtons(UnitFormat format, BitcoinUnit unit) {
List<Entry> selectedEntries = getSelectedEntries();
selectAll.setDisable(utxosTable.getRoot().getChildren().size() == utxosTable.getSelectionModel().getSelectedCells().size());
@ -220,16 +220,20 @@ public class UtxosController extends WalletFormController implements Initializab
long selectedTotal = selectedEntries.stream().mapToLong(Entry::getValue).sum();
if(selectedTotal > 0) {
if(format == null) {
format = UnitFormat.DOT;
}
if(unit == null || unit.equals(BitcoinUnit.AUTO)) {
unit = (selectedTotal >= BitcoinUnit.getAutoThreshold() ? BitcoinUnit.BTC : BitcoinUnit.SATOSHIS);
}
if(unit.equals(BitcoinUnit.BTC)) {
sendSelected.setText("Send Selected (" + CoinLabel.getBTCFormat().format((double)selectedTotal / Transaction.SATOSHIS_PER_BITCOIN) + " BTC)");
mixSelected.setText("Mix Selected (" + CoinLabel.getBTCFormat().format((double)selectedTotal / Transaction.SATOSHIS_PER_BITCOIN) + " BTC)");
sendSelected.setText("Send Selected (" + format.formatBtcValue(selectedTotal) + " BTC)");
mixSelected.setText("Mix Selected (" + format.formatBtcValue(selectedTotal) + " BTC)");
} else {
sendSelected.setText("Send Selected (" + String.format(Locale.ENGLISH, "%,d", selectedTotal) + " sats)");
mixSelected.setText("Mix Selected (" + String.format(Locale.ENGLISH, "%,d", selectedTotal) + " sats)");
sendSelected.setText("Send Selected (" + format.formatSatsValue(selectedTotal) + " sats)");
mixSelected.setText("Mix Selected (" + format.formatSatsValue(selectedTotal) + " sats)");
}
} else {
sendSelected.setText("Send Selected");
@ -486,9 +490,8 @@ public class UtxosController extends WalletFormController implements Initializab
}
private String getCoinValue(Long value) {
return BitcoinUnit.BTC.equals(utxosTable.getBitcoinUnit()) ?
CoinLabel.getBTCFormat().format(value.doubleValue() / Transaction.SATOSHIS_PER_BITCOIN) :
String.format(Locale.ENGLISH, "%d", value);
UnitFormat format = Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat();
return BitcoinUnit.BTC.equals(utxosTable.getBitcoinUnit()) ? format.formatBtcValue(value) : String.format(Locale.ENGLISH, "%d", value);
}
private static Glyph getExternalGlyph() {
@ -546,12 +549,14 @@ public class UtxosController extends WalletFormController implements Initializab
}
@Subscribe
public void bitcoinUnitChanged(BitcoinUnitChangedEvent event) {
utxosTable.setBitcoinUnit(getWalletForm().getWallet(), event.getBitcoinUnit());
utxosChart.setBitcoinUnit(getWalletForm().getWallet(), event.getBitcoinUnit());
balance.refresh(event.getBitcoinUnit());
mempoolBalance.refresh(event.getBitcoinUnit());
updateButtons(event.getBitcoinUnit());
public void unitFormatChanged(UnitFormatChangedEvent event) {
utxosTable.setUnitFormat(getWalletForm().getWallet(), event.getUnitFormat(), event.getBitcoinUnit());
utxosChart.setUnitFormat(getWalletForm().getWallet(), event.getUnitFormat(), event.getBitcoinUnit());
balance.refresh(event.getUnitFormat(), event.getBitcoinUnit());
mempoolBalance.refresh(event.getUnitFormat(), event.getBitcoinUnit());
updateButtons(event.getUnitFormat(), event.getBitcoinUnit());
fiatBalance.refresh(event.getUnitFormat());
fiatMempoolBalance.refresh(event.getUnitFormat());
}
@Subscribe
@ -588,7 +593,7 @@ public class UtxosController extends WalletFormController implements Initializab
public void walletUtxoStatusChanged(WalletUtxoStatusChangedEvent event) {
if(event.fromThisOrNested(getWalletForm().getWallet())) {
utxosTable.refresh();
updateButtons(Config.get().getBitcoinUnit());
updateButtons(Config.get().getUnitFormat(), Config.get().getBitcoinUnit());
}
}

View file

@ -5,12 +5,11 @@ import com.samourai.whirlpool.client.tx0.Tx0Previews;
import com.samourai.whirlpool.client.wallet.beans.Tx0FeeTarget;
import com.samourai.whirlpool.client.whirlpool.beans.Pool;
import com.sparrowwallet.drongo.BitcoinUnit;
import com.sparrowwallet.drongo.protocol.Transaction;
import com.sparrowwallet.drongo.wallet.MixConfig;
import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.control.CoinLabel;
import com.sparrowwallet.sparrow.control.CopyableCoinLabel;
import com.sparrowwallet.sparrow.control.CopyableLabel;
import com.sparrowwallet.sparrow.event.WalletMasterMixConfigChangedEvent;
@ -157,9 +156,10 @@ public class WhirlpoolController {
return "Fetching pools...";
}
UnitFormat format = Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat();
BitcoinUnit bitcoinUnit = wallet.getAutoUnit();
String satsValue = String.format(Locale.ENGLISH, "%,d", selectedPool.getDenomination()) + " sats";
String btcValue = CoinLabel.BTC_FORMAT.format((double)selectedPool.getDenomination() / Transaction.SATOSHIS_PER_BITCOIN) + " BTC";
String satsValue = format.formatSatsValue(selectedPool.getDenomination()) + " sats";
String btcValue = format.formatBtcValue(selectedPool.getDenomination()) + " BTC";
pool.setTooltip(bitcoinUnit == BitcoinUnit.BTC ? new Tooltip(satsValue) : new Tooltip(btcValue));
return bitcoinUnit == BitcoinUnit.BTC ? btcValue : satsValue;
@ -273,8 +273,9 @@ public class WhirlpoolController {
allPoolsService.setOnSucceeded(poolsStateEvent -> {
OptionalLong optMinValue = allPoolsService.getValue().stream().mapToLong(pool1 -> pool1.getPremixValueMin() + pool1.getFeeValue()).min();
if(optMinValue.isPresent() && totalUtxoValue < optMinValue.getAsLong()) {
String satsValue = String.format(Locale.ENGLISH, "%,d", optMinValue.getAsLong()) + " sats";
String btcValue = CoinLabel.getBTCFormat().format((double)optMinValue.getAsLong() / Transaction.SATOSHIS_PER_BITCOIN) + " BTC";
UnitFormat format = Config.get().getUnitFormat() == null ? UnitFormat.DOT : Config.get().getUnitFormat();
String satsValue = format.formatSatsValue(optMinValue.getAsLong()) + " sats";
String btcValue = format.formatBtcValue(optMinValue.getAsLong()) + " BTC";
poolInsufficient.setText("No available pools. Select a value over " + (Config.get().getBitcoinUnit() == BitcoinUnit.BTC ? btcValue : satsValue) + ".");
}
});

View file

@ -7,6 +7,7 @@
<?import javafx.scene.shape.Rectangle?>
<?import com.sparrowwallet.sparrow.control.UnlabeledToggleSwitch?>
<?import com.sparrowwallet.drongo.BitcoinUnit?>
<?import com.sparrowwallet.sparrow.UnitFormat?>
<?import com.sparrowwallet.sparrow.Theme?>
<?import impl.org.controlsfx.skin.DecorationPane?>
@ -56,6 +57,9 @@
<fx:define>
<ToggleGroup fx:id="bitcoinUnit"/>
</fx:define>
<fx:define>
<ToggleGroup fx:id="unitFormat"/>
</fx:define>
<fx:define>
<ToggleGroup fx:id="theme"/>
</fx:define>
@ -80,6 +84,20 @@
</RadioMenuItem>
</items>
</Menu>
<Menu mnemonicParsing="false" text="Unit Format">
<items>
<RadioMenuItem mnemonicParsing="false" text="1,234.56" toggleGroup="$unitFormat" onAction="#setUnitFormat">
<userData>
<UnitFormat fx:constant="DOT" />
</userData>
</RadioMenuItem>
<RadioMenuItem mnemonicParsing="false" text="1.234,56" toggleGroup="$unitFormat" onAction="#setUnitFormat">
<userData>
<UnitFormat fx:constant="COMMA" />
</userData>
</RadioMenuItem>
</items>
</Menu>
<Menu mnemonicParsing="false" text="Theme">
<items>
<RadioMenuItem mnemonicParsing="false" text="Light" toggleGroup="$theme" onAction="#setTheme">