Esempio n. 1
0
    def __init__(self, text=None):
        _QrCodeTextEdit.__init__(self, text)
        self.setReadOnly(1)
        self.qr_button = self.addButton(self.get_qr_icon(), self.qr_show,
                                        _("Show as QR code"))

        run_hook('show_text_edit', self)
Esempio n. 2
0
 def __init__(self, text="", allow_multi=False):
     _QrCodeTextEdit.__init__(self, text)
     self.allow_multi = allow_multi
     self.setReadOnly(0)
     self.qr_button = self.addButton(self.get_qr_icon(), self.qr_input,
                                     _("Read QR code"))
     self.addButton(":icons/file.png", self.file_input,
                    _("Read text or image file"))
     run_hook('scan_text_edit', self)
Esempio n. 3
0
    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 == 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_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.strip()))
        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: webopen(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 install_plugin_confirmed(self, plugin_archive_path):
     plugin_manager = self.main_window.gui_object.plugins
     result_code = plugin_manager.install_external_plugin(
         plugin_archive_path)
     if result_code != ExternalPluginCodes.SUCCESS:
         self.show_error(
             INSTALL_ERROR_MESSAGES.get(
                 result_code,
                 _("Unexpected error %d") % result_code))
     else:
         run_hook('init_qt', self.main_window.gui_object)
     self.refresh_ui()
 def on_toggle_plugin(self):
     selected_key = self.pluginsList.get_selected_key()
     if selected_key is not None:
         package_name = selected_key
         plugin_manager = self.main_window.gui_object.plugins
         plugin = plugin_manager.external_plugins.get(package_name, None)
         if plugin is not None and plugin.is_enabled():
             plugin_manager.disable_external_plugin(package_name)
         else:
             plugin_manager.enable_external_plugin(package_name)
             run_hook('init_qt', self.main_window.gui_object)
         self.refresh_ui()
Esempio n. 6
0
 def __init__(self, parent=None, msg=None):
     msg = msg or _('Please enter your password')
     WindowModalDialog.__init__(self, parent, _("Enter Password"))
     self.pw = pw = QLineEdit()
     pw.setEchoMode(2)
     vbox = QVBoxLayout()
     vbox.addWidget(QLabel(msg))
     grid = QGridLayout()
     grid.setSpacing(8)
     grid.addWidget(QLabel(_('Password')), 1, 0)
     grid.addWidget(pw, 1, 1)
     vbox.addLayout(grid)
     vbox.addLayout(Buttons(CancelButton(self), OkButton(self)))
     self.setLayout(vbox)
     run_hook('password_dialog', pw, grid, 1)
Esempio n. 7
0
    def new_cash_account_contact_dialog(self):
        ''' Context menu callback. Shows the "New Cash Account Contact"
        interface. '''

        items = cashacctqt.lookup_cash_account_dialog(
            self.parent,
            self.wallet,
            title=_("New Cash Account Contact"),
            blurb=_("<br>Add anyone's Cash Account to your Contacts"),
            button_type=cashacctqt.InfoGroupBox.ButtonType.Radio)
        if items:
            info, min_chash, name = items[0]
            self.parent.set_contact(name,
                                    info.address.to_ui_string(),
                                    typ='cashacct')
            run_hook('update_contacts_tab', self)
Esempio n. 8
0
    def item_changed(self, item, column):
        # Run the label of the changed item thru the filter hook
        if column != 3:
            return

        label = item.text(3)
        # NB: 'h_item' parameter is None due to performance reasons
        should_skip = run_hook(
            "history_list_filter", self, None, label, multi=True) or []
        if any(should_skip):
            item.setHidden(True)
Esempio n. 9
0
 def create_menu(self, position):
     item = self.itemAt(position)
     if not item:
         return
     self.setCurrentItem(item)  # sometimes it's not the current item.
     addr = item.data(0, Qt.UserRole)
     req = self.wallet.receive_requests[addr]
     column = self.currentColumn()
     column_title = self.headerItem().text(column)
     column_data = item.text(column)
     menu = QMenu(self)
     menu.addAction(
         _("Copy {}").format(column_title),
         lambda: self.parent.app.clipboard().setText(column_data.strip()))
     menu.addAction(
         _("Copy URI"), lambda: self.parent.view_and_paste(
             'URI', '', self.parent.get_request_URI(addr)))
     menu.addAction(_("Save as BIP70 file"),
                    lambda: self.parent.export_payment_request(addr))
     menu.addAction(_("Delete"),
                    lambda: self.parent.delete_payment_request(addr))
     run_hook('receive_list_menu', menu, addr)
     menu.exec_(self.viewport().mapToGlobal(position))
Esempio n. 10
0
    def on_update(self):
        if self.cleaned_up:
            return
        item = self.currentItem()
        current_contact = item.data(0,
                                    self.DataRoles.Contact) if item else None
        selected = self.selectedItems() or []
        selected_contacts = set(
            item.data(0, self.DataRoles.Contact) for item in selected)
        del item, selected  # must not hold a reference to a C++ object that will soon be deleted in self.clear()..
        self.clear()
        type_names = defaultdict(lambda: _("Unknown"))
        type_names.update({
            'openalias': _('OpenAlias'),
            'cashacct': _('Cash Account'),
            'cashacct_W': _('Cash Account') + ' [' + _('Mine') + ']',
            'cashacct_T': _('Cash Account') + ' [' + _('Pend') + ']',
            'address': _('Address'),
        })
        type_icons = {
            'openalias': self.icon_openalias,
            'cashacct': self.icon_cashacct,
            'cashacct_W': self.icon_cashacct,
            'cashacct_T': self.icon_unverif,
            'address': self.icon_contacts,
        }
        selected_items, current_item = [], None
        edited = self._edited_item_cur_sel
        for contact in self.get_full_contacts(
                include_pseudo=self.show_my_cashaccts):
            _type, name, address = contact.type, contact.name, contact.address
            label_key = address
            if _type in ('cashacct', 'cashacct_W', 'cashacct_T', 'address'):
                try:
                    # try and re-parse and re-display the address based on current UI string settings
                    addy = Address.from_string(address)
                    address = addy.to_ui_string()
                    label_key = addy.to_storage_string()
                    del addy
                except:
                    ''' This may happen because we may not have always enforced this as strictly as we could have in legacy code. Just move on.. '''
            label = self.wallet.get_label(label_key)
            item = QTreeWidgetItem(
                ["", name, label, address, type_names[_type]])
            item.setData(0, self.DataRoles.Contact, contact)
            item.DataRole = self.DataRoles.Contact
            if _type in ('cashacct', 'cashacct_W', 'cashacct_T'):
                ca_info = self.wallet.cashacct.get_verified(name)
                tt_warn = None
                if ca_info:
                    if self.wallet.is_mine(
                            ca_info.address) and not self.show_my_cashaccts:
                        # user may have added the contact to "self" manually
                        # but since they asked to not see their own cashaccts,
                        # we must do this to suppress it from being shown regardless
                        continue
                    item.setText(0, ca_info.emoji)
                    tt = _(
                        'Validated Cash Account: <b><pre>{emoji} {account_string}</pre></b>'
                    ).format(
                        emoji=ca_info.emoji,
                        account_string=
                        f'{ca_info.name}#{ca_info.number}.{ca_info.collision_hash};'
                    )
                else:
                    item.setIcon(0, self.icon_unverif)
                    if _type == 'cashacct_T':
                        tt_warn = tt = _(
                            'Cash Account pending confirmation and/or verification'
                        )
                    else:
                        tt_warn = tt = _(
                            'Warning: This Cash Account is not verified')
                item.setToolTip(0, tt)
                if tt_warn: item.setToolTip(1, tt_warn)
            if _type in type_icons:
                item.setIcon(4, type_icons[_type])
            # always give the "Address" field a monospace font even if it's
            # not strictly an address such as openalias...
            item.setFont(3, self.monospace_font)
            self.addTopLevelItem(item)
            if contact == current_contact or (contact == edited[0]
                                              and edited[1]):
                current_item = item  # this key was the current item before and it hasn't gone away
            if contact in selected_contacts or (contact == edited[0]
                                                and edited[2]):
                selected_items.append(
                    item
                )  # this key was selected before and it hasn't gone away

        if selected_items:  # sometimes currentItem is set even if nothing actually selected. grr..
            # restore current item & selections
            if current_item:
                # set the current item. this may also implicitly select it
                self.setCurrentItem(current_item)
            for item in selected_items:
                # restore the previous selection
                item.setSelected(True)
        self._edited_item_cur_sel = (None, ) * 3
        run_hook('update_contacts_tab', self)
Esempio n. 11
0
    def __init__(self, tx, parent, desc, prompt_if_unsaved):
        '''Transactions in the wallet will show their description.
        Pass desc to give a description for txs not yet in the wallet.
        '''
        # We want to be a top-level window
        QDialog.__init__(self, parent=None)
        # Take a copy; it might get updated in the main window by
        # e.g. the FX plugin.  If this happens during or after a long
        # sign operation the signatures are lost.
        self.tx = copy.deepcopy(tx)
        self.tx.deserialize()
        self.main_window = parent
        self.wallet = parent.wallet
        self.prompt_if_unsaved = prompt_if_unsaved
        self.saved = False
        self.desc = desc
        self.cashaddr_signal_slots = []
        self._dl_pct = None
        self._closed = False
        self.tx_hash = self.tx.txid_fast() if self.tx.raw and self.tx.is_complete() else None
        self.tx_height = self.wallet.get_tx_height(self.tx_hash)[0] or None
        self.block_hash = None
        Weak.finalization_print_error(self)  # track object lifecycle

        self.setMinimumWidth(750)
        self.setWindowTitle(_("Transaction"))

        vbox = QVBoxLayout()
        self.setLayout(vbox)

        self.tx_hash_e  = ButtonsLineEdit()
        l = QLabel(_("&Transaction ID:"))
        l.setBuddy(self.tx_hash_e)
        vbox.addWidget(l)
        self.tx_hash_e.addCopyButton()
        weakSelfRef = Weak.ref(self)
        qr_show = lambda: weakSelfRef() and weakSelfRef().main_window.show_qrcode(str(weakSelfRef().tx_hash_e.text()), _("Transaction ID"), parent=weakSelfRef())
        icon = ":icons/qrcode_white.svg" if ColorScheme.dark_scheme else ":icons/qrcode.svg"
        self.tx_hash_e.addButton(icon, qr_show, _("Show as QR code"))
        self.tx_hash_e.setReadOnly(True)
        vbox.addWidget(self.tx_hash_e)
        self.tx_desc = QLabel()
        vbox.addWidget(self.tx_desc)
        self.status_label = QLabel()
        vbox.addWidget(self.status_label)
        self.date_label = QLabel()
        vbox.addWidget(self.date_label)
        self.amount_label = QLabel()
        vbox.addWidget(self.amount_label)
        self.size_label = QLabel()
        vbox.addWidget(self.size_label)
        self.fee_label = QLabel()
        vbox.addWidget(self.fee_label)

        for l in (self.tx_desc, self.status_label, self.date_label, self.amount_label, self.size_label, self.fee_label):
            # make these labels selectable by mouse in case user wants to copy-paste things in tx dialog
            l.setTextInteractionFlags(l.textInteractionFlags() | Qt.TextSelectableByMouse)

        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.'))

        self.status_label.linkActivated.connect(open_be_url)

        self.add_io(vbox)

        self.freeze_button = b = QPushButton(self._make_freeze_button_text())
        b.setToolTip(_("Lock/unlock the coin(s) being spent in this transaction.\n\n"
                       "Use this facility if you wish to broadcast this transaction later,\n"
                       "in order to prevent its inputs from being accidentally spent."))
        b.clicked.connect(self.do_freeze_unfreeze)

        self.sign_button = b = QPushButton(_("&Sign"))
        b.clicked.connect(self.sign)
        b.setToolTip(_("Sign the transaction"))

        self.broadcast_button = b = QPushButton(_("&Broadcast"))
        b.clicked.connect(self.do_broadcast)
        b.setToolTip(_("Submit the transaction to the blockchain"))
        self.last_broadcast_time = 0

        self.save_button = b = QPushButton(_("S&ave"))
        b.setToolTip(_("Save the transaction to a file"))
        b.clicked.connect(self.save)

        self.cancel_button = b = CloseButton(self)

        self.qr_button = b = QPushButton()
        b.setToolTip(_("Show transaction QR code"))
        b.setIcon(QIcon(icon))
        b.clicked.connect(self.show_qr)
        b.setShortcut(QKeySequence(Qt.ALT + Qt.Key_Q))

        self.copy_button = b = CopyButton(lambda: str(weakSelfRef() and weakSelfRef().tx),
                                          callback=lambda: weakSelfRef() and weakSelfRef().show_message(
                                              _("Transaction raw hex copied to clipboard.")))
        b.setToolTip(_("Copy transaction raw hex to the clipboard"))


        # Action buttons
        self.buttons = [self.freeze_button, self.sign_button, self.broadcast_button, self.cancel_button]
        # Transaction sharing buttons
        self.sharing_buttons = [self.copy_button, self.qr_button, self.save_button]

        run_hook('transaction_dialog', self)

        hbox = QHBoxLayout()
        hbox.addLayout(Buttons(*self.sharing_buttons))
        hbox.addStretch(1)
        hbox.addLayout(Buttons(*self.buttons))
        vbox.addLayout(hbox)

        if self.tx_height:
            # this avoids downloading the block_height info if we already have it.
            self.tx.ephemeral['block_height'] = self.tx_height

        self.throttled_update_sig.connect(self.throttled_update, Qt.QueuedConnection)
        self.initiate_fetch_input_data(True)

        self.update()

        # connect slots so we update in realtime as blocks come in, etc
        parent.history_updated_signal.connect(self.update_tx_if_in_wallet)
        parent.labels_updated_signal.connect(self.update_tx_if_in_wallet)
        parent.network_signal.connect(self.got_verified_tx)
Esempio n. 12
0
    def update(self):
        if self._closed:
            # latent timer fire
            return
        desc = self.desc
        base_unit = self.main_window.base_unit()
        format_amount = self.main_window.format_amount
        delta2, info2 = self.wallet.get_tx_extended_info(self.tx)
        spends_coins_mine = delta2.spends_coins_mine
        tx_hash, status, label, can_broadcast, amount, fee, height, conf, timestamp, exp_n, status_enum = info2
        self.tx_height = height or self.tx.ephemeral.get('block_height') or None
        self.tx_hash = tx_hash
        desc = label or desc
        size = self.tx.estimated_size()

        # Update freeze/unfreeze button depending on tx state
        StatusEnum = self.wallet.StatusEnum
        if spends_coins_mine:
            has_frozen = bool(self.wallet.is_frozen_coin(set(spends_coins_mine)))
            self.freeze_button._coins = spends_coins_mine
            self.freeze_button._op = op = self.FreezeOp.Freeze if not has_frozen else self.FreezeOp.Unfreeze
            # Set the proper text (plural / singular form)
            self.freeze_button.setText(self._make_freeze_button_text(op, len(spends_coins_mine)))
            # Freeze/Unfreeze enabled only for signed transactions or transactions with frozen coins
            self.freeze_button.setEnabled(has_frozen or status_enum in (StatusEnum.Signed, StatusEnum.PartiallySigned))
        else:
            self.freeze_button.setEnabled(False)
            self.freeze_button.setText(self._make_freeze_button_text())

        # We enable the broadcast button IFF both of the following hold:
        # 1. can_broadcast is true (tx has not been seen yet on the network
        #    and is_complete).
        # 2. The last time user hit "Broadcast" (and it was successful) was
        #    more than BROADCAST_COOLDOWN_SECS ago. This second condition
        #    implements a broadcast cooldown timer which immediately disables
        #    the "Broadcast" button for a time after a successful broadcast.
        #    This prevents the user from being able to spam the broadcast
        #    button. See #1483.
        self.broadcast_button.setEnabled(can_broadcast
                                         and time.time() - self.last_broadcast_time
                                                >= self.BROADCAST_COOLDOWN_SECS)

        can_sign = not self.tx.is_complete() and \
            (self.wallet.can_sign(self.tx) or bool(self.main_window.tx_external_keypairs))
        self.sign_button.setEnabled(can_sign)
        self.tx_hash_e.setText(tx_hash or _('Unknown'))
        if fee is None:
            fee = self.try_calculate_fee()
        if fee is None:
            # see if we can grab the fee from the wallet internal cache which
            # sometimes has fees for tx's not entirely 'is_mine'
            if self.wallet and self.tx_hash:
                fee = self.wallet.tx_fees.get(self.tx_hash)
        if desc is None:
            self.tx_desc.hide()
        else:
            self.tx_desc.setText(_("Description") + ': ' + desc)
            self.tx_desc.show()

        if self.tx_height is not None and self.tx_height > 0 and tx_hash:
            status_extra = '&nbsp;&nbsp;( ' + _("Mined in block") + f': <a href="tx:{tx_hash}">{self.tx_height}</a>' + ' )'
        else:
            status_extra = ''

        self.status_label.setText(_('Status:') + ' ' + status + status_extra)

        if timestamp:
            time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
            self.date_label.setText(_("Date: {}").format(time_str))
            self.date_label.show()
        elif exp_n:
            text = '%d blocks'%(exp_n) if exp_n > 0 else _('unknown (low fee)')
            self.date_label.setText(_('Expected confirmation time') + ': ' + text)
            self.date_label.show()
        else:
            self.date_label.hide()
        if amount is None:
            amount_str = _("Transaction unrelated to your wallet")
        elif amount > 0:
            amount_str = _("Amount received:") + ' %s'% format_amount(amount) + ' ' + base_unit
        else:
            amount_str = _("Amount sent:") + ' %s'% format_amount(-amount) + ' ' + base_unit
        size_str = _("Size: {size} bytes").format(size=size)
        fee_str = _("Fee") + ": "
        if fee is not None:
            fee_str = _("Fee: {fee_amount} {fee_unit} ( {fee_rate} )")
            fee_str = fee_str.format(fee_amount=format_amount(fee), fee_unit=base_unit,
                                     fee_rate=self.main_window.format_fee_rate(fee/size*1000))
            dusty_fee = self.tx.ephemeral.get('dust_to_fee', 0)
            if dusty_fee:
                fee_str += ' <font color=#999999>' + (_("( %s in dust was added to fee )") % format_amount(dusty_fee)) + '</font>'
        elif self._dl_pct is not None:
            fee_str = _('Downloading input data, please wait...') + ' {:.0f}%'.format(self._dl_pct)
        else:
            fee_str += _("unknown")
        self.amount_label.setText(amount_str)
        self.fee_label.setText(fee_str)
        self.size_label.setText(size_str)
        self.update_io()
        run_hook('transaction_dialog_update', self)
Esempio n. 13
0
    def doSend_(self, preview : bool) -> None:
        #if run_hook('abort_send', self):
        #    return
        r = read_send_form(self)
        if not r:
            return
        outputs, fee, tx_desc, coins = r
        try:
            tx = wallet().make_unsigned_transaction(coins, outputs, config(), fee, sign_schnorr=parent().prefs_use_schnorr)
        except NotEnoughFunds:
            parent().show_error(_("Insufficient funds"))
            return
        except ExcessiveFee:
            parent().show_error(_("Your fee is too high.  Max is 50 sat/byte."))
            return
        except BaseException as e:
            traceback.print_exc(file=sys.stdout)
            parent().show_error(str(e))
            return

        amount = tx.output_value() if self.isMax else sum(map(lambda x:x[2], outputs))
        fee = tx.get_fee()

        #if fee < self.wallet.relayfee() * tx.estimated_size() / 1000 and tx.requires_fee(self.wallet):
            #parent().show_error(_("This transaction requires a higher fee, or it will not be propagated by the network"))
            #return

        if preview:
            self.showTransaction_desc_(tx.serialize(), tx_desc)
            return

        # confirmation dialog
        msg = [
            _("Amount to be sent") + ": " + parent().format_amount_and_units(amount),
            _("Mining fee") + ": " + parent().format_amount_and_units(fee),
        ]

        x_fee = run_hook('get_tx_extra_fee', wallet(), tx)
        if x_fee:
            x_fee_address, x_fee_amount = x_fee
            msg.append( _("Additional fees") + ": " + parent().format_amount_and_units(x_fee_amount) )

        confirm_rate = 2 * config().max_fee_rate()


        def DoSign(password : str) -> None:
            def sign_done(success) -> None:
                if success:
                    if not tx.is_complete():
                        self.showTransaction_desc_(tx.serialize(), tx_desc)
                        self.clear()
                    else:
                        parent().broadcast_transaction(tx, tx_desc)
                #else:
                #    parent().show_error(_("An Unknown Error Occurred"))
            parent().sign_tx_with_password(tx, sign_done, password)

        # IN THE FUTURE IF WE WANT TO APPEND SOMETHING IN THE MSG ABOUT THE FEE, CODE IS COMMENTED OUT:
        #if fee > confirm_rate * tx.estimated_size() / 1000:
        #    msg.append(_('Warning') + ': ' + _("The fee for this transaction seems unusually high."))
        password = None
        if wallet().has_password():
            parent().prompt_password_if_needed_asynch(callBack = DoSign, prompt = '\n'.join(msg), vc = self)
        else:
            msg.append(_('Proceed?'))
            parent().question(message = '\n'.join(msg), title = _("Confirm Send"), onOk = lambda: DoSign(None), vc = self)
Esempio n. 14
0
    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)
            if len(selected) > 1:
                column_title += f" ({len(selected)})"
            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].address):
                signAddr = Address.from_string(keys[0].address)
                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))
