fix for deleting an alias
[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
22 try:
23     import PyQt4
24 except:
25     print "could not import PyQt4"
26     print "on Linux systems, you may try 'sudo apt-get install python-qt4'"
27     sys.exit(1)
28
29 from PyQt4.QtGui import *
30 from PyQt4.QtCore import *
31 import PyQt4.QtCore as QtCore
32 import PyQt4.QtGui as QtGui
33 from interface import DEFAULT_SERVERS
34
35 try:
36     import icons_rc
37 except:
38     print "Could not import icons_rp.py"
39     print "Please generate it with: 'pyrcc4 icons.qrc -o icons_rc.py'"
40     sys.exit(1)
41
42 from wallet import format_satoshis
43 import bmp, mnemonic, pyqrnative
44
45 from decimal import Decimal
46
47 import platform
48 MONOSPACE_FONT = 'Lucida Console' if platform.system() == 'Windows' else 'monospace'
49 ALIAS_REGEXP = '^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$'    
50
51 def numbify(entry, is_int = False):
52     text = unicode(entry.text()).strip()
53     chars = '0123456789'
54     if not is_int: chars +='.'
55     s = ''.join([i for i in text if i in chars])
56     if not is_int:
57         if '.' in s:
58             p = s.find('.')
59             s = s.replace('.','')
60             s = s[:p] + '.' + s[p:p+8]
61         try:
62             amount = int( Decimal(s) * 100000000 )
63         except:
64             amount = None
65     else:
66         try:
67             amount = int( s )
68         except:
69             amount = None
70     entry.setText(s)
71     return amount
72
73
74 class Timer(QtCore.QThread):
75     def run(self):
76         while True:
77             self.emit(QtCore.SIGNAL('timersignal'))
78             time.sleep(0.5)
79
80 class EnterButton(QPushButton):
81     def __init__(self, text, func):
82         QPushButton.__init__(self, text)
83         self.func = func
84         self.clicked.connect(func)
85
86     def keyPressEvent(self, e):
87         if e.key() == QtCore.Qt.Key_Return:
88             apply(self.func,())
89
90 class StatusBarButton(QPushButton):
91     def __init__(self, icon, tooltip, func):
92         QPushButton.__init__(self, icon, '')
93         self.setToolTip(tooltip)
94         self.setFlat(True)
95         self.setMaximumWidth(25)
96         self.clicked.connect(func)
97         self.func = func
98
99     def keyPressEvent(self, e):
100         if e.key() == QtCore.Qt.Key_Return:
101             apply(self.func,())
102
103
104 class QRCodeWidget(QWidget):
105
106     def __init__(self, addr):
107         super(QRCodeWidget, self).__init__()
108         self.setGeometry(300, 300, 350, 350)
109         self.set_addr(addr)
110
111     def set_addr(self, addr):
112         self.addr = addr
113         self.qr = pyqrnative.QRCode(4, pyqrnative.QRErrorCorrectLevel.L)
114         self.qr.addData(addr)
115         self.qr.make()
116         
117     def paintEvent(self, e):
118         qp = QtGui.QPainter()
119         qp.begin(self)
120         boxsize = 7
121         size = self.qr.getModuleCount()*boxsize
122         k = self.qr.getModuleCount()
123         black = QColor(0, 0, 0, 255)
124         white = QColor(255, 255, 255, 255)
125         for r in range(k):
126             for c in range(k):
127                 if self.qr.isDark(r, c):
128                     qp.setBrush(black)
129                     qp.setPen(black)
130                 else:
131                     qp.setBrush(white)
132                     qp.setPen(white)
133                 qp.drawRect(c*boxsize, r*boxsize, boxsize, boxsize)
134         qp.end()
135         
136
137
138 def ok_cancel_buttons(dialog):
139     hbox = QHBoxLayout()
140     hbox.addStretch(1)
141     b = QPushButton("OK")
142     hbox.addWidget(b)
143     b.clicked.connect(dialog.accept)
144     b = QPushButton("Cancel")
145     hbox.addWidget(b)
146     b.clicked.connect(dialog.reject)
147     return hbox
148
149
150 class ElectrumWindow(QMainWindow):
151
152     def __init__(self, wallet):
153         QMainWindow.__init__(self)
154         self.wallet = wallet
155         self.wallet.gui_callback = self.update_callback
156
157         self.funds_error = False
158
159         self.tabs = tabs = QTabWidget(self)
160         tabs.addTab(self.create_history_tab(), _('History') )
161         if self.wallet.seed:
162             tabs.addTab(self.create_send_tab(), _('Send') )
163         tabs.addTab(self.create_receive_tab(), _('Receive') )
164         tabs.addTab(self.create_contacts_tab(), _('Contacts') )
165         tabs.addTab(self.create_wall_tab(), _('Wall') )
166         tabs.setMinimumSize(600, 400)
167         tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
168         self.setCentralWidget(tabs)
169         self.create_status_bar()
170         self.setGeometry(100,100,840,400)
171         title = 'Electrum ' + self.wallet.electrum_version + '  -  ' + self.wallet.path
172         if not self.wallet.seed: title += ' [seedless]'
173         self.setWindowTitle( title )
174         self.show()
175
176         QShortcut(QKeySequence("Ctrl+W"), self, self.close)
177         QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
178         QShortcut(QKeySequence("Ctrl+PgUp"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() - 1 )%tabs.count() ))
179         QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() + 1 )%tabs.count() ))
180         
181         self.connect(self, QtCore.SIGNAL('updatesignal'), self.update_wallet)
182
183
184     def connect_slots(self, sender):
185         if self.wallet.seed:
186             self.connect(sender, QtCore.SIGNAL('timersignal'), self.check_recipient)
187             self.previous_payto_e=''
188
189     def check_recipient(self):
190         if self.payto_e.hasFocus():
191             return
192         r = unicode( self.payto_e.text() )
193         if r != self.previous_payto_e:
194             self.previous_payto_e = r
195             r = r.strip()
196             if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
197                 try:
198                     to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
199                 except:
200                     return
201                 if to_address:
202                     s = r + ' <' + to_address + '>'
203                     self.payto_e.setText(s)
204
205
206     def update_callback(self):
207         self.emit(QtCore.SIGNAL('updatesignal'))
208
209     def update_wallet(self):
210         if self.wallet.interface and self.wallet.interface.is_connected:
211             if self.wallet.blocks == -1:
212                 text = _( "Connecting..." )
213                 icon = QIcon(":icons/status_disconnected.png")
214             elif self.wallet.blocks == 0:
215                 text = _( "Server not ready" )
216                 icon = QIcon(":icons/status_disconnected.png")
217             elif not self.wallet.up_to_date:
218                 text = _( "Synchronizing..." )
219                 icon = QIcon(":icons/status_waiting.png")
220             else:
221                 c, u = self.wallet.get_balance()
222                 text =  _( "Balance" ) + ": %s "%( format_satoshis(c,False,self.wallet.num_zeros) )
223                 if u: text +=  "[%s unconfirmed]"%( format_satoshis(u,True,self.wallet.num_zeros).strip() )
224                 icon = QIcon(":icons/status_connected.png")
225         else:
226             text = _( "Not connected" )
227             icon = QIcon(":icons/status_disconnected.png")
228
229         if self.funds_error:
230             text = _( "Not enough funds" )
231
232         self.statusBar().showMessage(text)
233         self.status_button.setIcon( icon )
234
235         if self.wallet.up_to_date:
236             self.textbox.setText( self.wallet.banner )
237             self.update_history_tab()
238             self.update_receive_tab()
239             self.update_contacts_tab()
240             self.update_completions()
241
242
243     def create_history_tab(self):
244         self.history_list = w = QTreeWidget(self)
245         #print w.getContentsMargins()
246         w.setColumnCount(5)
247         w.setColumnWidth(0, 40) 
248         w.setColumnWidth(1, 140) 
249         w.setColumnWidth(2, 350) 
250         w.setColumnWidth(3, 140) 
251         w.setColumnWidth(4, 140) 
252         w.setHeaderLabels( [ '', _( 'Date' ), _( 'Description' ) , _('Amount'), _('Balance')] )
253         self.connect(w, SIGNAL('itemActivated(QTreeWidgetItem*, int)'), self.tx_details)
254         self.connect(w, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.tx_label_clicked)
255         self.connect(w, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), self.tx_label_changed)
256         return w
257
258     def tx_details(self, item, column):
259         tx_hash = str(item.toolTip(0))
260         tx = self.wallet.tx_history.get(tx_hash)
261
262         if tx['height']:
263             conf = self.wallet.blocks - tx['height'] + 1
264             time_str = datetime.datetime.fromtimestamp( tx['timestamp']).isoformat(' ')[:-3]
265         else:
266             conf = 0
267             time_str = 'pending'
268
269         tx_details = _("Transaction Details") +"\n\n" \
270             + "Transaction ID:\n" + tx_hash + "\n\n" \
271             + "Status: %d confirmations\n\n"%conf  \
272             + "Date: %s\n\n"%time_str \
273             + "Inputs:\n-"+ '\n-'.join(tx['inputs']) + "\n\n" \
274             + "Outputs:\n-"+ '\n-'.join(tx['outputs'])
275
276         r = self.wallet.receipts.get(tx_hash)
277         if r:
278             tx_details += "\n_______________________________________" \
279                 + '\n\nSigned URI: ' + r[2] \
280                 + "\n\nSigned by: " + r[0] \
281                 + '\n\nSignature: ' + r[1]
282
283         QMessageBox.information(self, 'Details', tx_details, 'OK')
284
285
286     def tx_label_clicked(self, item, column):
287         if column==2 and item.isSelected():
288             tx_hash = str(item.toolTip(0))
289             self.is_edit=True
290             #if not self.wallet.labels.get(tx_hash): item.setText(2,'')
291             item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
292             self.history_list.editItem( item, column )
293             item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
294             self.is_edit=False
295
296     def tx_label_changed(self, item, column):
297         if self.is_edit: 
298             return
299         self.is_edit=True
300         tx_hash = str(item.toolTip(0))
301         tx = self.wallet.tx_history.get(tx_hash)
302         s = self.wallet.labels.get(tx_hash)
303         text = unicode( item.text(2) )
304         if text: 
305             self.wallet.labels[tx_hash] = text
306             item.setForeground(2, QBrush(QColor('black')))
307         else:
308             if s: self.wallet.labels.pop(tx_hash)
309             text = tx['default_label']
310             item.setText(2, text)
311             item.setForeground(2, QBrush(QColor('gray')))
312         self.is_edit=False
313
314     def edit_label(self, is_recv):
315         l = self.receive_list if is_recv else self.contacts_list
316         c = 2 if is_recv else 1
317         item = l.currentItem()
318         item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
319         l.editItem( item, c )
320         item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
321
322     def address_label_clicked(self, item, column, l, column_addr, column_label):
323         if column==column_label and item.isSelected():
324             addr = unicode( item.text(column_addr) )
325             label = unicode( item.text(column_label) )
326             if label in self.wallet.aliases.keys():
327                 return
328             item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
329             l.editItem( item, column )
330             item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
331
332     def address_label_changed(self, item, column, l, column_addr, column_label):
333         addr = unicode( item.text(column_addr) )
334         text = unicode( item.text(column_label) )
335         if text:
336             if text not in self.wallet.aliases.keys():
337                 self.wallet.labels[addr] = text
338             else:
339                 print "error: this is one of your aliases"
340                 label = self.wallet.labels.get(addr,'')
341                 item.setText(column_label, QString(label))
342         else:
343             s = self.wallet.labels.get(addr)
344             if s: self.wallet.labels.pop(addr)
345
346         self.update_history_tab()
347         self.update_completions()
348
349     def update_history_tab(self):
350         self.history_list.clear()
351         balance = 0
352         for tx in self.wallet.get_tx_history():
353             tx_hash = tx['tx_hash']
354             if tx['height']:
355                 conf = self.wallet.blocks - tx['height'] + 1
356                 time_str = datetime.datetime.fromtimestamp( tx['timestamp']).isoformat(' ')[:-3]
357                 icon = QIcon(":icons/confirmed.png")
358             else:
359                 conf = 0
360                 time_str = 'pending'
361                 icon = QIcon(":icons/unconfirmed.png")
362             v = tx['value']
363             balance += v 
364             label = self.wallet.labels.get(tx_hash)
365             is_default_label = (label == '') or (label is None)
366             if is_default_label: label = tx['default_label']
367
368             item = QTreeWidgetItem( [ '', time_str, label, format_satoshis(v,True,self.wallet.num_zeros), format_satoshis(balance,False,self.wallet.num_zeros)] )
369             item.setFont(2, QFont(MONOSPACE_FONT))
370             item.setFont(3, QFont(MONOSPACE_FONT))
371             item.setFont(4, QFont(MONOSPACE_FONT))
372             item.setToolTip(0, tx_hash)
373             if is_default_label:
374                 item.setForeground(2, QBrush(QColor('grey')))
375
376             item.setIcon(0, icon)
377             self.history_list.insertTopLevelItem(0,item)
378
379
380     def create_send_tab(self):
381         w = QWidget()
382
383         grid = QGridLayout()
384         grid.setSpacing(8)
385         grid.setColumnMinimumWidth(3,300)
386         grid.setColumnStretch(4,1)
387
388         self.payto_e = QLineEdit()
389         grid.addWidget(QLabel(_('Pay to')), 1, 0)
390         grid.addWidget(self.payto_e, 1, 1, 1, 3)
391
392         completer = QCompleter()
393         self.payto_e.setCompleter(completer)
394         self.completions = QStringListModel()
395         completer.setModel(self.completions)
396
397         self.message_e = QLineEdit()
398         grid.addWidget(QLabel(_('Description')), 2, 0)
399         grid.addWidget(self.message_e, 2, 1, 1, 3)
400
401         self.amount_e = QLineEdit()
402         grid.addWidget(QLabel(_('Amount')), 3, 0)
403         grid.addWidget(self.amount_e, 3, 1, 1, 2)
404         
405         self.fee_e = QLineEdit()
406         grid.addWidget(QLabel(_('Fee')), 4, 0)
407         grid.addWidget(self.fee_e, 4, 1, 1, 2)
408         
409         b = EnterButton(_("Send"), self.do_send)
410         grid.addWidget(b, 5, 1)
411
412         b = EnterButton(_("Clear"),self.do_clear)
413         grid.addWidget(b, 5, 2)
414
415         self.payto_sig = QLabel('')
416         grid.addWidget(self.payto_sig, 6, 0, 1, 4)
417
418         w.setLayout(grid) 
419         w.show()
420
421         w2 = QWidget()
422         vbox = QVBoxLayout()
423         vbox.addWidget(w)
424         vbox.addStretch(1)
425         w2.setLayout(vbox)
426
427         def entry_changed( is_fee ):
428             self.funds_error = False
429             amount = numbify(self.amount_e)
430             fee = numbify(self.fee_e)
431             if not is_fee: fee = None
432             if amount is None:
433                 return
434             inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee )
435             if not is_fee:
436                 self.fee_e.setText( str( Decimal( fee ) / 100000000 ) )
437             if inputs:
438                 palette = QPalette()
439                 palette.setColor(self.amount_e.foregroundRole(), QColor('black'))
440             else:
441                 palette = QPalette()
442                 palette.setColor(self.amount_e.foregroundRole(), QColor('red'))
443                 self.funds_error = True
444             self.amount_e.setPalette(palette)
445             self.fee_e.setPalette(palette)
446
447         self.amount_e.textChanged.connect(lambda: entry_changed(False) )
448         self.fee_e.textChanged.connect(lambda: entry_changed(True) )
449
450         return w2
451
452
453     def update_completions(self):
454         l = []
455         for addr,label in self.wallet.labels.items():
456             if addr in self.wallet.addressbook:
457                 l.append( label + '  <' + addr + '>')
458         l = l + self.wallet.aliases.keys()
459
460         self.completions.setStringList(l)
461
462
463
464     def do_send(self):
465
466         label = unicode( self.message_e.text() )
467         r = unicode( self.payto_e.text() )
468         r = r.strip()
469
470         # alias
471         m1 = re.match(ALIAS_REGEXP, r)
472         # label or alias, with address in brackets
473         m2 = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
474         
475         if m1:
476             to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
477             if not to_address:
478                 return
479         elif m2:
480             to_address = m2.group(2)
481         else:
482             to_address = r
483
484         if not self.wallet.is_valid(to_address):
485             QMessageBox.warning(self, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK'))
486             return
487
488         try:
489             amount = int( Decimal( unicode( self.amount_e.text())) * 100000000 )
490         except:
491             QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
492             return
493         try:
494             fee = int( Decimal( unicode( self.fee_e.text())) * 100000000 )
495         except:
496             QMessageBox.warning(self, _('Error'), _('Invalid Fee'), _('OK'))
497             return
498
499         if self.wallet.use_encryption:
500             password = self.password_dialog()
501             if not password:
502                 return
503         else:
504             password = None
505
506         try:
507             tx = self.wallet.mktx( to_address, amount, label, password, fee )
508         except BaseException, e:
509             self.show_message(str(e))
510             return
511             
512         status, msg = self.wallet.sendtx( tx )
513         if status:
514             QMessageBox.information(self, '', _('Payment sent.')+'\n'+msg, _('OK'))
515             self.do_clear()
516             self.update_contacts_tab()
517         else:
518             QMessageBox.warning(self, _('Error'), msg, _('OK'))
519
520
521     def set_url(self, url):
522         payto, amount, label, message, signature, identity, url = self.wallet.parse_url(url, self.show_message, self.question)
523         self.tabs.setCurrentIndex(1)
524         label = self.wallet.labels.get(payto)
525         if label:
526             self.payto_e.setText(label + ' <'+ payto+'>')
527         self.message_e.setText(message)
528         self.amount_e.setText(amount)
529         if identity:
530             self.set_frozen(self.payto_e,True)
531             self.set_frozen(self.amount_e,True)
532             self.set_frozen(self.message_e,True)
533             self.payto_sig.setText( '      The bitcoin URI was signed by ' + identity )
534         else:
535             self.payto_sig.setVisible(False)
536
537     def do_clear(self):
538         self.payto_sig.setVisible(False)
539         for e in [self.payto_e, self.message_e, self.amount_e, self.fee_e]:
540             e.setText('')
541             self.set_frozen(e,False)
542
543     def set_frozen(self,entry,frozen):
544         if frozen:
545             entry.setReadOnly(True)
546             entry.setFrame(False)
547             palette = QPalette()
548             palette.setColor(entry.backgroundRole(), QColor('lightgray'))
549             entry.setPalette(palette)
550         else:
551             entry.setReadOnly(False)
552             entry.setFrame(True)
553             palette = QPalette()
554             palette.setColor(entry.backgroundRole(), QColor('white'))
555             entry.setPalette(palette)
556
557
558     def get_current_addr(self, is_recv):
559         if is_recv:
560             l = self.receive_list
561             n = 1
562         else:
563             l = self.contacts_list
564             n = 0
565         i = l.currentItem()
566         if i: 
567             return unicode( i.text(n) )
568         else:
569             return ''
570
571
572
573     def toggle_freeze(self,addr):
574         if not addr: return
575         if addr in self.wallet.frozen_addresses:
576             self.wallet.unfreeze(addr)
577         else:
578             self.wallet.freeze(addr)
579         self.update_receive_tab()
580
581     def toggle_priority(self,addr):
582         if not addr: return
583         if addr in self.wallet.prioritized_addresses:
584             self.wallet.unprioritize(addr)
585         else:
586             self.wallet.prioritize(addr)
587         self.update_receive_tab()
588
589
590     def create_list_tab(self, headers):
591         "generic tab creatino method"
592         l = QTreeWidget(self)
593         l.setColumnCount( len(headers) )
594         l.setHeaderLabels( headers )
595
596         w = QWidget()
597         vbox = QVBoxLayout()
598         w.setLayout(vbox)
599
600         vbox.setMargin(0)
601         vbox.setSpacing(0)
602         vbox.addWidget(l)
603         buttons = QWidget()
604         vbox.addWidget(buttons)
605
606         hbox = QHBoxLayout()
607         hbox.setMargin(0)
608         hbox.setSpacing(0)
609         buttons.setLayout(hbox)
610
611         return l,w,hbox
612
613
614     def create_receive_tab(self):
615         l,w,hbox = self.create_list_tab([_('Flags'), _('Address'), _('Label'), _('Balance'), _('Tx')])
616         l.setContextMenuPolicy(Qt.CustomContextMenu)
617         l.customContextMenuRequested.connect(self.create_receive_menu)
618         self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,1,2))
619         self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,1,2))
620         self.receive_list = l
621         self.receive_buttons_hbox = hbox
622
623         self.new_address_button = EnterButton(_("New"), self.change_gap_limit_dialog)
624         self.new_address_button.setHidden(not self.wallet.expert_mode)
625         hbox.addWidget(self.new_address_button)
626         hbox.addStretch(1)
627
628         return w
629
630
631     def create_contacts_tab(self):
632         l,w,hbox = self.create_list_tab([_('Address'), _('Label'), _('Tx')])
633         l.setContextMenuPolicy(Qt.CustomContextMenu)
634         l.customContextMenuRequested.connect(self.create_contact_menu)
635         self.connect(l, SIGNAL('itemActivated(QTreeWidgetItem*, int)'), self.show_contact_details)
636         self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,0,1))
637         self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,0,1))
638         self.contacts_list = l
639         self.contacts_buttons_hbox = hbox
640         hbox.addWidget(EnterButton(_("New"), self.new_contact_dialog))
641         hbox.addStretch(1)
642         return w
643
644
645     def create_receive_menu(self, position):
646         # fixme: this function apparently has a side effect.
647         # if it is not called the menu pops up several times
648         self.receive_list.selectedIndexes() 
649         addr = self.get_current_addr(True)
650         menu = QMenu()
651         menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
652         menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
653         menu.addAction(_("Edit label"), lambda: self.edit_label(True))
654         if self.wallet.expert_mode:
655             t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
656             menu.addAction(t, lambda: self.toggle_freeze(addr))
657             t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
658             menu.addAction(t, lambda: self.toggle_priority(addr))
659         menu.exec_(self.receive_list.viewport().mapToGlobal(position))
660
661
662     def payto(self, addr):
663         if not addr:return
664         self.tabs.setCurrentIndex(1)
665         self.payto_e.setText(addr)
666         self.amount_e.setFocus()
667
668     def delete_contact(self, addr, is_alias):
669         if self.question("Do you want to remove %s from your list of contacts?"%addr):
670             if not is_alias and addr in self.wallet.addressbook:
671                 self.wallet.addressbook.remove(addr)
672                 if addr in self.wallet.labels.keys():
673                     self.wallet.labels.pop(addr)
674             elif is_alias and addr in self.wallet.aliases:
675                 self.wallet.aliases.pop(addr)
676             self.update_history_tab()
677             self.update_contacts_tab()
678             self.update_completions()
679
680     def create_contact_menu(self, position):
681         # fixme: this function apparently has a side effect.
682         # if it is not called the menu pops up several times
683         self.contacts_list.selectedIndexes() 
684         addr = self.get_current_addr(False)
685         menu = QMenu()
686         menu.addAction(_("Pay to"), lambda: self.payto(addr))
687         menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
688         menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
689         item = self.contacts_list.currentItem()
690         label = unicode( item.text(1) )
691         if label not in self.wallet.aliases.keys():
692             menu.addAction(_("Edit label"), lambda: self.edit_label(False))
693             menu.addAction(_("Delete"), lambda: self.delete_contact(addr,False))
694         else:
695             menu.addAction(_("Delete"), lambda: self.delete_contact(label,True))
696         menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
697
698
699     def update_receive_tab(self):
700         l = self.receive_list
701         l.clear()
702         l.setColumnHidden(0,not self.wallet.expert_mode)
703         l.setColumnHidden(3,not self.wallet.expert_mode)
704         l.setColumnHidden(4,not self.wallet.expert_mode)
705         l.setColumnWidth(0, 50) 
706         l.setColumnWidth(1, 310) 
707         l.setColumnWidth(2, 300)
708         l.setColumnWidth(3, 90) 
709         l.setColumnWidth(4, 10)
710         
711         self.new_address_button.setHidden(not self.wallet.expert_mode)
712         
713         #self.prioritize_button.setHidden(not self.wallet.expert_mode)
714         #self.freeze_button.setHidden(not self.wallet.expert_mode)
715
716         gap = 0
717         is_red = False
718         for address in self.wallet.all_addresses():
719
720             if self.wallet.is_change(address) and not self.wallet.expert_mode:
721                 continue
722
723             label = self.wallet.labels.get(address,'')
724             n = 0 
725             h = self.wallet.history.get(address,[])
726             for item in h:
727                 if not item['is_input'] : n=n+1
728
729             if n==0:
730                 tx = "None"
731                 if address in self.wallet.addresses:
732                     gap += 1
733                     if gap > self.wallet.gap_limit:
734                         is_red = True
735             else:
736                 tx = "%d"%n
737                 if address in self.wallet.addresses:
738                     gap = 0
739
740             c, u = self.wallet.get_addr_balance(address)
741             balance = format_satoshis( c + u, False, self.wallet.num_zeros )
742             flags = self.wallet.get_address_flags(address)
743             item = QTreeWidgetItem( [ flags, address, label, balance, tx] )
744
745             item.setFont(0, QFont(MONOSPACE_FONT))
746             item.setFont(1, QFont(MONOSPACE_FONT))
747             if address in self.wallet.frozen_addresses: 
748                 item.setBackgroundColor(1, QColor('lightblue'))
749             elif address in self.wallet.prioritized_addresses: 
750                 item.setBackgroundColor(1, QColor('lightgreen'))
751             if is_red and address in self.wallet.addresses:
752                 item.setBackgroundColor(1, QColor('red'))
753
754             self.receive_list.addTopLevelItem(item)
755
756     def show_contact_details(self, item, column):
757         m = unicode(item.text(0))
758         a = self.wallet.aliases.get(m)
759         if a:
760             if a[0] in self.wallet.authorities.keys():
761                 s = self.wallet.authorities.get(a[0])
762             else:
763                 s = "self-signed"
764             msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
765             QMessageBox.information(self, 'Alias', msg, 'OK')
766
767     def update_contacts_tab(self):
768
769         l = self.contacts_list
770         l.clear()
771         l.setColumnHidden(2, not self.wallet.expert_mode)
772         l.setColumnWidth(0, 350) 
773         l.setColumnWidth(1, 330)
774         l.setColumnWidth(2, 100) 
775
776         alias_targets = []
777         for alias, v in self.wallet.aliases.items():
778             s, target = v
779             alias_targets.append(target)
780             item = QTreeWidgetItem( [ target, alias, '-'] )
781             item.setBackgroundColor(1, QColor('lightgray'))
782             self.contacts_list.addTopLevelItem(item)
783             
784         for address in self.wallet.addressbook:
785             if address in alias_targets: continue
786             label = self.wallet.labels.get(address,'')
787             n = 0 
788             for item in self.wallet.tx_history.values():
789                 if address in item['outputs'] : n=n+1
790             tx = "None" if n==0 else "%d"%n
791             item = QTreeWidgetItem( [ address, label, tx] )
792             item.setFont(0, QFont(MONOSPACE_FONT))
793             self.contacts_list.addTopLevelItem(item)
794
795
796     def create_wall_tab(self):
797         self.textbox = textbox = QTextEdit(self)
798         textbox.setFont(QFont(MONOSPACE_FONT))
799         textbox.setReadOnly(True)
800         return textbox
801
802     def create_status_bar(self):
803         sb = QStatusBar()
804         sb.setFixedHeight(35)
805         if self.wallet.seed:
806             sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) )
807         sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) )
808         if self.wallet.seed:
809             sb.addPermanentWidget( StatusBarButton( QIcon(":icons/seed.png"), "Seed", lambda: self.show_seed_dialog(self.wallet, self) ) )
810         self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) ) 
811         sb.addPermanentWidget( self.status_button )
812         self.setStatusBar(sb)
813
814     def new_contact_dialog(self):
815         text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':')
816         address = unicode(text)
817         if ok:
818             if self.wallet.is_valid(address):
819                 self.wallet.addressbook.append(address)
820                 self.wallet.save()
821                 self.update_contacts_tab()
822                 self.update_history_tab()
823                 self.update_completions()
824             else:
825                 QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
826
827     @staticmethod
828     def show_seed_dialog(wallet, parent=None):
829
830         if not wallet.seed:
831             QMessageBox.information(parent, _('Message'), _('No seed'), _('OK'))
832             return
833
834         if wallet.use_encryption:
835             password = parent.password_dialog()
836             if not password: return
837         else:
838             password = None
839             
840         try:
841             seed = wallet.pw_decode( wallet.seed, password)
842         except:
843             QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
844             return
845
846         msg = _("Your wallet generation seed is") + ":\n\n" + seed + "\n\n"\
847               + _("Please keep it in a safe place; if you lose it, you will not be able to restore your wallet.") + "\n\n" \
848               + _("Equivalently, your wallet seed can be stored and recovered with the following mnemonic code") + ":\n\n\"" \
849               + ' '.join(mnemonic.mn_encode(seed)) + "\"\n\n\n"
850
851         d = QDialog(None)
852         d.setModal(1)
853         d.setWindowTitle(_("Seed"))
854         d.setMinimumSize(400, 270)
855
856         vbox = QVBoxLayout()
857         hbox = QHBoxLayout()
858         vbox2 = QVBoxLayout()
859         l = QLabel()
860         l.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
861         vbox2.addWidget(l)
862         vbox2.addStretch(1)
863         hbox.addLayout(vbox2)
864         hbox.addWidget(QLabel(msg))
865         vbox.addLayout(hbox)
866
867         hbox = QHBoxLayout()
868         hbox.addStretch(1)
869
870
871         if parent:
872             app = parent.app
873         else:
874             app = QApplication
875
876         b = QPushButton(_("Copy to Clipboard"))
877         b.clicked.connect(lambda: app.clipboard().setText(seed + ' "' + ' '.join(mnemonic.mn_encode(seed))+'"'))
878         hbox.addWidget(b)
879         b = QPushButton(_("View as QR Code"))
880         b.clicked.connect(lambda: ElectrumWindow.show_seed_qrcode(seed))
881         hbox.addWidget(b)
882
883         b = QPushButton(_("OK"))
884         b.clicked.connect(d.accept)
885         hbox.addWidget(b)
886         vbox.addLayout(hbox)
887         d.setLayout(vbox)
888         d.exec_()
889
890     @staticmethod
891     def show_seed_qrcode(seed):
892         if not seed: return
893         d = QDialog(None)
894         d.setModal(1)
895         d.setWindowTitle(_("Seed"))
896         d.setMinimumSize(270, 300)
897         vbox = QVBoxLayout()
898         vbox.addWidget(QRCodeWidget(seed))
899         hbox = QHBoxLayout()
900         hbox.addStretch(1)
901         b = QPushButton(_("OK"))
902         hbox.addWidget(b)
903         b.clicked.connect(d.accept)
904
905         vbox.addLayout(hbox)
906         d.setLayout(vbox)
907         d.exec_()
908
909
910     def show_address_qrcode(self,address):
911         if not address: return
912         d = QDialog(self)
913         d.setModal(1)
914         d.setWindowTitle(address)
915         d.setMinimumSize(270, 350)
916         vbox = QVBoxLayout()
917         qrw = QRCodeWidget(address)
918         vbox.addWidget(qrw)
919
920         hbox = QHBoxLayout()
921         amount_e = QLineEdit()
922         hbox.addWidget(QLabel(_('Amount')))
923         hbox.addWidget(amount_e)
924         vbox.addLayout(hbox)
925
926         #hbox = QHBoxLayout()
927         #label_e = QLineEdit()
928         #hbox.addWidget(QLabel('Label'))
929         #hbox.addWidget(label_e)
930         #vbox.addLayout(hbox)
931
932         def amount_changed():
933             amount = numbify(amount_e)
934             #label = str( label_e.getText() )
935             if amount is not None:
936                 qrw.set_addr('bitcoin:%s?amount=%s'%(address,str( Decimal(amount) /100000000)))
937             else:
938                 qrw.set_addr( address )
939             qrw.repaint()
940
941         def do_save():
942             bmp.save_qrcode(qrw.qr, "qrcode.bmp")
943             self.show_message(_("QR code saved to file") + " 'qrcode.bmp'")
944             
945         amount_e.textChanged.connect( amount_changed )
946
947         hbox = QHBoxLayout()
948         hbox.addStretch(1)
949         b = QPushButton(_("Save"))
950         b.clicked.connect(do_save)
951         hbox.addWidget(b)
952         b = QPushButton(_("Close"))
953         hbox.addWidget(b)
954         b.clicked.connect(d.accept)
955
956         vbox.addLayout(hbox)
957         d.setLayout(vbox)
958         d.exec_()
959
960     def question(self, msg):
961         return QMessageBox.question(self, _('Message'), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
962
963     def show_message(self, msg):
964         QMessageBox.information(self, _('Message'), msg, _('OK'))
965
966     def password_dialog(self ):
967         d = QDialog(self)
968         d.setModal(1)
969
970         pw = QLineEdit()
971         pw.setEchoMode(2)
972
973         vbox = QVBoxLayout()
974         msg = _('Please enter your password')
975         vbox.addWidget(QLabel(msg))
976
977         grid = QGridLayout()
978         grid.setSpacing(8)
979         grid.addWidget(QLabel(_('Password')), 1, 0)
980         grid.addWidget(pw, 1, 1)
981         vbox.addLayout(grid)
982
983         vbox.addLayout(ok_cancel_buttons(d))
984         d.setLayout(vbox) 
985
986         if not d.exec_(): return
987         return unicode(pw.text())
988
989
990     def change_gap_limit_dialog(self):
991         d = QDialog(self)
992         d.setModal(1)
993
994         vbox = QVBoxLayout()
995         
996         msg = _('In order to create more addresses, you need to raise your gap limit.') + '\n' \
997               + _('Your current gap limit is ') + '%d'%self.wallet.gap_limit + '\n' \
998               + _('The minimum for this wallet is: ') + '%d'%self.wallet.min_acceptable_gap() + '\n' 
999
1000         vbox.addWidget(QLabel(msg))
1001
1002         grid = QGridLayout()
1003         grid.setSpacing(8)
1004         grid.addWidget(QLabel(_('New gap limit: ')), 1, 0)
1005
1006         e = QLineEdit()
1007         grid.addWidget(e, 1, 1)
1008         vbox.addLayout(grid)
1009
1010         vbox.addLayout(ok_cancel_buttons(d))
1011         d.setLayout(vbox) 
1012
1013         if not d.exec_(): return
1014         try:
1015             n = int(e.text())
1016         except:
1017             QMessageBox.warning(self, _('Error'), _('Invalid Value'), _('OK'))
1018             return
1019
1020         r = self.wallet.change_gap_limit(n)
1021         if r:
1022             self.update_receive_tab()
1023         else:
1024             QMessageBox.warning(self, _('Error'), _('Invalid Value'), _('OK'))
1025
1026
1027
1028
1029
1030     @staticmethod
1031     def change_password_dialog( wallet, parent=None ):
1032
1033         if not wallet.seed:
1034             QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
1035             return
1036
1037         d = QDialog(parent)
1038         d.setModal(1)
1039
1040         pw = QLineEdit()
1041         pw.setEchoMode(2)
1042         new_pw = QLineEdit()
1043         new_pw.setEchoMode(2)
1044         conf_pw = QLineEdit()
1045         conf_pw.setEchoMode(2)
1046
1047         vbox = QVBoxLayout()
1048         if parent:
1049             msg = (_('Your wallet is encrypted. Use this dialog to change your password.')+'\n'+_('To disable wallet encryption, enter an empty new password.')) if wallet.use_encryption else _('Your wallet keys are not encrypted')
1050         else:
1051             msg = _("Please choose a password to encrypt your wallet keys.")+'\n'+_("Leave these fields empty if you want to disable encryption.")
1052         vbox.addWidget(QLabel(msg))
1053
1054         grid = QGridLayout()
1055         grid.setSpacing(8)
1056
1057         if wallet.use_encryption:
1058             grid.addWidget(QLabel(_('Password')), 1, 0)
1059             grid.addWidget(pw, 1, 1)
1060
1061         grid.addWidget(QLabel(_('New Password')), 2, 0)
1062         grid.addWidget(new_pw, 2, 1)
1063
1064         grid.addWidget(QLabel(_('Confirm Password')), 3, 0)
1065         grid.addWidget(conf_pw, 3, 1)
1066         vbox.addLayout(grid)
1067
1068         vbox.addLayout(ok_cancel_buttons(d))
1069         d.setLayout(vbox) 
1070
1071         if not d.exec_(): return
1072
1073         password = unicode(pw.text()) if wallet.use_encryption else None
1074         new_password = unicode(new_pw.text())
1075         new_password2 = unicode(conf_pw.text())
1076
1077         try:
1078             seed = wallet.pw_decode( wallet.seed, password)
1079         except:
1080             QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
1081             return
1082
1083         if new_password != new_password2:
1084             QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
1085             return
1086
1087         wallet.update_password(seed, password, new_password)
1088
1089     @staticmethod
1090     def seed_dialog(wallet, parent=None):
1091         d = QDialog(parent)
1092         d.setModal(1)
1093
1094         vbox = QVBoxLayout()
1095         msg = _("Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet.")
1096         vbox.addWidget(QLabel(msg))
1097
1098         grid = QGridLayout()
1099         grid.setSpacing(8)
1100
1101         seed_e = QLineEdit()
1102         grid.addWidget(QLabel(_('Seed or mnemonic')), 1, 0)
1103         grid.addWidget(seed_e, 1, 1)
1104
1105         gap_e = QLineEdit()
1106         gap_e.setText("5")
1107         grid.addWidget(QLabel(_('Gap limit')), 2, 0)
1108         grid.addWidget(gap_e, 2, 1)
1109         gap_e.textChanged.connect(lambda: numbify(gap_e,True))
1110         vbox.addLayout(grid)
1111
1112         vbox.addLayout(ok_cancel_buttons(d))
1113         d.setLayout(vbox) 
1114
1115         if not d.exec_(): return
1116
1117         try:
1118             gap = int(unicode(gap_e.text()))
1119         except:
1120             QMessageBox.warning(None, _('Error'), 'error', 'OK')
1121             sys.exit(0)
1122
1123         try:
1124             seed = unicode(seed_e.text())
1125             seed.decode('hex')
1126         except:
1127             print "not hex, trying decode"
1128             try:
1129                 seed = mnemonic.mn_decode( seed.split(' ') )
1130             except:
1131                 QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK'))
1132                 sys.exit(0)
1133         if not seed:
1134             QMessageBox.warning(None, _('Error'), _('No seed'), 'OK')
1135             sys.exit(0)
1136         
1137         wallet.seed = str(seed)
1138         #print repr(wallet.seed)
1139         wallet.gap_limit = gap
1140         return True
1141
1142
1143     def set_expert_mode(self, b):
1144         self.wallet.expert_mode = b
1145         self.wallet.save()
1146         self.update_receive_tab()
1147         self.update_contacts_tab()
1148         
1149
1150
1151     def settings_dialog(self):
1152         d = QDialog(self)
1153         d.setModal(1)
1154
1155         vbox = QVBoxLayout()
1156
1157         msg = _('Here are the settings of your wallet.')
1158         vbox.addWidget(QLabel(msg))
1159
1160         grid = QGridLayout()
1161         grid.setSpacing(8)
1162         vbox.addLayout(grid)
1163
1164         fee_e = QLineEdit()
1165         fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) )
1166         grid.addWidget(QLabel(_('Fee per tx. input')), 2, 0)
1167         grid.addWidget(fee_e, 2, 1)
1168         fee_e.textChanged.connect(lambda: numbify(fee_e,False))
1169
1170         nz_e = QLineEdit()
1171         nz_e.setText("%d"% self.wallet.num_zeros)
1172         grid.addWidget(QLabel(_('Zeros displayed after decimal point')), 3, 0)
1173         grid.addWidget(nz_e, 3, 1)
1174         nz_e.textChanged.connect(lambda: numbify(nz_e,True))
1175
1176         cb = QCheckBox('Expert mode')
1177         grid.addWidget(cb,4,0)
1178         cb.setChecked(self.wallet.expert_mode)
1179
1180         vbox.addLayout(ok_cancel_buttons(d))
1181         d.setLayout(vbox) 
1182
1183         if not d.exec_(): return
1184
1185         self.set_expert_mode(cb.isChecked())
1186
1187         fee = unicode(fee_e.text())
1188         try:
1189             fee = int( 100000000 * Decimal(fee) )
1190         except:
1191             QMessageBox.warning(self, _('Error'), _('Invalid value') +': %s'%fee, _('OK'))
1192             return
1193
1194         if self.wallet.fee != fee:
1195             self.wallet.fee = fee
1196             self.wallet.save()
1197         
1198         nz = unicode(nz_e.text())
1199         try:
1200             nz = int( nz )
1201             if nz>8: nz=8
1202         except:
1203             QMessageBox.warning(self, _('Error'), _('Invalid value')+':%s'%nz, _('OK'))
1204             return
1205
1206         if self.wallet.num_zeros != nz:
1207             self.wallet.num_zeros = nz
1208             self.update_history_tab()
1209             self.update_receive_tab()
1210             self.wallet.save()
1211
1212     @staticmethod 
1213     def network_dialog(wallet, parent=None):
1214         interface = wallet.interface
1215         if parent:
1216             if interface.is_connected:
1217                 status = _("Connected to")+" %s:%d\n%d blocks"%(interface.host, interface.port, wallet.blocks)
1218             else:
1219                 status = _("Not connected")
1220             server = wallet.server
1221         else:
1222             import random
1223             status = _("Please choose a server.")
1224             server = random.choice( DEFAULT_SERVERS )
1225
1226         if not wallet.interface.servers:
1227             servers_list = []
1228             for x in DEFAULT_SERVERS:
1229                 h,port,protocol = x.split(':')
1230                 servers_list.append( (h,[(protocol,port)] ) )
1231         else:
1232             servers_list = wallet.interface.servers
1233             
1234         plist = {}
1235         for item in servers_list:
1236             host, pp = item
1237             z = {}
1238             for item2 in pp:
1239                 protocol, port = item2
1240                 z[protocol] = port
1241             plist[host] = z
1242
1243         d = QDialog(parent)
1244         d.setModal(1)
1245         d.setWindowTitle(_('Server'))
1246         d.setMinimumSize(375, 20)
1247
1248         vbox = QVBoxLayout()
1249         vbox.setSpacing(20)
1250
1251         hbox = QHBoxLayout()
1252         l = QLabel()
1253         l.setPixmap(QPixmap(":icons/network.png"))
1254         hbox.addWidget(l)        
1255         hbox.addWidget(QLabel(status))
1256
1257         vbox.addLayout(hbox)
1258
1259         hbox = QHBoxLayout()
1260         host_line = QLineEdit()
1261         host_line.setText(server)
1262         hbox.addWidget(QLabel(_('Connect to') + ':'))
1263         hbox.addWidget(host_line)
1264         vbox.addLayout(hbox)
1265
1266         hbox = QHBoxLayout()
1267
1268         buttonGroup = QGroupBox(_("Protocol"))
1269         radio1 = QRadioButton("tcp", buttonGroup)
1270         radio2 = QRadioButton("http", buttonGroup)
1271
1272         def current_line():
1273             return unicode(host_line.text()).split(':')
1274             
1275         def set_button(protocol):
1276             if protocol == 't':
1277                 radio1.setChecked(1)
1278             elif protocol == 'h':
1279                 radio2.setChecked(1)
1280
1281         def set_protocol(protocol):
1282             host = current_line()[0]
1283             pp = plist[host]
1284             if protocol not in pp.keys():
1285                 protocol = pp.keys()[0]
1286                 set_button(protocol)
1287             port = pp[protocol]
1288             host_line.setText( host + ':' + port + ':' + protocol)
1289
1290         radio1.clicked.connect(lambda x: set_protocol('t') )
1291         radio2.clicked.connect(lambda x: set_protocol('h') )
1292
1293         set_button(current_line()[2])
1294
1295         hbox.addWidget(QLabel(_('Protocol')+':'))
1296         hbox.addWidget(radio1)
1297         hbox.addWidget(radio2)
1298
1299         vbox.addLayout(hbox)
1300
1301         if wallet.interface.servers:
1302             label = _('Active Servers')
1303         else:
1304             label = _('Default Servers')
1305         
1306         servers_list_widget = QTreeWidget(parent)
1307         servers_list_widget.setHeaderLabels( [ label ] )
1308         servers_list_widget.setMaximumHeight(150)
1309         for host in plist.keys():
1310             servers_list_widget.addTopLevelItem(QTreeWidgetItem( [ host ] ))
1311
1312         def do_set_line(x):
1313             host = unicode(x.text(0))
1314             pp = plist[host]
1315             if 't' in pp.keys():
1316                 protocol = 't'
1317             else:
1318                 protocol = pp.keys()[0]
1319             port = pp[protocol]
1320             host_line.setText( host + ':' + port + ':' + protocol)
1321             set_button(protocol)
1322
1323         servers_list_widget.connect(servers_list_widget, SIGNAL('itemClicked(QTreeWidgetItem*, int)'), do_set_line)
1324         vbox.addWidget(servers_list_widget)
1325
1326         vbox.addLayout(ok_cancel_buttons(d))
1327         d.setLayout(vbox) 
1328
1329         if not d.exec_(): return
1330         server = unicode( host_line.text() )
1331
1332         try:
1333             wallet.set_server(server)
1334         except:
1335             QMessageBox.information(None, _('Error'), 'error', _('OK'))
1336             if parent == None:
1337                 sys.exit(1)
1338             else:
1339                 return
1340
1341         return True
1342
1343
1344
1345 class ElectrumGui():
1346
1347     def __init__(self, wallet):
1348         self.wallet = wallet
1349         self.app = QApplication(sys.argv)
1350
1351     def waiting_dialog(self):
1352
1353         s = Timer()
1354         s.start()
1355         w = QDialog()
1356         w.resize(200, 70)
1357         w.setWindowTitle('Electrum')
1358         l = QLabel('')
1359         vbox = QVBoxLayout()
1360         vbox.addWidget(l)
1361         w.setLayout(vbox)
1362         w.show()
1363         def f():
1364             if self.wallet.up_to_date: 
1365                 w.close()
1366             else:
1367                 l.setText("Please wait...\nAddresses generated: %d\nKilobytes received: %.1f"\
1368                               %(len(self.wallet.all_addresses()), self.wallet.interface.bytes_received/1024.))
1369
1370         w.connect(s, QtCore.SIGNAL('timersignal'), f)
1371         self.wallet.interface.poke()
1372         w.exec_()
1373         w.destroy()
1374
1375
1376     def restore_or_create(self):
1377
1378         msg = _("Wallet file not found.")+"\n"+_("Do you want to create a new wallet, or to restore an existing one?")
1379         r = QMessageBox.question(None, _('Message'), msg, _('Create'), _('Restore'), _('Cancel'), 0, 2)
1380         if r==2: return False
1381         
1382         is_recovery = (r==1)
1383         wallet = self.wallet
1384         # ask for the server.
1385         if not ElectrumWindow.network_dialog( wallet, parent=None ): return False
1386
1387         if not is_recovery:
1388             wallet.new_seed(None)
1389             wallet.init_mpk( wallet.seed )
1390             wallet.up_to_date_event.clear()
1391             wallet.up_to_date = False
1392             self.waiting_dialog()
1393             # run a dialog indicating the seed, ask the user to remember it
1394             ElectrumWindow.show_seed_dialog(wallet)
1395             #ask for password
1396             ElectrumWindow.change_password_dialog(wallet)
1397         else:
1398             # ask for seed and gap.
1399             if not ElectrumWindow.seed_dialog( wallet ): return False
1400             wallet.init_mpk( wallet.seed )
1401             wallet.up_to_date_event.clear()
1402             wallet.up_to_date = False
1403             self.waiting_dialog()
1404             if wallet.is_found():
1405                 # history and addressbook
1406                 wallet.update_tx_history()
1407                 wallet.fill_addressbook()
1408                 print "recovery successful"
1409                 wallet.save()
1410             else:
1411                 QMessageBox.information(None, _('Error'), _("No transactions found for this seed"), _('OK'))
1412
1413         wallet.save()
1414         return True
1415
1416     def main(self,url):
1417         s = Timer()
1418         s.start()
1419         w = ElectrumWindow(self.wallet)
1420         if url: w.set_url(url)
1421         w.app = self.app
1422         w.connect_slots(s)
1423         w.update_wallet()
1424
1425         self.app.exec_()