separate blockchain and network
[electrum-nvc.git] / gui / gui_classic.py
index d3bff56..076f97d 100644 (file)
@@ -20,6 +20,8 @@ import sys, time, datetime, re, threading
 from i18n import _, set_language
 from electrum.util import print_error, print_msg
 import os.path, json, ast, traceback
+import shutil
+import StringIO
 
 
 try:
@@ -31,7 +33,7 @@ from PyQt4.QtGui import *
 from PyQt4.QtCore import *
 import PyQt4.QtCore as QtCore
 
-from electrum.bitcoin import MIN_RELAY_TX_FEE
+from electrum.bitcoin import MIN_RELAY_TX_FEE, is_valid
 
 try:
     import icons_rc
@@ -39,9 +41,11 @@ except:
     sys.exit("Error: Could not import icons_rc.py, please generate it with: 'pyrcc4 icons.qrc -o gui/icons_rc.py'")
 
 from electrum.wallet import format_satoshis
-from electrum.bitcoin import Transaction, is_valid
+from electrum import Transaction
 from electrum import mnemonic
-from electrum import util, bitcoin, commands
+from electrum import util, bitcoin, commands, Interface, Wallet
+from electrum import SimpleConfig, Wallet, WalletStorage
+
 
 import bmp, pyqrnative
 import exchange_rate
@@ -146,29 +150,6 @@ class UpdateLabel(QLabel):
 
 
 
-class Timer(QtCore.QThread):
-    def run(self):
-        while True:
-            self.emit(QtCore.SIGNAL('timersignal'))
-            time.sleep(0.5)
-
-class HelpButton(QPushButton):
-    def __init__(self, text):
-        QPushButton.__init__(self, '?')
-        self.setFocusPolicy(Qt.NoFocus)
-        self.setFixedWidth(20)
-        self.clicked.connect(lambda: QMessageBox.information(self, 'Help', text, 'OK') )
-
-
-class EnterButton(QPushButton):
-    def __init__(self, text, func):
-        QPushButton.__init__(self, text)
-        self.func = func
-        self.clicked.connect(func)
-
-    def keyPressEvent(self, e):
-        if e.key() == QtCore.Qt.Key_Return:
-            apply(self.func,())
 
 class MyTreeWidget(QTreeWidget):
     def __init__(self, parent):
@@ -198,6 +179,7 @@ class StatusBarButton(QPushButton):
         self.setMaximumWidth(25)
         self.clicked.connect(func)
         self.func = func
+        self.setIconSize(QSize(25,25))
 
     def keyPressEvent(self, e):
         if e.key() == QtCore.Qt.Key_Return:
@@ -207,26 +189,6 @@ class StatusBarButton(QPushButton):
 
 
 
-def waiting_dialog(f):
-
-    s = Timer()
-    s.start()
-    w = QDialog()
-    w.resize(200, 70)
-    w.setWindowTitle('Electrum')
-    l = QLabel('')
-    vbox = QVBoxLayout()
-    vbox.addWidget(l)
-    w.setLayout(vbox)
-    w.show()
-    def ff():
-        s = f()
-        if s: l.setText(s)
-        else: w.close()
-    w.connect(s, QtCore.SIGNAL('timersignal'), ff)
-    w.exec_()
-    w.destroy()
-
 
 
 
@@ -235,25 +197,60 @@ def waiting_dialog(f):
 default_column_widths = { "history":[40,140,350,140], "contacts":[350,330], "receive":[[370], [370,200,130]] }
 
 class ElectrumWindow(QMainWindow):
+    def changeEvent(self, event):
+        flags = self.windowFlags();
+        if event and event.type() == QtCore.QEvent.WindowStateChange:
+            if self.windowState() & QtCore.Qt.WindowMinimized:
+                self.build_menu(True)
+                # The only way to toggle the icon in the window managers taskbar is to use the Qt.Tooltip flag
+                # The problem is that it somehow creates an (in)visible window that will stay active and prevent
+                # Electrum from closing.
+                # As for now I have no clue how to implement a proper 'hide to tray' functionality.
+                # self.setWindowFlags(flags & ~Qt.ToolTip)
+            elif event.oldState() & QtCore.Qt.WindowMinimized:
+                self.build_menu(False)
+                #self.setWindowFlags(flags | Qt.ToolTip)
+
+    def build_menu(self, is_hidden = False):
+        m = QMenu()
+        if self.isMinimized():
+            m.addAction(_("Show"), self.showNormal)
+        else:
+            m.addAction(_("Hide"), self.showMinimized)
+
+        m.addSeparator()
+        m.addAction(_("Exit Electrum"), self.close)
+        self.tray.setContextMenu(m)
+
+    def tray_activated(self, reason):
+        if reason == QSystemTrayIcon.DoubleClick:
+            self.showNormal()
 
-    def __init__(self, wallet, config):
+
+    def __init__(self, config):
         QMainWindow.__init__(self)
-        self.lite = None
-        self.wallet = wallet
+
         self.config = config
+        self.init_plugins()
+
+        self._close_electrum = False
+        self.lite = None
         self.current_account = self.config.get("current_account", None)
 
-        self.init_plugins()
+        self.icon = QIcon(os.getcwd() + '/icons/electrum.png')
+        self.tray = QSystemTrayIcon(self.icon, self)
+        self.tray.setToolTip('Electrum')
+        self.tray.activated.connect(self.tray_activated)
+
+        self.build_menu()
+        self.tray.show()
         self.create_status_bar()
 
         self.need_update = threading.Event()
-        self.wallet.interface.register_callback('updated', lambda: self.need_update.set())
-        self.wallet.interface.register_callback('banner', lambda: self.emit(QtCore.SIGNAL('banner_signal')))
-        self.wallet.interface.register_callback('disconnected', lambda: self.emit(QtCore.SIGNAL('update_status')))
-        self.wallet.interface.register_callback('disconnecting', lambda: self.emit(QtCore.SIGNAL('update_status')))
 
