|
|
|
|
@ -38,7 +38,7 @@ from .util import MyTreeView, ColorScheme, MONOSPACE_FONT, EnterButton
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UTXOList(MyTreeView): |
|
|
|
|
_spend_set: Optional[Set[str]] # coins selected by the user to spend from |
|
|
|
|
_spend_set: Set[str] # coins selected by the user to spend from |
|
|
|
|
_utxo_dict: Dict[str, PartialTxInput] # coin name -> coin |
|
|
|
|
|
|
|
|
|
class Columns(IntEnum): |
|
|
|
|
@ -64,7 +64,7 @@ class UTXOList(MyTreeView):
|
|
|
|
|
def __init__(self, parent): |
|
|
|
|
super().__init__(parent, self.create_menu, |
|
|
|
|
stretch_column=self.stretch_column) |
|
|
|
|
self._spend_set = None |
|
|
|
|
self._spend_set = set() |
|
|
|
|
self._utxo_dict = {} |
|
|
|
|
self.wallet = self.parent.wallet |
|
|
|
|
|
|
|
|
|
@ -77,7 +77,7 @@ class UTXOList(MyTreeView):
|
|
|
|
|
def update(self): |
|
|
|
|
# not calling maybe_defer_update() as it interferes with coincontrol status bar |
|
|
|
|
utxos = self.wallet.get_utxos() |
|
|
|
|
self._maybe_reset_spend_list(utxos) |
|
|
|
|
self._maybe_reset_coincontrol(utxos) |
|
|
|
|
self._utxo_dict = {} |
|
|
|
|
self.model().clear() |
|
|
|
|
self.update_headers(self.__class__.headers) |
|
|
|
|
@ -103,7 +103,7 @@ class UTXOList(MyTreeView):
|
|
|
|
|
|
|
|
|
|
def update_coincontrol_bar(self): |
|
|
|
|
# update coincontrol status bar |
|
|
|
|
if self._spend_set is not None: |
|
|
|
|
if bool(self._spend_set): |
|
|
|
|
coins = [self._utxo_dict[x] for x in self._spend_set] |
|
|
|
|
coins = self._filter_frozen_coins(coins) |
|
|
|
|
amount = sum(x.value_sats() for x in coins) |
|
|
|
|
@ -121,7 +121,7 @@ class UTXOList(MyTreeView):
|
|
|
|
|
label = self.wallet.get_label_for_txid(utxo.prevout.txid.hex()) or self.wallet.get_label_for_address(address) |
|
|
|
|
utxo_item[self.Columns.LABEL].setText(label) |
|
|
|
|
SELECTED_TO_SPEND_TOOLTIP = _('Coin selected to be spent') |
|
|
|
|
if key in (self._spend_set or set()): |
|
|
|
|
if key in self._spend_set: |
|
|
|
|
tooltip = key + "\n" + SELECTED_TO_SPEND_TOOLTIP |
|
|
|
|
color = ColorScheme.GREEN.as_color(True) |
|
|
|
|
else: |
|
|
|
|
@ -149,29 +149,39 @@ class UTXOList(MyTreeView):
|
|
|
|
|
not self.wallet.is_frozen_coin(utxo))] |
|
|
|
|
return coins |
|
|
|
|
|
|
|
|
|
def set_spend_list(self, coins: Optional[List[PartialTxInput]]): |
|
|
|
|
if coins is not None: |
|
|
|
|
coins = self._filter_frozen_coins(coins) |
|
|
|
|
self._spend_set = {utxo.prevout.to_str() for utxo in coins} |
|
|
|
|
else: |
|
|
|
|
self._spend_set = None |
|
|
|
|
def add_to_coincontrol(self, coins: List[PartialTxInput]): |
|
|
|
|
coins = self._filter_frozen_coins(coins) |
|
|
|
|
for utxo in coins: |
|
|
|
|
self._spend_set.add(utxo.prevout.to_str()) |
|
|
|
|
self._refresh_coincontrol() |
|
|
|
|
|
|
|
|
|
def remove_from_coincontrol(self, coins: List[PartialTxInput]): |
|
|
|
|
for utxo in coins: |
|
|
|
|
self._spend_set.remove(utxo.prevout.to_str()) |
|
|
|
|
self._refresh_coincontrol() |
|
|
|
|
|
|
|
|
|
def clear_coincontrol(self): |
|
|
|
|
self._spend_set.clear() |
|
|
|
|
self._refresh_coincontrol() |
|
|
|
|
|
|
|
|
|
def _refresh_coincontrol(self): |
|
|
|
|
self.refresh_all() |
|
|
|
|
self.update_coincontrol_bar() |
|
|
|
|
self.selectionModel().clearSelection() |
|
|
|
|
|
|
|
|
|
def get_spend_list(self) -> Optional[Sequence[PartialTxInput]]: |
|
|
|
|
if self._spend_set is None: |
|
|
|
|
if bool(self._spend_set): |
|
|
|
|
return None |
|
|
|
|
utxos = [self._utxo_dict[x] for x in self._spend_set] |
|
|
|
|
return copy.deepcopy(utxos) # copy so that side-effects don't affect utxo_dict |
|
|
|
|
|
|
|
|
|
def _maybe_reset_spend_list(self, current_wallet_utxos: Sequence[PartialTxInput]) -> None: |
|
|
|
|
if self._spend_set is None: |
|
|
|
|
def _maybe_reset_coincontrol(self, current_wallet_utxos: Sequence[PartialTxInput]) -> None: |
|
|
|
|
if not bool(self._spend_set): |
|
|
|
|
return |
|
|
|
|
# if we spent one of the selected UTXOs, just reset selection |
|
|
|
|
utxo_set = {utxo.prevout.to_str() for utxo in current_wallet_utxos} |
|
|
|
|
if not all([prevout_str in utxo_set for prevout_str in self._spend_set]): |
|
|
|
|
self._spend_set = None |
|
|
|
|
self._spend_set.clear() |
|
|
|
|
|
|
|
|
|
def create_menu(self, position): |
|
|
|
|
selected = self.get_selected_outpoints() |
|
|
|
|
@ -180,10 +190,12 @@ class UTXOList(MyTreeView):
|
|
|
|
|
menu = QMenu() |
|
|
|
|
menu.setSeparatorsCollapsible(True) # consecutive separators are merged together |
|
|
|
|
coins = [self._utxo_dict[name] for name in selected] |
|
|
|
|
if len(coins) == 0: |
|
|
|
|
menu.addAction(_("Spend (select none)"), lambda: self.set_spend_list(coins)) |
|
|
|
|
else: |
|
|
|
|
menu.addAction(_("Spend"), lambda: self.set_spend_list(coins)) |
|
|
|
|
# coin control |
|
|
|
|
if coins: |
|
|
|
|
if all([utxo.prevout.to_str() in self._spend_set for utxo in coins]): |
|
|
|
|
menu.addAction(_("Remove from coin control"), lambda: self.remove_from_coincontrol(coins)) |
|
|
|
|
else: |
|
|
|
|
menu.addAction(_("Add to coin control"), lambda: self.add_to_coincontrol(coins)) |
|
|
|
|
|
|
|
|
|
if len(coins) == 1: |
|
|
|
|
utxo = coins[0] |
|
|
|
|
|