add switch-gui button for qt
[electrum-nvc.git] / lib / gui_lite.py
index 90b5fe1..dbf9291 100644 (file)
@@ -1,15 +1,21 @@
 import sys
 
+# Let's do some dep checking and handle missing ones gracefully
 try:
-  from PyQt4.QtCore import *
-  from PyQt4.QtGui import *
+    from PyQt4.QtCore import *
+    from PyQt4.QtGui import *
+    import PyQt4.QtCore as QtCore
+
 except ImportError:
-  print "You need to have PyQT installed to run Electrum"
-  print "If you have pip installed try 'sudo pip install pyqt' if you are on Debian/Ubuntu try 'sudo apt-get install python-qt4'"
-  sys.exit(1)
+    print "You need to have PyQT installed to run Electrum in graphical mode."
+    print "If you have pip installed try 'sudo pip install pyqt' if you are on Debian/Ubuntu try 'sudo apt-get install python-qt4'."
+    sys.exit(0)
+
+
+
 
 from decimal import Decimal as D
-from util import appdata_dir, get_resource_path as rsrc
+from util import get_resource_path as rsrc
 from i18n import _
 import decimal
 import exchange_rate
@@ -20,11 +26,15 @@ import time
 import wallet
 import webbrowser
 import history_widget
+import receiving_widget
+import util
+import csv 
+import datetime
 
-try:
-    import lib.gui_qt as gui_qt
-except ImportError:
-    import electrum.gui_qt as gui_qt
+from version import ELECTRUM_VERSION as electrum_version
+from wallet import format_satoshis
+import gui_qt
+import shutil
 
 bitcoin = lambda v: v * 100000000
 
@@ -45,56 +55,131 @@ def resize_line_edit_width(line_edit, text_input):
     text_input += "A"
     line_edit.setMinimumWidth(metrics.width(text_input))
 
-def cd_data_dir():
-    assert sys.argv
-    prefix_path = os.path.dirname(sys.argv[0])
-    local_data = os.path.join(prefix_path, "data")
-    if os.path.exists(os.path.join(local_data, "style.css")):
-        data_dir = local_data
-    else:
-        data_dir = appdata_dir()
-    QDir.setCurrent(data_dir)
+def load_theme_name(theme_path):
+    try:
+        with open(os.path.join(theme_path, "name.cfg")) as name_cfg_file:
+            return name_cfg_file.read().rstrip("\n").strip()
+    except IOError:
+        return None
+
+
+def theme_dirs_from_prefix(prefix):
+    if not os.path.exists(prefix):
+        return []
+    theme_paths = {}
+    for potential_theme in os.listdir(prefix):
+        theme_full_path = os.path.join(prefix, potential_theme)
+        theme_css = os.path.join(theme_full_path, "style.css")
+        if not os.path.exists(theme_css):
+            continue
+        theme_name = load_theme_name(theme_full_path)
+        if theme_name is None:
+            continue
+        theme_paths[theme_name] = prefix, potential_theme
+    return theme_paths
+
+def load_theme_paths():
+    theme_paths = {}
+    prefixes = (util.local_data_dir(), util.appdata_dir())
+    for prefix in prefixes:
+        theme_paths.update(theme_dirs_from_prefix(prefix))
+    return theme_paths
+
+
+def csv_transaction(wallet):
+    try:
+        fileName = QFileDialog.getSaveFileName(QWidget(), 'Select file to export your wallet transactions to', os.path.expanduser('~/'), "*.csv")
+        if fileName:
+            with open(fileName, "w+") as csvfile:
+                transaction = csv.writer(csvfile)
+                transaction.writerow(["transaction_hash","label", "confirmations", "value", "fee", "balance", "timestamp"])
+                for item in wallet.get_tx_history():
+                    tx_hash, confirmations, is_mine, value, fee, balance, timestamp = item
+                    if confirmations:
+                        if timestamp is not None:
+                            try:
+                                time_string = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
+                            except [RuntimeError, TypeError, NameError] as reason:
+                                time_string = "unknown"
+                                pass
+                        else:
+                          time_string = "unknown"
+                    else:
+                        time_string = "pending"
+
+                    if value is not None:
+                        value_string = format_satoshis(value, True, wallet.num_zeros)
+                    else:
+                        value_string = '--'
+
+                    if fee is not None:
+                        fee_string = format_satoshis(fee, True, wallet.num_zeros)
+                    else:
+                        fee_string = '0'
+
+                    if tx_hash:
+                        label, is_default_label = wallet.get_label(tx_hash)
+                    else:
+                      label = ""
+
+                    balance_string = format_satoshis(balance, False, wallet.num_zeros)
+                    transaction.writerow([tx_hash, label, confirmations, value_string, fee_string, balance_string, time_string])
+                QMessageBox.information(None,"CSV Export created", "Your CSV export has been succesfully created.")
+    except (IOError, os.error), reason:
+        QMessageBox.critical(None,"Unable to create csv", "Electrum was unable to produce a transaction export.\n" + str(reason))
+
+
+class ElectrumGui(QObject):
+
+    def __init__(self, wallet, config, expert=None):
+        super(QObject, self).__init__()
 