-        self.expert_mode = config.get('classic_expert_mode', False)
+        self.expert_mode   = config.get('classic_expert_mode', False)
         self.decimal_point = config.get('decimal_point', 8)
+        self.num_zeros     = int(config.get('num_zeros',0))
 
         set_language(config.get('language'))
 
@@ -273,17 +270,18 @@ class ElectrumWindow(QMainWindow):
 
         g = self.config.get("winpos-qt",[100, 100, 840, 400])
         self.setGeometry(g[0], g[1], g[2], g[3])
-        title = 'Electrum ' + self.wallet.electrum_version + '  -  ' + self.config.path
-        if not self.wallet.seed: title += ' [%s]' % (_('seedless'))
-        self.setWindowTitle( title )
+
+        self.init_menubar()
 
         QShortcut(QKeySequence("Ctrl+W"), self, self.close)
+        QShortcut(QKeySequence("Ctrl+R"), self, self.update_wallet)
         QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
         QShortcut(QKeySequence("Ctrl+PgUp"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() - 1 )%tabs.count() ))
         QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() + 1 )%tabs.count() ))
         
         self.connect(self, QtCore.SIGNAL('update_status'), self.update_status)
         self.connect(self, QtCore.SIGNAL('banner_signal'), lambda: self.console.showMessage(self.wallet.interface.banner) )
+        self.connect(self, QtCore.SIGNAL('transaction_signal'), lambda: self.notify_transactions() )
         self.history_list.setFocus(True)
         
         self.exchanger = exchange_rate.Exchanger(self)
@@ -295,12 +293,207 @@ class ElectrumWindow(QMainWindow):
             tabs.setCurrentIndex (n)
             tabs.setCurrentIndex (0)
 
+        # plugins that need to change the GUI do it here
+        self.run_hook('init')
+
+
+
+
+    def load_wallet(self, wallet):
+        import electrum
+        self.wallet = wallet
+
+        self.wallet.interface.register_callback('updated', lambda: self.need_update.set())
+        self.wallet.interface.register_callback('banner', lambda: self.emit(QtCore.SIGNAL('banner_signal')))
+        self.wallet.interface.register_callback('disconnected', lambda: self.emit(QtCore.SIGNAL('update_status')))
+        self.wallet.interface.register_callback('disconnecting', lambda: self.emit(QtCore.SIGNAL('update_status')))
+        self.wallet.interface.register_callback('new_transaction', lambda: self.emit(QtCore.SIGNAL('transaction_signal')))
+        title = 'Electrum ' + self.wallet.electrum_version + '  -  ' + self.wallet.storage.path
+        if not self.wallet.seed: title += ' [%s]' % (_('seedless'))
+        self.setWindowTitle( title )
+        self.update_wallet()
         # set initial message
         self.console.showMessage(self.wallet.interface.banner)
+        # Once GUI has been initialized check if we want to announce something since the callback has been called before the GUI was initialized
+        self.notify_transactions()
 
-        # plugins that need to change the GUI do it here
-        self.run_hook('init_gui')
+        # account selector
+        accounts = self.wallet.get_account_names()
+        self.account_selector.clear()
+        if len(accounts) > 1:
+            self.account_selector.addItems([_("All accounts")] + accounts.values())
+            self.account_selector.setCurrentIndex(0)
+            self.account_selector.show()
+        else:
+            self.account_selector.hide()
+
+        self.new_account.setEnabled(self.wallet.seed_version>4)
+
+        self.update_lock_icon()
+        self.update_buttons_on_seed()
+        self.update_console()
+
+        self.run_hook('load_wallet')
+
+
+    def select_wallet_file(self):
+        wallet_folder = self.wallet.storage.path
+        re.sub("(\/\w*.dat)$", "", wallet_folder)
+        file_name = unicode( QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder, "*.dat") )
+        return file_name
+
+
+    def open_wallet(self):
+
+        filename = self.select_wallet_file()
+        if not filename:
+            return
+
+        storage = WalletStorage({'wallet_path': filename})
+        if not storage.file_exists:
+            self.show_message("file not found "+ filename)
+            return
+
+        interface = self.wallet.interface
+        blockchain = self.wallet.verifier.blockchain
+        self.wallet.stop_threads()
+        
+        # create new wallet 
+        wallet = Wallet(storage)
+        wallet.start_threads(interface, blockchain)
+
+        self.load_wallet(wallet)
+
+
+    def new_wallet(self):
+        import installwizard
+
+        wallet_folder = self.wallet.storage.path
+        re.sub("(\/\w*.dat)$", "", wallet_folder)
+        filename = self.getSaveFileName("Select your wallet file", wallet_folder, "*.dat")
+
+        storage = WalletStorage({'wallet_path': filename})
+        assert not storage.file_exists
+
+        wizard = installwizard.InstallWizard(self.config, self.wallet.interface, self.wallet.verifier.blockchain, storage)
+        wallet = wizard.run()
+        if wallet: 
+            self.load_wallet(wallet)
+        
+
+
+    def init_menubar(self):
+        menubar = QMenuBar()
+
+        file_menu = menubar.addMenu(_("&File"))
+        open_wallet_action = file_menu.addAction(_("&Open"))
+        open_wallet_action.triggered.connect(self.open_wallet)
+
+        new_wallet_action = file_menu.addAction(_("&Create/Restore"))
+        new_wallet_action.triggered.connect(self.new_wallet)
+
+        wallet_backup = file_menu.addAction(_("&Copy"))
+        wallet_backup.triggered.connect(lambda: backup_wallet(self.wallet.storage.path))
+
+        quit_item = file_menu.addAction(_("&Close"))
+        quit_item.triggered.connect(self.close)
+
+        wallet_menu = menubar.addMenu(_("&Wallet"))
+
+        # Settings / Preferences are all reserved keywords in OSX using this as work around
+        preferences_name = _("Electrum preferences") if sys.platform == 'darwin' else _("Preferences")
+        preferences_menu = wallet_menu.addAction(preferences_name)
+        preferences_menu.triggered.connect(self.settings_dialog)
+
+        wallet_menu.addSeparator()
 
