use bitcoinrpc for "make local"
[bitcoin:spesmilo.git] / send.py
1 # -*- coding: utf-8 -*-
2 # Spesmilo -- Python Bitcoin user interface
3 # Copyright © 2011 Luke Dashjr
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, version 3 only.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 from decimal import Decimal
18 import re
19 from PySide.QtCore import *
20 from PySide.QtGui import *
21 from settings import humanAmount, humanToAmount, SpesmiloSettings
22
23 _windows = {}
24
25 class SendDialog(QDialog):
26     def __init__(self, core = None, parent = None, uri = None, autostart = True):
27         super(SendDialog, self).__init__(parent)
28         self.core = core
29         _windows[id(self)] = self
30         
31         formlay = QFormLayout()
32         self.destaddy = QLineEdit()
33         self.amount = QLineEdit()
34         self.amount.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
35         amount_max_width = self.fontMetrics().averageCharWidth() * 10
36         self.amount.setMaximumWidth(amount_max_width)
37         dv = QDoubleValidator(self.amount)
38         dv.setDecimals(2)
39         dv.setNotation(QDoubleValidator.StandardNotation)
40         self.dv = dv
41         tv = QRegExpValidator(QRegExp('-?(?:[\d\\xe9d9-\\xe9df]+\.?|[\d\\xe9d9-\\xe9df]*\.[\d\\xe9d9-\\xe9df]{1,4})'), self.amount)
42         self.tv = tv
43         self.amount_unit = QComboBox(self)
44         self.amount_unit.addItem(self.tr("BTC"), "BTC")
45         self.amount_unit.addItem(self.tr("TBC"), "TBC")
46         self.amount_unit.currentIndexChanged.connect(self.update_validator)
47         prefunit = 'TBC' if SpesmiloSettings.getNumberSystem() == 'Tonal' else 'BTC'
48         self.amount_unit.setCurrentIndex(self.amount_unit.findData(prefunit))
49         formlay.addRow(self.tr('Pay to:'), self.destaddy)
50         amountlay = QHBoxLayout()
51         amountlay.addWidget(self.amount)
52         amountlay.addWidget(self.amount_unit)
53         amountlay.addStretch()
54         formlay.addRow(self.tr('Amount:'), amountlay)
55
56         actionlay = QHBoxLayout()
57         sendbtn = QPushButton(self.tr('&Send'))
58         sendbtn.clicked.connect(self.do_payment)
59         sendbtn.setAutoDefault(True)
60         cancelbtn = QPushButton(self.tr('&Cancel'))
61         cancelbtn.clicked.connect(self.reject)
62         actionlay.addStretch()
63         actionlay.addWidget(sendbtn)
64         actionlay.addWidget(cancelbtn)
65
66         # layout includes form + instructions
67         instructions = QLabel(self.tr('<i>Enter a bitcoin address (e.g. 1A9Pv2PYuZYvfqku7sJxovw99Az72mZ4YH)</i>'))
68         mainlay = QVBoxLayout(self)
69         mainlay.addWidget(instructions)
70         mainlay.addLayout(formlay)
71         mainlay.addLayout(actionlay)
72
73         if parent is not None:
74             self.setWindowIcon(parent.bitcoin_icon)
75         self.setWindowTitle(self.tr('Send bitcoins'))
76
77         if not uri is None:
78             self.load_uri(uri)
79
80         if autostart:
81             self.start()
82
83     def start(self, options = None, args = None):
84         if not self.core:
85             import core_interface
86             uri = SpesmiloSettings.getEffectiveURI()
87             self.core = core_interface.CoreInterface(uri)
88         if args:
89             self.load_uri(args[0])
90
91         self.show()
92         self.destaddy.setFocus()
93
94     def load_uri(self, uri):
95         m = re.match(r'^bitcoin\:([1-9A-HJ-NP-Za-km-z]*)(?:\?(.*))?$', uri)
96         if m is None:
97             raise RuntimeError(self.tr('Invalid bitcoin URI'))
98         addr = m.group(1)
99         query = m.group(2)
100         param = {}
101         if not query is None:
102             query = re.split(r'[&?;]', query)
103             for q in query:
104                 k, v = q.split('=', 1)
105                 param[k] = v
106         self.destaddy.setText(addr)
107         if 'amount' in param:
108             amount = param['amount']
109             m = re.match(r'^(([\d.]+)(X(\d+))?|x([\da-f]*)(\.([\da-f]*))?(X([\da-f]+))?)$', amount, re.IGNORECASE)
110             if m.group(5):
111                 # TBC
112                 amount = float(int(m.group(5), 16))
113                 if m.group(7):
114                     amount += float(int(m.group(7), 16)) * pow(16, -(len(m.group(7))))
115                 if m.group(9):
116                     amount *= pow(16, int(m.group(9), 16))
117                 else:
118                     amount *= 0x10000
119                 NU = 'Tonal'
120             else:
121                 amount = Decimal(m.group(2))
122                 if m.group(4):
123                     amount *= 10 ** int(m.group(4))
124                 else:
125                     amount *= 100000000
126                 NU = 'Decimal'
127             amount = int(amount)
128             NU = SpesmiloSettings.ChooseUnits(amount, NU)
129             if NU == 'Tonal':
130                 self.amount_unit.setCurrentIndex(self.amount_unit.findData('TBC'))
131                 amount = SpesmiloSettings._toTBC(amount, addSign=False, wantTLA=False)
132             else:
133                 self.amount_unit.setCurrentIndex(self.amount_unit.findData('BTC'))
134                 amount = SpesmiloSettings._toBTC(amount, addSign=False, wantTLA=False)
135             self.amount.setText(amount)
136
137     def update_validator(self):
138         u = self.amount_unit.itemData(self.amount_unit.currentIndex())
139         if u == 'BTC':
140             self.amount.setValidator(self.dv)
141         elif u == 'TBC':
142             self.amount.setValidator(self.tv)
143         else:
144             self.amount.setValidator(None)
145
146     def do_payment(self):
147         if not self.amount.text():
148             self.amount.setFocus(Qt.OtherFocusReason)
149             return
150         self.hide()
151
152         addy = self.destaddy.text()
153         if not self.core.validate_address(addy):
154             error = QMessageBox(QMessageBox.Critical, 
155                                 self.tr('Invalid address'),
156                                 self.tr('Invalid address: %s')%addy)
157             error.exec_()
158             self.reject()
159             return
160
161         amount = self.amount.text()
162         amount += ' ' + self.amount_unit.itemData(self.amount_unit.currentIndex())
163         amount = humanToAmount(amount)
164
165         balance = self.core.balance()
166         if amount > balance:
167             error = QMessageBox(QMessageBox.Critical, 
168                                 self.tr('Insufficient balance'),
169                             self.tr('Balance of %g is too small.') % (humanAmount(balance),))
170             error.exec_()
171             self.reject()
172             return
173
174         try:
175             self.core.send(addy, amount)
176         except Exception, e:
177             error = QMessageBox(QMessageBox.Critical, 
178                                 self.tr('Error sending'),
179                             self.tr('Your send failed: %s') % (e,))
180             error.exec_()
181             self.reject()
182             return
183         self.accept()
184
185     def accept(self):
186         del _windows[id(self)]
187         QDialog.accept(self)
188
189     def reject(self):
190         del _windows[id(self)]
191         QDialog.reject(self)
192
193 if __name__ == '__main__':
194     import os
195     import sys
196     import core_interface
197     from settings import SpesmiloSettings
198     os.system('bitcoind')
199     app = QApplication(sys.argv)
200     SpesmiloSettings.loadTranslator()
201     send = SendDialog(None, None, sys.argv[1] if len(sys.argv) > 1 else None, autostart=False)
202     send.start()
203     sys.exit(app.exec_())