diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py index 4a18eab6e..025a5d67c 100644 --- a/electrum/gui/qt/main_window.py +++ b/electrum/gui/qt/main_window.py @@ -1593,7 +1593,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener): self.coincontrol_label.setTextInteractionFlags(Qt.TextSelectableByMouse) sb.addWidget(self.coincontrol_label) - clear_cc_button = EnterButton(_('Reset'), lambda: self.utxo_list.set_spend_list(None)) + clear_cc_button = EnterButton(_('Reset'), lambda: self.utxo_list.clear_coincontrol()) clear_cc_button.setStyleSheet("margin-right: 5px;") sb.addPermanentWidget(clear_cc_button) diff --git a/electrum/gui/qt/utxo_list.py b/electrum/gui/qt/utxo_list.py index 34309d8cc..e722eba3d 100644 --- a/electrum/gui/qt/utxo_list.py +++ b/electrum/gui/qt/utxo_list.py @@ -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]