+        raw_transaction_menu = wallet_menu.addMenu(_("&Load raw transaction"))
+
+        raw_transaction_file = raw_transaction_menu.addAction(_("&From file"))
+        raw_transaction_file.triggered.connect(self.do_process_from_file)
+
+        raw_transaction_text = raw_transaction_menu.addAction(_("&From text"))
+        raw_transaction_text.triggered.connect(self.do_process_from_text)
+
+        csv_transaction_menu = wallet_menu.addMenu(_("&Load CSV transaction"))
+
+        csv_transaction_file = csv_transaction_menu.addAction(_("&From file"))
+        csv_transaction_file.triggered.connect(self.do_process_from_csv_file)
+
+        csv_transaction_text = csv_transaction_menu.addAction(_("&From text"))
+        csv_transaction_text.triggered.connect(self.do_process_from_csv_text)
+
+        wallet_menu.addSeparator()
+
+        show_menu = wallet_menu.addMenu(_("Show"))
+
+        #if self.wallet.seed:
+        show_seed = show_menu.addAction(_("&Seed"))
+        show_seed.triggered.connect(self.show_seed_dialog)
+
+        show_mpk = show_menu.addAction(_("&Master Public Key"))
+        show_mpk.triggered.connect(self.show_master_public_key)
+
+        wallet_menu.addSeparator()
+        new_contact = wallet_menu.addAction(_("&New contact"))
+        new_contact.triggered.connect(self.new_contact_dialog)
+
+        self.new_account = wallet_menu.addAction(_("&New account"))
+        self.new_account.triggered.connect(self.new_account_dialog)
+
+        import_menu = menubar.addMenu(_("&Import"))
+        in_labels = import_menu.addAction(_("&Labels"))
+        in_labels.triggered.connect(self.do_import_labels)
+
+        in_private_keys = import_menu.addAction(_("&Private keys"))
+        in_private_keys.triggered.connect(self.do_import_privkey)
+
+        export_menu = menubar.addMenu(_("&Export"))
+        ex_private_keys = export_menu.addAction(_("&Private keys"))
+        ex_private_keys.triggered.connect(self.do_export_privkeys)
+
+        ex_history = export_menu.addAction(_("&History"))
+        ex_history.triggered.connect(self.do_export_history)
+
+        ex_labels = export_menu.addAction(_("&Labels"))
+        ex_labels.triggered.connect(self.do_export_labels)
+
+        help_menu = menubar.addMenu(_("&Help"))
+        doc_open = help_menu.addAction(_("&Documentation"))
+        doc_open.triggered.connect(lambda: webbrowser.open("http://electrum.org/documentation.html"))
+        web_open = help_menu.addAction(_("&Official website")) 
+        web_open.triggered.connect(lambda: webbrowser.open("http://electrum.org"))
+
+        self.setMenuBar(menubar)
+
+
+
+    def notify_transactions(self):
+        print_error("Notifying GUI")
+        if len(self.wallet.interface.pending_transactions_for_notifications) > 0:
+            # Combine the transactions if there are more then three
+            tx_amount = len(self.wallet.interface.pending_transactions_for_notifications)
+            if(tx_amount >= 3):
+                total_amount = 0
+                for tx in self.wallet.interface.pending_transactions_for_notifications:
+                    is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx)
+                    if(v > 0):
+                        total_amount += v
+
+                self.notify("%s new transactions received. Total amount received in the new transactions %s %s" \
+                                % (tx_amount, self.format_amount(total_amount), self.base_unit()))
+
+                self.wallet.interface.pending_transactions_for_notifications = []
+            else:
+              for tx in self.wallet.interface.pending_transactions_for_notifications:
+                  if tx:
+                      self.wallet.interface.pending_transactions_for_notifications.remove(tx)
+                      is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx)
+                      if(v > 0):
+                          self.notify("New transaction received. %s %s" % (self.format_amount(v), self.base_unit()))
+
+    def notify(self, message):
+        self.tray.showMessage("Electrum", message, QSystemTrayIcon.Information, 20000)
 
     # plugins
     def init_plugins(self):
@@ -317,9 +510,9 @@ class ElectrumWindow(QMainWindow):
             plugins = [ __import__('electrum_plugins.'+name, fromlist=['electrum_plugins']) for name in plugin_names]
 
         self.plugins = []
-        for p in plugins:
+        for name, p in zip(plugin_names, plugins):
             try:
-                self.plugins.append( p.Plugin(self) )
+                self.plugins.append( p.Plugin(self, name) )
             except:
                 print_msg("Error:cannot initialize plugin",p)
                 traceback.print_exc(file=sys.stdout)
@@ -341,13 +534,15 @@ class ElectrumWindow(QMainWindow):
                 
         return
 
-        
+
+
     def set_label(self, name, text = None):
         changed = False
         old_text = self.wallet.labels.get(name)
         if text:
             if old_text != text:
                 self.wallet.labels[name] = text
+                self.wallet.storage.put('labels', self.wallet.labels)
                 changed = True
         else:
             if old_text:
@@ -373,8 +568,6 @@ class ElectrumWindow(QMainWindow):
             self.config.set_key('io_dir', os.path.dirname(fileName), True)
         return fileName
 
-
-
     def close(self):
         QMainWindow.close(self)
         self.run_hook('close_main_window')
@@ -389,8 +582,8 @@ class ElectrumWindow(QMainWindow):
             self.need_update.clear()
         self.run_hook('timer_actions')
     
-    def format_amount(self, x, is_diff=False):
-        return format_satoshis(x, is_diff, self.wallet.num_zeros, self.decimal_point)
+    def format_amount(self, x, is_diff=False, whitespaces=False):
+        return format_satoshis(x, is_diff, self.num_zeros, self.decimal_point, whitespaces)
 
     def read_amount(self, x):
         if x in['.', '']: return None