-class ElectrumGui:
+        self.wallet = wallet
+        self.config = config
+        self.check_qt_version()
+        self.expert = expert
+        if self.expert != None:
+            self.app = self.expert.app
+        else:
+            self.app = QApplication(sys.argv)
 
-    def __init__(self, wallet):
+    def check_qt_version(self):
         qtVersion = qVersion()
         if not(int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7):
-          print "Sorry, Electrum requires Qt >= 4.7 to run"
-          print "Check your distributions packages or download it at http://qt.nokia.com/downloads"
-          sys.exit(0)
-          
-        self.wallet = wallet
-        self.app = QApplication(sys.argv)
-        # Should probably not modify the current path but instead
-        # change the behaviour of rsrc(...)
-        self.old_path = QDir.currentPath()
-        cd_data_dir()
-        with open(rsrc("style.css")) as style_file:
-            self.app.setStyleSheet(style_file.read())
+            app = QApplication(sys.argv)
+            QMessageBox.warning(None,"Could not start Lite GUI.", "Electrum was unable to load the 'Lite GUI' because it needs Qt version >= 4.7.\nChanging your config to use the 'Classic' GUI")
+            self.config.set_key('gui','classic',True)
+            sys.exit(0)
+
 
     def main(self, url):
         actuator = MiniActuator(self.wallet)
-        self.mini = MiniWindow(actuator, self.expand)
+        # Should probably not modify the current path but instead
+        # change the behaviour of rsrc(...)
+        old_path = QDir.currentPath()
+        actuator.load_theme()
+
+        self.mini = MiniWindow(actuator, self.expand, self.config)
         driver = MiniDriver(self.wallet, self.mini)
 
         # Reset path back to original value now that loading the GUI
         # is completed.
-        QDir.setCurrent(self.old_path)
+        QDir.setCurrent(old_path)
 
         if url:
             self.set_url(url)
-
-        timer = Timer()
-        timer.start()
-        self.expert = gui_qt.ElectrumWindow(self.wallet)
-        self.expert.app = self.app
-        self.expert.connect_slots(timer)
-        self.expert.update_wallet()
-
-        self.app.exec_()
+            
+        if self.expert == None:
+            timer = Timer()
+            timer.start()
+            self.expert = gui_qt.ElectrumWindow(self.wallet, self.config)
+            self.expert.app = self.app
+            self.expert.connect_slots(timer)
+            self.expert.update_wallet()
+            self.app.exec_()
 
     def expand(self):
+        """Hide the lite mode window and show pro-mode."""
         self.mini.hide()
         self.expert.show()
 
@@ -116,15 +201,50 @@ class ElectrumGui:
         qt_gui_object = gui_qt.ElectrumGui(self.wallet, self.app)
         return qt_gui_object.restore_or_create()
 
