fix: gui_object.payment_request
[electrum-nvc.git] / gui / qt / main_window.py
index c93ce9a..f7b62fc 100644 (file)
@@ -107,6 +107,7 @@ class ElectrumWindow(QMainWindow):
 
         self.config = config
         self.network = network
+        self.gui_object = gui_object
         self.tray = gui_object.tray
         self.go_lite = gui_object.go_lite
         self.lite = None
@@ -120,6 +121,7 @@ class ElectrumWindow(QMainWindow):
         set_language(config.get('language'))
 
         self.funds_error = False
+        self.payment_request = None
         self.completions = QStringListModel()
 
         self.tabs = tabs = QTabWidget(self)
@@ -135,6 +137,8 @@ class ElectrumWindow(QMainWindow):
 
         g = self.config.get("winpos-qt",[100, 100, 840, 400])
         self.setGeometry(g[0], g[1], g[2], g[3])
+        if self.config.get("is_maximized"):
+            self.showMaximized()
 
         self.setWindowIcon(QIcon(":icons/electrum.png"))
         self.init_menubar()
@@ -153,6 +157,8 @@ class ElectrumWindow(QMainWindow):
         self.connect(self, QtCore.SIGNAL('transaction_signal'), lambda: self.notify_transactions() )
         self.connect(self, QtCore.SIGNAL('send_tx2'), self.send_tx2)
         self.connect(self, QtCore.SIGNAL('send_tx3'), self.send_tx3)
+        self.connect(self, QtCore.SIGNAL('payment_request_ok'), self.payment_request_ok)
+        self.connect(self, QtCore.SIGNAL('payment_request_error'), self.payment_request_error)
 
         self.history_list.setFocus(True)
 
@@ -201,6 +207,7 @@ class ElectrumWindow(QMainWindow):
         self.password_menu.setEnabled(not self.wallet.is_watching_only())
         self.seed_menu.setEnabled(self.wallet.has_seed())
         self.mpk_menu.setEnabled(self.wallet.is_deterministic())
+        self.import_menu.setEnabled(self.wallet.can_import())
 
         self.update_lock_icon()
         self.update_buttons_on_seed()
@@ -294,10 +301,9 @@ class ElectrumWindow(QMainWindow):
 
         self.private_keys_menu = wallet_menu.addMenu(_("&Private keys"))
         self.private_keys_menu.addAction(_("&Sweep"), self.sweep_key_dialog)
-        self.private_keys_menu.addAction(_("&Import"), self.do_import_privkey)
+        self.import_menu = self.private_keys_menu.addAction(_("&Import"), self.do_import_privkey)
         self.private_keys_menu.addAction(_("&Export"), self.export_privkeys_dialog)
-
-        wallet_menu.addAction(_("&Export History"), self.do_export_history)
+        wallet_menu.addAction(_("&Export History"), self.export_history_dialog)
 
         tools_menu = menubar.addMenu(_("&Tools"))
 
@@ -765,24 +771,31 @@ class ElectrumWindow(QMainWindow):
 
 
     def do_send(self):
-
         label = unicode( self.message_e.text() )
-        r = unicode( self.payto_e.text() )
-        r = r.strip()
 
-        # label or alias, with address in brackets
-        m = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
-        to_address = m.group(2) if m else r
+        if self.gui_object.payment_request:
+            outputs = self.gui_object.payment_request.outputs
+            amount = self.gui_object.payment_request.get_amount()
 
