diff --git a/drongo b/drongo index 1805aeb3..b2c362d5 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 1805aeb3740a9b90cff321219f0e60c4a1ccd3a6 +Subproject commit b2c362d5a71b8c46f8990a6b6b9b96f81ee24c90 diff --git a/src/main/java/com/sparrowwallet/sparrow/control/AddressTreeTable.java b/src/main/java/com/sparrowwallet/sparrow/control/AddressTreeTable.java index e0093cd0..673e5a27 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/AddressTreeTable.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/AddressTreeTable.java @@ -1,7 +1,5 @@ package com.sparrowwallet.sparrow.control; -import com.sparrowwallet.drongo.KeyPurpose; -import com.sparrowwallet.drongo.wallet.TableType; import com.sparrowwallet.drongo.wallet.WalletNode; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.EventManager; @@ -79,7 +77,7 @@ public class AddressTreeTable extends CoinTreeTable { getColumns().forEach(col -> col.setContextMenu(contextMenu)); setEditable(true); - setupColumnWidths(rootEntry.getNode().getIndex() == KeyPurpose.RECEIVE.getPathIndex().num() ? TableType.RECEIVE_ADDRESSES : TableType.CHANGE_ADDRESSES); + setupColumnWidths(); addressCol.setSortType(TreeTableColumn.SortType.ASCENDING); getSortOrder().add(addressCol); diff --git a/src/main/java/com/sparrowwallet/sparrow/control/CoinTreeTable.java b/src/main/java/com/sparrowwallet/sparrow/control/CoinTreeTable.java index b4d605cc..3683c2d0 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/CoinTreeTable.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/CoinTreeTable.java @@ -1,6 +1,7 @@ package com.sparrowwallet.sparrow.control; import com.sparrowwallet.drongo.BitcoinUnit; +import com.sparrowwallet.drongo.wallet.SortDirection; import com.sparrowwallet.drongo.wallet.TableType; import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.WalletTable; @@ -8,7 +9,7 @@ import com.sparrowwallet.sparrow.CurrencyRate; import com.sparrowwallet.sparrow.UnitFormat; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.EventManager; -import com.sparrowwallet.sparrow.event.WalletTableColumnsResizedEvent; +import com.sparrowwallet.sparrow.event.WalletTableChangedEvent; import com.sparrowwallet.sparrow.event.WalletAddressesChangedEvent; import com.sparrowwallet.sparrow.event.WalletDataChangedEvent; import com.sparrowwallet.sparrow.event.WalletHistoryStatusEvent; @@ -19,6 +20,7 @@ import com.sparrowwallet.sparrow.wallet.Entry; import io.reactivex.Observable; import io.reactivex.subjects.PublishSubject; import javafx.application.Platform; +import javafx.collections.ListChangeListener; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.*; @@ -31,13 +33,22 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; public class CoinTreeTable extends TreeTableView { + private TableType tableType; private BitcoinUnit bitcoinUnit; private UnitFormat unitFormat; private CurrencyRate currencyRate; protected static final double STANDARD_WIDTH = 100.0; - private final PublishSubject columnResizeSubject = PublishSubject.create(); - private final Observable columnResizeEvents = columnResizeSubject.debounce(1, TimeUnit.SECONDS); + private final PublishSubject walletTableSubject = PublishSubject.create(); + private final Observable walletTableEvents = walletTableSubject.debounce(1, TimeUnit.SECONDS); + + public TableType getTableType() { + return tableType; + } + + public void setTableType(TableType tableType) { + this.tableType = tableType; + } public BitcoinUnit getBitcoinUnit() { return bitcoinUnit; @@ -143,17 +154,63 @@ public class CoinTreeTable extends TreeTableView { return stackPane; } - public void setSortColumn(int columnIndex, TreeTableColumn.SortType sortType) { - if(columnIndex >= 0 && columnIndex < getColumns().size() && getSortOrder().isEmpty() && !getRoot().getChildren().isEmpty()) { - TreeTableColumn column = getColumns().get(columnIndex); - column.setSortType(sortType == null ? TreeTableColumn.SortType.DESCENDING : sortType); + protected void setupColumnSort(int defaultColumnIndex, TreeTableColumn.SortType defaultSortType) { + WalletTable.Sort columnSort = getSavedColumnSort(); + if(columnSort == null) { + columnSort = new WalletTable.Sort(defaultColumnIndex, getSortDirection(defaultSortType)); + } + + setSortColumn(columnSort); + + getSortOrder().addListener((ListChangeListener>) c -> { + if(c.next()) { + walletTableChanged(); + } + }); + for(TreeTableColumn column : getColumns()) { + column.sortTypeProperty().addListener((_, _, _) -> walletTableChanged()); + } + } + + protected void resetSortColumn() { + setSortColumn(getColumnSort()); + } + + protected void setSortColumn(WalletTable.Sort sort) { + if(sort.sortColumn() >= 0 && sort.sortColumn() < getColumns().size() && getSortOrder().isEmpty() && !getRoot().getChildren().isEmpty()) { + TreeTableColumn column = getColumns().get(sort.sortColumn()); + column.setSortType(sort.sortDirection() == SortDirection.DESCENDING ? TreeTableColumn.SortType.DESCENDING : TreeTableColumn.SortType.ASCENDING); getSortOrder().add(column); } } + private WalletTable.Sort getColumnSort() { + if(getSortOrder().isEmpty() || !getColumns().contains(getSortOrder().getFirst())) { + return new WalletTable.Sort(tableType == TableType.UTXOS ? getColumns().size() - 1 : 0, SortDirection.DESCENDING); + } + + return new WalletTable.Sort(getColumns().indexOf(getSortOrder().getFirst()), getSortDirection(getSortOrder().getFirst().getSortType())); + } + + private SortDirection getSortDirection(TreeTableColumn.SortType sortType) { + return sortType == TreeTableColumn.SortType.ASCENDING ? SortDirection.ASCENDING : SortDirection.DESCENDING; + } + + private WalletTable.Sort getSavedColumnSort() { + if(getRoot() != null && getRoot().getValue() != null && getRoot().getValue().getWallet() != null) { + Wallet wallet = getRoot().getValue().getWallet(); + WalletTable walletTable = wallet.getWalletTable(tableType); + if(walletTable != null) { + return walletTable.getSort(); + } + } + + return null; + } + @SuppressWarnings("deprecation") - protected void setupColumnWidths(TableType tableType) { - Double[] savedWidths = getSavedColumnWidths(tableType); + protected void setupColumnWidths() { + Double[] savedWidths = getSavedColumnWidths(); for(int i = 0; i < getColumns().size(); i++) { TreeTableColumn column = getColumns().get(i); column.setPrefWidth(savedWidths != null && getColumns().size() == savedWidths.length ? savedWidths[i] : STANDARD_WIDTH); @@ -162,18 +219,27 @@ public class CoinTreeTable extends TreeTableView { //TODO: Replace with TreeTableView.CONSTRAINED_RESIZE_POLICY_FLEX_LAST_COLUMN when JavaFX 20+ has headless support setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY); - getColumns().getLast().widthProperty().addListener((_, _, _) -> { - if(getRoot() != null && getRoot().getValue() != null && getRoot().getValue().getWallet() != null) { - Double[] widths = getColumns().stream().map(TableColumnBase::getWidth).toArray(Double[]::new); - WalletTable walletTable = new WalletTable(tableType, widths); - columnResizeSubject.onNext(new WalletTableColumnsResizedEvent(getRoot().getValue().getWallet(), walletTable)); - } - }); + getColumns().getLast().widthProperty().addListener((_, _, _) -> walletTableChanged()); - columnResizeEvents.skip(3, TimeUnit.SECONDS).subscribe(event -> EventManager.get().post(event)); + //Ignore initial resizes during layout + walletTableEvents.skip(3, TimeUnit.SECONDS).subscribe(event -> { + event.getWallet().getWalletTables().put(event.getTableType(), event.getWalletTable()); + EventManager.get().post(event); + }); } - private Double[] getSavedColumnWidths(TableType tableType) { + private void walletTableChanged() { + if(getRoot() != null && getRoot().getValue() != null && getRoot().getValue().getWallet() != null) { + WalletTable walletTable = new WalletTable(tableType, getColumnWidths(), getColumnSort()); + walletTableSubject.onNext(new WalletTableChangedEvent(getRoot().getValue().getWallet(), walletTable)); + } + } + + private Double[] getColumnWidths() { + return getColumns().stream().map(TableColumnBase::getWidth).toArray(Double[]::new); + } + + private Double[] getSavedColumnWidths() { if(getRoot() != null && getRoot().getValue() != null && getRoot().getValue().getWallet() != null) { Wallet wallet = getRoot().getValue().getWallet(); WalletTable walletTable = wallet.getWalletTable(tableType); diff --git a/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java b/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java index 42276875..c4914ffc 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java @@ -806,7 +806,7 @@ public class EntryCell extends TreeTableCell implements Confirmati cell.getStyleClass().remove("utxo-row"); cell.getStyleClass().remove("unconfirmed-row"); cell.getStyleClass().remove("summary-row"); - cell.getStyleClass().remove("address-cell"); + boolean addressCell = cell.getStyleClass().remove("address-cell"); cell.getStyleClass().remove("hashindex-row"); cell.getStyleClass().remove("confirming"); cell.getStyleClass().remove("negative-amount"); @@ -835,7 +835,7 @@ public class EntryCell extends TreeTableCell implements Confirmati if(!utxoEntry.isSpendable()) { cell.getStyleClass().add("unspendable"); } - if(OsType.getCurrent() == OsType.MACOS && utxoEntry.getHashIndex().getHeight() > 0) { + if(OsType.getCurrent() == OsType.MACOS && utxoEntry.getHashIndex().getHeight() > 0 && !addressCell) { cell.getStyleClass().add("number-field"); } } else if(entry instanceof HashIndexEntry hashIndexEntry) { diff --git a/src/main/java/com/sparrowwallet/sparrow/control/SearchWalletDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/SearchWalletDialog.java index efbffdda..3332cb0a 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/SearchWalletDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/SearchWalletDialog.java @@ -87,6 +87,7 @@ public class SearchWalletDialog extends Dialog { form.getChildren().add(fieldset); results = new CoinTreeTable(); + results.setTableType(TableType.SEARCH_WALLET); results.setShowRoot(false); results.setPrefWidth(showWallet || showAccount ? 950 : 850); results.setUnitFormat(walletForms.iterator().next().getWallet()); @@ -174,7 +175,7 @@ public class SearchWalletDialog extends Dialog { results.setRoot(rootItem); setResizable(true); - results.setupColumnWidths(TableType.SEARCH_WALLET); + results.setupColumnWidths(); AppServices.moveToActiveWindowScreen(this); diff --git a/src/main/java/com/sparrowwallet/sparrow/control/TransactionsTreeTable.java b/src/main/java/com/sparrowwallet/sparrow/control/TransactionsTreeTable.java index 36f80aa7..7a310f87 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/TransactionsTreeTable.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/TransactionsTreeTable.java @@ -49,8 +49,8 @@ public class TransactionsTreeTable extends CoinTreeTable { setPlaceholder(getDefaultPlaceholder(rootEntry.getWallet())); setEditable(true); - setupColumnWidths(TableType.TRANSACTIONS); - setSortColumn(0, TreeTableColumn.SortType.DESCENDING); + setupColumnWidths(); + setupColumnSort(0, TreeTableColumn.SortType.DESCENDING); } public void updateAll(WalletTransactionsEntry rootEntry) { @@ -60,13 +60,13 @@ public class TransactionsTreeTable extends CoinTreeTable { setRoot(rootItem); rootItem.setExpanded(true); - setSortColumn(0, TreeTableColumn.SortType.DESCENDING); + resetSortColumn(); } public void updateHistory() { //Transaction entries should have already been updated using WalletTransactionsEntry.updateHistory, so only a resort required sort(); - setSortColumn(0, TreeTableColumn.SortType.DESCENDING); + resetSortColumn(); } public void updateLabel(Entry entry) { diff --git a/src/main/java/com/sparrowwallet/sparrow/control/UtxosTreeTable.java b/src/main/java/com/sparrowwallet/sparrow/control/UtxosTreeTable.java index 3a0e6cc8..dfe86a8e 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/UtxosTreeTable.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/UtxosTreeTable.java @@ -82,8 +82,8 @@ public class UtxosTreeTable extends CoinTreeTable { setPlaceholder(getDefaultPlaceholder(rootEntry.getWallet())); setEditable(true); - setupColumnWidths(TableType.UTXOS); - setSortColumn(getColumns().size() - 1, TreeTableColumn.SortType.DESCENDING); + setupColumnWidths(); + setupColumnSort(getColumns().size() - 1, TreeTableColumn.SortType.DESCENDING); getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); } @@ -95,14 +95,14 @@ public class UtxosTreeTable extends CoinTreeTable { setRoot(rootItem); rootItem.setExpanded(true); - setSortColumn(getColumns().size() - 1, TreeTableColumn.SortType.DESCENDING); + resetSortColumn(); } public void updateHistory() { //Utxo entries should have already been updated, so only a resort required if(!getRoot().getChildren().isEmpty()) { sort(); - setSortColumn(getColumns().size() - 1, TreeTableColumn.SortType.DESCENDING); + resetSortColumn(); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/WalletSummaryDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/WalletSummaryDialog.java index abc46826..7e85a318 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/WalletSummaryDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/WalletSummaryDialog.java @@ -55,6 +55,7 @@ public class WalletSummaryDialog extends Dialog { HBox hBox = new HBox(40); CoinTreeTable table = new CoinTreeTable(); + table.setTableType(TableType.WALLET_SUMMARY); TreeTableColumn nameColumn = new TreeTableColumn<>("Wallet"); nameColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures param) -> { @@ -102,7 +103,7 @@ public class WalletSummaryDialog extends Dialog { table.setRoot(rootItem); rootItem.setExpanded(true); - table.setupColumnWidths(TableType.WALLET_SUMMARY); + table.setupColumnWidths(); table.setPrefWidth(450); VBox vBox = new VBox(); diff --git a/src/main/java/com/sparrowwallet/sparrow/event/WalletTableColumnsResizedEvent.java b/src/main/java/com/sparrowwallet/sparrow/event/WalletTableChangedEvent.java similarity index 76% rename from src/main/java/com/sparrowwallet/sparrow/event/WalletTableColumnsResizedEvent.java rename to src/main/java/com/sparrowwallet/sparrow/event/WalletTableChangedEvent.java index 294ee215..1df367e7 100644 --- a/src/main/java/com/sparrowwallet/sparrow/event/WalletTableColumnsResizedEvent.java +++ b/src/main/java/com/sparrowwallet/sparrow/event/WalletTableChangedEvent.java @@ -4,10 +4,10 @@ import com.sparrowwallet.drongo.wallet.TableType; import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.WalletTable; -public class WalletTableColumnsResizedEvent extends WalletChangedEvent { +public class WalletTableChangedEvent extends WalletChangedEvent { private final WalletTable walletTable; - public WalletTableColumnsResizedEvent(Wallet wallet, WalletTable walletTable) { + public WalletTableChangedEvent(Wallet wallet, WalletTable walletTable) { super(wallet); this.walletTable = walletTable; } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java b/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java index 5649a763..ec612a81 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/db/DbPersistence.java @@ -774,7 +774,7 @@ public class DbPersistence implements Persistence { } @Subscribe - public void walletTableColumnsResized(WalletTableColumnsResizedEvent event) { + public void walletTableChanged(WalletTableChangedEvent event) { if(persistsFor(event.getWallet()) && event.getTableType() != null && event.getWallet().getWalletTable(event.getTableType()) != null) { updateExecutor.execute(() -> dirtyPersistablesMap.computeIfAbsent(event.getWallet(), key -> new DirtyPersistables()).walletTable = event.getWalletTable()); } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/db/WalletTableDao.java b/src/main/java/com/sparrowwallet/sparrow/io/db/WalletTableDao.java index e3eb4577..11ce2f09 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/db/WalletTableDao.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/db/WalletTableDao.java @@ -12,20 +12,20 @@ import java.util.HashMap; import java.util.Map; public interface WalletTableDao { - @SqlQuery("select id, type, widths from walletTable where wallet = ?") + @SqlQuery("select id, type, widths, sortColumn, sortDirection from walletTable where wallet = ?") @RegisterRowMapper(WalletTableMapper.class) Map getForWalletId(Long id); - @SqlQuery("select id, type, widths from walletTable where type = ?") + @SqlQuery("select id, type, widths, sortColumn, sortDirection from walletTable where type = ?") @RegisterRowMapper(WalletTableMapper.class) Map getForTypeId(int tableTypeId); - @SqlUpdate("insert into walletTable (type, widths, wallet) values (?, ?, ?)") + @SqlUpdate("insert into walletTable (type, widths, sortColumn, sortDirection, wallet) values (?, ?, ?, ?, ?)") @GetGeneratedKeys("id") - long insertWalletTable(int tableType, Double[] widths, long wallet); + long insertWalletTable(int tableType, Double[] widths, int sortColumn, int sortDirection, long wallet); - @SqlUpdate("update walletTable set type = ?, widths = ?, wallet = ? where id = ?") - void updateWalletTable(int tableType, Double[] widths, long wallet, long id); + @SqlUpdate("update walletTable set type = ?, widths = ?, sortColumn = ?, sortDirection = ?, wallet = ? where id = ?") + void updateWalletTable(int tableType, Double[] widths, int sortColumn, int sortDirection, long wallet, long id); default void addWalletTables(Wallet wallet) { Map walletTables = new HashMap<>(wallet.getWalletTables()); @@ -39,11 +39,13 @@ public interface WalletTableDao { Map existing = getForTypeId(tableType.ordinal()); if(existing.isEmpty() && walletTable.getId() == null) { - long id = insertWalletTable(walletTable.getTableType().ordinal(), walletTable.getWidths(), wallet.getId()); + long id = insertWalletTable(walletTable.getTableType().ordinal(), walletTable.getWidths(), + walletTable.getSortColumn(), walletTable.getSortDirection().ordinal(), wallet.getId()); walletTable.setId(id); } else { Long existingId = existing.get(tableType) != null ? existing.get(tableType).getId() : walletTable.getId(); - updateWalletTable(walletTable.getTableType().ordinal(), walletTable.getWidths(), wallet.getId(), existingId); + updateWalletTable(walletTable.getTableType().ordinal(), walletTable.getWidths(), + walletTable.getSortColumn(), walletTable.getSortDirection().ordinal(), wallet.getId(), existingId); walletTable.setId(existingId); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/db/WalletTableMapper.java b/src/main/java/com/sparrowwallet/sparrow/io/db/WalletTableMapper.java index c307a391..152f542d 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/db/WalletTableMapper.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/db/WalletTableMapper.java @@ -1,5 +1,6 @@ package com.sparrowwallet.sparrow.io.db; +import com.sparrowwallet.drongo.wallet.SortDirection; import com.sparrowwallet.drongo.wallet.TableType; import com.sparrowwallet.drongo.wallet.WalletTable; import org.jdbi.v3.core.mapper.RowMapper; @@ -16,8 +17,10 @@ public class WalletTableMapper implements RowMapper() { diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java index 8e4fb3b6..2fb7ca71 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletForm.java @@ -594,9 +594,8 @@ public class WalletForm { } @Subscribe - public void walletTableColumnsResized(WalletTableColumnsResizedEvent event) { + public void walletTableChanged(WalletTableChangedEvent event) { if(event.getWallet() == wallet && event.getTableType() != null) { - wallet.getWalletTables().put(event.getTableType(), event.getWalletTable()); Platform.runLater(() -> EventManager.get().post(new WalletDataChangedEvent(wallet))); } } diff --git a/src/main/resources/com/sparrowwallet/sparrow/sql/V9__WalletTable.sql b/src/main/resources/com/sparrowwallet/sparrow/sql/V9__WalletTable.sql index 3590afa3..52610f98 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/sql/V9__WalletTable.sql +++ b/src/main/resources/com/sparrowwallet/sparrow/sql/V9__WalletTable.sql @@ -1,2 +1,2 @@ -create table walletTable (id identity not null, type integer not null, widths double precision array not null, wallet bigint not null); +create table walletTable (id identity not null, type integer not null, widths double precision array not null, sortColumn integer not null, sortDirection integer not null, wallet bigint not null); alter table keystore add column deviceRegistration varbinary(32) after externalPaymentCode; \ No newline at end of file diff --git a/src/main/resources/com/sparrowwallet/sparrow/wallet/addresses.fxml b/src/main/resources/com/sparrowwallet/sparrow/wallet/addresses.fxml index fbf4887b..ab2df4b4 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/wallet/addresses.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/wallet/addresses.fxml @@ -45,7 +45,7 @@
- +
@@ -63,7 +63,7 @@
- +
diff --git a/src/main/resources/com/sparrowwallet/sparrow/wallet/transactions.fxml b/src/main/resources/com/sparrowwallet/sparrow/wallet/transactions.fxml index 27e46a7d..89a6d6df 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/wallet/transactions.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/wallet/transactions.fxml @@ -64,7 +64,7 @@ - +