+class TransactionWindow(QDialog):
+
+    def set_label(self):
+        label = unicode(self.label_edit.text())
+        self.parent.wallet.labels[self.tx_id] = label
+
+        super(TransactionWindow, self).accept() 
+
+    def __init__(self, transaction_id, parent):
+        super(TransactionWindow, self).__init__()
+
+        self.tx_id = str(transaction_id)
+        self.parent = parent
+
+        self.setModal(True)
+        self.resize(200,100)
+        self.setWindowTitle("Transaction successfully sent")
+
+        self.layout = QGridLayout(self)
+        self.layout.addWidget(QLabel("Your transaction has been sent.\nPlease enter a label for this transaction for future reference."))
+
+        self.label_edit = QLineEdit()
+        self.label_edit.setPlaceholderText(_("Transaction label"))
+        self.label_edit.setObjectName("label_input")
+        self.label_edit.setAttribute(Qt.WA_MacShowFocusRect, 0)
+        self.label_edit.setFocusPolicy(Qt.ClickFocus)
+        self.layout.addWidget(self.label_edit)
+
+        self.save_button = QPushButton(_("Save"))
+        self.layout.addWidget(self.save_button)
+        self.save_button.clicked.connect(self.set_label)
+
+        self.exec_()
+
 class MiniWindow(QDialog):
 
-    def __init__(self, actuator, expand_callback):
+    def __init__(self, actuator, expand_callback, config):
         super(MiniWindow, self).__init__()
+        tx = "e08115d0f7819aee65b9d24f81ef9d46eb62bb67ddef5318156cbc3ceb7b703e"
 
         self.actuator = actuator
-
+        self.config = config
         self.btc_balance = None
-        self.quote_currencies = ["EUR", "USD", "GBP"]
+        self.quote_currencies = ["BRL", "CNY", "EUR", "GBP", "RUB", "USD"]
         self.actuator.set_configured_currency(self.set_quote_currency)
         self.exchanger = exchange_rate.Exchanger(self)
         # Needed because price discovery is done in a different thread
@@ -134,18 +254,15 @@ class MiniWindow(QDialog):
         self.balance_label = BalanceLabel(self.change_quote_currency)
         self.balance_label.setObjectName("balance_label")
 
-        self.receive_button = QPushButton(_("&Receive"))
-        self.receive_button.setObjectName("receive_button")
-        self.receive_button.setDefault(True)
-        self.receive_button.clicked.connect(self.copy_address)
 
         # Bitcoin address code
         self.address_input = QLineEdit()
-        self.address_input.setPlaceholderText(_("Enter a Bitcoin address..."))
+        self.address_input.setPlaceholderText(_("Enter a Bitcoin address or contact"))
         self.address_input.setObjectName("address_input")
 
+        self.address_input.setFocusPolicy(Qt.ClickFocus)
 
-        self.address_input.textEdited.connect(self.address_field_changed)
+        self.address_input.textChanged.connect(self.address_field_changed)
         resize_line_edit_width(self.address_input,
                                "1BtaFUr3qVvAmwrsuDuu5zk6e4s2rxd2Gy")
 
@@ -161,6 +278,8 @@ class MiniWindow(QDialog):
         self.amount_input = QLineEdit()
         self.amount_input.setPlaceholderText(_("... and amount"))
         self.amount_input.setObjectName("amount_input")
+
+        self.amount_input.setFocusPolicy(Qt.ClickFocus)
         # This is changed according to the user's displayed balance
         self.amount_validator = QDoubleValidator(self.amount_input)
         self.amount_validator.setNotation(QDoubleValidator.StandardNotation)
@@ -177,12 +296,17 @@ class MiniWindow(QDialog):
         self.send_button.setDisabled(True);
         self.send_button.clicked.connect(self.send)
 
+        # Creating the receive button
+        self.receive_button = QPushButton(_("&Receive"))
+        self.receive_button.setObjectName("receive_button")
+        self.receive_button.setDefault(True)
+
         main_layout = QGridLayout(self)
 
         main_layout.addWidget(self.balance_label, 0, 0)
         main_layout.addWidget(self.receive_button, 0, 1)
 
