hide meaning, in order to replace misplaced interpretations with a sentiment of helpl...
[electrum-nvc.git] / lib / gui_qt.py
1 #!/usr/bin/env python
2 #
3 # Electrum - lightweight Bitcoin client
4 # Copyright (C) 2012 thomasv@gitorious
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 import sys, time, datetime, re
20 from i18n import _
21 from util import print_error
22
23 try:
24     import PyQt4
25 except:
26     sys.exit("Error: Could not import PyQt4 on Linux systems, you may try 'sudo apt-get install python-qt4'")
27
28 from PyQt4.QtGui import *
29 from PyQt4.QtCore import *
30 import PyQt4.QtCore as QtCore
31 import PyQt4.QtGui as QtGui
32 from interface import DEFAULT_SERVERS
33
34 try:
35     import icons_rc
36 except:
37     sys.exit("Error: Could not import icons_rc.py, please generate it with: 'pyrcc4 icons.qrc -o lib/icons_rc.py'")
38
39 from wallet import format_satoshis
40 import bmp, mnemonic, pyqrnative, qrscanner
41
42 from decimal import Decimal
43
44 import platform
45
46 if platform.system() == 'Windows':
47     MONOSPACE_FONT = 'Lucida Console'
48 elif platform.system() == 'Darwin':
49     MONOSPACE_FONT = 'Monaco'
50 else:
51     MONOSPACE_FONT = 'monospace'
52
53 ALIAS_REGEXP = '^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$'    
54
55 def numbify(entry, is_int = False):
56     text = unicode(entry.text()).strip()
57     pos = entry.cursorPosition()
58     chars = '0123456789'
59     if not is_int: chars +='.'
60     s = ''.join([i for i in text if i in chars])
61     if not is_int:
62         if '.' in s:
63             p = s.find('.')
64             s = s.replace('.','')
65             s = s[:p] + '.' + s[p:p+8]
66         try:
67             amount = int( Decimal(s) * 100000000 )
68         except:
69             amount = None
70     else:
71         try:
72             amount = int( s )
73         except:
74             amount = None
75     entry.setText(s)
76     entry.setCursorPosition(pos)
77     return amount
78
79
80 class Timer(QtCore.QThread):
81     def run(self):
82         while True:
83             self.emit(QtCore.SIGNAL('timersignal'))
84             time.sleep(0.5)
85
86 class HelpButton(QPushButton):
87     def __init__(self, text):
88         QPushButton.__init__(self, '?')
89         self.setFocusPolicy(Qt.NoFocus)
90         self.setFixedWidth(20)
91         self.clicked.connect(lambda: QMessageBox.information(self, 'Help', text, 'OK') )
92
93
94 class EnterButton(QPushButton):
95     def __init__(self, text, func):
96         QPushButton.__init__(self, text)
97         self.func = func
98         self.clicked.connect(func)
99
100     def keyPressEvent(self, e):
101         if e.key() == QtCore.Qt.Key_Return:
102             apply(self.func,())
103
104 class MyTreeWidget(QTreeWidget):
105     def __init__(self, parent):
106         QTreeWidget.__init__(self, parent)
107         def ddfr(item):
108             if not item: return
109             for i in range(0,self.viewport().height()/5):
110                 if self.itemAt(QPoint(0,i*5)) == item:
111                     break
112             else:
113                 return
114             for j in range(0,30):
115                 if self.itemAt(QPoint(0,i*5 + j)) != item:
116                     break
117             self.emit(SIGNAL('customContextMenuRequested(const QPoint&)'), QPoint(50, i*5 + j - 1))
118
119         self.connect(self, SIGNAL('itemActivated(QTreeWidgetItem*, int)'), ddfr)
120         
121
122
123
124 class StatusBarButton(QPushButton):
125     def __init__(self, icon, tooltip, func):
126         QPushButton.__init__(self, icon, '')
127         self.setToolTip(tooltip)
128         self.setFlat(True)
129         self.setMaximumWidth(25)
130         self.clicked.connect(func)
131         self.func = func
132
133     def keyPressEvent(self, e):
134         if e.key() == QtCore.Qt.Key_Return:
135             apply(self.func,())
136
137
138 class QRCodeWidget(QWidget):
139
140     def __init__(self, data = None):
141         QWidget.__init__(self)
142         self.setMinimumSize(210, 210)
143         self.addr = None
144         self.qr = None
145         if data:
146             self.set_addr(data)
147             self.update_qr()
148
149     def set_addr(self, addr):
150         if self.addr != addr:
151             self.addr = addr
152             self.qr = None
153             self.update()
154
155     def update_qr(self):
156         if self.addr and not self.qr:
157             self.qr = pyqrnative.QRCode(4, pyqrnative.QRErrorCorrectLevel.L)
158             self.qr.addData(self.addr)
159             self.qr.make()
160             self.update()
161
162     def paintEvent(self, e):
163
164         if not self.addr:
165             return
166
167         black = QColor(0, 0, 0, 255)
168         white = QColor(255, 255, 255, 255)
169         boxsize = 6
170
171         if not self.qr:
172             qp = QtGui.QPainter()
173             qp.begin(self)
174             qp.setBrush(white)
175             qp.setPen(white)
176             qp.drawRect(0, 0, 198, 198)
177             qp.end()
178             return
179         
180         size = self.qr.getModuleCount()*boxsize
181         k = self.qr.getModuleCount()
182         qp = QtGui.QPainter()
183         qp.begin(self)
184         for r in range(k):
185             for c in range(k):
186                 if self.qr.isDark(r, c):
187                     qp.setBrush(black)
188                     qp.setPen(black)
189                 else:
190                     qp.setBrush(white)
191                     qp.setPen(white)
192                 qp.drawRect(c*boxsize, r*boxsize, boxsize, boxsize)
193         qp.end()
194         
195
196
197 class QR_Window(QWidget):
198
199     def __init__(self):
200         QWidget.__init__(self)
201         self.setWindowTitle('Electrum - Invoice')
202         self.setMinimumSize(800, 250)
203         self.address = ''
204         self.labe = ''
205         self.amount = 0
206         self.setFocusPolicy(QtCore.Qt.NoFocus)
207
208         main_box = QHBoxLayout()
209         
210         self.qrw = QRCodeWidget()
211         main_box.addWidget(self.qrw)
212
213         vbox = QVBoxLayout()
214         main_box.addLayout(vbox)
215
216         main_box.addStretch(1)
217
218         self.address_label = QLabel("")
219         self.address_label.setFont(QFont(MONOSPACE_FONT))
220         vbox.addWidget(self.address_label)
221
222         self.label_label = QLabel("")
223         vbox.addWidget(self.label_label)
224
225         self.amount_label = QLabel("")
226         vbox.addWidget(self.amount_label)
227
228         vbox.addStretch(1)
229         self.setLayout(main_box)
230
231
232     def set_content(self, addr, label, amount):
233         self.address = addr
234         address_text = "<span style='font-size: 18pt'>%s</span>" % addr if addr else ""
235         self.address_label.setText(address_text)
236
237         self.amount = amount
238         amount_text = "<span style='font-size: 21pt'>%s</span> <span style='font-size: 16pt'>BTC</span> " % format_satoshis(amount) if amount else ""
239         self.amount_label.setText(amount_text)
240
241         self.label = label
242         label_text = "<span style='font-size: 21pt'>%s</span>" % label if label else ""
243         self.label_label.setText(label_text)
244
245         msg = 'bitcoin:'+self.address
246         if self.amount is not None:
247             msg += '?amount=%s'%(str( Decimal(self.amount) /100000000))
248             if self.label is not None:
249                 msg += '&label=%s'%(self.label)
250         elif self.label is not None:
251             msg += '?label=%s'%(self.label)
252             
253         self.qrw.set_addr( msg )
254
255             
256
257
258 def waiting_dialog(f):
259
260     s = Timer()
261     s.start()
262     w = QDialog()
263     w.resize(200, 70)
264     w.setWindowTitle('Electrum')
265     l = QLabel('')
266     vbox = QVBoxLayout()
267     vbox.addWidget(l)
268     w.setLayout(vbox)
269     w.show()
270     def ff():
271         s = f()
272         if s: l.setText(s)
273         else: w.close()
274     w.connect(s, QtCore.SIGNAL('timersignal'), ff)
275     w.exec_()
276     w.destroy()
277
278
279 def ok_cancel_buttons(dialog):
280     hbox = QHBoxLayout()
281     hbox.addStretch(1)
282     b = QPushButton("OK")
283     hbox.addWidget(b)
284     b.clicked.connect(dialog.accept)
285     b = QPushButton("Cancel")
286     hbox.addWidget(b)
287     b.clicked.connect(dialog.reject)
288     return hbox
289
290
291 class ElectrumWindow(QMainWindow):
292
293     def __init__(self, wallet, config):
294         QMainWindow.__init__(self)
295         self.wallet = wallet
296         self.config = config
297         self.wallet.interface.register_callback('updated', self.update_callback)
298         self.wallet.interface.register_callback('connected', self.update_callback)
299         self.wallet.interface.register_callback('disconnected', self.update_callback)
300         self.wallet.interface.register_callback('disconnecting', self.update_callback)
301
302         self.receive_tab_mode = config.get('qt_receive_tab_mode', 0)
303         self.merchant_name = config.get('merchant_name', 'Invoice')
304
305         self.qr_window = None
306         self.funds_error = False
307         self.completions = QStringListModel()
308
309         self.tabs = tabs = QTabWidget(self)
310         tabs.addTab(self.create_history_tab(), _('History') )
311         tabs.addTab(self.create_send_tab(), _('Send') )
312         tabs.addTab(self.create_receive_tab(), _('Receive') )
313         tabs.addTab(self.create_contacts_tab(), _('Contacts') )
314         tabs.addTab(self.create_wall_tab(), _('Wall') )
315         tabs.setMinimumSize(600, 400)
316         tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
317         self.setCentralWidget(tabs)
318         self.create_status_bar()
319         self.toggle_QR_window(self.receive_tab_mode == 2)
320
321         g = self.config.get("winpos-qt",[100, 100, 840, 400])
322         self.setGeometry(g[0], g[1], g[2], g[3])
323         title = 'Electrum ' + self.wallet.electrum_version + '  -  ' + self.config.path
324         if not self.wallet.seed: title += ' [seedless]'
325         self.setWindowTitle( title )
326
327         QShortcut(QKeySequence("Ctrl+W"), self, self.close)
328         QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
329         QShortcut(QKeySequence("Ctrl+PgUp"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() - 1 )%tabs.count() ))
330         QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() + 1 )%tabs.count() ))
331         
332         self.connect(self, QtCore.SIGNAL('updatesignal'), self.update_wallet)
333         #self.connect(self, SIGNAL('editamount'), self.edit_amount)
334         self.history_list.setFocus(True)
335
336         # dark magic fix by flatfly; https://bitcointalk.org/index.php?topic=73651.msg959913#msg959913
337         if platform.system() == 'Windows':
338             n = 3 if self.wallet.seed else 2
339             tabs.setCurrentIndex (n)
340             tabs.setCurrentIndex (0)
341
342     def close(self):
343         QMainWindow.close(self)
344         if self.qr_window: 
345             self.qr_window.close()
346             self.qr_window = None
347
348     def connect_slots(self, sender):
349         self.connect(sender, QtCore.SIGNAL('timersignal'), self.timer_actions)
350         self.previous_payto_e=''
351
352     def timer_actions(self):
353         if self.qr_window:
354             self.qr_window.qrw.update_qr()
355             
356         if self.payto_e.hasFocus():
357             return
358         r = unicode( self.payto_e.text() )
359         if r != self.previous_payto_e:
360             self.previous_payto_e = r
361             r = r.strip()
362             if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
363                 try:
364                     to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
365                 except:
366                     return
367                 if to_address:
368                     s = r + '  <' + to_address + '>'
369                     self.payto_e.setText(s)
370
371
372     def update_callback(self):
373         self.emit(QtCore.SIGNAL('updatesignal'))
374
375     def update_wallet(self):
376         if self.wallet.interface and self.wallet.interface.is_connected:
377             if not self.wallet.up_to_date:
378                 text = _( "Synchronizing..." )
379                 icon = QIcon(":icons/status_waiting.png")
380             else:
381                 c, u = self.wallet.get_balance()
382                 text =  _( "Balance" ) + ": %s "%( format_satoshis(c,False,self.wallet.num_zeros) )
383                 if u: text +=  "[%s unconfirmed]"%( format_satoshis(u,True,self.wallet.num_zeros).strip() )
384                 icon = QIcon(":icons/status_connected.png")
385         else:
386             text = _( "Not connected" )
387             icon = QIcon(":icons/status_disconnected.png")
388
389         if self.funds_error:
390             text = _( "Not enough funds" )
391
392         self.statusBar().showMessage(text)
393         self.status_button.setIcon( icon )
394
395         if self.wallet.up_to_date or not self.wallet.interface.is_connected:
396             self.textbox.setText( self.wallet.banner )
397             self.update_history_tab()
398             self.update_receive_tab()
399             self.update_contacts_tab()
400             self.update_completions()
401
402
403     def create_history_tab(self):
404         self.history_list = l = MyTreeWidget(self)
405         l.setColumnCount(5)
406         l.setColumnWidth(0, 40) 
407         l.setColumnWidth(1, 140) 
408         l.setColumnWidth(2, 350) 
409         l.setColumnWidth(3, 140) 
410         l.setColumnWidth(4, 140) 
411         l.setHeaderLabels( [ '', _( 'Date' ), _( 'Description' ) , _('Amount'), _('Balance')] )
412         self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.tx_label_clicked)
413         self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), self.tx_label_changed)
414
415         l.setContextMenuPolicy(Qt.CustomContextMenu)
416         l.customContextMenuRequested.connect(self.create_history_menu)
417         return l
418
419
420     def create_history_menu(self, position):
421         self.history_list.selectedIndexes() 
422         item = self.history_list.currentItem()
423         if not item: return
424         tx_hash = str(item.toolTip(0))
425         if not tx_hash: return
426         menu = QMenu()
427         menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash))
428         menu.addAction(_("Details"), lambda: self.tx_details(tx_hash))
429         menu.addAction(_("Edit description"), lambda: self.tx_label_clicked(item,2))
430         menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
431
432
433     def tx_details(self, tx_hash):
434         tx_details = self.wallet.get_tx_details(tx_hash)
435         QMessageBox.information(self, 'Details', tx_details, 'OK')
436
437
438     def tx_label_clicked(self, item, column):
439         if column==2 and item.isSelected():
440             tx_hash = str(item.toolTip(0))
441             self.is_edit=True
442             #if not self.wallet.labels.get(tx_hash): item.setText(2,'')
443             item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
444             self.history_list.editItem( item, column )
445             item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
446             self.is_edit=False
447
448     def tx_label_changed(self, item, column):
449         if self.is_edit: 
450             return
451         self.is_edit=True
452         tx_hash = str(item.toolTip(0))
453         tx = self.wallet.transactions.get(tx_hash)
454         s = self.wallet.labels.get(tx_hash)
455         text = unicode( item.text(2) )
456         if text: 
457             self.wallet.labels[tx_hash] = text
458             item.setForeground(2, QBrush(QColor('black')))
459         else:
460             if s: self.wallet.labels.pop(tx_hash)
461             text = self.wallet.get_default_label(tx_hash)
462             item.setText(2, text)
463             item.setForeground(2, QBrush(QColor('gray')))
464         self.is_edit=False
465
466
467     def edit_label(self, is_recv):
468         l = self.receive_list if is_recv else self.contacts_list
469         c = 2 if is_recv else 1
470         item = l.currentItem()
471         item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
472         l.editItem( item, c )
473         item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
474
475     def edit_amount(self):
476         l = self.receive_list
477         item = l.currentItem()
478         item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
479         l.editItem( item, 3 )
480         item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
481
482
483     def address_label_clicked(self, item, column, l, column_addr, column_label):
484         if column == column_label and item.isSelected():
485             addr = unicode( item.text(column_addr) )
486             label = unicode( item.text(column_label) )
487             if label in self.wallet.aliases.keys():
488                 return
489             item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
490             l.editItem( item, column )
491             item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
492
493
494     def address_label_changed(self, item, column, l, column_addr, column_label):
495
496         if column == column_label:
497             addr = unicode( item.text(column_addr) )
498             text = unicode( item.text(column_label) )
499             changed = False
500
501             if text:
502                 if text not in self.wallet.aliases.keys():
503                     old_addr = self.wallet.labels.get(text)
504                     if old_addr != addr:
505                         self.wallet.labels[addr] = text
506                         changed = True
507                 else:
508                     print_error("Error: This is one of your aliases")
509                     label = self.wallet.labels.get(addr,'')
510                     item.setText(column_label, QString(label))
511             else:
512                 s = self.wallet.labels.get(addr)
513                 if s: 
514                     self.wallet.labels.pop(addr)
515                     changed = True
516
517             if changed:
518                 self.update_history_tab()
519                 self.update_completions()
520                 
521             self.recv_changed(item)
522
523         if column == 3:
524             address = unicode( item.text(column_addr) )
525             text = unicode( item.text(3) )
526             try:
527                 index = self.wallet.addresses.index(address)
528             except:
529                 return
530
531             try:
532                 amount = int( Decimal(text) * 100000000 )
533                 item.setText(3,format_satoshis(amount,False, self.wallet.num_zeros))
534             except:
535                 amount = self.wallet.requested_amounts.get(address)
536                 if amount: 
537                     item.setText(3,format_satoshis(amount,False, self.wallet.num_zeros))
538                 else:
539                     item.setText(3,"")
540                 return
541
542             self.wallet.requested_amounts[address] = amount
543
544             label = self.wallet.labels.get(address)
545             if label is None:
546                 label = self.merchant_name + ' - %04d'%(index+1)
547                 self.wallet.labels[address] = label
548
549             self.update_receive_item(self.receive_list.currentItem())
550             if self.qr_window:
551                 self.qr_window.set_content( address, label, amount )
552
553
554     def recv_changed(self, a):
555         "current item changed"
556         if a is not None and self.qr_window and self.qr_window.isVisible():
557             address = str(a.text(1))
558             label = self.wallet.labels.get(address)
559             amount = self.wallet.requested_amounts.get(address)
560             self.qr_window.set_content( address, label, amount )
561
562
563     def update_history_tab(self):
564
565         self.history_list.clear()
566         for item in self.wallet.get_tx_history():
567             tx_hash, conf, is_mine, value, fee, balance, timestamp = item
568             if conf:
569                 try:
570                     time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3]
571                 except:
572                     time_str = "unknown"
573                 if conf == -1:
574                     icon = None
575                 if conf == 0:
576                     icon = QIcon(":icons/unconfirmed.png")
577                 elif conf < 6:
578                     icon = QIcon(":icons/clock%d.png"%conf)
579                 else:
580                     icon = QIcon(":icons/confirmed.png")
581             else:
582                 time_str = 'pending'
583                 icon = QIcon(":icons/unconfirmed.png")
584
585             if value is not None:
586                 v_str = format_satoshis(value, True, self.wallet.num_zeros)
587             else:
588                 v_str = '--'
589
590             balance_str = format_satoshis(balance, False, self.wallet.num_zeros)
591             
592             if tx_hash:
593                 label, is_default_label = self.wallet.get_label(tx_hash)
594             else:
595                 label = _('Pruned transaction outputs')
596                 is_default_label = False
597
598             item = QTreeWidgetItem( [ '', time_str, label, v_str, balance_str] )
599             item.setFont(2, QFont(MONOSPACE_FONT))
600             item.setFont(3, QFont(MONOSPACE_FONT))
601             item.setFont(4, QFont(MONOSPACE_FONT))
602             if tx_hash:
603                 item.setToolTip(0, tx_hash)
604             if is_default_label:
605                 item.setForeground(2, QBrush(QColor('grey')))
606
607             item.setIcon(0, icon)
608             self.history_list.insertTopLevelItem(0,item)
609             
610
611         self.history_list.setCurrentItem(self.history_list.topLevelItem(0))
612
613
614     def create_send_tab(self):
615         w = QWidget()
616
617         grid = QGridLayout()
618         grid.setSpacing(8)
619         grid.setColumnMinimumWidth(3,300)
620         grid.setColumnStretch(5,1)
621
622         self.payto_e = QLineEdit()
623         grid.addWidget(QLabel(_('Pay to')), 1, 0)
624         grid.addWidget(self.payto_e, 1, 1, 1, 3)
625         
626         def fill_from_qr():
627             qrcode = qrscanner.scan_qr()
628             if 'address' in qrcode:
629                 self.payto_e.setText(qrcode['address'])
630             if 'amount' in qrcode:
631                 self.amount_e.setText(str(qrcode['amount']))
632             if 'label' in qrcode:
633                 self.message_e.setText(qrcode['label'])
634             if 'message' in qrcode:
635                 self.message_e.setText("%s (%s)" % (self.message_e.text(), qrcode['message']))
636                 
637
638         if qrscanner.is_available():
639             b = QPushButton(_("Scan QR code"))
640             b.clicked.connect(fill_from_qr)
641             grid.addWidget(b, 1, 5)
642     
643         grid.addWidget(HelpButton(_('Recipient of the funds.') + '\n\n' + _('You may enter a Bitcoin address, a label from your list of contacts (a list of completions will be proposed), or an alias (email-like address that forwards to a Bitcoin address)')), 1, 4)
644
645         completer = QCompleter()
646         completer.setCaseSensitivity(False)
647         self.payto_e.setCompleter(completer)
648         completer.setModel(self.completions)
649
650         self.message_e = QLineEdit()
651         grid.addWidget(QLabel(_('Description')), 2, 0)
652         grid.addWidget(self.message_e, 2, 1, 1, 3)
653         grid.addWidget(HelpButton(_('Description of the transaction (not mandatory).') + '\n\n' + _('The description is not sent to the recipient of the funds. It is stored in your wallet file, and displayed in the \'History\' tab.')), 2, 4)
654
655         self.amount_e = QLineEdit()
656         grid.addWidget(QLabel(_('Amount')), 3, 0)
657         grid.addWidget(self.amount_e, 3, 1, 1, 2)
658         grid.addWidget(HelpButton(
659                 _('Amount to be sent.') + '\n\n' \
660                     + _('The amount will be displayed in red if you do not have enough funds in your wallet. Note that if you have frozen some of your addresses, the available funds will be lower than your total balance.')), 3, 3)
661         
662         self.fee_e = QLineEdit()
663         grid.addWidget(QLabel(_('Fee')), 4, 0)
664         grid.addWidget(self.fee_e, 4, 1, 1, 2) 
665         grid.addWidget(HelpButton(
666                 _('Bitcoin transactions are in general not free. A transaction fee is paid by the sender of the funds.') + '\n\n'\
667                     + _('The amount of fee can be decided freely by the sender. However, transactions with low fees take more time to be processed.') + '\n\n'\
668                     + _('A suggested fee is automatically added to this field. You may override it. The suggested fee increases with the size of the transaction.')), 4, 3)
669         
670         b = EnterButton(_("Send"), self.do_send)
671         grid.addWidget(b, 6, 1)
672
673         b = EnterButton(_("Clear"),self.do_clear)
674         grid.addWidget(b, 6, 2)
675
676         self.payto_sig = QLabel('')
677         grid.addWidget(self.payto_sig, 7, 0, 1, 4)
678
679         QShortcut(QKeySequence("Up"), w, w.focusPreviousChild)
680         QShortcut(QKeySequence("Down"), w, w.focusNextChild)
681         w.setLayout(grid) 
682
683         w2 = QWidget()
684         vbox = QVBoxLayout()
685         vbox.addWidget(w)
686         vbox.addStretch(1)
687         w2.setLayout(vbox)
688
689         def entry_changed( is_fee ):
690             self.funds_error = False
691             amount = numbify(self.amount_e)
692             fee = numbify(self.fee_e)
693             if not is_fee: fee = None
694             if amount is None:
695                 return
696             inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee )
697             if not is_fee:
698                 self.fee_e.setText( str( Decimal( fee ) / 100000000 ) )
699             if inputs:
700                 palette = QPalette()
701                 palette.setColor(self.amount_e.foregroundRole(), QColor('black'))
702             else:
703                 palette = QPalette()
704                 palette.setColor(self.amount_e.foregroundRole(), QColor('red'))
705                 self.funds_error = True
706             self.amount_e.setPalette(palette)
707             self.fee_e.setPalette(palette)
708
709         self.amount_e.textChanged.connect(lambda: entry_changed(False) )
710         self.fee_e.textChanged.connect(lambda: entry_changed(True) )
711
712         return w2
713
714
715     def update_completions(self):
716         l = []
717         for addr,label in self.wallet.labels.items():
718             if addr in self.wallet.addressbook:
719                 l.append( label + '  <' + addr + '>')
720         l = l + self.wallet.aliases.keys()
721
722         self.completions.setStringList(l)
723
724
725
726     def do_send(self):
727
728         label = unicode( self.message_e.text() )
729         r = unicode( self.payto_e.text() )
730         r = r.strip()
731
732         # alias
733         m1 = re.match(ALIAS_REGEXP, r)
734         # label or alias, with address in brackets
735         m2 = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
736         
737         if m1:
738             to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
739             if not to_address:
740                 return
741         elif m2:
742             to_address = m2.group(2)
743         else:
744             to_address = r
745
746         if not self.wallet.is_valid(to_address):
747             QMessageBox.warning(self, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK'))
748             return
749
750         try:
751             amount = int( Decimal( unicode( self.amount_e.text())) * 100000000 )
752         except:
753             QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
754             return
755         try:
756             fee = int( Decimal( unicode( self.fee_e.text())) * 100000000 )
757         except:
758             QMessageBox.warning(self, _('Error'), _('Invalid Fee'), _('OK'))
759             return
760
761         if self.wallet.use_encryption:
762             password = self.password_dialog()
763             if not password:
764                 return
765         else:
766             password = None
767
768         try:
769             tx = self.wallet.mktx( [(to_address, amount)], label, password, fee)
770         except BaseException, e:
771             self.show_message(str(e))
772             return
773
774         if self.wallet.seed:
775             h = self.wallet.send_tx(tx)
776             waiting_dialog(lambda: False if self.wallet.tx_event.isSet() else _("Please wait..."))
777             status, msg = self.wallet.receive_tx( h )
778             if status:
779                 QMessageBox.information(self, '', _('Payment sent.')+'\n'+msg, _('OK'))
780                 self.do_clear()
781                 self.update_contacts_tab()
782             else:
783                 QMessageBox.warning(self, _('Error'), msg, _('OK'))
784         else:
785             filename = 'unsigned_tx'
786             f = open(filename,'w')
787             f.write(tx)
788             f.close()
789             QMessageBox.information(self, _('Unsigned transaction'), _("Unsigned transaction was saved to file:") + " " +filename, _('OK'))
790
791
792     def set_url(self, url):
793         payto, amount, label, message, signature, identity, url = self.wallet.parse_url(url, self.show_message, self.question)
794         self.tabs.setCurrentIndex(1)
795         label = self.wallet.labels.get(payto)
796         m_addr = label + '  <'+ payto+'>' if label else payto
797         self.payto_e.setText(m_addr)
798
799         self.message_e.setText(message)
800         self.amount_e.setText(amount)
801         if identity:
802             self.set_frozen(self.payto_e,True)
803             self.set_frozen(self.amount_e,True)
804             self.set_frozen(self.message_e,True)
805             self.payto_sig.setText( '      The bitcoin URI was signed by ' + identity )
806         else:
807             self.payto_sig.setVisible(False)
808
809     def do_clear(self):
810         self.payto_sig.setVisible(False)
811         for e in [self.payto_e, self.message_e, self.amount_e, self.fee_e]:
812             e.setText('')
813             self.set_frozen(e,False)
814
815     def set_frozen(self,entry,frozen):
816         if frozen:
817             entry.setReadOnly(True)
818             entry.setFrame(False)
819             palette = QPalette()
820             palette.setColor(entry.backgroundRole(), QColor('lightgray'))
821             entry.setPalette(palette)
822         else:
823             entry.setReadOnly(False)
824             entry.setFrame(True)
825             palette = QPalette()
826             palette.setColor(entry.backgroundRole(), QColor('white'))
827             entry.setPalette(palette)
828
829
830     def toggle_freeze(self,addr):
831         if not addr: return
832         if addr in self.wallet.frozen_addresses:
833             self.wallet.unfreeze(addr)
834         else:
835             self.wallet.freeze(addr)
836         self.update_receive_tab()
837
838     def toggle_priority(self,addr):
839         if not addr: return
840         if addr in self.wallet.prioritized_addresses:
841             self.wallet.unprioritize(addr)
842         else:
843             self.wallet.prioritize(addr)
844         self.update_receive_tab()
845
846
847     def create_list_tab(self, headers):
848         "generic tab creation method"
849         l = MyTreeWidget(self)
850         l.setColumnCount( len(headers) )
851         l.setHeaderLabels( headers )
852
853         w = QWidget()
854         vbox = QVBoxLayout()
855         w.setLayout(vbox)
856
857         vbox.setMargin(0)
858         vbox.setSpacing(0)
859         vbox.addWidget(l)
860         buttons = QWidget()
861         vbox.addWidget(buttons)
862
863         hbox = QHBoxLayout()
864         hbox.setMargin(0)
865         hbox.setSpacing(0)
866         buttons.setLayout(hbox)
867
868         return l,w,hbox
869
870
871     def create_receive_tab(self):
872         l,w,hbox = self.create_list_tab([_('Flags'), _('Address'), _('Label'), _('Requested'), _('Balance'), _('Tx')])
873         l.setContextMenuPolicy(Qt.CustomContextMenu)
874         l.customContextMenuRequested.connect(self.create_receive_menu)
875         self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,1,2))
876         self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,1,2))
877         self.connect(l, SIGNAL('currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)'), lambda a,b: self.recv_changed(a))
878         self.receive_list = l
879         self.receive_buttons_hbox = hbox
880         hbox.addStretch(1)
881         return w
882
883
884
885     def receive_tab_set_mode(self, i):
886         self.receive_tab_mode = i
887         self.config.set_key('qt_receive_tab_mode', self.receive_tab_mode, True)
888         self.wallet.save()
889         self.update_receive_tab()
890         self.toggle_QR_window(self.receive_tab_mode == 2)
891
892
893     def create_contacts_tab(self):
894         l,w,hbox = self.create_list_tab([_('Address'), _('Label'), _('Tx')])
895         l.setContextMenuPolicy(Qt.CustomContextMenu)
896         l.customContextMenuRequested.connect(self.create_contact_menu)
897         self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,0,1))
898         self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,0,1))
899         self.contacts_list = l
900         self.contacts_buttons_hbox = hbox
901         hbox.addWidget(EnterButton(_("New"), self.new_contact_dialog))
902         hbox.addStretch(1)
903         return w
904
905
906     def create_receive_menu(self, position):
907         # fixme: this function apparently has a side effect.
908         # if it is not called the menu pops up several times
909         #self.receive_list.selectedIndexes() 
910
911         item = self.receive_list.itemAt(position)
912         if not item: return
913         addr = unicode(item.text(1))
914         menu = QMenu()
915         menu.addAction(_("Copy to clipboard"), lambda: self.app.clipboard().setText(addr))
916         if self.receive_tab_mode == 2:
917             menu.addAction(_("Request amount"), lambda: self.edit_amount())
918         menu.addAction(_("View QR"), lambda: ElectrumWindow.show_qrcode("Address","bitcoin:"+addr) )
919         menu.addAction(_("Edit label"), lambda: self.edit_label(True))
920         menu.addAction(_("Sign message"), lambda: self.sign_message(addr))
921
922         t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
923         menu.addAction(t, lambda: self.toggle_freeze(addr))
924         t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
925         menu.addAction(t, lambda: self.toggle_priority(addr))
926         menu.exec_(self.receive_list.viewport().mapToGlobal(position))
927
928
929     def payto(self, x, is_alias):
930         if not x: return
931         if is_alias:
932             label = x
933             m_addr = label
934         else:
935             addr = x
936             label = self.wallet.labels.get(addr)
937             m_addr = label + '  <' + addr + '>' if label else addr
938         self.tabs.setCurrentIndex(1)
939         self.payto_e.setText(m_addr)
940         self.amount_e.setFocus()
941
942     def delete_contact(self, x, is_alias):
943         if self.question("Do you want to remove %s from your list of contacts?"%x):
944             if not is_alias and x in self.wallet.addressbook:
945                 self.wallet.addressbook.remove(x)
946                 if x in self.wallet.labels.keys():
947                     self.wallet.labels.pop(x)
948             elif is_alias and x in self.wallet.aliases:
949                 self.wallet.aliases.pop(x)
950             self.update_history_tab()
951             self.update_contacts_tab()
952             self.update_completions()
953
954     def create_contact_menu(self, position):
955         # fixme: this function apparently has a side effect.
956         # if it is not called the menu pops up several times
957         #self.contacts_list.selectedIndexes() 
958
959         item = self.contacts_list.itemAt(position)
960         if not item: return
961         addr = unicode(item.text(0))
962         label = unicode(item.text(1))
963         is_alias = label in self.wallet.aliases.keys()
964         x = label if is_alias else addr
965         menu = QMenu()
966         menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
967         menu.addAction(_("Pay to"), lambda: self.payto(x, is_alias))
968         menu.addAction(_("View QR code"),lambda: self.show_qrcode("Address","bitcoin:"+addr))
969         if not is_alias:
970             menu.addAction(_("Edit label"), lambda: self.edit_label(False))
971         else:
972             menu.addAction(_("View alias details"), lambda: self.show_contact_details(label))
973         menu.addAction(_("Delete"), lambda: self.delete_contact(x,is_alias))
974         menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
975
976
977     def update_receive_item(self, item):
978         address = str( item.data(1,0).toString() )
979
980         flags = self.wallet.get_address_flags(address)
981         item.setData(0,0,flags)
982
983         label = self.wallet.labels.get(address,'')
984         item.setData(2,0,label)
985
986         amount = self.wallet.requested_amounts.get(address,None)
987         amount_str = format_satoshis( amount, False, self.wallet.num_zeros ) if amount is not None  else ""
988         item.setData(3,0,amount_str)
989         
990         c, u = self.wallet.get_addr_balance(address)
991         balance = format_satoshis( c + u, False, self.wallet.num_zeros )
992         item.setData(4,0,balance)
993
994         if address in self.wallet.frozen_addresses: 
995             item.setBackgroundColor(1, QColor('lightblue'))
996         elif address in self.wallet.prioritized_addresses: 
997             item.setBackgroundColor(1, QColor('lightgreen'))
998         
999
1000     def update_receive_tab(self):
1001         l = self.receive_list
1002         
1003         l.clear()
1004         l.setColumnHidden(0, not self.receive_tab_mode == 1)
1005         l.setColumnHidden(3, not self.receive_tab_mode == 2)
1006         l.setColumnHidden(4, self.receive_tab_mode == 0)
1007         l.setColumnHidden(5, not self.receive_tab_mode == 1)
1008         l.setColumnWidth(0, 50)
1009         l.setColumnWidth(1, 310) 
1010         l.setColumnWidth(2, 200)
1011         l.setColumnWidth(3, 130)
1012         l.setColumnWidth(4, 130)
1013         l.setColumnWidth(5, 10)
1014
1015         gap = 0
1016         is_red = False
1017         for address in self.wallet.all_addresses():
1018
1019             if self.wallet.is_change(address) and self.receive_tab_mode != 1:
1020                 continue
1021
1022             n = 0 
1023             h = self.wallet.history.get(address,[])
1024
1025             if h != ['*']: 
1026                 for tx_hash, tx_height in h:
1027                     tx = self.wallet.transactions.get(tx_hash)
1028                     if tx: n += 1
1029                 num_tx = "%d "%n
1030             else:
1031                 n = -1
1032                 num_tx = "*"
1033
1034             if n==0:
1035                 if address in self.wallet.addresses:
1036                     gap += 1
1037                     if gap > self.wallet.gap_limit:
1038                         is_red = True
1039             else:
1040                 if address in self.wallet.addresses:
1041                     gap = 0
1042
1043             item = QTreeWidgetItem( [ '', address, '', '', '', num_tx] )
1044             item.setFont(0, QFont(MONOSPACE_FONT))
1045             item.setFont(1, QFont(MONOSPACE_FONT))
1046             item.setFont(3, QFont(MONOSPACE_FONT))
1047             self.update_receive_item(item)
1048             if is_red and address in self.wallet.addresses:
1049                 item.setBackgroundColor(1, QColor('red'))
1050             l.addTopLevelItem(item)
1051
1052         # we use column 1 because column 0 may be hidden
1053         l.setCurrentItem(l.topLevelItem(0),1)
1054
1055     def show_contact_details(self, m):
1056         a = self.wallet.aliases.get(m)
1057         if a:
1058             if a[0] in self.wallet.authorities.keys():
1059                 s = self.wallet.authorities.get(a[0])
1060             else:
1061                 s = "self-signed"
1062             msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
1063             QMessageBox.information(self, 'Alias', msg, 'OK')
1064
1065     def update_contacts_tab(self):
1066
1067         l = self.contacts_list
1068         l.clear()
1069         l.setColumnWidth(0, 350) 
1070         l.setColumnWidth(1, 330)
1071         l.setColumnWidth(2, 100) 
1072
1073         alias_targets = []
1074         for alias, v in self.wallet.aliases.items():
1075             s, target = v
1076             alias_targets.append(target)
1077             item = QTreeWidgetItem( [ target, alias, '-'] )
1078             item.setBackgroundColor(0, QColor('lightgray'))
1079             l.addTopLevelItem(item)
1080             
1081         for address in self.wallet.addressbook:
1082             if address in alias_targets: continue
1083             label = self.wallet.labels.get(address,'')
1084             n = 0 
1085             for item in self.wallet.transactions.values():
1086                 if address in item['outputs'] : n=n+1
1087             tx = "%d"%n
1088             item = QTreeWidgetItem( [ address, label, tx] )
1089             item.setFont(0, QFont(MONOSPACE_FONT))
1090             l.addTopLevelItem(item)
1091
1092         l.setCurrentItem(l.topLevelItem(0))
1093
1094     def create_wall_tab(self):
1095         self.textbox = textbox = QTextEdit(self)
1096         textbox.setFont(QFont(MONOSPACE_FONT))
1097         textbox.setReadOnly(True)
1098         return textbox
1099
1100     def create_status_bar(self):
1101         sb = QStatusBar()
1102         sb.setFixedHeight(35)
1103         if self.wallet.seed:
1104             sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) )
1105         sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) )
1106         if self.wallet.seed:
1107             sb.addPermanentWidget( StatusBarButton( QIcon(":icons/seed.png"), "Seed", lambda: self.show_seed_dialog(self.wallet, self) ) )
1108         self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) ) 
1109         sb.addPermanentWidget( self.status_button )
1110         self.setStatusBar(sb)
1111
1112     def new_contact_dialog(self):
1113         text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':')
1114         address = unicode(text)
1115         if ok:
1116             if self.wallet.is_valid(address):
1117                 self.wallet.addressbook.append(address)
1118                 self.wallet.save()
1119                 self.update_contacts_tab()
1120                 self.update_history_tab()
1121                 self.update_completions()
1122             else:
1123                 QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
1124
1125     @staticmethod
1126     def show_seed_dialog(wallet, parent=None):
1127         if not wallet.seed:
1128             QMessageBox.information(parent, _('Message'),
1129                                     _('No seed'), _('OK'))
1130             return
1131
1132         if wallet.use_encryption:
1133             password = parent.password_dialog()
1134             if not password:
1135                 return
1136         else:
1137             password = None
1138             
1139         try:
1140             seed = wallet.pw_decode(wallet.seed, password)
1141         except:
1142             QMessageBox.warning(parent, _('Error'),
1143                                 _('Incorrect Password'), _('OK'))
1144             return
1145
1146         dialog = QDialog(None)
1147         dialog.setModal(1)
1148         dialog.setWindowTitle("Electrum")
1149
1150         brainwallet = ' '.join(mnemonic.mn_encode(seed))
1151
1152         msg =   _("Your wallet generation seed is") +":<p>\"" + brainwallet + "\"<p>" \
1153               + _("Please write down or memorize these 12 words (order is important).") + " " \
1154               + _("This seed will allow you to recover your wallet in case of computer failure.") + "<p>" \
1155               + _("WARNING: Never disclose your seed. Never type it on a website.") + "<p>"
1156
1157         main_text = QLabel(msg)
1158         main_text.setWordWrap(True)
1159
1160         logo = QLabel()
1161         logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
1162
1163         if parent:
1164             app = parent.app
1165         else:
1166             app = QApplication
1167
1168         copy_function = lambda: app.clipboard().setText(brainwallet)
1169         copy_button = QPushButton(_("Copy to Clipboard"))
1170         copy_button.clicked.connect(copy_function)
1171
1172         show_qr_function = lambda: ElectrumWindow.show_qrcode(_("Seed"), seed)
1173         qr_button = QPushButton(_("View as QR Code"))
1174         qr_button.clicked.connect(show_qr_function)
1175
1176         ok_button = QPushButton(_("OK"))
1177         ok_button.setDefault(True)
1178         ok_button.clicked.connect(dialog.accept)
1179
1180         main_layout = QGridLayout()
1181         main_layout.addWidget(logo, 0, 0)
1182         main_layout.addWidget(main_text, 0, 1, 1, -1)
1183         main_layout.addWidget(copy_button, 1, 1)
1184         main_layout.addWidget(qr_button, 1, 2)
1185         main_layout.addWidget(ok_button, 1, 3)
1186         dialog.setLayout(main_layout)
1187
1188         dialog.exec_()
1189
1190     @staticmethod
1191     def show_qrcode(title, data):
1192         if not data: return
1193         d = QDialog(None)
1194         d.setModal(1)
1195         d.setWindowTitle(title)
1196         d.setMinimumSize(270, 300)
1197         vbox = QVBoxLayout()
1198         qrw = QRCodeWidget(data)
1199         vbox.addWidget(qrw)
1200         vbox.addWidget(QLabel(data))
1201         hbox = QHBoxLayout()
1202         hbox.addStretch(1)
1203
1204         def print_qr(self):
1205             filename = "qrcode.bmp"
1206             bmp.save_qrcode(qrw.qr, filename)
1207             QMessageBox.information(None, _('Message'), _("QR code saved to file") + " " + filename, _('OK'))
1208
1209         b = QPushButton(_("Print"))
1210         hbox.addWidget(b)
1211         b.clicked.connect(print_qr)
1212
1213         b = QPushButton(_("Close"))
1214         hbox.addWidget(b)
1215         b.clicked.connect(d.accept)
1216
1217         vbox.addLayout(hbox)
1218         d.setLayout(vbox)
1219         d.exec_()
1220
1221     def sign_message(self,address):
1222         if not address: return
1223         d = QDialog(self)
1224         d.setModal(1)
1225         d.setWindowTitle('Sign Message')
1226         d.setMinimumSize(270, 350)
1227
1228         tab_widget = QTabWidget()
1229         tab = QWidget()
1230         layout = QGridLayout(tab)
1231
1232         sign_address = QLineEdit()
1233         sign_address.setText(address)
1234         layout.addWidget(QLabel(_('Address')), 1, 0)
1235         layout.addWidget(sign_address, 1, 1)
1236
1237         sign_message = QTextEdit()
1238         layout.addWidget(QLabel(_('Message')), 2, 0)
1239         layout.addWidget(sign_message, 2, 1, 2, 1)
1240
1241         sign_signature = QLineEdit()
1242         layout.addWidget(QLabel(_('Signature')), 3, 0)
1243         layout.addWidget(sign_signature, 3, 1)
1244
1245         def do_sign():
1246             if self.wallet.use_encryption:
1247                 password = self.password_dialog()
1248                 if not password:
1249                     return
1250             else:
1251                 password = None
1252
1253             try:
1254                 signature = self.wallet.sign_message(sign_address.text(), str(sign_message.toPlainText()), password)
1255                 sign_signature.setText(signature)
1256             except BaseException, e:
1257                 self.show_message(str(e))
1258                 return
1259
1260         hbox = QHBoxLayout()
1261         b = QPushButton(_("Sign"))
1262         hbox.addWidget(b)
1263         b.clicked.connect(do_sign)
1264         b = QPushButton(_("Close"))
1265         b.clicked.connect(d.accept)
1266         hbox.addWidget(b)
1267         layout.addLayout(hbox, 4, 1)
1268         tab_widget.addTab(tab, "Sign")
1269
1270
1271         tab = QWidget()
1272         layout = QGridLayout(tab)
1273
1274         verify_address = QLineEdit()
1275         layout.addWidget(QLabel(_('Address')), 1, 0)
1276         layout.addWidget(verify_address, 1, 1)
1277
1278         verify_message = QTextEdit()
1279         layout.addWidget(QLabel(_('Message')), 2, 0)
1280         layout.addWidget(verify_message, 2, 1, 2, 1)
1281
1282         verify_signature = QLineEdit()
1283         layout.addWidget(QLabel(_('Signature')), 3, 0)
1284         layout.addWidget(verify_signature, 3, 1)
1285
1286         def do_verify():
1287             try:
1288                 self.wallet.verify_message(verify_address.text(), verify_signature.text(), str(verify_message.toPlainText()))
1289                 self.show_message("Signature verified")
1290             except BaseException, e:
1291                 self.show_message(str(e))
1292                 return
1293
1294         hbox = QHBoxLayout()
1295         b = QPushButton(_("Verify"))
1296         b.clicked.connect(do_verify)
1297         hbox.addWidget(b)
1298         b = QPushButton(_("Close"))
1299         b.clicked.connect(d.accept)
1300         hbox.addWidget(b)
1301         layout.addLayout(hbox, 4, 1)
1302         tab_widget.addTab(tab, "Verify")
1303
1304         vbox = QVBoxLayout()
1305         vbox.addWidget(tab_widget)
1306         d.setLayout(vbox)
1307         d.exec_()
1308
1309         
1310     def toggle_QR_window(self, show):
1311         if show and not self.qr_window:
1312             self.qr_window = QR_Window()
1313             self.qr_window.setVisible(True)
1314             self.qr_window_geometry = self.qr_window.geometry()
1315             item = self.receive_list.currentItem()
1316             if item:
1317                 address = str(item.text(1))
1318                 label = self.wallet.labels.get(address)
1319                 amount = self.wallet.requested_amounts.get(address)
1320                 self.qr_window.set_content( address, label, amount )
1321
1322         elif show and self.qr_window and not self.qr_window.isVisible():
1323             self.qr_window.setVisible(True)
1324             self.qr_window.setGeometry(self.qr_window_geometry)
1325
1326         elif not show and self.qr_window and self.qr_window.isVisible():
1327             self.qr_window_geometry = self.qr_window.geometry()
1328             self.qr_window.setVisible(False)
1329
1330         #self.print_button.setHidden(self.qr_window is None or not self.qr_window.isVisible())
1331         self.receive_list.setColumnHidden(3, self.qr_window is None or not self.qr_window.isVisible())
1332         self.receive_list.setColumnWidth(2, 200)
1333
1334
1335     def question(self, msg):
1336         return QMessageBox.question(self, _('Message'), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
1337
1338     def show_message(self, msg):
1339         QMessageBox.information(self, _('Message'), msg, _('OK'))
1340
1341     def password_dialog(self ):
1342         d = QDialog(self)
1343         d.setModal(1)
1344
1345         pw = QLineEdit()
1346         pw.setEchoMode(2)
1347
1348         vbox = QVBoxLayout()
1349         msg = _('Please enter your password')
1350         vbox.addWidget(QLabel(msg))
1351
1352         grid = QGridLayout()
1353         grid.setSpacing(8)
1354         grid.addWidget(QLabel(_('Password')), 1, 0)
1355         grid.addWidget(pw, 1, 1)
1356         vbox.addLayout(grid)
1357
1358         vbox.addLayout(ok_cancel_buttons(d))
1359         d.setLayout(vbox) 
1360
1361         if not d.exec_(): return
1362         return unicode(pw.text())
1363
1364
1365
1366
1367
1368     @staticmethod
1369     def change_password_dialog( wallet, parent=None ):
1370
1371         if not wallet.seed:
1372             QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
1373             return
1374
1375         d = QDialog(parent)
1376         d.setModal(1)
1377
1378         pw = QLineEdit()
1379         pw.setEchoMode(2)
1380         new_pw = QLineEdit()
1381         new_pw.setEchoMode(2)
1382         conf_pw = QLineEdit()
1383         conf_pw.setEchoMode(2)
1384
1385         vbox = QVBoxLayout()
1386         if parent:
1387             msg = (_('Your wallet is encrypted. Use this dialog to change your password.')+'\n'\
1388                    +_('To disable wallet encryption, enter an empty new password.')) \
1389                    if wallet.use_encryption else _('Your wallet keys are not encrypted')
1390         else:
1391             msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
1392                   +_("Leave these fields empty if you want to disable encryption.")
1393         vbox.addWidget(QLabel(msg))
1394
1395         grid = QGridLayout()
1396         grid.setSpacing(8)
1397
1398         if wallet.use_encryption:
1399             grid.addWidget(QLabel(_('Password')), 1, 0)
1400             grid.addWidget(pw, 1, 1)
1401
1402         grid.addWidget(QLabel(_('New Password')), 2, 0)
1403         grid.addWidget(new_pw, 2, 1)
1404
1405         grid.addWidget(QLabel(_('Confirm Password')), 3, 0)
1406         grid.addWidget(conf_pw, 3, 1)
1407         vbox.addLayout(grid)
1408
1409         vbox.addLayout(ok_cancel_buttons(d))
1410         d.setLayout(vbox) 
1411
1412         if not d.exec_(): return
1413
1414         password = unicode(pw.text()) if wallet.use_encryption else None
1415         new_password = unicode(new_pw.text())
1416         new_password2 = unicode(conf_pw.text())
1417
1418         try:
1419             seed = wallet.pw_decode( wallet.seed, password)
1420         except:
1421             QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
1422             return
1423
1424         if new_password != new_password2:
1425             QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
1426             return ElectrumWindow.change_password_dialog(wallet, parent) # Retry
1427
1428         wallet.update_password(seed, password, new_password)
1429
1430     @staticmethod
1431     def seed_dialog(wallet, parent=None):
1432         d = QDialog(parent)
1433         d.setModal(1)
1434
1435         vbox = QVBoxLayout()
1436         msg = _("Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet.")
1437         vbox.addWidget(QLabel(msg))
1438
1439         grid = QGridLayout()
1440         grid.setSpacing(8)
1441
1442         seed_e = QLineEdit()
1443         grid.addWidget(QLabel(_('Seed or mnemonic')), 1, 0)
1444         grid.addWidget(seed_e, 1, 1)
1445
1446         gap_e = QLineEdit()
1447         gap_e.setText("5")
1448         grid.addWidget(QLabel(_('Gap limit')), 2, 0)
1449         grid.addWidget(gap_e, 2, 1)
1450         gap_e.textChanged.connect(lambda: numbify(gap_e,True))
1451         vbox.addLayout(grid)
1452
1453         vbox.addLayout(ok_cancel_buttons(d))
1454         d.setLayout(vbox) 
1455
1456         if not d.exec_(): return
1457
1458         try:
1459             gap = int(unicode(gap_e.text()))
1460         except:
1461             QMessageBox.warning(None, _('Error'), 'error', 'OK')
1462             sys.exit(0)
1463
1464         try:
1465             seed = unicode(seed_e.text())
1466             seed.decode('hex')
1467         except:
1468             print_error("Warning: Not hex, trying decode")
1469             try:
1470                 seed = mnemonic.mn_decode( seed.split(' ') )
1471             except:
1472                 QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK'))
1473                 sys.exit(0)
1474         if not seed:
1475             QMessageBox.warning(None, _('Error'), _('No seed'), 'OK')
1476             sys.exit(0)
1477         
1478         wallet.seed = str(seed)
1479         #print repr(wallet.seed)
1480         wallet.gap_limit = gap
1481         return True
1482
1483
1484
1485     def settings_dialog(self):
1486         d = QDialog(self)
1487         d.setWindowTitle(_('Electrum Settings'))
1488         d.setModal(1)
1489         vbox = QVBoxLayout()
1490         msg = _('Here are the settings of your wallet.') + '\n'\
1491               + _('For more explanations, click on the help buttons next to each field.')
1492
1493         label = QLabel(msg)
1494         label.setFixedWidth(250)
1495         label.setWordWrap(True)
1496         label.setAlignment(Qt.AlignJustify)
1497
1498         tabs = QTabWidget(self)
1499         vbox.addWidget(tabs)
1500
1501         vbox.addWidget(label)
1502
1503         tab = QWidget()
1504         grid_wallet = QGridLayout(tab)
1505         grid_wallet.setColumnStretch(0,1)
1506         tabs.addTab(tab, _('Wallet') )
1507         
1508         tab2 = QWidget()
1509         grid_ui = QGridLayout(tab2)
1510         grid_ui.setColumnStretch(0,1)
1511         tabs.addTab(tab2, _('Display') )
1512
1513         fee_label = QLabel(_('Transaction fee'))
1514         grid_wallet.addWidget(fee_label, 2, 0)
1515         fee_e = QLineEdit()
1516         fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) )
1517         grid_wallet.addWidget(fee_e, 2, 1)
1518         msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \
1519             + _('Recommended value') + ': 0.001'
1520         grid_wallet.addWidget(HelpButton(msg), 2, 2)
1521         fee_e.textChanged.connect(lambda: numbify(fee_e,False))
1522         if not self.config.is_modifiable('fee'):
1523             for w in [fee_e, fee_label]: w.setEnabled(False)
1524
1525         nz_label = QLabel(_('Display zeros'))
1526         grid_ui.addWidget(nz_label, 3, 0)
1527         nz_e = QLineEdit()
1528         nz_e.setText("%d"% self.wallet.num_zeros)
1529         grid_ui.addWidget(nz_e, 3, 1)
1530         msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
1531         grid_ui.addWidget(HelpButton(msg), 3, 2)
1532         nz_e.textChanged.connect(lambda: numbify(nz_e,True))
1533         if not self.config.is_modifiable('num_zeros'):
1534             for w in [nz_e, nz_label]: w.setEnabled(False)
1535
1536
1537         usechange_label = QLabel(_('Use change addresses'))
1538         grid_wallet.addWidget(usechange_label, 5, 0)
1539         usechange_combo = QComboBox()
1540         usechange_combo.addItems(['Yes', 'No'])
1541         usechange_combo.setCurrentIndex(not self.wallet.use_change)
1542         grid_wallet.addWidget(usechange_combo, 5, 1)
1543         grid_wallet.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions. ')), 5, 2)
1544         if not self.config.is_modifiable('use_change'): usechange_combo.setEnabled(False)
1545
1546         gap_label = QLabel(_('Gap limit'))
1547         grid_wallet.addWidget(gap_label, 6, 0)
1548         gap_e = QLineEdit()
1549         gap_e.setText("%d"% self.wallet.gap_limit)
1550         grid_wallet.addWidget(gap_e, 6, 1)
1551         msg =  _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \
1552               + _('You may increase it if you need more receiving addresses.') + '\n\n' \
1553               + _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \
1554               + _('Given the current status of your address sequence, the minimum gap limit you can use is: ') + '%d'%self.wallet.min_acceptable_gap() + '\n\n' \
1555               + _('Warning') + ': ' \
1556               + _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \
1557               + _('Do not modify it if you do not understand what you are doing, or if you expect to recover your wallet without knowing it!') + '\n\n' 
1558         grid_wallet.addWidget(HelpButton(msg), 6, 2)
1559         gap_e.textChanged.connect(lambda: numbify(nz_e,True))
1560         if not self.config.is_modifiable('gap_limit'):
1561             for w in [gap_e, gap_label]: w.setEnabled(False)
1562         
1563         gui_label=QLabel(_('Default GUI') + ':')
1564         grid_ui.addWidget(gui_label , 7, 0)
1565         gui_combo = QComboBox()
1566         gui_combo.addItems(['Lite', 'Classic', 'Gtk', 'Text'])
1567         index = gui_combo.findText(self.config.get("gui","classic").capitalize())
1568         if index==-1: index = 1
1569         gui_combo.setCurrentIndex(index)
1570         grid_ui.addWidget(gui_combo, 7, 1)
1571         grid_ui.addWidget(HelpButton(_('Select which GUI mode to use at start up. ')), 7, 2)
1572         if not self.config.is_modifiable('gui'):
1573             for w in [gui_combo, gui_label]: w.setEnabled(False)
1574
1575         lang_label=QLabel(_('Language') + ':')
1576         grid_ui.addWidget(lang_label , 8, 0)
1577         lang_combo = QComboBox()
1578         languages = {'':_('Default'), 'br':_('Brasilian'), 'cs':_('Czech'), 'de':_('German'),
1579                      'eo':_('Esperanto'), 'en':_('English'), 'es':_('Spanish'), 'fr':_('French'),
1580                      'it':_('Italian'), 'lv':_('Latvian'), 'nl':_('Dutch'), 'ru':_('Russian'),
1581                      'sl':_('Slovenian'), 'vi':_('Vietnamese'), 'zh':_('Chinese')
1582                      }
1583         lang_combo.addItems(languages.values())
1584         try:
1585             index = languages.keys().index(self.config.get("language",''))
1586         except:
1587             index = 0
1588         lang_combo.setCurrentIndex(index)
1589         grid_ui.addWidget(lang_combo, 8, 1)
1590         grid_ui.addWidget(HelpButton(_('Select which language is used in the GUI (after restart). ')), 8, 2)
1591         if not self.config.is_modifiable('language'):
1592             for w in [lang_combo, lang_label]: w.setEnabled(False)
1593
1594
1595         view_label=QLabel(_('Receive mode') + ':')
1596         grid_ui.addWidget(view_label , 9, 0)
1597         view_combo = QComboBox()
1598         view_combo.addItems([_('Simple View'), _('Detailed View'), _('Point of Sale')])
1599         view_combo.setCurrentIndex(self.receive_tab_mode)
1600         grid_ui.addWidget(view_combo, 9, 1)
1601         grid_ui.addWidget(HelpButton(_('View mode for your "Receive" tab. ')), 9, 2)
1602         
1603         vbox.addLayout(ok_cancel_buttons(d))
1604         d.setLayout(vbox) 
1605
1606         # run the dialog
1607         if not d.exec_(): return
1608
1609         fee = unicode(fee_e.text())
1610         try:
1611             fee = int( 100000000 * Decimal(fee) )
1612         except:
1613             QMessageBox.warning(self, _('Error'), _('Invalid value') +': %s'%fee, _('OK'))
1614             return
1615
1616         if self.wallet.fee != fee:
1617             self.wallet.fee = fee
1618             self.wallet.save()
1619         
1620         nz = unicode(nz_e.text())
1621         try:
1622             nz = int( nz )
1623             if nz>8: nz=8
1624         except:
1625             QMessageBox.warning(self, _('Error'), _('Invalid value')+':%s'%nz, _('OK'))
1626             return
1627
1628         if self.wallet.num_zeros != nz:
1629             self.wallet.num_zeros = nz
1630             self.config.set_key('num_zeros', nz, True)
1631             self.update_history_tab()
1632             self.update_receive_tab()
1633
1634         usechange_result = usechange_combo.currentIndex() == 0
1635         if self.wallet.use_change != usechange_result:
1636             self.wallet.use_change = usechange_result
1637             self.config.set_key('use_change', self.wallet.use_change, True)
1638         
1639         try:
1640             n = int(gap_e.text())
1641         except:
1642             QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
1643             return
1644
1645         if self.wallet.gap_limit != n:
1646             r = self.wallet.change_gap_limit(n)
1647             if r:
1648                 self.update_receive_tab()
1649                 self.config.set_key('gap_limit', self.wallet.gap_limit, True)
1650             else:
1651                 QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
1652
1653         need_restart = False
1654
1655         gui_request = str(gui_combo.currentText()).lower()
1656         if gui_request != self.config.get('gui'):
1657             self.config.set_key('gui', gui_request, True)
1658             need_restart = True
1659             
1660         lang_request = languages.keys()[lang_combo.currentIndex()]
1661         if lang_request != self.config.get('language'):
1662             self.config.set_key("language", lang_request, True)
1663             need_restart = True
1664
1665         if need_restart:
1666             QMessageBox.warning(self, _('Success'), _('Please restart Electrum to activate the new GUI settings'), _('OK'))
1667
1668         self.receive_tab_set_mode(view_combo.currentIndex())
1669
1670
1671     @staticmethod 
1672     def network_dialog(wallet, parent=None):
1673         interface = wallet.interface
1674         if parent:
1675             if interface.is_connected:
1676                 status = _("Connected to")+" %s\n%d blocks"%(interface.host, wallet.verifier.height)
1677             else:
1678                 status = _("Not connected")
1679             server = interface.server
1680         else:
1681             import random
1682             status = _("Please choose a server.") + "\n" + _("Select 'Cancel' if you are offline.")
1683             server = interface.server
1684
1685         plist, servers_list = interface.get_servers_list()
1686
1687         d = QDialog(parent)
1688         d.setModal(1)
1689         d.setWindowTitle(_('Server'))
1690         d.setMinimumSize(375, 20)
1691
1692         vbox = QVBoxLayout()
1693         vbox.setSpacing(30)
1694
1695         hbox = QHBoxLayout()
1696         l = QLabel()
1697         l.setPixmap(QPixmap(":icons/network.png"))
1698         hbox.addStretch(10)
1699         hbox.addWidget(l)
1700         hbox.addWidget(QLabel(status))
1701         hbox.addStretch(50)
1702         vbox.addLayout(hbox)
1703
1704
1705         # grid layout
1706         grid = QGridLayout()
1707         grid.setSpacing(8)
1708         vbox.addLayout(grid)
1709
1710         # server
1711         server_protocol = QComboBox()
1712         server_host = QLineEdit()
1713         server_host.setFixedWidth(200)
1714         server_port = QLineEdit()
1715         server_port.setFixedWidth(60)
1716
1717         protocol_names = ['TCP', 'HTTP', 'TCP/SSL', 'HTTPS']
1718         protocol_letters = 'thsg'
1719         DEFAULT_PORTS = {'t':'50001', 's':'50002', 'h':'8081', 'g':'8082'}
1720         server_protocol.addItems(protocol_names)
1721
1722         grid.addWidget(QLabel(_('Server') + ':'), 0, 0)
1723         grid.addWidget(server_protocol, 0, 1)
1724         grid.addWidget(server_host, 0, 2)
1725         grid.addWidget(server_port, 0, 3)
1726
1727         def change_protocol(p):
1728             protocol = protocol_letters[p]
1729             host = unicode(server_host.text())
1730             pp = plist.get(host,DEFAULT_PORTS)
1731             if protocol not in pp.keys():
1732                 protocol = pp.keys()[0]
1733             port = pp[protocol]
1734             server_host.setText( host )
1735             server_port.setText( port )
1736
1737         server_protocol.connect(server_protocol, SIGNAL('currentIndexChanged(int)'), change_protocol)
1738         
1739         label = _('Active Servers') if wallet.interface.servers else _('Default Servers')
1740         servers_list_widget = QTreeWidget(parent)
1741         servers_list_widget.setHeaderLabels( [ label, _('Type') ] )
1742         servers_list_widget.setMaximumHeight(150)
1743         servers_list_widget.setColumnWidth(0, 240)
1744         for _host in servers_list.keys():
1745             _type = 'P' if servers_list[_host].get('pruning') else 'F'
1746             servers_list_widget.addTopLevelItem(QTreeWidgetItem( [ _host, _type ] ))
1747
1748         def change_server(host, protocol=None):
1749             pp = plist.get(host,DEFAULT_PORTS)
1750             if protocol:
1751                 port = pp.get(protocol)
1752                 if not port: protocol = None
1753                     
1754             if not protocol:
1755                 if 't' in pp.keys():
1756                     protocol = 't'
1757                     port = pp.get(protocol)
1758                 else:
1759                     protocol = pp.keys()[0]
1760                     port = pp.get(protocol)
1761             
1762             server_host.setText( host )
1763             server_port.setText( port )
1764             server_protocol.setCurrentIndex(protocol_letters.index(protocol))
1765
1766             if not plist: return
1767             for p in protocol_letters:
1768                 i = protocol_letters.index(p)
1769                 j = server_protocol.model().index(i,0)
1770                 if p not in pp.keys():
1771                     server_protocol.model().setData(j, QtCore.QVariant(0), QtCore.Qt.UserRole-1)
1772                 else:
1773                     server_protocol.model().setData(j, QtCore.QVariant(0,False), QtCore.Qt.UserRole-1)
1774
1775
1776         if server:
1777             host, port, protocol = server.split(':')
1778             change_server(host,protocol)
1779
1780         servers_list_widget.connect(servers_list_widget, SIGNAL('itemClicked(QTreeWidgetItem*, int)'), lambda x: change_server(unicode(x.text(0))))
1781         grid.addWidget(servers_list_widget, 1, 1, 1, 3)
1782
1783         if not wallet.config.is_modifiable('server'):
1784             for w in [server_host, server_port, server_protocol, servers_list_widget]: w.setEnabled(False)
1785
1786         # proxy setting
1787         proxy_mode = QComboBox()
1788         proxy_host = QLineEdit()
1789         proxy_host.setFixedWidth(200)
1790         proxy_port = QLineEdit()
1791         proxy_port.setFixedWidth(60)
1792         proxy_mode.addItems(['NONE', 'SOCKS4', 'SOCKS5', 'HTTP'])
1793
1794         def check_for_disable(index = False):
1795             if proxy_mode.currentText() != 'NONE':
1796                 proxy_host.setEnabled(True)
1797                 proxy_port.setEnabled(True)
1798             else:
1799                 proxy_host.setEnabled(False)
1800                 proxy_port.setEnabled(False)
1801
1802         check_for_disable()
1803         proxy_mode.connect(proxy_mode, SIGNAL('currentIndexChanged(int)'), check_for_disable)
1804
1805         if not wallet.config.is_modifiable('proxy'):
1806             for w in [proxy_host, proxy_port, proxy_mode]: w.setEnabled(False)
1807
1808         proxy_config = interface.proxy if interface.proxy else { "mode":"none", "host":"localhost", "port":"8080"}
1809         proxy_mode.setCurrentIndex(proxy_mode.findText(str(proxy_config.get("mode").upper())))
1810         proxy_host.setText(proxy_config.get("host"))
1811         proxy_port.setText(proxy_config.get("port"))
1812
1813         grid.addWidget(QLabel(_('Proxy') + ':'), 2, 0)
1814         grid.addWidget(proxy_mode, 2, 1)
1815         grid.addWidget(proxy_host, 2, 2)
1816         grid.addWidget(proxy_port, 2, 3)
1817
1818         # buttons
1819         vbox.addLayout(ok_cancel_buttons(d))
1820         d.setLayout(vbox) 
1821
1822         if not d.exec_(): return
1823
1824         server = unicode( server_host.text() ) + ':' + unicode( server_port.text() ) + ':' + (protocol_letters[server_protocol.currentIndex()])
1825         if proxy_mode.currentText() != 'NONE':
1826             proxy = { u'mode':unicode(proxy_mode.currentText()).lower(), u'host':unicode(proxy_host.text()), u'port':unicode(proxy_port.text()) }
1827         else:
1828             proxy = None
1829
1830         wallet.config.set_key("proxy", proxy, True)
1831         wallet.config.set_key("server", server, True)
1832         interface.set_server(server, proxy)
1833                 
1834         return True
1835
1836     def closeEvent(self, event):
1837         g = self.geometry()
1838         self.config.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()], True)
1839         event.accept()
1840
1841
1842 class ElectrumGui:
1843
1844     def __init__(self, wallet, config, app=None):
1845         self.wallet = wallet
1846         self.config = config
1847         if app is None:
1848             self.app = QApplication(sys.argv)
1849
1850
1851     def restore_or_create(self):
1852         msg = _("Wallet file not found.")+"\n"+_("Do you want to create a new wallet, or to restore an existing one?")
1853         r = QMessageBox.question(None, _('Message'), msg, _('Create'), _('Restore'), _('Cancel'), 0, 2)
1854         if r==2: return None
1855         return 'restore' if r==1 else 'create'
1856
1857     def seed_dialog(self):
1858         return ElectrumWindow.seed_dialog( self.wallet )
1859
1860     def network_dialog(self):
1861         return ElectrumWindow.network_dialog( self.wallet, parent=None )
1862         
1863
1864     def show_seed(self):
1865         ElectrumWindow.show_seed_dialog(self.wallet)
1866
1867
1868     def password_dialog(self):
1869         ElectrumWindow.change_password_dialog(self.wallet)
1870
1871
1872     def restore_wallet(self):
1873         wallet = self.wallet
1874         # wait until we are connected, because the user might have selected another server
1875         if not wallet.interface.is_connected:
1876             waiting = lambda: False if wallet.interface.is_connected else "connecting...\n"
1877             waiting_dialog(waiting)
1878
1879         waiting = lambda: False if wallet.is_up_to_date() else "Please wait...\nAddresses generated: %d\nKilobytes received: %.1f"\
1880             %(len(wallet.all_addresses()), wallet.interface.bytes_received/1024.)
1881
1882         wallet.set_up_to_date(False)
1883         wallet.interface.poke('synchronizer')
1884         waiting_dialog(waiting)
1885         if wallet.is_found():
1886             print_error( "Recovery successful" )
1887         else:
1888             QMessageBox.information(None, _('Error'), _("No transactions found for this seed"), _('OK'))
1889
1890         return True
1891
1892     def main(self,url):
1893         s = Timer()
1894         s.start()
1895         w = ElectrumWindow(self.wallet, self.config)
1896         if url: w.set_url(url)
1897         w.app = self.app
1898         w.connect_slots(s)
1899         w.update_wallet()
1900         w.show()
1901
1902         self.app.exec_()