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