-        main_layout.addWidget(self.address_input, 1, 0, 1, -1)
+        main_layout.addWidget(self.address_input, 1, 0)
 
         main_layout.addWidget(self.amount_input, 2, 0)
         main_layout.addWidget(self.send_button, 2, 1)
@@ -191,29 +315,86 @@ class MiniWindow(QDialog):
         self.history_list.setObjectName("history")
         self.history_list.hide()
         self.history_list.setAlternatingRowColors(True)
-        main_layout.addWidget(self.history_list, 3, 0, 1, -1)
 
+        main_layout.addWidget(self.history_list, 3, 0, 1, 2)
+        
+
+        self.receiving = receiving_widget.ReceivingWidget(self)
+        self.receiving.setObjectName("receiving")
+
+        # Add to the right side 
+        self.receiving_box = QGroupBox(_("Select a receiving address"))
+        extra_layout = QGridLayout()
+
+        # Checkbox to filter used addresses
+        hide_used = QCheckBox(_('Hide used addresses'))
+        hide_used.setChecked(True)
+        hide_used.stateChanged.connect(self.receiving.toggle_used)
+
+        # Events for receiving addresses
+        self.receiving.clicked.connect(self.receiving.copy_address)
+        self.receiving.itemDoubleClicked.connect(self.receiving.edit_label)
+        self.receiving.itemChanged.connect(self.receiving.update_label)
+
+        # Label
+        extra_layout.addWidget( QLabel(_('Selecting an address will copy it to the clipboard.\nDouble clicking the label will allow you to edit it.') ),0,0)
+
+        extra_layout.addWidget(self.receiving, 1,0)
+        extra_layout.addWidget(hide_used, 2,0)
+        extra_layout.setColumnMinimumWidth(0,200)
+
+        self.receiving_box.setLayout(extra_layout)
+        main_layout.addWidget(self.receiving_box,0,3,-1,3)
+        self.receiving_box.hide()
+
+        self.receive_button.clicked.connect(self.toggle_receiving_layout)
+
+        # Creating the menu bar
         menubar = QMenuBar()
         electrum_menu = menubar.addMenu(_("&Bitcoin"))
-        self.servers_menu = electrum_menu.addMenu(_("&Servers"))
-        self.servers_menu.addAction(_("Foo"))
+
         electrum_menu.addSeparator()
-        brain_seed = electrum_menu.addAction(_("&BrainWallet Info"))
-        brain_seed.triggered.connect(self.actuator.show_seed_dialog)
-        electrum_menu.addAction(_("&Quit"))
+
+        quit_option = electrum_menu.addAction(_("&Quit"))
+        quit_option.triggered.connect(self.close)
 
         view_menu = menubar.addMenu(_("&View"))
-        expert_gui = view_menu.addAction(_("&Pro Mode"))
+        extra_menu = menubar.addMenu(_("&Extra"))
+
+        backup_wallet = extra_menu.addAction( _("&Create wallet backup"))
+        backup_wallet.triggered.connect(self.backup_wallet)
+
+        export_csv = extra_menu.addAction( _("&Export transactions to CSV") )
+        export_csv.triggered.connect(lambda: csv_transaction(self.wallet))
+        
+        master_key = extra_menu.addAction( _("Copy master public key to clipboard") ) 
+        master_key.triggered.connect(self.actuator.copy_master_public_key)
+
+        expert_gui = view_menu.addAction(_("&Classic GUI"))
         expert_gui.triggered.connect(expand_callback)
-        view_menu.addMenu(_("&Themes"))
+        themes_menu = view_menu.addMenu(_("&Themes"))
+        selected_theme = self.actuator.selected_theme()
+        theme_group = QActionGroup(self)
+        for theme_name in self.actuator.theme_names():
+            theme_action = themes_menu.addAction(theme_name)
+            theme_action.setCheckable(True)
+            if selected_theme == theme_name:
+                theme_action.setChecked(True)
+            class SelectThemeFunctor:
+                def __init__(self, theme_name, toggle_theme):
+                    self.theme_name = theme_name
+                    self.toggle_theme = toggle_theme
+                def __call__(self, checked):
+                    if checked:
+                        self.toggle_theme(self.theme_name)
+            delegate = SelectThemeFunctor(theme_name, self.toggle_theme)
+            theme_action.toggled.connect(delegate)
+            theme_group.addAction(theme_action)
         view_menu.addSeparator()
         show_history = view_menu.addAction(_("Show History"))
         show_history.setCheckable(True)
         show_history.toggled.connect(self.show_history)
 