@@ -411,13 +604,13 @@ class ElectrumWindow(QMainWindow):
                 text =  _( "Balance" ) + ": %s "%( self.format_amount(c) ) + self.base_unit()
                 if u: text +=  " [%s unconfirmed]"%( self.format_amount(u,True).strip() )
                 text += self.create_quote_text(Decimal(c+u)/100000000)
+                self.tray.setToolTip(text)
                 icon = QIcon(":icons/status_connected.png")
         else:
             text = _("Not connected")
             icon = QIcon(":icons/status_disconnected.png")
 
-        self.status_text = text
-        self.statusBar().showMessage(text)
+        self.balance_label.setText(text)
         self.status_button.setIcon( icon )
 
     def update_wallet(self):
@@ -605,11 +798,11 @@ class ElectrumWindow(QMainWindow):
                 icon = QIcon(":icons/confirmed.png")
 
             if value is not None:
-                v_str = self.format_amount(value, True)
+                v_str = self.format_amount(value, True, whitespaces=True)
             else:
                 v_str = '--'
 
-            balance_str = self.format_amount(balance)
+            balance_str = self.format_amount(balance, whitespaces=True)
             
             if tx_hash:
                 label, is_default_label = self.wallet.get_label(tx_hash)
@@ -676,12 +869,10 @@ class ElectrumWindow(QMainWindow):
                 _('Bitcoin transactions are in general not free. A transaction fee is paid by the sender of the funds.') + '\n\n'\
                     + _('The amount of fee can be decided freely by the sender. However, transactions with low fees take more time to be processed.') + '\n\n'\
                     + _('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)
-        b = ''
-        if self.wallet.seed: 
-            b = EnterButton(_("Send"), self.do_send)
-        else:
-            b = EnterButton(_("Create unsigned transaction"), self.do_send)
-        grid.addWidget(b, 6, 1)
+
+
+        self.send_button = EnterButton(_("Send"), self.do_send)
+        grid.addWidget(self.send_button, 6, 1)
 
         b = EnterButton(_("Clear"),self.do_clear)
         grid.addWidget(b, 6, 2)
@@ -724,7 +915,7 @@ class ElectrumWindow(QMainWindow):
             if inputs:
                 palette = QPalette()
                 palette.setColor(self.amount_e.foregroundRole(), QColor('black'))
-                text = self.status_text
+                text = ""
             else:
                 palette = QPalette()
                 palette.setColor(self.amount_e.foregroundRole(), QColor('red'))
@@ -758,8 +949,7 @@ class ElectrumWindow(QMainWindow):
         return lambda s, *args: s.do_protect(func, args)
 
 
-    @protected
-    def do_send(self, password):
+    def do_send(self):
 
         label = unicode( self.message_e.text() )
         r = unicode( self.payto_e.text() )
@@ -784,9 +974,21 @@ class ElectrumWindow(QMainWindow):
             QMessageBox.warning(self, _('Error'), _('Invalid Fee'), _('OK'))
             return
 
+        confirm_amount = self.config.get('confirm_amount', 100000000)
+        if amount >= confirm_amount:
+            if not self.question("send %s to %s?"%(self.format_amount(amount) + ' '+ self.base_unit(), to_address)):
+                return
+
+        self.send_tx(to_address, amount, fee, label)
+
+
+    @protected
+    def send_tx(self, to_address, amount, fee, label, password):
+
         try:
             tx = self.wallet.mktx( [(to_address, amount)], password, fee, account=self.current_account)
         except BaseException, e:
+            traceback.print_exc(file=sys.stdout)
             self.show_message(str(e))
             return
 
@@ -819,6 +1021,10 @@ class ElectrumWindow(QMainWindow):
             except:
                 QMessageBox.warning(self, _('Error'), _('Could not write transaction to file'), _('OK'))
 
+        # add recipient to addressbook
+        if to_address not in self.wallet.addressbook and not self.wallet.is_mine(to_address):
+            self.wallet.addressbook.append(to_address)
+
 
 
 
@@ -963,7 +1169,6 @@ class ElectrumWindow(QMainWindow):
         self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,0,1))
         self.contacts_list = l
         self.contacts_buttons_hbox = hbox
-        hbox.addWidget(EnterButton(_("New"), self.new_contact_dialog))
         hbox.addStretch(1)
         return w
 
@@ -1079,7 +1284,7 @@ class ElectrumWindow(QMainWindow):
             account_items = []
 
         for k, account in account_items:
-            name = account.get('name',str(k))
+            name = self.wallet.get_account_name(k)
             c,u = self.wallet.get_account_balance(k)
             account_item = QTreeWidgetItem( [ name, '', self.format_amount(c+u), ''] )
             l.addTopLevelItem(account_item)
@@ -1096,7 +1301,7 @@ class ElectrumWindow(QMainWindow):
                 is_red = False
                 gap = 0
 
-                for address in account[is_change]:
+                for address in account.get_addresses(is_change):
                     h = self.wallet.history.get(address,[])
             
                     if h == []:
@@ -1130,7 +1335,6 @@ class ElectrumWindow(QMainWindow):
 
 
     def update_contacts_tab(self):
-
         l = self.contacts_list
         l.clear()
 
@@ -1153,10 +1357,15 @@ class ElectrumWindow(QMainWindow):
     def create_console_tab(self):
         from qt_console import Console
         self.console = console = Console()
-        self.console.history = self.config.get("console-history",[])
-        self.console.history_index = len(self.console.history)
+        return console
 
-        console.updateNamespace({'wallet' : self.wallet, 'interface' : self.wallet.interface, 'gui':self})
+
+    def update_console(self):
+        console = self.console
+        console.history = self.config.get("console-history",[])
+        console.history_index = len(console.history)
+
+        console.updateNamespace({'wallet' : self.wallet, 'network' : self.wallet.network, 'gui':self})
         console.updateNamespace({'util' : util, 'bitcoin':bitcoin})
 
         c = commands.Commands(self.wallet, self.wallet.interface, lambda: self.console.set_json(True))