Esempio n. 15
0
 def on_update(self):
     self.wallet = self.parent.wallet
     h = self.wallet.get_history(self.get_domain(), reverse=True)
     sels = self.selectedItems()
     current_tx = sels[0].data(0, Qt.UserRole) if sels else None
     del sels  #  make sure not to hold stale ref to C++ list of items which will be deleted in clear() call below
     self.clear()
     self.has_unknown_balances = False
     fx = self.parent.fx
     if fx: fx.history_used_spot = False
     for h_item in h:
         tx_hash, height, conf, timestamp, value, balance = h_item
         label = self.wallet.get_label(tx_hash)
         should_skip = run_hook(
             "history_list_filter", self, h_item, label, multi=True) or []
         if any(should_skip):
             # For implementation of fast plugin filters (such as CashShuffle
             # shuffle tx filtering), we short-circuit return. This is
             # faster than using the MyTreeWidget filter definted in .util
             continue
         if value is None or balance is None:
             # Workaround to the fact that sometimes the wallet doesn't
             # know the actual balance for history items while it's
             # downloading history, and we want to flag that situation
             # and redraw the GUI sometime later when it finishes updating.
             # This flag is checked in main_window.py, TxUpadteMgr class.
             self.has_unknown_balances = True
         status, status_str = self.wallet.get_tx_status(
             tx_hash, height, conf, timestamp)
         has_invoice = self.wallet.invoices.paid.get(tx_hash)
         icon = self._get_icon_for_status(status)
         v_str = self.parent.format_amount(value, True, whitespaces=True)
         balance_str = self.parent.format_amount(balance, whitespaces=True)
         entry = ['', tx_hash, status_str, label, v_str, balance_str]
         if fx and fx.show_history():
             date = timestamp_to_datetime(
                 time.time() if conf <= 0 else timestamp)
             for amount in [value, balance]:
                 text = fx.historical_value_str(amount, date)
                 entry.append(text)
         item = SortableTreeWidgetItem(entry)
         if icon: item.setIcon(0, icon)
         item.setToolTip(
             0,
             str(conf) + " confirmation" + ("s" if conf != 1 else ""))
         item.setData(0, SortableTreeWidgetItem.DataRole, (status, conf))
         if has_invoice:
             item.setIcon(3, self.invoiceIcon)
         for i in range(len(entry)):
             if i > 3:
                 item.setTextAlignment(i, Qt.AlignRight | Qt.AlignVCenter)
             if i != 2:
                 item.setFont(i, self.monospaceFont)
         if value and value < 0:
             item.setForeground(3, self.withdrawalBrush)
             item.setForeground(4, self.withdrawalBrush)
         item.setData(0, Qt.UserRole, tx_hash)
         self.addTopLevelItem(item, tx_hash)
         if current_tx == tx_hash:
             # Note that it's faster to setSelected once the item is in
             # the tree. Also note that doing setSelected() on the item
             # itself is much faster than doing setCurrentItem()
             # which must do a linear search in the tree (wastefully)
             item.setSelected(True)