-        settings_menu = menubar.addMenu(_("&Settings"))
-        settings_menu.addAction(_("&Configure Electrum"))
-        
         help_menu = menubar.addMenu(_("&Help"))
         the_website = help_menu.addAction(_("&Website"))
         the_website.triggered.connect(self.the_website)
@@ -223,24 +404,54 @@ class MiniWindow(QDialog):
         show_about = help_menu.addAction(_("&About"))
         show_about.triggered.connect(self.show_about)
         main_layout.setMenuBar(menubar)
+        self.main_layout = main_layout
 
         quit_shortcut = QShortcut(QKeySequence("Ctrl+Q"), self)
         quit_shortcut.activated.connect(self.close)
         close_shortcut = QShortcut(QKeySequence("Ctrl+W"), self)
         close_shortcut.activated.connect(self.close)
 
+        g = self.config.get("winpos-lite",[4, 25, 351, 149])
+        self.setGeometry(g[0], g[1], g[2], g[3])
+
+        show_hist = self.config.get("gui_show_history",False)
+        show_history.setChecked(show_hist)
+        self.show_history(show_hist)
+        
         self.setWindowIcon(QIcon(":electrum.png"))
         self.setWindowTitle("Electrum")
         self.setWindowFlags(Qt.Window|Qt.MSWindowsFixedSizeDialogHint)
         self.layout().setSizeConstraint(QLayout.SetFixedSize)
         self.setObjectName("main_window")
         self.show()
-    
-    def recompute_style(self):
+
+    def toggle_receiving_layout(self):
+        if self.receiving_box.isVisible():
+            self.receiving_box.hide()
+            self.receive_button.setProperty("isActive", False)
+
+            qApp.style().unpolish(self.receive_button)
+            qApp.style().polish(self.receive_button)
+        else:
+            self.receiving_box.show()
+            self.receive_button.setProperty("isActive", 'true')
+
+            qApp.style().unpolish(self.receive_button)
+            qApp.style().polish(self.receive_button)
+
+    def toggle_theme(self, theme_name):
+        old_path = QDir.currentPath()
+        self.actuator.change_theme(theme_name)
+        # Recompute style globally
         qApp.style().unpolish(self)
         qApp.style().polish(self)
+        QDir.setCurrent(old_path)
 
     def closeEvent(self, event):
+        g = self.geometry()
+        self.config.set_key("winpos-lite", [g.left(),g.top(),g.width(),g.height()],True)
+        self.config.set_key("gui_show_history", self.history_list.isVisible(),True)
+        
         super(MiniWindow, self).closeEvent(event)
         qApp.quit()
 
@@ -256,9 +467,10 @@ class MiniWindow(QDialog):
         pass
 
     def set_quote_currency(self, currency):
+        """Set and display the fiat currency country."""
         assert currency in self.quote_currencies
         self.quote_currencies.remove(currency)
-        self.quote_currencies = [currency] + self.quote_currencies
+        self.quote_currencies.insert(0, currency)
         self.refresh_balance()
 
     def change_quote_currency(self):
@@ -276,15 +488,17 @@ class MiniWindow(QDialog):
         self.amount_input_changed(self.amount_input.text())
 
     def set_balances(self, btc_balance):
+        """Set the bitcoin balance and update the amount label accordingly."""
         self.btc_balance = btc_balance
         quote_text = self.create_quote_text(btc_balance)
         if quote_text:
             quote_text = "(%s)" % quote_text
         btc_balance = "%.2f" % (btc_balance / bitcoin(1))
         self.balance_label.set_balance_text(btc_balance, quote_text)