@@ -1168,13 +1377,13 @@ class ElectrumWindow(QMainWindow):
             methods[m] = mkfunc(c._run, m)
             
         console.updateNamespace(methods)
-        return console
+
 
     def change_account(self,s):
         if s == _("All accounts"):
             self.current_account = None
         else:
-            accounts = self.wallet.get_accounts()
+            accounts = self.wallet.get_account_names()
             for k, v in accounts.items():
                 if v == s:
                     self.current_account = k
@@ -1183,38 +1392,62 @@ class ElectrumWindow(QMainWindow):
         self.update_receive_tab()
 
     def create_status_bar(self):
-        self.status_text = ""
+
         sb = QStatusBar()
         sb.setFixedHeight(35)
         qtVersion = qVersion()
 
+        self.balance_label = QLabel("")
+        sb.addWidget(self.balance_label)
+
         update_notification = UpdateLabel(self.config)
         if(update_notification.new_version):
             sb.addPermanentWidget(update_notification)
 
-        accounts = self.wallet.get_accounts()
-        if len(accounts) > 1:
-            from_combo = QComboBox()
-            from_combo.addItems([_("All accounts")] + accounts.values())
-            from_combo.setCurrentIndex(0)
-            self.connect(from_combo,SIGNAL("activated(QString)"),self.change_account) 
-            sb.addPermanentWidget(from_combo)
+        self.account_selector = QComboBox()
+        self.connect(self.account_selector,SIGNAL("activated(QString)"),self.change_account) 
+        sb.addPermanentWidget(self.account_selector)
 
         if (int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7):
             sb.addPermanentWidget( StatusBarButton( QIcon(":icons/switchgui.png"), _("Switch to Lite Mode"), self.go_lite ) )
-        if self.wallet.seed:
-            self.lock_icon = QIcon(":icons/lock.png") if self.wallet.use_encryption else QIcon(":icons/unlock.png")
-            self.password_button = StatusBarButton( self.lock_icon, _("Password"), lambda: self.change_password_dialog(self.wallet, self) )
-            sb.addPermanentWidget( self.password_button )
+
+        self.lock_icon = QIcon()
+        self.password_button = StatusBarButton( self.lock_icon, _("Password"), self.change_password_dialog )
+        sb.addPermanentWidget( self.password_button )
+            
         sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), _("Preferences"), self.settings_dialog ) )
-        if self.wallet.seed:
-            sb.addPermanentWidget( StatusBarButton( QIcon(":icons/seed.png"), _("Seed"), self.show_seed_dialog ) )
+        self.seed_button = StatusBarButton( QIcon(":icons/seed.png"), _("Seed"), self.show_seed_dialog ) 
+        sb.addPermanentWidget( self.seed_button )
         self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), _("Network"), self.run_network_dialog ) 
         sb.addPermanentWidget( self.status_button )
 
         self.run_hook('create_status_bar', (sb,))
 
         self.setStatusBar(sb)
+
+
+    def update_lock_icon(self):
+        icon = QIcon(":icons/lock.png") if self.wallet.use_encryption else QIcon(":icons/unlock.png")
+        self.password_button.setIcon( icon )
+
+
+    def update_buttons_on_seed(self):
+        if self.wallet.seed:
+           self.seed_button.show()
+           self.password_button.show()
+           self.send_button.setText(_("Send"))
+        else:
+           self.password_button.hide()
+           self.seed_button.hide()
+           self.send_button.setText(_("Create unsigned transaction"))
+
+
+    def change_password_dialog(self):
+        from password_dialog import PasswordDialog
+        d = PasswordDialog(self.wallet, self)
+        d.run()
+        self.update_lock_icon()
+
         
     def go_lite(self):
         import gui_lite
@@ -1223,9 +1456,10 @@ class ElectrumWindow(QMainWindow):
         if self.lite:
             self.lite.mini.show()
         else:
-            self.lite = gui_lite.ElectrumGui(self.wallet, self.config, self)
+            self.lite = gui_lite.ElectrumGui(self.config, None, None, self)
             self.lite.main(None)
 
+
     def new_contact_dialog(self):
         text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':')
         address = unicode(text)
@@ -1238,6 +1472,34 @@ class ElectrumWindow(QMainWindow):
             else:
                 QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
 
+
+    def new_account_dialog(self):
+
+        dialog = QDialog(self)
+        dialog.setModal(1)
+        dialog.setWindowTitle(_("New Account"))
+
+        addr = self.wallet.new_account_address()
+        vbox = QVBoxLayout()
+        vbox.addWidget(QLabel(_("To create a new account, please send coins to the first address of that account:")))
+        e = QLineEdit(addr)
+        e.setReadOnly(True)
+        vbox.addWidget(e)
+
+        ok_button = QPushButton(_("OK"))
+        ok_button.setDefault(True)
+        ok_button.clicked.connect(dialog.accept)
+
+        hbox = QHBoxLayout()
+        hbox.addStretch(1)
+        hbox.addWidget(ok_button)
+        vbox.addLayout(hbox)
+
+        dialog.setLayout(vbox)
+        dialog.exec_()
+
+            
+
     def show_master_public_key(self):
         dialog = QDialog(self)
         dialog.setModal(1)
@@ -1280,63 +1542,12 @@ class ElectrumWindow(QMainWindow):
         except:
             QMessageBox.warning(self, _('Error'), _('Incorrect Password'), _('OK'))
             return