-        if not is_valid(to_address):
-            QMessageBox.warning(self, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK'))
-            return
+        else:
+            r = unicode( self.payto_e.text() )
+            r = r.strip()
+
+            # label or alias, with address in brackets
+            m = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
+            to_address = m.group(2) if m else r
+            if not is_valid(to_address):
+                QMessageBox.warning(self, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK'))
+                return
+
+            try:
+                amount = self.read_amount(unicode( self.amount_e.text()))
+            except Exception:
+                QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
+                return
+
+            outputs = [(to_address, amount)]
 
-        try:
-            amount = self.read_amount(unicode( self.amount_e.text()))
-        except Exception:
-            QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
-            return
         try:
             fee = self.read_amount(unicode( self.fee_e.text()))
         except Exception:
@@ -799,7 +812,7 @@ class ElectrumWindow(QMainWindow):
             if not self.question(_("The fee for this transaction seems unusually high.\nAre you really sure you want to pay %(fee)s in fees?")%{ 'fee' : self.format_amount(fee) + ' '+ self.base_unit()}):
                 return
 
-        self.send_tx(to_address, amount, fee, label)
+        self.send_tx(outputs, fee, label)
 
 
     def waiting_dialog(self, message):
@@ -813,11 +826,10 @@ class ElectrumWindow(QMainWindow):
 
 
     @protected
-    def send_tx(self, to_address, amount, fee, label, password):
+    def send_tx(self, outputs, fee, label, password):
 
         # first, create an unsigned tx 
         domain = self.get_payment_sources()
-        outputs = [(to_address, amount)]
         try:
             tx = self.wallet.make_unsigned_transaction(outputs, fee, None, domain)
             tx.error = None
@@ -840,9 +852,6 @@ class ElectrumWindow(QMainWindow):
         self.tx_wait_dialog = self.waiting_dialog('Signing..')
         threading.Thread(target=sign_thread).start()
 
-        # add recipient to addressbook
-        if to_address not in self.wallet.addressbook and not self.wallet.is_mine(to_address):
-            self.wallet.addressbook.append(to_address)
 
 
     def send_tx2(self):
@@ -864,28 +873,47 @@ class ElectrumWindow(QMainWindow):
             self.show_transaction(tx)
             return
 
-        # broadcast the tx
         def broadcast_thread():
+            if self.payment_request:
+                refund_address = self.wallet.addresses()[0]
+                self.payment_request.send_ack(str(tx), refund_address)
+                self.payment_request = None
+            # note: BIP 70 recommends not broadcasting the tx to the network and letting the merchant do that
             self.tx_broadcast_result =  self.wallet.sendtx(tx)
             self.emit(SIGNAL('send_tx3'))
+
         self.tx_broadcast_dialog = self.waiting_dialog('Broadcasting..')
         threading.Thread(target=broadcast_thread).start()
 
 
+
     def send_tx3(self):
         self.tx_broadcast_dialog.accept()
         status, msg = self.tx_broadcast_result
         if status:
             QMessageBox.information(self, '', _('Payment sent.') + '\n' + msg, _('OK'))
             self.do_clear()
-            self.update_contacts_tab()
         else:
             QMessageBox.warning(self, _('Error'), msg, _('OK'))
 
 
 
+    def prepare_for_payment_request(self):
+        style = "QWidget { background-color:none;border:none;}"
+        self.tabs.setCurrentIndex(1)
+        self.payto_e.setReadOnly(True)
+        self.payto_e.setStyleSheet(style)
+        self.amount_e.setReadOnly(True)
+        self.payto_e.setText(_("please wait..."))
+        self.amount_e.setStyleSheet(style)
+        return True
 
+    def payment_request_ok(self):
+        self.payto_e.setText(self.gui_object.payment_request.domain)
+        self.amount_e.setText(self.format_amount(self.gui_object.payment_request.get_amount()))
 
+    def payment_request_error(self):
+        self.payto_e.setText(self.gui_object.payment_request.error)
 
 
     def set_send(self, address, amount, label, message):
@@ -911,6 +939,7 @@ class ElectrumWindow(QMainWindow):
         for e in [self.payto_e, self.message_e, self.amount_e, self.fee_e]:
             e.setText('')
             self.set_frozen(e,False)
+            e.setStyleSheet("")
 
         self.set_pay_from([])
         self.update_status()
@@ -1878,24 +1907,10 @@ class ElectrumWindow(QMainWindow):
         e.setReadOnly(True)
         vbox.addWidget(e)
 
-        hbox = QHBoxLayout()
-        vbox.addLayout(hbox)
-
         defaultname = 'electrum-private-keys.csv'
-        directory = self.config.get('io_dir', unicode(os.path.expanduser('~')))
-        path = os.path.join( directory, defaultname )
-        filename_e = QLineEdit()
-        filename_e.setText(path)
-        def func():
-            select_export = _('Select file to export your private keys to')
-            p = self.getSaveFileName(select_export, defaultname, "*.csv")
-            if p:
-                filename_e.setText(p)
-
-        button = QPushButton(_('File'))
-        button.clicked.connect(func)
-        hbox.addWidget(button)
-        hbox.addWidget(filename_e)
+        select_msg = _('Select file to export your private keys to')
+        hbox, filename_e, csv_button = filename_field(self, self.config, defaultname, select_msg)
+        vbox.addLayout(hbox)
 
         h, b = ok_cancel_buttons2(d, _('Export'))
         b.setEnabled(False)
@@ -1903,9 +1918,12 @@ class ElectrumWindow(QMainWindow):
 
         private_keys = {}
         addresses = self.wallet.addresses(True)
+        done = False
         def privkeys_thread():
-            time.sleep(0.1)
             for addr in addresses:
+                time.sleep(0.1)
+                if done: 
+                    break
                 private_keys[addr] = "\n".join(self.wallet.get_private_key(addr, password))
                 d.emit(SIGNAL('computing_privkeys'))
             d.emit(SIGNAL('show_privkeys'))
@@ -1920,21 +1938,15 @@ class ElectrumWindow(QMainWindow):
         threading.Thread(target=privkeys_thread).start()
 
         if not d.exec_():
+            done = True
             return
 
         filename = filename_e.text()
         if not filename:
             return
-        self.do_export_privkeys(filename, private_keys)
-
 
-    def do_export_privkeys(self, fileName, pklist):
         try:
-            with open(fileName, "w+") as csvfile:
-                transaction = csv.writer(csvfile)
-                transaction.writerow(["address", "private_key"])
-                for addr, pk in pklist.items():
-                    transaction.writerow(["%34s"%addr,pk])
+            self.do_export_privkeys(filename, private_keys, csv_button.isChecked())
         except (IOError, os.error), reason:
             export_error_label = _("Electrum was unable to produce a private key-export.")
             QMessageBox.critical(None, _("Unable to create csv"), export_error_label + "\n" + str(reason))
@@ -1946,6 +1958,18 @@ class ElectrumWindow(QMainWindow):
         self.show_message(_("Private keys exported."))
 
 
+    def do_export_privkeys(self, fileName, pklist, is_csv):
+        with open(fileName, "w+") as f:
+            if is_csv:
+                transaction = csv.writer(f)
+                transaction.writerow(["address", "private_key"])
+                for addr, pk in pklist.items():
+                    transaction.writerow(["%34s"%addr,pk])
+            else:
+                import json
+                f.write(json.dumps(pklist, indent = 4))
+
+
     def do_import_labels(self):
         labelsFile = self.getOpenFileName(_("Open labels file"), "*.dat")
         if not labelsFile: return
@@ -1972,59 +1996,94 @@ class ElectrumWindow(QMainWindow):
             QMessageBox.critical(None, _("Unable to export labels"), _("Electrum was unable to export your labels.")+"\n" + str(reason))
 
 
-    def do_export_history(self):
-        wallet = self.wallet
-        select_export = _('Select file to export your wallet transactions to')
-        fileName = QFileDialog.getSaveFileName(QWidget(), select_export, os.path.expanduser('~/electrum-history.csv'), "*.csv")
-        if not fileName:
-            return
+    def export_history_dialog(self):
 
-        try:
-            with open(fileName, "w+") as csvfile:
-                transaction = csv.writer(csvfile)
-                transaction.writerow(["transaction_hash","label", "confirmations", "value", "fee", "balance", "timestamp"])
-                for item in wallet.get_tx_history():
-                    tx_hash, confirmations, is_mine, value, fee, balance, timestamp = item
-                    if confirmations:
-                        if timestamp is not None:
-                            try:
-                                time_string = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
-                            except [RuntimeError, TypeError, NameError] as reason:
-                                time_string = "unknown"
-                                pass
-                        else:
-                          time_string = "unknown"
-                    else:
-                        time_string = "pending"
+        d = QDialog(self)
+        d.setWindowTitle(_('Export History'))
+        d.setMinimumSize(400, 200)
+        vbox = QVBoxLayout(d)
 
-                    if value is not None:
-                        value_string = format_satoshis(value, True)
-                    else:
-                        value_string = '--'
+        defaultname = os.path.expanduser('~/electrum-history.csv')
+        select_msg = _('Select file to export your wallet transactions to')
 
-                    if fee is not None:
-                        fee_string = format_satoshis(fee, True)
-                    else:
-                        fee_string = '0'
+        hbox, filename_e, csv_button = filename_field(self, self.config, defaultname, select_msg)
+        vbox.addLayout(hbox)
 
-                    if tx_hash:
-                        label, is_default_label = wallet.get_label(tx_hash)
-                        label = label.encode('utf-8')
-                    else:
-                      label = ""
+        vbox.addStretch(1)
 
-                    balance_string = format_satoshis(balance, False)
-                    transaction.writerow([tx_hash, label, confirmations, value_string, fee_string, balance_string, time_string])
-                QMessageBox.information(None,_("CSV Export created"), _("Your CSV export has been successfully created."))
+        h, b = ok_cancel_buttons2(d, _('Export'))
+        vbox.addLayout(h)
+        if not d.exec_():
+            return
+
+        filename = filename_e.text()
+        if not filename:
+            return
 
+        try:
+            self.do_export_history(self.wallet, filename, csv_button.isChecked())
         except (IOError, os.error), reason:
             export_error_label = _("Electrum was unable to produce a transaction export.")
-            QMessageBox.critical(None,_("Unable to create csv"), export_error_label + "\n" + str(reason))
+            QMessageBox.critical(self, _("Unable to export history"), export_error_label + "\n" + str(reason))
+            return
+
+        QMessageBox.information(self,_("History exported"), _("Your wallet history has been successfully exported."))
+
+
+    def do_export_history(self, wallet, fileName, is_csv):
+        history = wallet.get_tx_history()
+        lines = []
+        for item in history:
+            tx_hash, confirmations, is_mine, value, fee, balance, timestamp = item
+            if confirmations:
+                if timestamp is not None:
+                    try:
+                        time_string = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
+                    except [RuntimeError, TypeError, NameError] as reason:
+                        time_string = "unknown"
+                        pass
+                else:
+                    time_string = "unknown"
+            else:
+                time_string = "pending"
+
+            if value is not None:
+                value_string = format_satoshis(value, True)
+            else:
+                value_string = '--'
+
+            if fee is not None:
+                fee_string = format_satoshis(fee, True)
+            else:
+                fee_string = '0'
+
+            if tx_hash:
+                label, is_default_label = wallet.get_label(tx_hash)
+                label = label.encode('utf-8')
+            else:
+                label = ""
+
+            balance_string = format_satoshis(balance, False)
+            if is_csv:
+                lines.append([tx_hash, label, confirmations, value_string, fee_string, balance_string, time_string])
+            else:
+                lines.append({'txid':tx_hash, 'date':"%16s"%time_string, 'label':label, 'value':value_string})
+
+        with open(fileName, "w+") as f:
+            if is_csv:
+                transaction = csv.writer(f)
+                transaction.writerow(["transaction_hash","label", "confirmations", "value", "fee", "balance", "timestamp"])
+                for line in lines:
+                    transaction.writerow(line)
+            else:
+                import json
+                f.write(json.dumps(lines, indent = 4))
 
 
     def sweep_key_dialog(self):
         d = QDialog(self)
         d.setWindowTitle(_('Sweep private keys'))
+        d.setMinimumSize(600, 300)
 
         vbox = QVBoxLayout(d)
         vbox.addWidget(QLabel(_("Enter private keys")))
@@ -2032,20 +2091,33 @@ class ElectrumWindow(QMainWindow):
         keys_e = QTextEdit()
         keys_e.setTabChangesFocus(True)
         vbox.addWidget(keys_e)
+
+        h, address_e = address_field(self.wallet.addresses())
+        vbox.addLayout(h)
+
         vbox.addStretch(1)
         hbox, button = ok_cancel_buttons2(d, _('Sweep'))
         vbox.addLayout(hbox)
         button.setEnabled(False)
 
-        keys_e.textChanged.connect(lambda: button.setEnabled(Wallet.is_private_key(str(keys_e.toPlainText()).strip())))
+        def get_address():
+            addr = str(address_e.text())
+            if bitcoin.is_address(addr):
+                return addr
+
+        def get_pk():
+            pk = str(keys_e.toPlainText()).strip()
+            if Wallet.is_private_key(pk):
+                return pk.split()
+
+        f = lambda: button.setEnabled(get_address() is not None and get_pk() is not None)
+        keys_e.textChanged.connect(f)
+        address_e.textChanged.connect(f)
         if not d.exec_():
             return
 
-        text = str(keys_e.toPlainText()).strip()
-        privkeys = text.split()
-        to_address = self.wallet.addresses()[0]
         fee = self.wallet.fee
-        tx = Transaction.sweep(privkeys, self.network, to_address, fee)
+        tx = Transaction.sweep(get_pk(), self.network, get_address(), fee)
         self.show_transaction(tx)
 
 
@@ -2227,8 +2299,10 @@ class ElectrumWindow(QMainWindow):
 
     def closeEvent(self, event):
         self.tray.hide()
-        g = self.geometry()
-        self.config.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()], True)
+        self.config.set_key("is_maximized", self.isMaximized())
+        if not self.isMaximized():
+            g = self.geometry()
+            self.config.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()])
         self.save_column_widths()
         self.config.set_key("console-history", self.console.history[-50:], True)
         self.wallet.storage.put('accounts_expanded', self.accounts_expanded)