Esempio n. 16
0
    def create_menu(self, position):
        menu = QMenu()
        selected = self.get_selected()

        def create_menu_inner():
            if not selected:
                return
            coins = filter(lambda x: self.get_name(x) in selected, self.utxos)
            if not coins:
                return
            spendable_coins = list(
                filter(lambda x: not selected.get(self.get_name(x), ''),
                       coins))
            # Unconditionally add the "Spend" option but leave it disabled if there are no spendable_coins
            spend_action = menu.addAction(
                _("Spend"), lambda: self.parent.spend_coins(spendable_coins))
            spend_action.setEnabled(bool(spendable_coins))
            if len(selected) == 1:
                # "Copy ..."
                item = self.itemAt(position)
                if not item:
                    return

                col = self.currentColumn()
                column_title = self.headerItem().text(col)
                alt_column_title, alt_copy_text = None, None
                slp_token = item.data(0, self.DataRoles.slp_token)
                ca_info = None
                if col == self.Col.output_point:
                    copy_text = item.data(0, self.DataRoles.name)
                elif col == self.Col.address:
                    addr = item.data(0, self.DataRoles.address)
                    # Determine the "alt copy text" "Legacy Address" or "Cash Address"
                    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')
                    ca_info = item.data(
                        0, self.DataRoles.cash_account)  # may be None
                    del addr
                else:
                    copy_text = item.text(col)
                if copy_text:
                    copy_text = copy_text.strip(
                    )  # make sure formatted amount is not whitespaced
                menu.addAction(
                    _("Copy {}").format(column_title), lambda: QApplication.
                    instance().clipboard().setText(copy_text))
                if alt_copy_text and alt_column_title:
                    menu.addAction(
                        _("Copy {}").format(alt_column_title),
                        lambda: QApplication.instance().clipboard().setText(
                            alt_copy_text))
                if ca_info:
                    self.wallet.cashacct.fmt_info(
                        ca_info
                    )  # paranoia: pre-cache minimal chash (may go out to network)
                    menu.addAction(
                        _("Copy Cash Account"), lambda: self.wallet and
                        QApplication.instance().clipboard().setText(
                            self.wallet.cashacct.fmt_info(ca_info, emoji=True)
                        ))

                # single selection, offer them the "Details" option and also coin/address "freeze" status, if any
                txid = list(selected.keys())[0].split(':')[0]
                frozen_flags = list(selected.values())[0]
                tx = self.wallet.transactions.get(txid)
                if tx:
                    label = self.wallet.get_label(txid) or None
                    menu.addAction(
                        _("Details"),
                        lambda: self.parent.show_transaction(tx, label))
                act = None
                needsep = True
                if 'c' in frozen_flags:
                    menu.addSeparator()
                    menu.addAction(_("Coin is frozen"),
                                   lambda: None).setEnabled(False)
                    menu.addAction(
                        _("Unfreeze Coin"), lambda: self.set_frozen_coins(
                            list(selected.keys()), False))
                    menu.addSeparator()
                    needsep = False
                else:
                    menu.addAction(
                        _("Freeze Coin"), lambda: self.set_frozen_coins(
                            list(selected.keys()), True))
                if 'a' in frozen_flags:
                    if needsep: menu.addSeparator()
                    menu.addAction(_("Address is frozen"),
                                   lambda: None).setEnabled(False)
                    menu.addAction(
                        _("Unfreeze Address"),
                        lambda: self.set_frozen_addresses_for_coins(
                            list(selected.keys()), False))
                else:
                    menu.addAction(
                        _("Freeze Address"),
                        lambda: self.set_frozen_addresses_for_coins(
                            list(selected.keys()), True))
                if not spend_action.isEnabled():
                    if slp_token:
                        spend_action.setText(_("SLP Token: Spend Locked"))
                    elif 'i' in frozen_flags:  # immature coinbase
                        spend_action.setText(
                            _("Immature Coinbase: Spend Locked"))
            else:
                # multi-selection
                menu.addSeparator()
                if any(['c' not in flags for flags in selected.values()]):
                    # they have some coin-level non-frozen in the selection, so add the menu action "Freeze coins"
                    menu.addAction(
                        _("Freeze Coins"), lambda: self.set_frozen_coins(
                            list(selected.keys()), True))
                if any(['c' in flags for flags in selected.values()]):
                    # they have some coin-level frozen in the selection, so add the menu action "Unfreeze coins"
                    menu.addAction(
                        _("Unfreeze Coins"), lambda: self.set_frozen_coins(
                            list(selected.keys()), False))
                if any(['a' not in flags for flags in selected.values()]):
                    # they have some address-level non-frozen in the selection, so add the menu action "Freeze addresses"
                    menu.addAction(
                        _("Freeze Addresses"),
                        lambda: self.set_frozen_addresses_for_coins(
                            list(selected.keys()), True))
                if any(['a' in flags for flags in selected.values()]):
                    # they have some address-level frozen in the selection, so add the menu action "Unfreeze addresses"
                    menu.addAction(
                        _("Unfreeze Addresses"),
                        lambda: self.set_frozen_addresses_for_coins(
                            list(selected.keys()), False))

        create_menu_inner()

        run_hook('utxo_list_context_menu_setup', self, menu, selected)

        # add optional toggle actions
        menu.addSeparator()

        def toggle():
            self.show_cash_accounts = not self.show_cash_accounts

        a = menu.addAction(_("Show Cash Accounts"), toggle)
        a.setCheckable(True)
        a.setChecked(self.show_cash_accounts)

        menu.exec_(self.viewport().mapToGlobal(position))