-        self.show_seed(seed, self.wallet.imported_keys, self)
-
-
-    @classmethod
-    def show_seed(self, seed, imported_keys, parent=None):
-        dialog = QDialog(parent)
-        dialog.setModal(1)
-        dialog.setWindowTitle('Electrum' + ' - ' + _('Seed'))
-
-        brainwallet = ' '.join(mnemonic.mn_encode(seed))
-
-        label1 = QLabel(_("Your wallet generation seed is")+ ":")
-
-        seed_text = QTextEdit(brainwallet)
-        seed_text.setReadOnly(True)
-        seed_text.setMaximumHeight(130)
-        
-        msg2 =  _("Please write down or memorize these 12 words (order is important).") + " " \
-              + _("This seed will allow you to recover your wallet in case of computer failure.") + " " \
-              + _("Your seed is also displayed as QR code, in case you want to transfer it to a mobile phone.") + "<p>" \
-              + "<b>"+_("WARNING")+":</b> " + _("Never disclose your seed. Never type it on a website.") + "</b><p>"
-        if imported_keys:
-            msg2 += "<b>"+_("WARNING")+":</b> " + _("Your wallet contains imported keys. These keys cannot be recovered from seed.") + "</b><p>"
-        label2 = QLabel(msg2)
-        label2.setWordWrap(True)
 
-        logo = QLabel()
-        logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
-        logo.setMaximumWidth(60)
+        from seed_dialog import SeedDialog
+        d = SeedDialog(self)
+        d.show_seed(seed, self.wallet.imported_keys)
 
-        qrw = QRCodeWidget(seed)
 
-        ok_button = QPushButton(_("OK"))
-        ok_button.setDefault(True)
-        ok_button.clicked.connect(dialog.accept)
-
-        grid = QGridLayout()
-        #main_layout.addWidget(logo, 0, 0)
-
-        grid.addWidget(logo, 0, 0)
-        grid.addWidget(label1, 0, 1)
-
-        grid.addWidget(seed_text, 1, 0, 1, 2)
-
-        grid.addWidget(qrw, 0, 2, 2, 1)
-
-        vbox = QVBoxLayout()
-        vbox.addLayout(grid)
-        vbox.addWidget(label2)
-
-        hbox = QHBoxLayout()
-        hbox.addStretch(1)
-        hbox.addWidget(ok_button)
-        vbox.addLayout(hbox)
-
-        dialog.setLayout(vbox)
-        dialog.exec_()
 
     def show_qrcode(self, data, title = "QR code"):
         if not data: return
@@ -1518,79 +1729,6 @@ class ElectrumWindow(QMainWindow):
 
 
 
-    @staticmethod
-    def change_password_dialog( wallet, parent=None ):
-
-        if not wallet.seed:
-            QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
-            return
-
-        d = QDialog(parent)
-        d.setModal(1)
-
-        pw = QLineEdit()
-        pw.setEchoMode(2)
-        new_pw = QLineEdit()
-        new_pw.setEchoMode(2)
-        conf_pw = QLineEdit()
-        conf_pw.setEchoMode(2)
-
-        vbox = QVBoxLayout()
-        if parent:
-            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')
-        else:
-            msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
-                  +_("Leave these fields empty if you want to disable encryption.")
-        vbox.addWidget(QLabel(msg))
-
-        grid = QGridLayout()
-        grid.setSpacing(8)
-
-        if wallet.use_encryption:
-            grid.addWidget(QLabel(_('Password')), 1, 0)
-            grid.addWidget(pw, 1, 1)
-
-        grid.addWidget(QLabel(_('New Password')), 2, 0)
-        grid.addWidget(new_pw, 2, 1)
-
-        grid.addWidget(QLabel(_('Confirm Password')), 3, 0)
-        grid.addWidget(conf_pw, 3, 1)
-        vbox.addLayout(grid)
-
-        vbox.addLayout(ok_cancel_buttons(d))
-        d.setLayout(vbox) 
-
-        if not d.exec_(): return
-
-        password = unicode(pw.text()) if wallet.use_encryption else None
-        new_password = unicode(new_pw.text())
-        new_password2 = unicode(conf_pw.text())
-
-        try:
-            seed = wallet.decode_seed(password)
-        except:
-            QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
-            return
-
-        if new_password != new_password2:
-            QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
-            return ElectrumWindow.change_password_dialog(wallet, parent) # Retry
-
-        try:
-            wallet.update_password(seed, password, new_password)
-        except:
-            QMessageBox.warning(parent, _('Error'), _('Failed to update password'), _('OK'))
-            return
-
-        QMessageBox.information(parent, _('Success'), _('Password was updated successfully'), _('OK'))
-
-        if parent: 
-            icon = QIcon(":icons/lock.png") if wallet.use_encryption else QIcon(":icons/unlock.png")
-            parent.password_button.setIcon( icon )
-
-
 
     def generate_transaction_information_widget(self, tx):
         tabs = QTabWidget(self)
@@ -1697,6 +1835,47 @@ class ElectrumWindow(QMainWindow):
         if tx_dict: 
             self.create_process_transaction_window(tx_dict)
 
+    def do_process_from_csvReader(self, csvReader):
+        outputs = []
+        try:
+            for row in csvReader:
+                address = row[0]
+                amount = float(row[1])
+                amount = int(100000000*amount)
+                outputs.append((address, amount))
+        except (ValueError, IOError, os.error), reason:
+            QMessageBox.critical(None,"Unable to read file or no transaction found", _("Electrum was unable to open your transaction file") + "\n" + str(reason))
+            return
+
+        try:
+            tx = self.wallet.make_unsigned_transaction(outputs, None, None, account=self.current_account)
+        except BaseException, e:
+            self.show_message(str(e))
+            return
+
+        tx_dict = tx.as_dict()
+        self.create_process_transaction_window(tx_dict)
+
+    def do_process_from_csv_file(self):
+        fileName = self.getOpenFileName(_("Select your transaction CSV"), "*.csv")
+        if not fileName:
+            return
+        try:
+            with open(fileName, "r") as f:
+                csvReader = csv.reader(f)
+                self.do_process_from_csvReader(csvReader)
+        except (ValueError, IOError, os.error), reason:
+            QMessageBox.critical(None,"Unable to read file or no transaction found", _("Electrum was unable to open your transaction file") + "\n" + str(reason))
+            return
+
+    def do_process_from_csv_text(self):
+        text = text_dialog(self, _('Input CSV'), _("CSV:"), _("Load CSV"))
+        if not text:
+            return
+        f = StringIO.StringIO(text)
+        csvReader = csv.reader(f)
+        self.do_process_from_csvReader(csvReader)
+
     def create_process_transaction_window(self, tx_dict):
         tx = Transaction(tx_dict["hex"])
             