-        self.setWindowTitle("Electrum - %s BTC" % btc_balance)
+        self.setWindowTitle("Electrum %s - %s BTC" % (electrum_version, btc_balance))
 
     def amount_input_changed(self, amount_text):
+        """Update the number of bitcoins displayed."""
         self.check_button_status()
 
         try:
@@ -300,6 +514,8 @@ class MiniWindow(QDialog):
                 self.balance_label.show_balance()
 
     def create_quote_text(self, btc_balance):
+        """Return a string copy of the amount fiat currency the 
+        user has in bitcoins."""
         quote_currency = self.quote_currencies[0]
         quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
         if quote_balance is None:
@@ -316,13 +532,28 @@ class MiniWindow(QDialog):
             self.amount_input.setText("")
 
     def check_button_status(self):
+        """Check that the bitcoin address is valid and that something
+        is entered in the amount before making the send button clickable."""
+        try:
+            value = D(str(self.amount_input.text())) * 10**8
+        except decimal.InvalidOperation:
+            value = None
+        # self.address_input.property(...) returns a qVariant, not a bool.
+        # The == is needed to properly invoke a comparison.
         if (self.address_input.property("isValid") == True and
-            len(self.amount_input.text()) > 0):
+            value is not None and 0 < value <= self.btc_balance):
             self.send_button.setDisabled(False)
         else:
             self.send_button.setDisabled(True)
 
     def address_field_changed(self, address):
+        # label or alias, with address in brackets
+        match2 = re.match("(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>",
+                          address)
+        if match2:
+          address = match2.group(2)
+          self.address_input.setText(address)
+
         if self.actuator.is_valid(address):
             self.check_button_status()
             self.address_input.setProperty("isValid", True)
@@ -346,12 +577,19 @@ class MiniWindow(QDialog):
 
     def update_completions(self, completions):
         self.address_completions.setStringList(completions)
 
     def update_history(self, tx_history):
-        for tx in tx_history[-10:]:
-            address = tx["default_label"]
-            amount = D(tx["value"]) / 10**8
-            self.history_list.append(address, amount)
+        from util import format_satoshis, age
+
+        self.history_list.empty()
+
+        for item in tx_history[-10:]:
+            tx_hash, conf, is_mine, value, fee, balance, timestamp = item
+            label = self.actuator.wallet.get_label(tx_hash)[0]
+            #amount = D(value) / 10**8
+            v_str = format_satoshis(value, True)
+            self.history_list.append(label, v_str, age(timestamp))
 
     def acceptbit(self):
         self.actuator.acceptbit(self.quote_currencies[0])
@@ -361,18 +599,33 @@ class MiniWindow(QDialog):
 
     def show_about(self):
         QMessageBox.about(self, "Electrum",
-            _("Electrum's focus is speed, with low resource usage and simplifying Bitcoin. You do not need to perform regular backups, because your wallet can be recovered from a secret phrase that you can memorize or write on paper. Startup times are instant because it operates in conjuction with high-performance servers that handle the most complicated parts of the Bitcoin system.\n\nSend donations to 1JwTMv4GWaPdf931N6LNPJeZBfZgZJ3zX1"))
+            _("Electrum's focus is speed, with low resource usage and simplifying Bitcoin. You do not need to perform regular backups, because your wallet can be recovered from a secret phrase that you can memorize or write on paper. Startup times are instant because it operates in conjuction with high-performance servers that handle the most complicated parts of the Bitcoin system."))
 
     def show_report_bug(self):
         QMessageBox.information(self, "Electrum - " + _("Reporting Bugs"),
-            _("Email bug reports to %s") % "genjix" + "@" + "riseup.net")
+            _("Please report any bugs as issues on github: <a href=\"https://github.com/spesmilo/electrum/issues\">https://github.com/spesmilo/electrum/issues</a>"))
 
     def show_history(self, toggle_state):
         if toggle_state:
+            self.main_layout.setRowMinimumHeight(3,200)
             self.history_list.show()
         else:
+            self.main_layout.setRowMinimumHeight(3,0)
             self.history_list.hide()
 