Esempio n. 17
0
 def on_update(self):
     local_maturity_height = (self.wallet.get_local_height() +
                              1) - COINBASE_MATURITY
     prev_selection = self.get_selected(
     )  # cache previous selection, if any
     self.clear()
     ca_by_addr = defaultdict(list)
     if self.show_cash_accounts:
         addr_set = set()
         self.utxos = self.wallet.get_utxos(addr_set_out=addr_set,
                                            exclude_slp=False)
         # grab all cash accounts so that we may add the emoji char
         for info in self.wallet.cashacct.get_cashaccounts(addr_set):
             ca_by_addr[info.address].append(info)
             del info
         for ca_list in ca_by_addr.values():
             ca_list.sort(
                 key=lambda info: (
                     (info.number or 0), str(info.collision_hash))
             )  # sort the ca_lists by number, required by cashacct.get_address_default
             del ca_list  # reference still exists inside ca_by_addr dict, this is just deleted here because we re-use this name below.
         del addr_set  # clean-up. We don't want the below code to ever depend on the existence of this cell.
     else:
         self.utxos = self.wallet.get_utxos(exclude_slp=False)
     for x in self.utxos:
         address = x['address']
         address_text = address.to_ui_string()
         ca_info = None
         ca_list = ca_by_addr.get(address)
         tool_tip0 = None
         if ca_list:
             ca_info = self.wallet.cashacct.get_address_default(ca_list)
             address_text = f'{ca_info.emoji} {address_text}'  # prepend the address emoji char
             tool_tip0 = self.wallet.cashacct.fmt_info(ca_info, emoji=True)
         height = x['height']
         is_immature = x['coinbase'] and height > local_maturity_height
         name = self.get_name(x)
         name_short = self.get_name_short(x)
         label = self.wallet.get_label(x['prevout_hash'])
         amount = self.parent.format_amount(x['value'],
                                            is_diff=False,
                                            whitespaces=True)
         utxo_item = SortableTreeWidgetItem(
             [address_text, label, amount,
              str(height), name_short])
         if label:
             utxo_item.setToolTip(
                 1, label
             )  # just in case it doesn't fit horizontally, we also provide it as a tool tip where hopefully it won't be elided
         if tool_tip0:
             utxo_item.setToolTip(0, tool_tip0)
         utxo_item.setToolTip(
             4, name)  # just in case they like to see lots of hex digits :)
         utxo_item.DataRole = Qt.UserRole + 100  # set this here to avoid sorting based on Qt.UserRole+1
         utxo_item.setFont(0, self.monospaceFont)
         utxo_item.setFont(2, self.monospaceFont)
         utxo_item.setFont(4, self.monospaceFont)
         utxo_item.setData(0, self.DataRoles.name, name)
         a_frozen = self.wallet.is_frozen(address)
         c_frozen = x['is_frozen_coin']
         toolTipMisc = ''
         slp_token = x['slp_token']
         if is_immature:
             for colNum in range(self.columnCount()):
                 if colNum == self.Col.label:
                     continue  # don't color the label column
                 utxo_item.setForeground(colNum, self.immatureColor)
             toolTipMisc = _('Coin is not yet mature')
         elif slp_token:
             utxo_item.setBackground(0, self.slpBG)
             toolTipMisc = _('Coin contains an SLP token')
         elif a_frozen and not c_frozen:
             # address is frozen, coin is not frozen
             # emulate the "Look" off the address_list .py's frozen entry
             utxo_item.setBackground(0, self.lightBlue)
             toolTipMisc = _("Address is frozen")
         elif c_frozen and not a_frozen:
             # coin is frozen, address is not frozen
             utxo_item.setBackground(0, self.blue)
             toolTipMisc = _("Coin is frozen")
         elif c_frozen and a_frozen:
             # both coin and address are frozen so color-code it to indicate that.
             utxo_item.setBackground(0, self.lightBlue)
             utxo_item.setForeground(0, self.cyanBlue)
             toolTipMisc = _("Coin & Address are frozen")
         # save the address-level-frozen and coin-level-frozen flags to the data item for retrieval later in create_menu() below.
         utxo_item.setData(
             0, self.DataRoles.frozen_flags, "{}{}{}{}".format(
                 ("a" if a_frozen else ""), ("c" if c_frozen else ""),
                 ("s" if slp_token else ""), ("i" if is_immature else "")))
         # store the address
         utxo_item.setData(0, self.DataRoles.address, address)
         # store the ca_info for this address -- if any
         if ca_info:
             utxo_item.setData(0, self.DataRoles.cash_account, ca_info)
         # store the slp_token
         utxo_item.setData(0, self.DataRoles.slp_token, slp_token)
         if toolTipMisc:
             utxo_item.setToolTip(0, toolTipMisc)
         run_hook("utxo_list_item_setup", self, utxo_item, x, name)
         self.addChild(utxo_item)
         if name in prev_selection:
             # NB: This needs to be here after the item is added to the widget. See #979.
             utxo_item.setSelected(True)  # restore previous selection
     self._update_utxo_count_display(len(self.utxos))
Esempio n. 18
0
    def create_menu(self, position):
        if self.picker:
            # picker mode has no menu
            return
        from oregano.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, '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))

        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))