@@ -1843,7 +2022,7 @@ class ElectrumWindow(QMainWindow):
         nz_label = QLabel(_('Display zeros'))
         grid_ui.addWidget(nz_label, 0, 0)
         nz_e = AmountEdit(None,True)
-        nz_e.setText("%d"% self.wallet.num_zeros)
+        nz_e.setText("%d"% self.num_zeros)
         grid_ui.addWidget(nz_e, 0, 1)
         msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
         grid_ui.addWidget(HelpButton(msg), 0, 2)
@@ -1902,7 +2081,7 @@ class ElectrumWindow(QMainWindow):
         fee_e.setText(self.format_amount(self.wallet.fee).strip())
         grid_wallet.addWidget(fee_e, 0, 2)
         msg = _('Fee per kilobyte of transaction.') + ' ' \
-            + _('Recommended value') + ': ' + self.format_amount(20000)
+            + _('Recommended value') + ': ' + self.format_amount(50000)
         grid_wallet.addWidget(HelpButton(msg), 0, 3)
         if not self.config.is_modifiable('fee_per_kb'):
             for w in [fee_e, fee_label]: w.setEnabled(False)
@@ -1941,43 +2120,6 @@ class ElectrumWindow(QMainWindow):
                                              + _(' This settings affects the fields in the Send tab')+' '), 3, 3)
         grid_wallet.setRowStretch(4,1)
 
-
-        # import/export tab
-        tab3 = QWidget()
-        grid_io = QGridLayout(tab3)
-        grid_io.setColumnStretch(0,1)
-        tabs.addTab(tab3, _('Import/Export') )
-        
-        grid_io.addWidget(QLabel(_('Labels')), 1, 0)
-        grid_io.addWidget(EnterButton(_("Export"), self.do_export_labels), 1, 1)
-        grid_io.addWidget(EnterButton(_("Import"), self.do_import_labels), 1, 2)
-        grid_io.addWidget(HelpButton(_('Export your labels as json')), 1, 3)
-
-        grid_io.addWidget(QLabel(_('History')), 2, 0)
-        grid_io.addWidget(EnterButton(_("Export"), self.do_export_history), 2, 1)
-        grid_io.addWidget(HelpButton(_('Export your transaction history as csv')), 2, 3)
-
-        grid_io.addWidget(QLabel(_('Private keys')), 3, 0)
-
-        grid_io.addWidget(EnterButton(_("Export"), self.do_export_privkeys), 3, 1)
-        grid_io.addWidget(EnterButton(_("Import"), self.do_import_privkey), 3, 2)
-        grid_io.addWidget(HelpButton(_('Import private key')), 3, 3)
-
-        grid_io.addWidget(QLabel(_('Master Public Key')), 4, 0)
-        grid_io.addWidget(EnterButton(_("Show"), self.show_master_public_key), 4, 1)
-        grid_io.addWidget(HelpButton(_('Your Master Public Key can be used to create receiving addresses, but not to sign transactions.') + ' ' \
-                              + _('If you give it to someone, they will be able to see your transactions, but not to spend your money.') + ' ' \
-                              + _('If you restore your wallet from it, a watching-only (deseeded) wallet will be created.')), 4, 3)
-
-
-        grid_io.addWidget(QLabel(_("Load transaction")), 5, 0)
-        grid_io.addWidget(EnterButton(_("From file"), self.do_process_from_file), 5, 1)
-        grid_io.addWidget(EnterButton(_("From text"), self.do_process_from_text), 5, 2)
-        grid_io.addWidget(HelpButton(_("This will give you the option to sign or broadcast a transaction based on it's status.")), 5, 3)
-
-        grid_io.setRowStretch(6,1)
-
-
         # plugins
         if self.plugins:
             tab5 = QScrollArea()
@@ -1990,7 +2132,6 @@ class ElectrumWindow(QMainWindow):
             w = QWidget()
             w.setLayout(grid_plugins)
             tab5.setWidget(w)
-            tab5.setMaximumSize(tab3.size())  # optional
 
             w.setMinimumHeight(len(self.plugins)*35)
 
@@ -1999,15 +2140,14 @@ class ElectrumWindow(QMainWindow):
                 return lambda: cb.setChecked(p.toggle())
             for i, p in enumerate(self.plugins):
                 try:
-                    name, description = p.get_info()
-                    cb = QCheckBox(name)
+                    cb = QCheckBox(p.fullname())
                     cb.setDisabled(not p.is_available())
                     cb.setChecked(p.is_enabled())
                     cb.clicked.connect(mk_toggle(cb,p))
                     grid_plugins.addWidget(cb, i, 0)
                     if p.requires_settings():
                         grid_plugins.addWidget(EnterButton(_('Settings'), p.settings_dialog), i, 1)
-                    grid_plugins.addWidget(HelpButton(description), i, 2)
+                    grid_plugins.addWidget(HelpButton(p.description()), i, 2)
                 except:
                     print_msg("Error: cannot display plugin", p)
                     traceback.print_exc(file=sys.stdout)
@@ -2038,8 +2178,8 @@ class ElectrumWindow(QMainWindow):
             QMessageBox.warning(self, _('Error'), _('Invalid value')+':%s'%nz, _('OK'))
             return
 
-        if self.wallet.num_zeros != nz:
-            self.wallet.num_zeros = nz
+        if self.num_zeros != nz:
+            self.num_zeros = nz
             self.config.set_key('num_zeros', nz, True)
             self.update_history_tab()
             self.update_receive_tab()