+    def backup_wallet(self):
+        try:
+          folderName = QFileDialog.getExistingDirectory(QWidget(), 'Select folder to save a copy of your wallet to', os.path.expanduser('~/'))
+          if folderName:
+            sourceFile = util.user_dir() + '/electrum.dat'
+            shutil.copy2(sourceFile, str(folderName))
+            QMessageBox.information(None,"Wallet backup created", "A copy of your wallet file was created in '%s'" % str(folderName))
+        except (IOError, os.error), reason:
+          QMessageBox.critical(None,"Unable to create backup", "Electrum was unable copy your wallet file to the specified location.\n" + str(reason))
+
+
+
+
 class BalanceLabel(QLabel):
 
     SHOW_CONNECTING = 1
@@ -387,10 +640,12 @@ class BalanceLabel(QLabel):
         self.amount_text = ""
 
     def mousePressEvent(self, event):
+        """Change the fiat currency selection if window background is clicked."""
         if self.state != self.SHOW_CONNECTING:
             self.change_quote_currency()
 
     def set_balance_text(self, btc_balance, quote_text):
+        """Set the amount of bitcoins in the gui."""
         if self.state == self.SHOW_CONNECTING:
             self.state = self.SHOW_BALANCE
         self.balance_text = "<span style='font-size: 18pt'>%s</span> <span style='font-size: 10pt'>BTC</span> <span style='font-size: 10pt'>%s</span>" % (btc_balance, quote_text)
@@ -468,7 +723,8 @@ class ReceivePopup(QDialog):
 
         self.setMouseTracking(True)
         self.setWindowTitle("Electrum - " + _("Receive Bitcoin payment"))
-        self.setWindowFlags(Qt.Window|Qt.FramelessWindowHint|Qt.MSWindowsFixedSizeDialogHint)
+        self.setWindowFlags(Qt.Window|Qt.FramelessWindowHint|
+                            Qt.MSWindowsFixedSizeDialogHint)
         self.layout().setSizeConstraint(QLayout.SetFixedSize)
         #self.setFrameStyle(QFrame.WinPanel|QFrame.Raised)
         #self.setAlignment(Qt.AlignCenter)
@@ -482,21 +738,56 @@ class ReceivePopup(QDialog):
         self.show()
 
 class MiniActuator:
-
+    """Initialize the definitions relating to themes and 
+    sending/recieving bitcoins."""
+    
+    
     def __init__(self, wallet):
+        """Retrieve the gui theme used in previous session."""
         self.wallet = wallet
+        self.theme_name = self.wallet.config.get('litegui_theme','Cleanlook')
+        self.themes = load_theme_paths()
 
+    def load_theme(self):
+        """Load theme retrieved from wallet file."""
+        try:
+            theme_prefix, theme_path = self.themes[self.theme_name]
+        except KeyError:
+            util.print_error("Theme not found!", self.theme_name)
+            return
+        QDir.setCurrent(os.path.join(theme_prefix, theme_path))
+        with open(rsrc("style.css")) as style_file:
+            qApp.setStyleSheet(style_file.read())
+
+    def theme_names(self):
+        """Sort themes."""
+        return sorted(self.themes.keys())
+    
+    def selected_theme(self):
+        """Select theme."""
+        return self.theme_name
+
+    def change_theme(self, theme_name):
+        """Change theme."""
+        self.theme_name = theme_name
+        self.wallet.config.set_key('litegui_theme',theme_name)
+        self.load_theme()
+    
     def set_configured_currency(self, set_quote_currency):
-        currency = self.wallet.conversion_currency
+        """Set the inital fiat currency conversion country (USD/EUR/GBP) in 
+        the GUI to what it was set to in the wallet."""
+        currency = self.wallet.config.get('conversion_currency')
         # currency can be none when Electrum is used for the first
         # time and no setting has been created yet.
         if currency is not None:
             set_quote_currency(currency)
 
     def set_config_currency(self, conversion_currency):
-        self.wallet.conversion_currency = conversion_currency
+        """Change the wallet fiat currency country."""
+        self.wallet.config.set_key('conversion_currency',conversion_currency,True)
 
     def copy_address(self, receive_popup):
