def create_menu(self, position): menu = QMenu() selected = self.selectedItems() if not selected: menu.addAction(_("New contact"), lambda: self.parent.new_contact_dialog()) menu.addAction(_("Import file"), lambda: self.import_contacts()) else: names = [item.text(0) for item in selected] keys = [item.text(1) for item in selected] column = self.currentColumn() column_title = self.headerItem().text(column) column_data = '\n'.join([item.text(column) for item in selected]) menu.addAction( _("Copy %s") % column_title, lambda: self.parent.app.clipboard().setText(column_data)) if column in self.editable_columns: item = self.currentItem() menu.addAction( _("Edit %s") % column_title, lambda: self.editItem(item, column)) menu.addAction(_("Pay to"), lambda: self.parent.payto_contacts(keys)) menu.addAction(_("Delete"), lambda: self.parent.delete_contacts(keys)) URLs = [ web.BE_URL(self.config, 'addr', Address.from_string(key)) for key in keys if Address.is_valid(key) ] if URLs: menu.addAction(_("View on block explorer"), lambda: [webbrowser.open(URL) for URL in URLs]) run_hook('create_contact_menu', menu, selected) menu.exec_(self.viewport().mapToGlobal(position))
def _add_addr_to_io_menu_lists_for_widget(self, addr, show_list, copy_list, widget): if hasattr(addr, 'to_ui_string'): addr_text = addr.to_ui_string() if isinstance(addr, Address) and self.wallet.is_mine(addr): show_list += [(_("Address Details"), lambda: self._open_internal_link(addr_text))] addr_URL = web.BE_URL(self.main_window.config, web.ExplorerUrlParts.ADDR, addr) if addr_URL: show_list += [(_("View on block explorer"), lambda: webopen(addr_URL))] if isinstance(addr, ScriptOutput): action_text = _("Copy Script Text") elif isinstance(addr, PublicKey): action_text = _("Copy Public Key") else: action_text = _("Copy Address") copy_list += [(action_text, lambda: self._copy_to_clipboard(addr_text, widget))] # also add script hex copy to clipboard if isinstance(addr, ScriptOutput): hex_text = addr.to_script().hex() or '' if hex_text: copy_list += [ (_("Copy Script Hex"), lambda: self._copy_to_clipboard(hex_text, widget)) ]
def create_menu(self, position): item = self.currentItem() if not item: return column = self.currentColumn() tx_hash = item.data(0, Qt.UserRole) token_id = item.data(4, Qt.UserRole) if not tx_hash: return if column == 0: column_title = "ID" column_data = tx_hash else: column_title = self.headerItem().text(column) column_data = item.text(column) tx_URL = web.BE_URL(self.config, 'tx', tx_hash) height, conf, timestamp = self.wallet.get_tx_height(tx_hash) tx = self.wallet.transactions.get(tx_hash) is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx) is_unconfirmed = height <= 0 pr_key = self.wallet.invoices.paid.get(tx_hash) menu = QMenu() if not self.wallet.token_types.get(token_id): menu.addAction( _("Add this token"), lambda: SlpAddTokenDialog(self.parent, token_id_hex=token_id)) elif self.wallet.token_types.get(token_id)['decimals'] == '?': menu.addAction( _("Add this token"), lambda: SlpAddTokenDialog( self.parent, token_id_hex=token_id, allow_overwrite=True)) menu.addAction( _("Copy {}").format(column_title), lambda: self.parent.app.clipboard().setText(column_data)) if column in self.editable_columns: # We grab a fresh reference to the current item, as it has been deleted in a reported issue. menu.addAction( _("Edit {}").format(column_title), lambda: self.currentItem() and self.editItem( self.currentItem(), column)) menu.addAction(_("Details"), lambda: self.parent.show_transaction(tx)) if is_unconfirmed and tx: child_tx = self.wallet.cpfp(tx, 0) if child_tx: menu.addAction(_("Child pays for parent"), lambda: self.parent.cpfp(tx, child_tx)) if pr_key: menu.addAction(QIcon(":icons/seal.svg"), _("View invoice"), lambda: self.parent.show_invoice(pr_key)) if tx_URL: menu.addAction(_("View on block explorer"), lambda: webbrowser.open(tx_URL)) if self.wallet.get_slp_token_info(tx_hash)['validity'] == 2: menu.addAction(_("Revalidate"), lambda: self.wallet.revalidate(tx_hash, tx)) menu.exec_(self.viewport().mapToGlobal(position))
def create_menu(self, position): from electroncash.wallet import Multisig_Wallet is_multisig = isinstance(self.wallet, Multisig_Wallet) can_delete = self.wallet.can_delete_address() selected = self.selectedItems() multi_select = len(selected) > 1 addrs = [item.data(0, Qt.UserRole) for item in selected] if not addrs: return addrs = [addr for addr in addrs if isinstance(addr, Address)] menu = QMenu() if not multi_select: item = self.itemAt(position) col = self.currentColumn() if not item: return if not addrs: item.setExpanded(not item.isExpanded()) return addr = addrs[0] column_title = self.headerItem().text(col) if col == 0: copy_text = addr.to_full_ui_string() else: copy_text = item.text(col) menu.addAction(_("Copy {}").format(column_title), lambda: self.parent.app.clipboard().setText(copy_text)) menu.addAction(_('Details'), lambda: self.parent.show_address(addr)) if col in self.editable_columns: menu.addAction(_("Edit {}").format(column_title), lambda: self.editItem(self.itemAt(position), # NB: C++ item may go away if this widget is refreshed while menu is up -- so need to re-grab and not store in lamba. See #953 col)) menu.addAction(_("Request payment"), lambda: self.parent.receive_at(addr)) if self.wallet.can_export(): menu.addAction(_("Private key"), lambda: self.parent.show_private_key(addr)) if not is_multisig and not self.wallet.is_watching_only(): menu.addAction(_("Sign/verify message"), lambda: self.parent.sign_verify_message(addr)) menu.addAction(_("Encrypt/decrypt message"), lambda: self.parent.encrypt_message(addr)) if can_delete: menu.addAction(_("Remove from wallet"), lambda: self.parent.remove_address(addr)) addr_URL = web.BE_URL(self.config, 'addr', addr) if addr_URL: menu.addAction(_("View on block explorer"), lambda: webbrowser.open(addr_URL)) freeze = self.parent.set_frozen_state if any(self.wallet.is_frozen(addr) for addr in addrs): menu.addAction(_("Unfreeze"), partial(freeze, addrs, False)) if not all(self.wallet.is_frozen(addr) for addr in addrs): menu.addAction(_("Freeze"), partial(freeze, addrs, True)) coins = self.wallet.get_spendable_coins(domain = addrs, config = self.config) if coins: menu.addAction(_("Spend from"), partial(self.parent.spend_coins, coins)) run_hook('receive_menu', menu, addrs, self.wallet) menu.exec_(self.viewport().mapToGlobal(position))
def create_menu(self, position): item = self.currentItem() if not item: return column = self.currentColumn() tx_hash = item.data(0, Qt.UserRole) if not tx_hash: return if column is 0: column_title = "ID" column_data = tx_hash else: column_title = self.headerItem().text(column) column_data = item.text(column) tx_URL = web.BE_URL(self.config, 'tx', tx_hash) height, conf, timestamp = self.wallet.get_tx_height(tx_hash) tx = self.wallet.transactions.get(tx_hash) if not tx: return # this happens sometimes on wallet synch when first starting up. is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx) is_unconfirmed = height <= 0 pr_key = self.wallet.invoices.paid.get(tx_hash) menu = QMenu() menu.addAction( _("Copy {}").format(column_title), lambda: self.parent.app.clipboard().setText(column_data)) if column in self.editable_columns: # We grab a fresh reference to the current item, as it has been deleted in a reported issue. menu.addAction( _("Edit {}").format(column_title), lambda: self.currentItem() and self.editItem( self.currentItem(), column)) label = self.wallet.get_label(tx_hash) or None menu.addAction(_("Details"), lambda: self.parent.show_transaction(tx, label)) if is_unconfirmed and tx: child_tx = self.wallet.cpfp(tx, 0) if child_tx: menu.addAction(_("Child pays for parent"), lambda: self.parent.cpfp(tx, child_tx)) if pr_key: menu.addAction(self.invoiceIcon, _("View invoice"), lambda: self.parent.show_invoice(pr_key)) if tx_URL: menu.addAction(_("View on block explorer"), lambda: webbrowser.open(tx_URL)) run_hook("history_list_context_menu_setup", self, menu, item, tx_hash) # Plugins can modify menu menu.exec_(self.viewport().mapToGlobal(position))
def open_be_url(link): if link: try: kind, thing = link.split(':') url = web.BE_URL(self.main_window.config, kind, thing) except: url = None if url: webopen( url ) else: self.show_error(_('Unable to open in block explorer. Please be sure your block explorer is configured correctly in preferences.'))
def open_be_url(link): if link: try: _, txid = link.split(':') url = web.BE_URL(self.main_window.config, web.ExplorerUrlParts.TX, txid) except: raise url = None if url: webopen( url ) else: self.show_error(_('Unable to open in block explorer. Please be sure your block explorer is configured correctly in preferences.'))
def open_link(link): if Address.is_valid(link): addr = Address.from_string(link) if wallet.is_mine(addr): parent.show_address(addr) else: addr_URL = web.BE_URL(parent.config, 'addr', addr) if addr_URL: webopen(addr_URL) return if link.startswith('http'): webopen(link)
def create_menu(self, position): self.selectedIndexes() item = self.currentItem() if not item: return column = self.currentColumn() tx_hash = item.data(0, Qt.UserRole) if not tx_hash: return if column is 0: column_title = "ID" column_data = tx_hash else: column_title = self.headerItem().text(column) column_data = item.text(column) tx_URL = web.BE_URL(self.config, 'tx', tx_hash) height, conf, timestamp = self.wallet.get_tx_height(tx_hash) tx = self.wallet.transactions.get(tx_hash) is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx) is_unconfirmed = height <= 0 pr_key = self.wallet.invoices.paid.get(tx_hash) menu = QMenu() menu.addAction( _("Copy {}").format(column_title), lambda: self.parent.app.clipboard().setText(column_data)) if column in self.editable_columns: # We grab a fresh reference to the current item, as it has been deleted in a reported issue. menu.addAction( _("Edit {}").format(column_title), lambda: self.currentItem() and self.editItem( self.currentItem(), column)) menu.addAction(_("Details"), lambda: self.parent.show_transaction(tx)) if is_unconfirmed and tx: child_tx = self.wallet.cpfp(tx, 0) if child_tx: menu.addAction(_("Child pays for parent"), lambda: self.parent.cpfp(tx, child_tx)) if pr_key: menu.addAction(QIcon(":icons/seal"), _("View invoice"), lambda: self.parent.show_invoice(pr_key)) if tx_URL: menu.addAction(_("View on block explorer"), lambda: webbrowser.open(tx_URL)) menu.exec_(self.viewport().mapToGlobal(position))
def open_link(link): if Address.is_valid(link): addr = Address.from_string(link) if wallet.is_mine(addr): parent.show_address(addr) else: addr_URL = web.BE_URL(parent.config, web.ExplorerUrlParts.ADDR, addr) if addr_URL: webopen(addr_URL) return if link.startswith('http'): webopen(link) elif len(link) == 64: # 64 character txid tx = wallet.transactions.get(link) if tx: parent.show_transaction(tx, tx_desc=wallet.get_label(link)) else: parent.do_process_from_txid(txid=link, tx_desc=wallet.get_label(link)) return
def create_menu(self, position): self.selectedIndexes() item = self.currentItem() if not item: return column = self.currentColumn() tx_hash = item.data(0, Qt.UserRole) if not tx_hash: return if column is 0: column_title = "ID" column_data = tx_hash else: column_title = self.headerItem().text(column) column_data = item.text(column) tx_URL = web.BE_URL(self.config, 'tx', tx_hash) height, conf, timestamp = self.wallet.get_tx_height(tx_hash) tx = self.wallet.transactions.get(tx_hash) otherpubkey = item.data(1, Qt.UserRole) menu = QMenu() if otherpubkey: menu.addAction(_("Reply"), lambda: self.parent.write_message(otherpubkey.hex())) menu.addAction(_("Copy {}").format(column_title), lambda: self.parent.parent.app.clipboard().setText(column_data)) if column in self.editable_columns: # We grab a fresh reference to the current item, as it has been deleted in a reported issue. menu.addAction(_("Edit {}").format(column_title), lambda: self.currentItem() and self.editItem(self.currentItem(), column)) menu.addAction(_("Details"), lambda: self.parent.parent.show_transaction(tx)) if tx_URL: menu.addAction(_("View on block explorer"), lambda: webbrowser.open(tx_URL)) menu.exec_(self.viewport().mapToGlobal(position))
def doOpenBlockExplorerTX(txid: str): URL = web.BE_URL(self.config, 'tx', txid) webopen(URL)
def create_menu(self, position): menu = QMenu() selected = self.selectedItems() i2c = self._i2c ca_unverified = self._get_ca_unverified(include_temp=False) if selected: names = [item.text(1) for item in selected] keys = [i2c(item) for item in selected] payable_keys = [k for k in keys if k.type != 'cashacct_T'] deletable_keys = [k for k in keys if k.type in contact_types] needs_verif_keys = [k for k in keys if k in ca_unverified] column = self.currentColumn() column_title = self.headerItem().text(column) column_data = '\n'.join([item.text(column) for item in selected]) item = self.currentItem() typ = i2c(item).type if item else 'unknown' ca_info = None if item and typ in ('cashacct', 'cashacct_W'): ca_info = self.wallet.cashacct.get_verified(i2c(item).name) if column == 1 and len(selected) == 1: # hack .. for Cash Accounts just say "Copy Cash Account" column_title = _('Cash Account') if ca_info: column_data = self.wallet.cashacct.fmt_info(ca_info, emoji=True) menu.addAction(_("Copy {}").format(column_title), lambda: self.parent.app.clipboard().setText(column_data)) if item and column in self.editable_columns and self.on_permit_edit(item, column): key = item.data(0, self.DataRoles.Contact) # this key & find_item business is so we don't hold a reference # to the ephemeral item, which may be deleted while the # context menu is up. Accessing the item after on_update runs # means the item is deleted and you get a C++ object deleted # runtime error. menu.addAction(_("Edit {}").format(column_title), lambda: self._on_edit_item(key, column)) a = menu.addAction(_("Pay to"), lambda: self.parent.payto_contacts(payable_keys)) if needs_verif_keys or not payable_keys: a.setDisabled(True) a = menu.addAction(_("Delete"), lambda: self.parent.delete_contacts(deletable_keys)) if not deletable_keys: a.setDisabled(True) # Add sign/verify and encrypt/decrypt menu - but only if just 1 thing selected if len(keys) == 1 and Address.is_valid(keys[0]): signAddr = Address.from_string(keys[0]) a = menu.addAction(_("Sign/verify message") + "...", lambda: self.parent.sign_verify_message(signAddr)) if signAddr.kind != Address.ADDR_P2PKH: a.setDisabled(True) # We only allow this for P2PKH since it makes no sense for P2SH (ambiguous public key) URLs = [web.BE_URL(self.config, 'addr', Address.from_string(key.address)) for key in keys if Address.is_valid(key.address)] a = menu.addAction(_("View on block explorer"), lambda: [URL and webopen(URL) for URL in URLs]) if not any(URLs): a.setDisabled(True) if ca_info: menu.addAction(_("View registration tx..."), lambda: self.parent.do_process_from_txid(txid=ca_info.txid, tx_desc=self.wallet.get_label(ca_info.txid))) if typ in ('cashacct_W', 'cashacct'): _contact_d = i2c(item) menu.addAction(_("Details..."), lambda: cashacctqt.cash_account_detail_dialog(self.parent, _contact_d.name)) menu.addSeparator() menu.addAction(self.icon_cashacct, _("Add Contact") + " - " + _("Cash Account"), self.new_cash_account_contact_dialog) menu.addAction(self.icon_contacts, _("Add Contact") + " - " + _("Address"), self.parent.new_contact_dialog) menu.addSeparator() menu.addAction(self.icon_cashacct, _("Register Cash Account..."), self.parent.register_new_cash_account) menu.addSeparator() menu.addAction(QIcon(":icons/import.svg" if not ColorScheme.dark_scheme else ":icons/import_dark_theme.svg"), _("Import file"), self.import_contacts) if not self.parent.contacts.empty: menu.addAction(QIcon(":icons/save.svg" if not ColorScheme.dark_scheme else ":icons/save_dark_theme.svg"), _("Export file"), self.export_contacts) menu.addSeparator() a = menu.addAction(_("Show My Cash Accounts"), self.toggle_show_my_cashaccts) a.setCheckable(True) a.setChecked(self.show_my_cashaccts) if ca_unverified: def kick_off_verify(): bnums = set() for contact in ca_unverified: tup = self.wallet.cashacct.parse_string(contact.name) if not tup: continue bnums.add(tup[1]) # number ret = cashacctqt.verify_multiple_blocks(bnums, self.parent, self.wallet) if ret is None: # user cancel return verified = ca_unverified - self._get_ca_unverified() if not verified: self.parent.show_error(_("Cash Account verification failure")) menu.addSeparator() num = len(ca_unverified) a = menu.addAction(self.icon_unverif, ngettext("Verify {count} Cash Account", "Verify {count} Cash Accounts", num).format(count=num), kick_off_verify) if not self.wallet.network: a.setDisabled(True) run_hook('create_contact_menu', menu, selected) menu.exec_(self.viewport().mapToGlobal(position))
def doOpenBlockExplorerAddress(address: Address): URL = web.BE_URL(self.config, 'addr', address) webopen(URL)
def create_menu(self, position): from electroncash.wallet import Multisig_Wallet is_multisig = isinstance(self.wallet, Multisig_Wallet) can_delete = self.wallet.can_delete_address() selected = self.selectedItems() multi_select = len(selected) > 1 addrs = [item.data(0, self.DataRoles.address) for item in selected] if not addrs: return addrs = [addr for addr in addrs if isinstance(addr, Address)] menu = QMenu() where_to_insert_dupe_copy_cash_account = None if not multi_select: item = self.itemAt(position) col = self.currentColumn() if not item: return if not addrs: item.setExpanded(not item.isExpanded()) return addr = addrs[0] column_title = self.headerItem().text(col) alt_copy_text, alt_column_title = None, None if col == 0: copy_text = addr.to_full_ui_string() if Address.FMT_UI == Address.FMT_LEGACY: alt_copy_text, alt_column_title = addr.to_full_string( Address.FMT_CASHADDR), _('Cash Address') else: alt_copy_text, alt_column_title = addr.to_full_string( Address.FMT_LEGACY), _('Legacy Address') else: copy_text = item.text(col) def doCopy(txt): txt = txt.strip() self.parent.copy_to_clipboard(txt) menu.addAction( _("Copy {}").format(column_title), lambda: doCopy(copy_text)) if alt_copy_text and alt_column_title: # Add 'Copy Legacy Address' and 'Copy Cash Address' alternates if right-click is on column 0 menu.addAction( _("Copy {}").format(alt_column_title), lambda: doCopy(alt_copy_text)) a = menu.addAction( _('Details') + "...", lambda: self.parent.show_address(addr)) if col == 0: where_to_insert_dupe_copy_cash_account = a if col in self.editable_columns: menu.addAction( _("Edit {}").format(column_title), lambda: self.editItem( self.itemAt( position ), # NB: C++ item may go away if this widget is refreshed while menu is up -- so need to re-grab and not store in lamba. See #953 col)) menu.addAction(_("Request payment"), lambda: self.parent.receive_at(addr)) if self.wallet.can_export(): menu.addAction(_("Private key"), lambda: self.parent.show_private_key(addr)) if not is_multisig and not self.wallet.is_watching_only(): menu.addAction(_("Sign/verify message"), lambda: self.parent.sign_verify_message(addr)) menu.addAction(_("Encrypt/decrypt message"), lambda: self.parent.encrypt_message(addr)) if can_delete: menu.addAction(_("Remove from wallet"), lambda: self.parent.remove_address(addr)) addr_URL = web.BE_URL(self.config, 'addr', addr) if addr_URL: menu.addAction(_("View on block explorer"), lambda: webopen(addr_URL)) freeze = self.parent.set_frozen_state if any(self.wallet.is_frozen(addr) for addr in addrs): menu.addAction(_("Unfreeze"), partial(freeze, addrs, False)) if not all(self.wallet.is_frozen(addr) for addr in addrs): menu.addAction(_("Freeze"), partial(freeze, addrs, True)) coins = self.wallet.get_spendable_coins(domain=addrs, config=self.config) if coins: menu.addAction(_("Spend from"), partial(self.parent.spend_coins, coins)) # Add Cash Accounts section at the end, if relevant if not multi_select: ca_list = item.data(0, self.DataRoles.cash_accounts) menu.addSeparator() a1 = menu.addAction(_("Cash Accounts"), lambda: None) a1.setDisabled(True) if ca_list: ca_default = self._ca_get_default(ca_list) for ca_info in ca_list: ca_text = self.wallet.cashacct.fmt_info( ca_info, ca_info.minimal_chash) ca_text_em = self.wallet.cashacct.fmt_info( ca_info, ca_info.minimal_chash, emoji=True) m = menu.addMenu(ca_info.emoji + " " + ca_text) a_ca_copy = m.addAction( _("Copy Cash Account"), lambda x=None, text=ca_text_em: doCopy(text)) a = m.addAction( _("Details") + "...", lambda x=None, ca_text=ca_text: cashacctqt. cash_account_detail_dialog(self.parent, ca_text)) a = m.addAction(_("View registration tx") + "...", lambda x=None, ca=ca_info: self.parent. do_process_from_txid(txid=ca.txid)) a = a_def = m.addAction(_("Make default for address"), lambda x=None, ca=ca_info: self. _ca_set_default(ca, True)) if ca_info == ca_default: if where_to_insert_dupe_copy_cash_account and a_ca_copy: # insert a dupe of "Copy Cash Account" for the default cash account for this address in the top-level menu menu.insertAction( where_to_insert_dupe_copy_cash_account, a_ca_copy) m.setTitle(m.title() + " " + "★") a_def.setDisabled(True) a_def.setCheckable(True) a_def.setChecked(True) a_def.setText(_("Is default for address")) else: a1.setText(_("No Cash Accounts")) a_new = menu.addAction(_("Register new..."), lambda x=None, addr=addr: self.parent. register_new_cash_account(addr)) if not ca_list and __class__._cashacct_icon: # we only add an icon if there are no cashaccounts # for this address. This is because the icon alongside the emojis # made the ui look too "busy"... a_new.setIcon(__class__._cashacct_icon) run_hook('receive_menu', menu, addrs, self.wallet) menu.exec_(self.viewport().mapToGlobal(position))
def create_menu(self, position): from electroncash.wallet import Multisig_Wallet is_multisig = isinstance(self.wallet, Multisig_Wallet) can_delete = self.wallet.can_delete_address() selected = self.selectedItems() multi_select = len(selected) > 1 addrs = [item.data(0, self.DataRoles.address) for item in selected] if not addrs: return addrs = [addr for addr in addrs if isinstance(addr, Address)] menu = QMenu() where_to_insert_dupe_copy_cash_account = None def doCopy(txt): txt = txt.strip() self.parent.copy_to_clipboard(txt) col = self.currentColumn() column_title = self.headerItem().text(col) if not multi_select: item = self.itemAt(position) if not item: return if not addrs: item.setExpanded(not item.isExpanded()) return addr = addrs[0] alt_copy_text, alt_column_title = None, None if col == 0: copy_text = addr.to_full_ui_string() if Address.FMT_UI == Address.FMT_LEGACY: alt_copy_text, alt_column_title = addr.to_full_string( Address.FMT_CASHADDR), _('Cash Address') else: alt_copy_text, alt_column_title = addr.to_full_string( Address.FMT_LEGACY), _('Legacy Address') else: copy_text = item.text(col) menu.addAction( _("Copy {}").format(column_title), lambda: doCopy(copy_text)) a = menu.addAction( _('Details') + "...", lambda: self.parent.show_address(addr)) if col == 0: where_to_insert_dupe_copy_cash_account = a if col in self.editable_columns: menu.addAction( _("Edit {}").format(column_title), lambda: self.editItem( self.itemAt( position ), # NB: C++ item may go away if this widget is refreshed while menu is up -- so need to re-grab and not store in lamba. See #953 col)) menu.addAction(_("Request payment"), lambda: self.parent.receive_at(addr)) if self.wallet.can_export(): menu.addAction(_("Private key"), lambda: self.parent.show_private_key(addr)) if not is_multisig and not self.wallet.is_watching_only(): menu.addAction(_("Sign/verify message"), lambda: self.parent.sign_verify_message(addr)) menu.addAction(_("Encrypt/decrypt message"), lambda: self.parent.encrypt_message(addr)) if can_delete: menu.addAction(_("Remove from wallet"), lambda: self.parent.remove_address(addr)) addr_URL = web.BE_URL(self.config, 'addr', addr) if addr_URL: menu.addAction(_("View on block explorer"), lambda: webopen(addr_URL)) else: # multi-select if col > -1: texts, alt_copy, alt_copy_text = None, None, None if col == 0: # address column texts = [a.to_ui_string() for a in addrs] # Add additional copy option: "Address, Balance (n)" alt_copy = _("Copy {}").format( _("Address") + ", " + _("Balance")) + f" ({len(addrs)})" alt_copy_text = "\n".join([ a.to_ui_string() + ", " + self.parent.format_amount( sum(self.wallet.get_addr_balance(a))) for a in addrs ]) else: texts = [i.text(col).strip() for i in selected] texts = [t for t in texts if t] # omit empty items if texts: copy_text = '\n'.join(texts) menu.addAction( _("Copy {}").format(column_title) + f" ({len(texts)})", lambda: doCopy(copy_text)) if alt_copy and alt_copy_text: menu.addAction(alt_copy, lambda: doCopy(alt_copy_text)) freeze = self.parent.set_frozen_state if any(self.wallet.is_frozen(addr) for addr in addrs): menu.addAction(_("Unfreeze"), partial(freeze, addrs, False)) if not all(self.wallet.is_frozen(addr) for addr in addrs): menu.addAction(_("Freeze"), partial(freeze, addrs, True)) coins = self.wallet.get_spendable_coins(domain=addrs, config=self.config) if coins: menu.addAction(_("Spend from"), partial(self.parent.spend_coins, coins)) # Add Cash Accounts section at the end, if relevant run_hook('receive_menu', menu, addrs, self.wallet) menu.exec_(self.viewport().mapToGlobal(position))
def __init__(self, main_window, address, password): QDialog.__init__(self, parent=main_window) self.main_window = main_window self.wallet = main_window.wallet self.config = main_window.config #self.address = address # address to spend from self.password = password # save for funding if address: self.fund_domain = [address] self.fund_change_address = address self.default_redeem_address = address self.entropy_address = address else: self.fund_domain = None self.fund_change_address = None self.default_redeem_address = self.wallet.get_unused_address() self.entropy_address = self.wallet.get_addresses()[0] # Extract private key index = self.wallet.get_address_index(self.entropy_address) key = self.wallet.keystore.get_private_key(index, password) privkey = int.from_bytes(key[0],'big') # Create contract derived from private key self.contract = SplitContract(privkey) self.setWindowTitle(_("OP_CHECKDATASIG Coin Splitting")) vbox = QVBoxLayout() self.setLayout(vbox) l = QLabel(_("Master address") + ": " + self.entropy_address.to_ui_string()) l.setTextInteractionFlags(Qt.TextSelectableByMouse) vbox.addWidget(l) l = QLabel(_("Split contract") + ": " + self.contract.address.to_ui_string()) l.setTextInteractionFlags(Qt.TextSelectableByMouse) vbox.addWidget(l) hbox = QHBoxLayout() vbox.addLayout(hbox) addr_URL = web.BE_URL(self.config, 'addr', self.contract.address) b = QPushButton(_("View on block explorer")) b.clicked.connect(lambda: webbrowser.open(addr_URL)) hbox.addWidget(b) if not addr_URL: b.setDisabled(True) b = QPushButton(_("View redeem script...")) b.clicked.connect(self.showscript) hbox.addWidget(b) hbox.addStretch(1) l = QLabel("<b>%s</b>"%(_("Splittable coin creation/finding:"))) vbox.addWidget(l) hbox = QHBoxLayout() vbox.addLayout(hbox) b = QPushButton(_("Create splittable coin")) b.clicked.connect(self.fund) hbox.addWidget(b) self.fund_button = b b = QPushButton("x") b.clicked.connect(self.search) hbox.addWidget(b) self.search_button = b hbox.addStretch(1) grid = QGridLayout() vbox.addLayout(grid) l = QLabel(_("TXID")) grid.addWidget(l, 0, 0) l = QLabel(_("Out#")) grid.addWidget(l, 0, 1) l = QLabel(_("Value (sats)")) grid.addWidget(l, 0, 2) self.fund_txid_e = QLineEdit() self.fund_txid_e.textEdited.connect(self.changed_coin) grid.addWidget(self.fund_txid_e, 1, 0) self.fund_txout_e = QLineEdit() self.fund_txout_e.setMaximumWidth(40) self.fund_txout_e.setAlignment(Qt.AlignRight) self.fund_txout_e.textEdited.connect(self.changed_coin) grid.addWidget(self.fund_txout_e, 1, 1) self.fund_value_e = QLineEdit() self.fund_value_e.setMaximumWidth(70) self.fund_value_e.setAlignment(Qt.AlignRight) self.fund_value_e.textEdited.connect(self.changed_coin) grid.addWidget(self.fund_value_e, 1, 2) l = QLabel("<b>%s</b>"%(_("Splittable coin spending:"))) vbox.addWidget(l) self.option1_rb = QRadioButton(_("Only spend splittable coin")) self.option2_rb = QRadioButton() self.option3_rb = QRadioButton(_("Combine with all coins from wallet") + ' "%s"'%(self.wallet.basename())) vbox.addWidget(self.option1_rb) vbox.addWidget(self.option2_rb) vbox.addWidget(self.option3_rb) if self.fund_change_address: self.option2_rb.setText(_("Combine with all coins from address") + " %.10s..."%(self.fund_change_address.to_ui_string())) self.option2_rb.setChecked(True) else: self.option3_rb.setChecked(True) self.option2_rb.setHidden(True) hbox = QHBoxLayout() vbox.addLayout(hbox) l = QLabel(_("Output to:")) hbox.addWidget(l) self.redeem_address_e = QLineEdit() self.redeem_address_e.setText(self.default_redeem_address.to_full_ui_string()) hbox.addWidget(self.redeem_address_e) hbox = QHBoxLayout() vbox.addLayout(hbox) b = QPushButton(_("Redeem with split (CDS chain only)")) b.clicked.connect(lambda: self.spend('redeem')) hbox.addWidget(b) self.redeem_button = b b = QPushButton(_("Refund (any chain)")) b.clicked.connect(lambda: self.spend('refund')) hbox.addWidget(b) self.refund_button = b self.changed_coin() self.search_done_signal.connect(self.search_done) self.search()
def __init__(self, parent, plugin, wallet_name, address): QDialog.__init__(self, parent) self.main_window = parent self.wallet = parent.wallet self.plugin = plugin self.wallet_name = wallet_name self.config = parent.config self.password = None if self.wallet.has_password(): self.main_window.show_error( _("Coinsplitter Plugin requires password. It will get access to your private keys." )) self.password = parent.password_dialog() if not self.password: return if address: self.fund_domain = [address] self.fund_change_address = address self.default_redeem_address = address self.entropy_address = address else: self.fund_domain = None self.fund_change_address = None self.default_redeem_address = self.wallet.get_unused_address() self.entropy_address = self.wallet.get_addresses()[0] if not self.default_redeem_address: # self.wallet.get_unused_address() returns None for imported privkey wallets. self.main_window.show_error( _("For imported private key wallets, please open the coin splitter from the Addresses tab by right clicking on an address, instead of via the Tools menu." )) return # Extract private key index = self.wallet.get_address_index(self.entropy_address) key = self.wallet.keystore.get_private_key(index, self.password) privkey = int.from_bytes(key[0], 'big') if isinstance(self.wallet, Multisig_Wallet): self.main_window.show_error( "Multi-sig wallet support is partial.\nThe splitter coin itself is *not* multisig and belongs to you alone (it cannot be redeemed by other parties)." ) self.contract = SplitContract(privkey) self.setWindowTitle(_("OP_CHECKDATASIG Coin Splitting")) vbox = QVBoxLayout() self.setLayout(vbox) l = QLabel( _("Master address") + ": " + self.entropy_address.to_ui_string()) l.setTextInteractionFlags(Qt.TextSelectableByMouse) vbox.addWidget(l) l = QLabel( _("Split contract") + ": " + self.contract.address.to_ui_string()) l.setTextInteractionFlags(Qt.TextSelectableByMouse) vbox.addWidget(l) hbox = QHBoxLayout() vbox.addLayout(hbox) addr_URL = web.BE_URL(self.config, 'addr', self.contract.address) b = QPushButton(_("View on block explorer")) b.clicked.connect(lambda: webbrowser.open(addr_URL)) hbox.addWidget(b) if not addr_URL: b.setDisabled(True) b = QPushButton(_("View redeem script...")) b.clicked.connect(self.showscript) hbox.addWidget(b) hbox.addStretch(1) l = QLabel("<b>%s</b>" % (_("Splittable coin creation/finding:"))) vbox.addWidget(l) hbox = QHBoxLayout() vbox.addLayout(hbox) b = QPushButton(_("Create splittable coin")) b.clicked.connect(self.fund) hbox.addWidget(b) self.fund_button = b b = QPushButton("x") b.clicked.connect(self.search) hbox.addWidget(b) self.search_button = b hbox.addStretch(1) grid = QGridLayout() vbox.addLayout(grid) l = QLabel(_("TXID")) grid.addWidget(l, 0, 0) l = QLabel(_("Out#")) grid.addWidget(l, 0, 1) l = QLabel(_("Value (sats)")) grid.addWidget(l, 0, 2) self.fund_txid_e = QLineEdit() self.fund_txid_e.textEdited.connect(self.changed_coin) grid.addWidget(self.fund_txid_e, 1, 0) self.fund_txout_e = QLineEdit() self.fund_txout_e.setMaximumWidth(40) self.fund_txout_e.setAlignment(Qt.AlignRight) self.fund_txout_e.textEdited.connect(self.changed_coin) grid.addWidget(self.fund_txout_e, 1, 1) self.fund_value_e = QLineEdit() self.fund_value_e.setMaximumWidth(70) self.fund_value_e.setAlignment(Qt.AlignRight) self.fund_value_e.textEdited.connect(self.changed_coin) grid.addWidget(self.fund_value_e, 1, 2) l = QLabel("<b>%s</b>" % (_("Splittable coin spending:"))) vbox.addWidget(l) self.option1_rb = QRadioButton(_("Only spend splittable coin")) self.option2_rb = QRadioButton() self.option3_rb = QRadioButton( _("Combine with all coins from wallet") + ' "%s"' % (self.wallet.basename())) vbox.addWidget(self.option1_rb) vbox.addWidget(self.option2_rb) vbox.addWidget(self.option3_rb) if self.fund_change_address: self.option2_rb.setText( _("Combine with all coins from address") + " %.10s..." % (self.fund_change_address.to_ui_string())) self.option2_rb.setChecked(True) else: self.option3_rb.setChecked(True) self.option2_rb.setHidden(True) hbox = QHBoxLayout() vbox.addLayout(hbox) l = QLabel(_("Output to:")) hbox.addWidget(l) self.redeem_address_e = QLineEdit() self.redeem_address_e.setText( self.default_redeem_address.to_full_ui_string()) hbox.addWidget(self.redeem_address_e) hbox = QHBoxLayout() vbox.addLayout(hbox) b = QPushButton(_("Redeem with split (CDS chain only)")) b.clicked.connect(lambda: self.spend('redeem')) hbox.addWidget(b) self.redeem_button = b b = QPushButton(_("Refund (any chain)")) b.clicked.connect(lambda: self.spend('refund')) hbox.addWidget(b) self.refund_button = b self.changed_coin() self.search_done_signal.connect(self.search_done) self.search() self.show()
def create_menu(self, position): if self.picker: # picker mode has no menu return from electroncash.wallet import Multisig_Wallet is_multisig = isinstance(self.wallet, Multisig_Wallet) can_delete = self.wallet.can_delete_address() selected = self.selectedItems() multi_select = len(selected) > 1 addrs = [item.data(0, self.DataRoles.address) for item in selected] if not addrs: return addrs = [addr for addr in addrs if isinstance(addr, Address)] menu = QMenu() where_to_insert_dupe_copy_cash_account = None def doCopy(txt): txt = txt.strip() self.parent.copy_to_clipboard(txt) col = self.currentColumn() column_title = self.headerItem().text(col) if not multi_select: item = self.itemAt(position) if not item: return if not addrs: item.setExpanded(not item.isExpanded()) return addr = addrs[0] alt_copy_text, alt_column_title = None, None if col == 0: copy_text = addr.to_full_ui_string() if Address.FMT_UI == Address.FMT_LEGACY: alt_copy_text, alt_column_title = addr.to_full_string(Address.FMT_CASHADDR), _('Cash Address') else: alt_copy_text, alt_column_title = addr.to_full_string(Address.FMT_LEGACY), _('Legacy Address') else: copy_text = item.text(col) menu.addAction(_("Copy {}").format(column_title), lambda: doCopy(copy_text)) if alt_copy_text and alt_column_title: # Add 'Copy Legacy Address' and 'Copy Cash Address' alternates if right-click is on column 0 menu.addAction(_("Copy {}").format(alt_column_title), lambda: doCopy(alt_copy_text)) a = menu.addAction(_('Details') + "...", lambda: self.parent.show_address(addr)) if col == 0: where_to_insert_dupe_copy_cash_account = a if col in self.editable_columns: menu.addAction(_("Edit {}").format(column_title), lambda: self.editItem(self.itemAt(position), # NB: C++ item may go away if this widget is refreshed while menu is up -- so need to re-grab and not store in lamba. See #953 col)) a = menu.addAction(_("Request payment"), lambda: self.parent.receive_at(addr)) if self.wallet.get_num_tx(addr) or self.wallet.has_payment_request(addr): # This address cannot be used for a payment request because # the receive tab will refuse to display it and will instead # create a request with a new address, if we were to call # self.parent.receive_at(addr). This is because the recieve tab # now strongly enforces no-address-reuse. See #1552. a.setDisabled(True) if self.wallet.can_export(): menu.addAction(_("Private key"), lambda: self.parent.show_private_key(addr)) if not is_multisig and not self.wallet.is_watching_only(): menu.addAction(_("Sign/verify message") + "...", lambda: self.parent.sign_verify_message(addr)) menu.addAction(_("Encrypt/decrypt message") + "...", lambda: self.parent.encrypt_message(addr)) if can_delete: menu.addAction(_("Remove from wallet"), lambda: self.parent.remove_address(addr)) addr_URL = web.BE_URL(self.config, web.ExplorerUrlParts.ADDR, addr) if addr_URL: menu.addAction(_("View on block explorer"), lambda: webopen(addr_URL)) else: # multi-select if col > -1: texts, alt_copy, alt_copy_text = None, None, None if col == 0: # address column texts = [a.to_full_ui_string() for a in addrs] # Add additional copy option: "Address, Balance (n)" alt_copy = _("Copy {}").format(_("Address") + ", " + _("Balance")) + f" ({len(addrs)})" alt_copy_text = "\n".join([a.to_full_ui_string() + ", " + self.parent.format_amount(sum(self.wallet.get_addr_balance(a))) for a in addrs]) else: texts = [i.text(col).strip() for i in selected] texts = [t for t in texts if t] # omit empty items if texts: copy_text = '\n'.join(texts) menu.addAction(_("Copy {}").format(column_title) + f" ({len(texts)})", lambda: doCopy(copy_text)) if alt_copy and alt_copy_text: menu.addAction(alt_copy, lambda: doCopy(alt_copy_text)) freeze = self.parent.set_frozen_state if any(self.wallet.is_frozen(addr) for addr in addrs): menu.addAction(_("Unfreeze"), partial(freeze, addrs, False)) if not all(self.wallet.is_frozen(addr) for addr in addrs): menu.addAction(_("Freeze"), partial(freeze, addrs, True)) coins = self.wallet.get_spendable_coins(domain = addrs, config = self.config) if coins: menu.addAction(_("Spend from"), partial(self.parent.spend_coins, coins)) run_hook('address_list_context_menu_setup', self, menu, addrs) # Add Cash Accounts section at the end, if relevant if not multi_select: ca_list = item.data(0, self.DataRoles.cash_accounts) menu.addSeparator() a1 = menu.addAction(_("Cash Accounts"), lambda: None) a1.setDisabled(True) if ca_list: ca_default = self._ca_get_default(ca_list) for ca_info in ca_list: ca_text = self.wallet.cashacct.fmt_info(ca_info, ca_info.minimal_chash) ca_text_em = self.wallet.cashacct.fmt_info(ca_info, ca_info.minimal_chash, emoji=True) m = menu.addMenu(ca_info.emoji + " " + ca_text) a_ca_copy = m.addAction(_("Copy Cash Account"), lambda x=None, text=ca_text_em: doCopy(text)) a = m.addAction(_("Details") + "...", lambda x=None,ca_text=ca_text: cashacctqt.cash_account_detail_dialog(self.parent, ca_text)) a = m.addAction(_("View registration tx") + "...", lambda x=None, ca=ca_info: self.parent.do_process_from_txid(txid=ca.txid)) a = a_def = m.addAction(_("Make default for address"), lambda x=None, ca=ca_info: self._ca_set_default(ca, True)) if ca_info == ca_default: if where_to_insert_dupe_copy_cash_account and a_ca_copy: # insert a dupe of "Copy Cash Account" for the default cash account for this address in the top-level menu menu.insertAction(where_to_insert_dupe_copy_cash_account, a_ca_copy) m.setTitle(m.title() + " " + "★") a_def.setDisabled(True) a_def.setCheckable(True) a_def.setChecked(True) a_def.setText(_("Is default for address")) else: a1.setText(_("No Cash Accounts")) a_new = menu.addAction(_("Register new..."), lambda x=None, addr=addr: self.parent.register_new_cash_account(addr)) a_new.setIcon(__class__._cashacct_icon) run_hook('receive_menu', menu, addrs, self.wallet) menu.exec_(self.viewport().mapToGlobal(position))
def __init__(self, main_window, address, password): QDialog.__init__(self, parent=main_window) self.main_window = main_window self.wallet = main_window.wallet self.config = main_window.config #self.address = address # address to spend from self.password = password # save for funding if address: self.fund_domain = [address] self.fund_change_address = address self.default_redeem_address = address self.entropy_address = address else: self.fund_domain = None self.fund_change_address = None self.default_redeem_address = self.wallet.get_unused_address() self.entropy_address = self.wallet.get_addresses()[0] if not self.default_redeem_address: # self.wallet.get_unused_address() returns None for imported privkey wallets. self.close() self.main_window.show_error( _("For imported private key wallets, please open the coin splitter from the Addresses tab by right clicking on an address, instead of via the Tools menu." )) return # Extract private key index = self.wallet.get_address_index(self.entropy_address) try: key = self.wallet.keystore.get_private_key(index, password) except: self.close() self.main_window.show_error( _("Coin splitter only works with wallets possessing private keys. See the User Guide for details on how to split other wallets' coins, at:" ) + "\nhttps://github.com/markblundeberg/coinsplitter_checkdatasig/blob/master/doc/coinsplitter_user_guide.md" ) return privkey = int.from_bytes(key[0], 'big') if isinstance(self.wallet, Multisig_Wallet): self.main_window.show_error( "Multi-sig wallet support is partial.\nThe splitter coin itself is *not* multisig and belongs to you alone (it cannot be redeemed by other parties)." ) # Create contract derived from private key self.contract = SplitContract(privkey) self.setWindowTitle(_("OP_MUL Coin Splitting")) vbox = QVBoxLayout() self.setLayout(vbox) l = QLabel( _("Master address") + ": " + self.entropy_address.to_ui_string()) l.setTextInteractionFlags(Qt.TextSelectableByMouse) vbox.addWidget(l) l = QLabel( _("Split contract") + ": " + self.contract.address.to_ui_string()) l.setTextInteractionFlags(Qt.TextSelectableByMouse) vbox.addWidget(l) hbox = QHBoxLayout() vbox.addLayout(hbox) l1 = QLabel() l1.setPixmap(QIcon(":icons/warning").pixmap(QSize(40, 40))) hbox.addWidget(l1) l2 = QLabel( _("<b>EXPERTS ONLY!</b><br/>Coins sent to split contract cannot be recovered<br/>on chains that do not support OP_MUL." )) hbox.addWidget(l2) hbox.addStretch(1) hbox = QHBoxLayout() vbox.addLayout(hbox) addr_URL = web.BE_URL(self.config, 'addr', self.contract.address) b = QPushButton(_("View on block explorer")) b.clicked.connect(lambda: webbrowser.open(addr_URL)) hbox.addWidget(b) if not addr_URL: b.setDisabled(True) b = QPushButton(_("View redeem script...")) b.clicked.connect(self.showscript) hbox.addWidget(b) hbox.addStretch(1) l = QLabel("<b>%s</b>" % (_("Splittable coin creation/finding:"))) vbox.addWidget(l) hbox = QHBoxLayout() vbox.addLayout(hbox) b = QPushButton(_("Create splittable coin")) b.clicked.connect(self.fund) hbox.addWidget(b) self.fund_button = b b = QPushButton("x") b.clicked.connect(self.search) hbox.addWidget(b) self.search_button = b hbox.addStretch(1) grid = QGridLayout() vbox.addLayout(grid) l = QLabel(_("TXID")) grid.addWidget(l, 0, 0) l = QLabel(_("Out#")) grid.addWidget(l, 0, 1) l = QLabel(_("Value (sats)")) grid.addWidget(l, 0, 2) self.fund_txid_e = QLineEdit() self.fund_txid_e.textEdited.connect(self.changed_coin) grid.addWidget(self.fund_txid_e, 1, 0) self.fund_txout_e = QLineEdit() self.fund_txout_e.setMaximumWidth(40) self.fund_txout_e.setAlignment(Qt.AlignRight) self.fund_txout_e.textEdited.connect(self.changed_coin) grid.addWidget(self.fund_txout_e, 1, 1) self.fund_value_e = QLineEdit() self.fund_value_e.setMaximumWidth(70) self.fund_value_e.setAlignment(Qt.AlignRight) self.fund_value_e.textEdited.connect(self.changed_coin) grid.addWidget(self.fund_value_e, 1, 2) l = QLabel("<b>%s</b>" % (_("Splittable coin spending:"))) vbox.addWidget(l) self.option1_rb = QRadioButton(_("Only spend splittable coin")) self.option2_rb = QRadioButton() self.option3_rb = QRadioButton( _("Combine with all coins from wallet") + ' "%s"' % (self.wallet.basename())) vbox.addWidget(self.option1_rb) vbox.addWidget(self.option2_rb) vbox.addWidget(self.option3_rb) if self.fund_change_address: self.option2_rb.setText( _("Combine with all coins from address") + " %.10s..." % (self.fund_change_address.to_ui_string())) self.option2_rb.setChecked(True) else: self.option3_rb.setChecked(True) self.option2_rb.setHidden(True) hbox = QHBoxLayout() vbox.addLayout(hbox) l = QLabel(_("Output to:")) hbox.addWidget(l) self.redeem_address_e = QLineEdit() self.redeem_address_e.setText( self.default_redeem_address.to_full_ui_string()) hbox.addWidget(self.redeem_address_e) hbox = QHBoxLayout() vbox.addLayout(hbox) b = QPushButton(_("Redeem (MUL chain only)")) b.clicked.connect(self.spend) hbox.addWidget(b) self.redeem_button = b self.changed_coin() self.search_done_signal.connect(self.search_done) self.search() self.show()