@@ -2096,135 +2236,58 @@ class ElectrumWindow(QMainWindow):
         g = self.geometry()
         self.config.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()], True)
         self.save_column_widths()
-        self.config.set_key("console-history",self.console.history[-50:])
+        self.config.set_key("console-history", self.console.history[-50:], True)
         event.accept()
 
+class OpenFileEventFilter(QObject):
+    def __init__(self, windows):
+        self.windows = windows
+        super(OpenFileEventFilter, self).__init__()
+
+    def eventFilter(self, obj, event):
+        if event.type() == QtCore.QEvent.FileOpen:
+            if len(self.windows) >= 1:
+                self.windows[0].set_url(event.url().toString())
+                return True
+        return False
 
 
 
 
 class ElectrumGui:
 
-    def __init__(self, wallet, config, app=None):
-        self.wallet = wallet
+    def __init__(self, config, network, app=None):
+        self.network = network
+        #self.interface = interface
         self.config = config
+        #self.blockchain = network.blockchain
+        self.windows = []
+        self.efilter = OpenFileEventFilter(self.windows)
         if app is None:
             self.app = QApplication(sys.argv)
+        self.app.installEventFilter(self.efilter)
 
 
-    def restore_or_create(self):
-        msg = _("Wallet file not found.")+"\n"+_("Do you want to create a new wallet, or to restore an existing one?")
-        r = QMessageBox.question(None, _('Message'), msg, _('Create'), _('Restore'), _('Cancel'), 0, 2)
-        if r==2: return None
-        return 'restore' if r==1 else 'create'
+    def main(self, url):
 
-
-    def verify_seed(self):
-        r = self.seed_dialog(False)
-        if r != self.wallet.seed:
-            QMessageBox.warning(None, _('Error'), 'incorrect seed', 'OK')
-            return False
+        storage = WalletStorage(self.config)
+        if not storage.file_exists:
+            import installwizard
+            wizard = installwizard.InstallWizard(self.config, self.interface, self.blockchain, storage)
+            wallet = wizard.run()
+            if not wallet: 
+                exit()
         else:
-            return True
-        
+            wallet = Wallet(storage)
 
+        wallet.start_threads(self.network)
 
-    def seed_dialog(self, is_restore=True):
-        d = QDialog()
-        d.setModal(1)
-
-        vbox = QVBoxLayout()
-        if is_restore:
-            msg = _("Please enter your wallet seed (or your master public key if you want to create a watching-only wallet)." + ' ')
-        else:
-            msg = _("Your seed is important! To make sure that you have properly saved your seed, please type it here." + ' ')
-
-        msg += _("Your seed can be entered as a sequence of words, or as a hexadecimal string."+ '\n')
-        
-        label=QLabel(msg)
-        label.setWordWrap(True)
-        vbox.addWidget(label)
-
-        seed_e = QTextEdit()
-        seed_e.setMaximumHeight(100)
-        vbox.addWidget(seed_e)
-
-        if is_restore:
-            grid = QGridLayout()
-            grid.setSpacing(8)
-            gap_e = AmountEdit(None, True)
-            gap_e.setText("5")
-            grid.addWidget(QLabel(_('Gap limit')), 2, 0)
-            grid.addWidget(gap_e, 2, 1)
-            grid.addWidget(HelpButton(_('Keep the default value unless you modified this parameter in your wallet.')), 2, 3)
-            vbox.addLayout(grid)
-
-        vbox.addLayout(ok_cancel_buttons(d))
-        d.setLayout(vbox) 
-
-        if not d.exec_(): return
-
-        try:
-            seed = str(seed_e.toPlainText())
-            seed.decode('hex')
-        except:
-            try:
-                seed = mnemonic.mn_decode( seed.split() )
-            except:
-                QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK'))
-                return
-
-        if not seed:
-            QMessageBox.warning(None, _('Error'), _('No seed'), _('OK'))
-            return
-
-        if not is_restore:
-            return seed
-        else:
-            try:
-                gap = int(unicode(gap_e.text()))
-            except:
-                QMessageBox.warning(None, _('Error'), 'error', 'OK')
-                return
-            return seed, gap
-
-
-    def network_dialog(self):
-        return NetworkDialog(self.wallet.interface, self.config, None).do_exec()
-        
-
-    def show_seed(self):
-        ElectrumWindow.show_seed(self.wallet.seed, self.wallet.imported_keys)
-
-    def password_dialog(self):
-        if self.wallet.seed:
-            ElectrumWindow.change_password_dialog(self.wallet)
-
-
-    def restore_wallet(self):
-        wallet = self.wallet
-        # wait until we are connected, because the user might have selected another server
-        if not wallet.interface.is_connected:
-            waiting = lambda: False if wallet.interface.is_connected else "%s \n" % (_("Connecting..."))
-            waiting_dialog(waiting)
-
-        waiting = lambda: False if wallet.is_up_to_date() else "%s\n%s %d\n%s %.1f"\
-            %(_("Please wait..."),_("Addresses generated:"),len(wallet.addresses(True)),_("Kilobytes received:"), wallet.interface.bytes_received/1024.)
-
-        wallet.set_up_to_date(False)
-        wallet.interface.poke('synchronizer')
-        waiting_dialog(waiting)
-        if wallet.is_found():
-            print_error( "Recovery successful" )
-        else:
-            QMessageBox.information(None, _('Error'), _("No transactions found for this seed"), _('OK'))
-
-        return True
-
-    def main(self,url):
         s = Timer()
         s.start()
-        w = ElectrumWindow(self.wallet, self.config)
+        w = ElectrumWindow(self.config)
+        w.load_wallet(wallet)
+
+        self.windows.append(w)
         if url: w.set_url(url)
         w.app = self.app
         w.connect_slots(s)
@@ -2233,4 +2296,6 @@ class ElectrumGui:
 
         self.app.exec_()
 
+        wallet.stop_threads()
+