+        """Copy the wallet addresses into the client."""
         addrs = [addr for addr in self.wallet.all_addresses()
                  if not self.wallet.is_change(addr)]
         # Select most recent addresses from gap limit
@@ -506,7 +797,28 @@ class MiniActuator:
         receive_popup.setup(copied_address)
         receive_popup.popup()
 
+    def waiting_dialog(self, f):
+        s = Timer()
+        s.start()
+        w = QDialog()
+        w.resize(200, 70)
+        w.setWindowTitle('Electrum')
+        l = QLabel('Sending transaction, please wait.')
+        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()
+
+
     def send(self, address, amount, parent_window):
+        """Send bitcoins to the target address."""
         dest_address = self.fetch_destination(address)
 
         if dest_address is None or not self.wallet.is_valid(dest_address):
@@ -533,18 +845,29 @@ class MiniActuator:
             fee = bitcoin(1) / 1000
 
         try:
-            tx = self.wallet.mktx(dest_address, amount, "", password, fee)
+            tx = self.wallet.mktx([(dest_address, amount)], "", password, fee)
         except BaseException as error:
             QMessageBox.warning(parent_window, _('Error'), str(error), _('OK'))
             return False
-            
-        status, message = self.wallet.sendtx(tx)
+
+        h = self.wallet.send_tx(tx)
+
+        self.waiting_dialog(lambda: False if self.wallet.tx_event.isSet() else _("Sending transaction, please wait..."))
+          
+        status, message = self.wallet.receive_tx(h)
+
         if not status:
+            import tempfile
+            dumpf = tempfile.NamedTemporaryFile(delete=False)
+            dumpf.write(tx)
+            dumpf.close()
+            print "Dumped error tx to", dumpf.name
             QMessageBox.warning(parent_window, _('Error'), message, _('OK'))
             return False
-
-        QMessageBox.information(parent_window, '',
-            _('Payment sent.') + '\n' + message, _('OK'))
+      
+        TransactionWindow(message, self)
+#        QMessageBox.information(parent_window, '',
+#            _('Your transaction has been sent.') + '\n' + message, _('OK'))
         return True
 
     def fetch_destination(self, address):
@@ -569,10 +892,18 @@ class MiniActuator:
             return recipient
 
     def is_valid(self, address):
+        """Check if bitcoin address is valid."""
+
         return self.wallet.is_valid(address)
 
+    def copy_master_public_key(self):
+        master_pubkey = self.wallet.master_public_key
+        qApp.clipboard().setText(master_pubkey)
+        QMessageBox.information(None,"Copy succesful", "Your public master key has been copied to your clipboard.")
+        
+
     def acceptbit(self, currency):
-        master_pubkey = self.wallet.master_public_key.encode("hex")
+        master_pubkey = self.wallet.master_public_key
         url = "http://acceptbit.com/mpk/%s/%s" % (master_pubkey, currency)
         webbrowser.open(url)
 
@@ -592,7 +923,9 @@ class MiniDriver(QObject):
         self.wallet = wallet
         self.window = window
 
-        self.wallet.register_callback(self.update_callback)
+        self.wallet.interface.register_callback('updated',self.update_callback)
+        self.wallet.interface.register_callback('connected', self.update_callback)
+        self.wallet.interface.register_callback('disconnected', self.update_callback)
 
         self.state = None
 
@@ -611,9 +944,7 @@ class MiniDriver(QObject):
             self.initializing()
         elif not self.wallet.interface.is_connected:
             self.connecting()
-        elif not self.wallet.blocks == -1:
-            self.connecting()
-        elif not self.wallet.is_up_to_date:
+        elif not self.wallet.up_to_date:
             self.synchronizing()
         else:
             self.ready()
@@ -664,6 +995,7 @@ class MiniDriver(QObject):
         tx_history = self.wallet.get_tx_history()
         self.window.update_history(tx_history)
 
+
 if __name__ == "__main__":
     app = QApplication(sys.argv)
     with open(rsrc("style.css")) as style_file: