optional icon in installwizard question
[electrum-nvc.git] / gui / qt / installwizard.py
1 from PyQt4.QtGui import *
2 from PyQt4.QtCore import *
3 import PyQt4.QtCore as QtCore
4
5 from electrum.i18n import _
6 from electrum import Wallet, Wallet_2of3
7
8 import seed_dialog
9 from network_dialog import NetworkDialog
10 from util import *
11 from amountedit import AmountEdit
12
13 import sys
14 import threading
15 from electrum.plugins import run_hook
16
17 class InstallWizard(QDialog):
18
19     def __init__(self, config, network, storage):
20         QDialog.__init__(self)
21         self.config = config
22         self.network = network
23         self.storage = storage
24         self.setMinimumSize(575, 400)
25         self.setWindowTitle('Electrum')
26         self.connect(self, QtCore.SIGNAL('accept'), self.accept)
27
28         self.stack = QStackedLayout()
29         self.setLayout(self.stack)
30
31
32     def set_layout(self, layout):
33         w = QWidget()
34         w.setLayout(layout)
35         self.stack.setCurrentIndex(self.stack.addWidget(w))
36
37
38     def restore_or_create(self):
39
40         grid = QGridLayout()
41         grid.setSpacing(5)
42
43         msg = _("Electrum could not find an existing wallet.") + "\n\n" \
44             + _("What do you want to do?") + "\n"
45         label = QLabel(msg)
46         label.setWordWrap(True)
47         grid.addWidget(label, 0, 0)
48
49         gb = QGroupBox()
50
51         b1 = QRadioButton(gb)
52         b1.setText(_("Create new wallet"))
53         b1.setChecked(True)
54
55         b2 = QRadioButton(gb)
56         b2.setText(_("Restore an existing wallet"))
57
58         grid.addWidget(b1,1,0)
59         grid.addWidget(b2,2,0)
60
61         vbox = QVBoxLayout()
62         self.set_layout(vbox)
63
64         vbox.addLayout(grid)
65         vbox.addStretch(1)
66         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
67
68         if not self.exec_():
69             return
70         
71         return 'create' if b1.isChecked() else 'restore'
72
73
74
75     def verify_seed(self, seed, sid):
76         r = self.enter_seed_dialog(False, sid)
77         if not r:
78             return
79
80         if r != seed:
81             QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
82             return False
83         else:
84             return True
85
86
87     def get_seed_text(self, seed_e):
88         text = unicode(seed_e.toPlainText()).strip()
89         text = ' '.join(text.split())
90         return text
91
92
93     def is_seed(self, seed_e):
94         text = self.get_seed_text(seed_e)
95         return Wallet.is_seed(text) or Wallet.is_mpk(text)
96
97
98     def enter_seed_dialog(self, is_restore, sid):
99         vbox, seed_e = seed_dialog.enter_seed_box(is_restore, sid)
100         vbox.addStretch(1)
101         hbox, button = ok_cancel_buttons2(self, _('Next'))
102         vbox.addLayout(hbox)
103         button.setEnabled(False)
104         seed_e.textChanged.connect(lambda: button.setEnabled(self.is_seed(seed_e)))
105         self.set_layout(vbox)
106         if not self.exec_():
107             return
108         return self.get_seed_text(seed_e)
109
110
111     def double_seed_dialog(self):
112         vbox = QVBoxLayout()
113         vbox1, seed_e1 = seed_dialog.enter_seed_box(True, 'hot')
114         vbox2, seed_e2 = seed_dialog.enter_seed_box(True, 'cold')
115         vbox.addLayout(vbox1)
116         vbox.addLayout(vbox2)
117         vbox.addStretch(1)
118         hbox, button = ok_cancel_buttons2(self, _('Next'))
119         vbox.addLayout(hbox)
120         button.setEnabled(False)
121         f = lambda: button.setEnabled(self.is_seed(seed_e1) and self.is_seed(seed_e2))
122         seed_e1.textChanged.connect(f)
123         seed_e2.textChanged.connect(f)
124         self.set_layout(vbox)
125         if not self.exec_():
126             return 
127         return self.get_seed_text(seed_e1), self.get_seed_text(seed_e2)
128
129
130
131
132     def waiting_dialog(self, task, msg= _("Electrum is generating your addresses, please wait.")):
133         def target():
134             task()
135             self.emit(QtCore.SIGNAL('accept'))
136
137         vbox = QVBoxLayout()
138         self.waiting_label = QLabel(msg)
139         vbox.addWidget(self.waiting_label)
140         self.set_layout(vbox)
141         t = threading.Thread(target = target)
142         t.start()
143         self.exec_()
144
145
146
147
148     def network_dialog(self):
149         
150         grid = QGridLayout()
151         grid.setSpacing(5)
152
153         label = QLabel(_("Electrum communicates with remote servers to get information about your transactions and addresses. The servers all fulfil the same purpose only differing in hardware. In most cases you simply want to let Electrum pick one at random if you have a preference though feel free to select a server manually.") + "\n\n" \
154                       + _("How do you want to connect to a server:")+" ")
155         label.setWordWrap(True)
156         grid.addWidget(label, 0, 0)
157
158         gb = QGroupBox()
159
160         b1 = QRadioButton(gb)
161         b1.setText(_("Auto connect"))
162         b1.setChecked(True)
163
164         b2 = QRadioButton(gb)
165         b2.setText(_("Select server manually"))
166
167         #b3 = QRadioButton(gb)
168         #b3.setText(_("Stay offline"))
169
170         grid.addWidget(b1,1,0)
171         grid.addWidget(b2,2,0)
172         #grid.addWidget(b3,3,0)
173
174         vbox = QVBoxLayout()
175         vbox.addLayout(grid)
176
177         vbox.addStretch(1)
178         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
179
180         self.set_layout(vbox)
181         if not self.exec_():
182             return
183         
184         if b2.isChecked():
185             return NetworkDialog(self.network, self.config, None).do_exec()
186
187         elif b1.isChecked():
188             self.config.set_key('auto_cycle', True, True)
189             return
190
191         else:
192             self.config.set_key("server", None, True)
193             self.config.set_key('auto_cycle', False, True)
194             return
195         
196
197     def show_message(self, msg, icon=None):
198         vbox = QVBoxLayout()
199         self.set_layout(vbox)
200         if icon:
201             logo = QLabel()
202             logo.setPixmap(icon)
203             vbox.addWidget(logo)
204         vbox.addWidget(QLabel(msg))
205         vbox.addStretch(1)
206         vbox.addLayout(close_button(self, _('Next')))
207         if not self.exec_(): 
208             return None
209
210
211     def question(self, msg, icon=None):
212         vbox = QVBoxLayout()
213         self.set_layout(vbox)
214         if icon:
215             logo = QLabel()
216             logo.setPixmap(icon)
217             vbox.addWidget(logo)
218         vbox.addWidget(QLabel(msg))
219         vbox.addStretch(1)
220         vbox.addLayout(ok_cancel_buttons(self, _('OK')))
221         if not self.exec_(): 
222             return None
223         return True
224
225
226     def show_seed(self, seed, sid):
227         vbox = seed_dialog.show_seed_box(seed, sid)
228         vbox.addLayout(ok_cancel_buttons(self, _("Next")))
229         self.set_layout(vbox)
230         return self.exec_()
231
232
233     def password_dialog(self):
234         msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
235               +_("Leave these fields empty if you want to disable encryption.")
236         from password_dialog import make_password_dialog, run_password_dialog
237         self.set_layout( make_password_dialog(self, None, msg) )
238         return run_password_dialog(self, None, self)[2]
239
240
241     def choose_wallet_type(self):
242         grid = QGridLayout()
243         grid.setSpacing(5)
244
245         msg = _("Choose your wallet.")
246         label = QLabel(msg)
247         label.setWordWrap(True)
248         grid.addWidget(label, 0, 0)
249
250         gb = QGroupBox()
251
252         b1 = QRadioButton(gb)
253         b1.setText(_("Standard wallet (protected by password)"))
254         b1.setChecked(True)
255
256         b2 = QRadioButton(gb)
257         b2.setText(_("Multi-signature wallet (two-factor authentication)"))
258
259         grid.addWidget(b1,1,0)
260         grid.addWidget(b2,2,0)
261
262         vbox = QVBoxLayout()
263
264         vbox.addLayout(grid)
265         vbox.addStretch(1)
266         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
267
268         self.set_layout(vbox)
269         if not self.exec_():
270             return
271         
272         if b1.isChecked():
273             return 'standard'
274         elif b2.isChecked():
275             return '2of3'
276
277
278     def run(self, action = None):
279
280         if action is None:
281             action = self.restore_or_create()
282
283         if action is None: 
284             return
285
286         if action == 'create':            
287             t = self.choose_wallet_type()
288             if not t:
289                 return 
290
291             if t == '2of3':
292                 run_hook('create_cold_seed', self.storage, self)
293                 return
294
295
296         if action in ['create', 'create2of3']:
297
298             wallet = Wallet(self.storage)
299             seed = wallet.make_seed()
300             sid = 'hot' if action == 'create2of3' else None
301             if not self.show_seed(seed, sid):
302                 return
303             if not self.verify_seed(seed, sid):
304                 return
305             password = self.password_dialog()
306             wallet.add_seed(seed, password)
307
308             if action == 'create2of3':
309                 run_hook('create_third_key', wallet, self)
310                 if not wallet.master_public_keys.get("remote/"):
311                     return
312
313             wallet.create_accounts(password)
314             # generate first addresses offline
315             self.waiting_dialog(wallet.synchronize)
316
317         elif action == 'restore':
318             t = self.choose_wallet_type()
319             if not t: 
320                 return
321
322             if t == 'standard':
323                 text = self.enter_seed_dialog(True, None)
324                 if Wallet.is_seed(text):
325                     password = self.password_dialog()
326                     wallet = Wallet.from_seed(text, self.storage)
327                     wallet.add_seed(text, password)
328                     wallet.create_accounts(password)
329                 elif Wallet.is_mpk(text):
330                     wallet = Wallet.from_mpk(text, self.storage)
331                 else:
332                     raise
333
334             elif t in ['2of2', '2of3']:
335                 r = self.double_seed_dialog()
336                 if not r: 
337                     return
338                 text1, text2 = r
339                 password = self.password_dialog()
340                 wallet = Wallet_2of3(self.storage)
341
342                 if Wallet.is_seed(text1):
343                     wallet.add_seed(text1, password)
344                     if Wallet.is_seed(text2):
345                         wallet.add_cold_seed(text2, password)
346                     else:
347                         wallet.add_master_public_key("cold/", text2)
348
349                 elif Wallet.is_mpk(text1):
350                     if Wallet.is_seed(text2):
351                         wallet.add_seed(text2, password)
352                         wallet.add_master_public_key("cold/", text1)
353                     else:
354                         wallet.add_master_public_key("m/", text1)
355                         wallet.add_master_public_key("cold/", text2)
356
357                 run_hook('restore_third_key', wallet, self)
358
359                 wallet.create_accounts(None)
360
361             else:
362                 raise
363
364
365
366
367         else: raise
368                 
369         #if not self.config.get('server'):
370         if self.network:
371             if self.network.interfaces:
372                 self.network_dialog()
373             else:
374                 QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK'))
375                 self.network.stop()
376                 self.network = None
377
378         # start wallet threads
379         wallet.start_threads(self.network)
380
381         if action == 'restore':
382
383             self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
384
385             if self.network:
386                 if wallet.is_found():
387                     QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
388                 else:
389                     QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
390             else:
391                 QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))
392
393         return wallet