updates for gtk gui
[electrum-nvc.git] / gui / gtk.py
index 635deb1..ed8d608 100644 (file)
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 
+
 import datetime
 import thread, time, ast, sys, re
 import socket, traceback
-import pygtk
-pygtk.require('2.0')
-import gtk, gobject
+import gi
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk, Gdk, GObject, cairo
 from decimal import Decimal
 from electrum.util import print_error
 from electrum.bitcoin import is_valid
-from electrum import mnemonic, pyqrnative, WalletStorage, Wallet
+from electrum import mnemonic, WalletStorage, Wallet
 
-gtk.gdk.threads_init()
+Gdk.threads_init()
 APP_NAME = "Electrum"
 import platform
 MONOSPACE_FONT = 'Lucida Console' if platform.system() == 'Windows' else 'monospace'
 
-from electrum.util import format_satoshis
+from electrum.util import format_satoshis, parse_URI
 from electrum.network import DEFAULT_SERVERS
 from electrum.bitcoin import MIN_RELAY_TX_FEE
 
@@ -48,12 +49,12 @@ def numbify(entry, is_int = False):
             s = s[:p] + '.' + s[p:p+8]
         try:
             amount = int( Decimal(s) * 100000000 )
-        except:
+        except Exception:
             amount = None
     else:
         try:
             amount = int( s )
-        except:
+        except Exception:
             amount = None
     entry.set_text(s)
     return amount
@@ -61,38 +62,33 @@ def numbify(entry, is_int = False):
 
 
 
-def show_seed_dialog(wallet, password, parent):
-    if not wallet.seed:
+def show_seed_dialog(seed, parent):
+    if not seed:
         show_message("No seed")
         return
-    try:
-        seed = wallet.decode_seed(password)
-    except:
-        show_message("Incorrect password")
-        return
-    dialog = gtk.MessageDialog(
+
+    dialog = Gtk.MessageDialog(
         parent = parent,
-        flags = gtk.DIALOG_MODAL, 
-        buttons = gtk.BUTTONS_OK, 
-        message_format = "Your wallet generation seed is:\n\n" + seed \
-            + "\n\nPlease keep it in a safe place; if you lose it, you will not be able to restore your wallet.\n\n" \
-            + "Equivalently, your wallet seed can be stored and recovered with the following mnemonic code:\n\n\"" + ' '.join(mnemonic.mn_encode(seed)) + "\"" )
+        flags = Gtk.DialogFlags.MODAL, 
+        buttons = Gtk.ButtonsType.OK, 
+        message_format = "Your wallet generation seed is:\n\n" + '"' + seed + '"'\
+            + "\n\nPlease keep it in a safe place; if you lose it, you will not be able to restore your wallet.\n\n" )
     dialog.set_title("Seed")
     dialog.show()
     dialog.run()
     dialog.destroy()
 
-def restore_create_dialog(wallet):
+def restore_create_dialog():
 
     # ask if the user wants to create a new wallet, or recover from a seed. 
     # if he wants to recover, and nothing is found, do not create wallet
-    dialog = gtk.Dialog("electrum", parent=None, 
-                        flags=gtk.DIALOG_MODAL|gtk.DIALOG_NO_SEPARATOR, 
+    dialog = Gtk.Dialog("electrum", parent=None, 
+                        flags=Gtk.DialogFlags.MODAL,
                         buttons= ("create", 0, "restore",1, "cancel",2)  )
 
-    label = gtk.Label("Wallet file not found.\nDo you want to create a new wallet,\n or to restore an existing one?"  )
+    label = Gtk.Label("Wallet file not found.\nDo you want to create a new wallet,\n or to restore an existing one?"  )
     label.show()
-    dialog.vbox.pack_start(label)
+    dialog.vbox.pack_start(label, True, True, 0)
     dialog.show()
     r = dialog.run()
     dialog.destroy()
@@ -102,24 +98,24 @@ def restore_create_dialog(wallet):
 
 
 
-def run_recovery_dialog(wallet):
+def run_recovery_dialog():
     message = "Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet."
-    dialog = gtk.MessageDialog(
+    dialog = Gtk.MessageDialog(
         parent = None,
-        flags = gtk.DIALOG_MODAL, 
-        buttons = gtk.BUTTONS_OK_CANCEL,
+        flags = Gtk.DialogFlags.MODAL, 
+        buttons = Gtk.ButtonsType.OK_CANCEL,
         message_format = message)
 
     vbox = dialog.vbox
-    dialog.set_default_response(gtk.RESPONSE_OK)
+    dialog.set_default_response(Gtk.ResponseType.OK)
 
     # ask seed, server and gap in the same dialog
-    seed_box = gtk.HBox()
-    seed_label = gtk.Label('Seed or mnemonic:')
+    seed_box = Gtk.HBox()
+    seed_label = Gtk.Label(label='Seed or mnemonic:')
     seed_label.set_size_request(150,-1)
     seed_box.pack_start(seed_label, False, False, 10)
     seed_label.show()
-    seed_entry = gtk.Entry()
+    seed_entry = Gtk.Entry()
     seed_entry.show()
     seed_entry.set_size_request(450,-1)
     seed_box.pack_start(seed_entry, False, False, 10)
@@ -127,74 +123,48 @@ def run_recovery_dialog(wallet):
     seed_box.show()
     vbox.pack_start(seed_box, False, False, 5)    
 
-    gap = gtk.HBox()
-    gap_label = gtk.Label('Gap limit:')
-    gap_label.set_size_request(150,10)
-    gap_label.show()
-    gap.pack_start(gap_label,False, False, 10)
-    gap_entry = gtk.Entry()
-    gap_entry.set_text("%d"%wallet.gap_limit)
-    gap_entry.connect('changed', numbify, True)
-    gap_entry.show()
-    gap.pack_start(gap_entry,False,False, 10)
-    add_help_button(gap, 'The maximum gap that is allowed between unused addresses in your wallet. During wallet recovery, this parameter is used to decide when to stop the recovery process. If you increase this value, you will need to remember it in order to be able to recover your wallet from seed.')
-    gap.show()
-    vbox.pack_start(gap, False,False, 5)
-
     dialog.show()
     r = dialog.run()
-    gap = gap_entry.get_text()        
     seed = seed_entry.get_text()
     dialog.destroy()
 
-    if r==gtk.RESPONSE_CANCEL:
+    if r==Gtk.ResponseType.CANCEL:
         return False
 
-    try:
-        gap = int(gap)
-    except:
-        show_message("error")
-        return False
+    if Wallet.is_seed(seed):
+        return seed
 
-    try:
-        seed.decode('hex')
-    except:
-        print_error("Warning: Not hex, trying decode")
-        seed = mnemonic.mn_decode( seed.split(' ') )
-    if not seed:
-        show_message("no seed")
-        return False
-        
-    return seed, gap
+    show_message("no seed")
+    return False
 
 
 
-def run_settings_dialog(wallet, parent):
+def run_settings_dialog(self):
 
     message = "Here are the settings of your wallet. For more explanations, click on the question mark buttons next to each input field."
         
-    dialog = gtk.MessageDialog(
-        parent = parent,
-        flags = gtk.DIALOG_MODAL, 
-        buttons = gtk.BUTTONS_OK_CANCEL,
+    dialog = Gtk.MessageDialog(
+        parent = self.window,
+        flags = Gtk.DialogFlags.MODAL, 
+        buttons = Gtk.ButtonsType.OK_CANCEL,
         message_format = message)
 
-    image = gtk.Image()
-    image.set_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_DIALOG)
+    image = Gtk.Image()
+    image.set_from_stock(Gtk.STOCK_PREFERENCES, Gtk.IconSize.DIALOG)
     image.show()
     dialog.set_image(image)
     dialog.set_title("Settings")
 
     vbox = dialog.vbox
-    dialog.set_default_response(gtk.RESPONSE_OK)
+    dialog.set_default_response(Gtk.ResponseType.OK)
 
-    fee = gtk.HBox()
-    fee_entry = gtk.Entry()
-    fee_label = gtk.Label('Transaction fee:')
+    fee = Gtk.HBox()
+    fee_entry = Gtk.Entry()
+    fee_label = Gtk.Label(label='Transaction fee:')
     fee_label.set_size_request(150,10)
     fee_label.show()
     fee.pack_start(fee_label,False, False, 10)
-    fee_entry.set_text( str( Decimal(wallet.fee) /100000000 ) )
+    fee_entry.set_text( str( Decimal(self.wallet.fee) /100000000 ) )
     fee_entry.connect('changed', numbify, False)
     fee_entry.show()
     fee.pack_start(fee_entry,False,False, 10)
@@ -202,13 +172,13 @@ def run_settings_dialog(wallet, parent):
     fee.show()
     vbox.pack_start(fee, False,False, 5)
             
-    nz = gtk.HBox()
-    nz_entry = gtk.Entry()
-    nz_label = gtk.Label('Display zeros:')
+    nz = Gtk.HBox()
+    nz_entry = Gtk.Entry()
+    nz_label = Gtk.Label(label='Display zeros:')
     nz_label.set_size_request(150,10)
     nz_label.show()
     nz.pack_start(nz_label,False, False, 10)
-    nz_entry.set_text( str( wallet.num_zeros ))
+    nz_entry.set_text( str( self.num_zeros ))
     nz_entry.connect('changed', numbify, True)
     nz_entry.show()
     nz.pack_start(nz_entry,False,False, 10)
@@ -216,154 +186,138 @@ def run_settings_dialog(wallet, parent):
     nz.show()
     vbox.pack_start(nz, False,False, 5)
             
-    # gui setting
-    gui_box = gtk.HBox()
-    gui_label = gtk.Label('Default GUI:')
-    gui_label.set_size_request(150,10)
-    gui_label.show()
-    gui_box.pack_start(gui_label,False, False, 10)
-    gui_combo = gtk.combo_box_new_text()
-    gui_names = ['lite', 'classic', 'gtk', 'text']
-    for name in gui_names: gui_combo.append_text(name.capitalize())
-    gui_combo.show()
-    gui_box.pack_start(gui_combo,False, False, 10)
-    gui_combo.set_active( gui_names.index( wallet.config.get("gui","lite")) )
-    gui_box.show()
-    add_help_button(gui_box, "Select which GUI mode to use at start up.")
-
-    vbox.pack_start(gui_box, False,False, 5)
-
     dialog.show()
     r = dialog.run()
     fee = fee_entry.get_text()
     nz = nz_entry.get_text()
-    gui = gui_names[ gui_combo.get_active()]
         
     dialog.destroy()
-    if r==gtk.RESPONSE_CANCEL:
+    if r==Gtk.ResponseType.CANCEL:
         return
 
     try:
         fee = int( 100000000 * Decimal(fee) )
-    except:
+    except Exception:
         show_message("error")
         return
-    wallet.set_fee(fee)
+    self.wallet.set_fee(fee)
 
     try:
         nz = int( nz )
         if nz>8: nz = 8
-    except:
+    except Exception:
         show_message("error")
         return
-    if wallet.num_zeros != nz:
-        wallet.num_zeros = nz
-        wallet.save()
 
-    wallet.config.set_key('gui',gui,True)
+    if self.num_zeros != nz:
+        self.num_zeros = nz
+        self.config.set_key('num_zeros',nz,True)
+        self.update_history_tab()
 
 
 
 
-def run_network_dialog( wallet, parent ):
-    image = gtk.Image()
-    image.set_from_stock(gtk.STOCK_NETWORK, gtk.ICON_SIZE_DIALOG)
-    interface = wallet.network.interface
+def run_network_dialog( network, parent ):
+    image = Gtk.Image()
+    image.set_from_stock(Gtk.STOCK_NETWORK, Gtk.IconSize.DIALOG)
     if parent:
-        if interface.is_connected:
-            status = "Connected to %s:%d\n%d blocks"%(interface.host, interface.port, wallet.network.blockchain.height)
+        if network.is_connected():
+            interface = network.interface
+            status = "Connected to %s:%d\n%d blocks"%(interface.host, interface.port, network.blockchain.height())
         else:
             status = "Not connected"
     else:
         import random
         status = "Please choose a server.\nSelect cancel if you are offline."
 
-    server = interface.server
-    servers = wallet.network.get_servers()
+    if network.is_connected():
+        server = interface.server
+        host, port, protocol = server.split(':')
+
+    servers = network.get_servers()
 
-    dialog = gtk.MessageDialog( parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
-                                    gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, status)
+    dialog = Gtk.MessageDialog( parent, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+                                    Gtk.MessageType.QUESTION, Gtk.ButtonsType.OK_CANCEL, status)
     dialog.set_title("Server")
     dialog.set_image(image)
     image.show()
     
     vbox = dialog.vbox
-    host_box = gtk.HBox()
-    host_label = gtk.Label('Connect to:')
+    host_box = Gtk.HBox()
+    host_label = Gtk.Label(label='Connect to:')
     host_label.set_size_request(100,-1)
     host_label.show()
     host_box.pack_start(host_label, False, False, 10)
-    host_entry = gtk.Entry()
+    host_entry = Gtk.Entry()
     host_entry.set_size_request(200,-1)
-    host_entry.set_text(server)
+    if network.is_connected():
+        host_entry.set_text(server)
+    else:
+        host_entry.set_text("Not Connected")
     host_entry.show()
     host_box.pack_start(host_entry, False, False, 10)
-    add_help_button(host_box, 'The name and port number of your Electrum server, separated by a colon. Example: "ecdsa.org:50000". If no port number is provided, port 50000 will be tried. Some servers allow you to connect through http (port 80) or https (port 443)')
+    add_help_button(host_box, 'The name, port number and protocol of your Electrum server, separated by a colon. Example: "ecdsa.org:50002:s". Some servers allow you to connect through http (port 80) or https (port 443)')
     host_box.show()
 
-
-    p_box = gtk.HBox(False, 10)
+    p_box = Gtk.HBox(False, 10)
     p_box.show()
 
-    p_label = gtk.Label('Protocol:')
+    p_label = Gtk.Label(label='Protocol:')
     p_label.set_size_request(100,-1)
     p_label.show()
     p_box.pack_start(p_label, False, False, 10)
 
-    radio1 = gtk.RadioButton(None, "tcp")
-    p_box.pack_start(radio1, True, True, 0)
-    radio1.show()
-    radio2 = gtk.RadioButton(radio1, "http")
-    p_box.pack_start(radio2, True, True, 0)
-    radio2.show()
+    combobox = Gtk.ComboBoxText()
+    combobox.show()
+    combobox.append_text("TCP")
+    combobox.append_text("SSL")
+    combobox.append_text("HTTP")
+    combobox.append_text("HTTPS")
+
+    p_box.pack_start(combobox, True, True, 0)
 
     def current_line():
         return unicode(host_entry.get_text()).split(':')
-    
-    def set_button(protocol):
-        if protocol == 't':
-            radio1.set_active(1)
-        elif protocol == 'h':
-            radio2.set_active(1)
+
+    def set_combobox(protocol):
+        combobox.set_active('tshg'.index(protocol))
 
     def set_protocol(protocol):
         host = current_line()[0]
         pp = servers[host]
         if protocol not in pp.keys():
             protocol = pp.keys()[0]
-            set_button(protocol)
+            set_combobox(protocol)
         port = pp[protocol]
         host_entry.set_text( host + ':' + port + ':' + protocol)
 
-    radio1.connect("toggled", lambda x,y:set_protocol('t'), "radio button 1")
-    radio2.connect("toggled", lambda x,y:set_protocol('h'), "radio button 1")
+    combobox.connect("changed", lambda x:set_protocol('tshg'[combobox.get_active()]))
+    if network.is_connected():
+        set_combobox(protocol)
         
-    server_list = gtk.ListStore(str)
+    server_list = Gtk.ListStore(str)
     for host in servers.keys():
         server_list.append([host])
     
-    treeview = gtk.TreeView(model=server_list)
+    treeview = Gtk.TreeView(model=server_list)
     treeview.show()
 
-    if interface.servers:
-        label = 'Active Servers'
-    else:
-        label = 'Default Servers'
-        
-    tvcolumn = gtk.TreeViewColumn(label)
+    label = 'Active Servers' if network.irc_servers else 'Default Servers'
+    tvcolumn = Gtk.TreeViewColumn(label)
     treeview.append_column(tvcolumn)
-    cell = gtk.CellRendererText()
+    cell = Gtk.CellRendererText()
     tvcolumn.pack_start(cell, False)
     tvcolumn.add_attribute(cell, 'text', 0)
 
-    scroll = gtk.ScrolledWindow()
-    scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-    scroll.add(treeview)
-    scroll.show()
-
     vbox.pack_start(host_box, False,False, 5)
     vbox.pack_start(p_box, True, True, 0)
-    vbox.pack_start(scroll)
+
+    #scroll = Gtk.ScrolledWindow()
+    #scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.ALWAYS)
+    #scroll.add_with_viewport(treeview)
+    #scroll.show()
+    #vbox.pack_start(scroll, True)
+    vbox.pack_start(treeview, True, True, 0)
 
     def my_treeview_cb(treeview):
         path, view_column = treeview.get_cursor()
@@ -376,47 +330,48 @@ def run_network_dialog( wallet, parent ):
             protocol = pp.keys()[0]
         port = pp[protocol]
         host_entry.set_text( host + ':' + port + ':' + protocol)
-        set_button(protocol)
+        set_combobox(protocol)
 
     treeview.connect('cursor-changed', my_treeview_cb)
 
-    dialog.show()
+    dialog.show_all()
     r = dialog.run()
     server = host_entry.get_text()
     dialog.destroy()
 
-    if r==gtk.RESPONSE_CANCEL:
+    if r==Gtk.ResponseType.CANCEL:
         return False
 
     try:
-        interface.set_server(server)
-    except:
+        host, port, protocol = server.split(':')
+        proxy = network.config.get('proxy')
+        auto_connect = network.config.get('auto_cycle')
+        network.set_parameters(host, port, protocol, proxy, auto_connect)
+    except Exception:
         show_message("error:" + server)
         return False
 
-    if parent:
-        wallet.config.set_key("server", server, True)
-    return True
+
 
 
 
 def show_message(message, parent=None):
-    dialog = gtk.MessageDialog(
+    dialog = Gtk.MessageDialog(
         parent = parent,
-        flags = gtk.DIALOG_MODAL, 
-        buttons = gtk.BUTTONS_CLOSE, 
+        flags = Gtk.DialogFlags.MODAL, 
+        buttons = Gtk.ButtonsType.CLOSE, 
         message_format = message )
     dialog.show()
     dialog.run()
     dialog.destroy()
 
 def password_line(label):
-    password = gtk.HBox()
-    password_label = gtk.Label(label)
+    password = Gtk.HBox()
+    password_label = Gtk.Label(label=label)
     password_label.set_size_request(120,10)
     password_label.show()
     password.pack_start(password_label,False, False, 10)
-    password_entry = gtk.Entry()
+    password_entry = Gtk.Entry()
     password_entry.set_size_request(300,-1)
     password_entry.set_visibility(False)
     password_entry.show()
@@ -425,36 +380,34 @@ def password_line(label):
     return password, password_entry
 
 def password_dialog(parent):
-    dialog = gtk.MessageDialog( parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
-                                gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL,  "Please enter your password.")
+    dialog = Gtk.MessageDialog( parent, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+                                Gtk.MessageType.QUESTION, Gtk.ButtonsType.OK_CANCEL,  "Please enter your password.")
     dialog.get_image().set_visible(False)
     current_pw, current_pw_entry = password_line('Password:')
-    current_pw_entry.connect("activate", lambda entry, dialog, response: dialog.response(response), dialog, gtk.RESPONSE_OK)
+    current_pw_entry.connect("activate", lambda entry, dialog, response: dialog.response(response), dialog, Gtk.ResponseType.OK)
     dialog.vbox.pack_start(current_pw, False, True, 0)
     dialog.show()
     result = dialog.run()
     pw = current_pw_entry.get_text()
     dialog.destroy()
-    if result != gtk.RESPONSE_CANCEL: return pw
+    if result != Gtk.ResponseType.CANCEL: return pw
 
-def change_password_dialog(wallet, parent, icon):
-    if not wallet.seed:
-        show_message("No seed")
-        return
+
+def change_password_dialog(is_encrypted, parent):
 
     if parent:
-        msg = 'Your wallet is encrypted. Use this dialog to change the password. To disable wallet encryption, enter an empty new password.' if wallet.use_encryption else 'Your wallet keys are not encrypted'
+        msg = 'Your wallet is encrypted. Use this dialog to change the password. To disable wallet encryption, enter an empty new password.' if is_encrypted else 'Your wallet keys are not encrypted'
     else:
         msg = "Please choose a password to encrypt your wallet keys"
 
-    dialog = gtk.MessageDialog( parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, msg)
+    dialog = Gtk.MessageDialog( parent, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.QUESTION, Gtk.ButtonsType.OK_CANCEL, msg)
     dialog.set_title("Change password")
-    image = gtk.Image()
-    image.set_from_stock(gtk.STOCK_DIALOG_AUTHENTICATION, gtk.ICON_SIZE_DIALOG)
+    image = Gtk.Image()
+    image.set_from_stock(Gtk.STOCK_DIALOG_AUTHENTICATION, Gtk.IconSize.DIALOG)
     image.show()
     dialog.set_image(image)
 
-    if wallet.use_encryption:
+    if is_encrypted:
         current_pw, current_pw_entry = password_line('Current password:')
         dialog.vbox.pack_start(current_pw, False, True, 0)
 
@@ -465,44 +418,37 @@ def change_password_dialog(wallet, parent, icon):
 
     dialog.show()
     result = dialog.run()
-    password = current_pw_entry.get_text() if wallet.use_encryption else None
+    password = current_pw_entry.get_text() if is_encrypted else None
     new_password = password_entry.get_text()
     new_password2 = password2_entry.get_text()
     dialog.destroy()
-    if result == gtk.RESPONSE_CANCEL: 
-        return
-
-    try:
-        seed = wallet.decode_seed(password)
-    except:
-        show_message("Incorrect password")
+    if result == Gtk.ResponseType.CANCEL: 
         return
 
     if new_password != new_password2:
         show_message("passwords do not match")
-        return
+        return change_password_dialog(is_encrypted, parent)
 
-    wallet.update_password(seed, password, new_password)
+    if not new_password:
+        new_password = None
+
+    return True, password, new_password
 
-    if icon:
-        if wallet.use_encryption:
-            icon.set_tooltip_text('wallet is encrypted')
-        else:
-            icon.set_tooltip_text('wallet is unencrypted')
 
 
 def add_help_button(hbox, message):
-    button = gtk.Button('?')
+    button = Gtk.Button('?')
     button.connect("clicked", lambda x: show_message(message))
     button.show()
-    hbox.pack_start(button,False, False)
+    hbox.pack_start(button,False, False, 0)
 
 
-class MyWindow(gtk.Window): __gsignals__ = dict( mykeypress = (gobject.SIGNAL_RUN_LAST | gobject.SIGNAL_ACTION, None, (str,)) )
+class MyWindow(Gtk.Window): __gsignals__ = dict( mykeypress = (GObject.SignalFlags.RUN_LAST | GObject.SignalFlags.ACTION, None, (str,)) )
 
-gobject.type_register(MyWindow)
-gtk.binding_entry_add_signal(MyWindow, gtk.keysyms.W, gtk.gdk.CONTROL_MASK, 'mykeypress', str, 'ctrl+W')
-gtk.binding_entry_add_signal(MyWindow, gtk.keysyms.Q, gtk.gdk.CONTROL_MASK, 'mykeypress', str, 'ctrl+Q')
+GObject.type_register(MyWindow)
+#FIXME: can't find docs how to create keybindings in PyGI
+#Gtk.binding_entry_add_signall(MyWindow, Gdk.KEY_W, Gdk.ModifierType.CONTROL_MASK, 'mykeypress', ['ctrl+W'])
+#Gtk.binding_entry_add_signall(MyWindow, Gdk.KEY_Q, Gdk.ModifierType.CONTROL_MASK, 'mykeypress', ['ctrl+Q'])
 
 
 class ElectrumWindow:
@@ -510,25 +456,26 @@ class ElectrumWindow:
     def show_message(self, msg):
         show_message(msg, self.window)
 
-    def __init__(self, wallet, config):
+    def __init__(self, wallet, config, network):
         self.config = config
         self.wallet = wallet
+        self.network = network
         self.funds_error = False # True if not enough funds
-        self.num_zeros     = int(self.config.get('num_zeros',0))
+        self.num_zeros = int(self.config.get('num_zeros',0))
 
-        self.window = MyWindow(gtk.WINDOW_TOPLEVEL)
+        self.window = MyWindow(Gtk.WindowType.TOPLEVEL)
         title = 'Electrum ' + self.wallet.electrum_version + '  -  ' + self.config.path
         if not self.wallet.seed: title += ' [seedless]'
         self.window.set_title(title)
-        self.window.connect("destroy", gtk.main_quit)
+        self.window.connect("destroy", Gtk.main_quit)
         self.window.set_border_width(0)
-        self.window.connect('mykeypress', gtk.main_quit)
+        self.window.connect('mykeypress', Gtk.main_quit)
         self.window.set_default_size(720, 350)
         self.wallet_updated = False
 
-        vbox = gtk.VBox()
+        vbox = Gtk.VBox()
 
-        self.notebook = gtk.Notebook()
+        self.notebook = Gtk.Notebook()
         self.create_history_tab()
         if self.wallet.seed:
             self.create_send_tab()
@@ -538,20 +485,20 @@ class ElectrumWindow:
         self.notebook.show()
         vbox.pack_start(self.notebook, True, True, 2)
         
-        self.status_bar = gtk.Statusbar()
+        self.status_bar = Gtk.Statusbar()
         vbox.pack_start(self.status_bar, False, False, 0)
 
-        self.status_image = gtk.Image()
-        self.status_image.set_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_MENU)
+        self.status_image = Gtk.Image()
+        self.status_image.set_from_stock(Gtk.STOCK_NO, Gtk.IconSize.MENU)
         self.status_image.set_alignment(True, 0.5  )
         self.status_image.show()
 
-        self.network_button = gtk.Button()
-        self.network_button.connect("clicked", lambda x: run_network_dialog(self.wallet, self.window) )
+        self.network_button = Gtk.Button()
+        self.network_button.connect("clicked", lambda x: run_network_dialog(self.network, self.window) )
         self.network_button.add(self.status_image)
-        self.network_button.set_relief(gtk.RELIEF_NONE)
+        self.network_button.set_relief(Gtk.ReliefStyle.NONE)
         self.network_button.show()
-        self.status_bar.pack_end(self.network_button, False, False)
+        self.status_bar.pack_end(self.network_button, False, False, 0)
 
         if self.wallet.seed:
             def seedb(w, wallet):
@@ -559,40 +506,47 @@ class ElectrumWindow:
                     password = password_dialog(self.window)
                     if not password: return
                 else: password = None
-                show_seed_dialog(wallet, password, self.window)
-            button = gtk.Button('S')
-            button.connect("clicked", seedb, wallet )
-            button.set_relief(gtk.RELIEF_NONE)
+                seed = wallet.get_mnemonic(password)
+                show_seed_dialog(seed, self.window)
+            button = Gtk.Button('S')
+            button.connect("clicked", seedb, self.wallet )
+            button.set_relief(Gtk.ReliefStyle.NONE)
             button.show()
-            self.status_bar.pack_end(button,False, False)
+            self.status_bar.pack_end(button,False, False, 0)
 
-        settings_icon = gtk.Image()
-        settings_icon.set_from_stock(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_MENU)
+        settings_icon = Gtk.Image()
+        settings_icon.set_from_stock(Gtk.STOCK_PREFERENCES, Gtk.IconSize.MENU)
         settings_icon.set_alignment(0.5, 0.5)
         settings_icon.set_size_request(16,16 )
         settings_icon.show()
 
-        prefs_button = gtk.Button()
-        prefs_button.connect("clicked", lambda x: run_settings_dialog(self.wallet, self.window) )
+        prefs_button = Gtk.Button()
+        prefs_button.connect("clicked", lambda x: run_settings_dialog(self) )
         prefs_button.add(settings_icon)
         prefs_button.set_tooltip_text("Settings")
-        prefs_button.set_relief(gtk.RELIEF_NONE)
+        prefs_button.set_relief(Gtk.ReliefStyle.NONE)
         prefs_button.show()
-        self.status_bar.pack_end(prefs_button,False,False)
+        self.status_bar.pack_end(prefs_button,False,False, 0)
 
-        pw_icon = gtk.Image()
-        pw_icon.set_from_stock(gtk.STOCK_DIALOG_AUTHENTICATION, gtk.ICON_SIZE_MENU)
-        pw_icon.set_alignment(0.5, 0.5)
-        pw_icon.set_size_request(16,16 )
-        pw_icon.show()
+        self.pw_icon = Gtk.Image()
+        self.pw_icon.set_from_stock(Gtk.STOCK_DIALOG_AUTHENTICATION, Gtk.IconSize.MENU)
+        self.pw_icon.set_alignment(0.5, 0.5)
+        self.pw_icon.set_size_request(16,16 )
+        self.pw_icon.show()
 
         if self.wallet.seed:
-            password_button = gtk.Button()
-            password_button.connect("clicked", lambda x: change_password_dialog(self.wallet, self.window, pw_icon))
-            password_button.add(pw_icon)
-            password_button.set_relief(gtk.RELIEF_NONE)
+
+            if self.wallet.use_encryption:
+                self.pw_icon.set_tooltip_text('Wallet is encrypted')
+            else:
+                self.pw_icon.set_tooltip_text('Wallet is unencrypted')
+
+            password_button = Gtk.Button()
+            password_button.connect("clicked", self.do_update_password, self.wallet)
+            password_button.add(self.pw_icon)
+            password_button.set_relief(Gtk.ReliefStyle.NONE)
             password_button.show()
-            self.status_bar.pack_end(password_button,False,False)
+            self.status_bar.pack_end(password_button,False,False, 0)
 
         self.window.add(vbox)
         self.window.show_all()
@@ -601,12 +555,12 @@ class ElectrumWindow:
         self.context_id = self.status_bar.get_context_id("statusbar")
         self.update_status_bar()
 
-        self.wallet.network.register_callback('updated', self.update_callback)
+        self.network.register_callback('updated', self.update_callback)
 
 
         def update_status_bar_thread():
             while True:
-                gobject.idle_add( self.update_status_bar )
+                GObject.idle_add( self.update_status_bar )
                 time.sleep(0.5)
 
 
@@ -623,11 +577,11 @@ class ElectrumWindow:
                     if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
                         try:
                             to_address = self.wallet.get_alias(r, interactive=False)
-                        except:
+                        except Exception:
                             continue
                         if to_address:
                             s = r + ' <' + to_address + '>'
-                            gobject.idle_add( lambda: self.payto_entry.set_text(s) )
+                            GObject.idle_add( lambda: self.payto_entry.set_text(s) )
                 
 
         thread.start_new_thread(update_status_bar_thread, ())
@@ -638,62 +592,84 @@ class ElectrumWindow:
     def update_callback(self):
         self.wallet_updated = True
 
+    def do_update_password(self, button, wallet):
+        if not wallet.seed:
+            show_message("No seed")
+            return
+
+        res = change_password_dialog(wallet.use_encryption, self.window)
+        if res:
+            _, password, new_password = res
+
+            try:
+                wallet.get_seed(password)
+            except Exception:
+                show_message("Incorrect password")
+                return
+
+            wallet.update_password(password, new_password)
+
+            if wallet.use_encryption:
+                self.pw_icon.set_tooltip_text('Wallet is encrypted')
+            else:
+                self.pw_icon.set_tooltip_text('Wallet is unencrypted')
+
 
     def add_tab(self, page, name):
-        tab_label = gtk.Label(name)
+        tab_label = Gtk.Label(label=name)
         tab_label.show()
         self.notebook.append_page(page, tab_label)
 
 
     def create_send_tab(self):
         
-        page = vbox = gtk.VBox()
+        page = vbox = Gtk.VBox()
         page.show()
 
-        payto = gtk.HBox()
-        payto_label = gtk.Label('Pay to:')
+        payto = Gtk.HBox()
+        payto_label = Gtk.Label(label='Pay to:')
         payto_label.set_size_request(100,-1)
-        payto.pack_start(payto_label, False)
-        payto_entry = gtk.Entry()
+        payto.pack_start(payto_label, False, False, 0)
+        payto_entry = Gtk.Entry()
         payto_entry.set_size_request(450, 26)
-        payto.pack_start(payto_entry, False)
+        payto.pack_start(payto_entry, False, False, 0)
         vbox.pack_start(payto, False, False, 5)
 
-        message = gtk.HBox()
-        message_label = gtk.Label('Description:')
+        message = Gtk.HBox()
+        message_label = Gtk.Label(label='Description:')
         message_label.set_size_request(100,-1)
-        message.pack_start(message_label, False)
-        message_entry = gtk.Entry()
+        message.pack_start(message_label, False, False, 0)
+        message_entry = Gtk.Entry()
         message_entry.set_size_request(450, 26)
-        message.pack_start(message_entry, False)
+        message.pack_start(message_entry, False, False, 0)
         vbox.pack_start(message, False, False, 5)
 
-        amount_box = gtk.HBox()
-        amount_label = gtk.Label('Amount:')
+        amount_box = Gtk.HBox()
+        amount_label = Gtk.Label(label='Amount:')
         amount_label.set_size_request(100,-1)
-        amount_box.pack_start(amount_label, False)
-        amount_entry = gtk.Entry()
+        amount_box.pack_start(amount_label, False, False, 0)
+        amount_entry = Gtk.Entry()
         amount_entry.set_size_request(120, -1)
-        amount_box.pack_start(amount_entry, False)
+        amount_box.pack_start(amount_entry, False, False, 0)
         vbox.pack_start(amount_box, False, False, 5)
 
-        self.fee_box = fee_box = gtk.HBox()
-        fee_label = gtk.Label('Fee:')
+        self.fee_box = fee_box = Gtk.HBox()
+        fee_label = Gtk.Label(label='Fee:')
         fee_label.set_size_request(100,-1)
-        fee_box.pack_start(fee_label, False)
-        fee_entry = gtk.Entry()
+        fee_box.pack_start(fee_label, False, False, 0)
+        fee_entry = Gtk.Entry()
         fee_entry.set_size_request(60, 26)
-        fee_box.pack_start(fee_entry, False)
+        fee_box.pack_start(fee_entry, False, False, 0)
         vbox.pack_start(fee_box, False, False, 5)
 
-        end_box = gtk.HBox()
-        empty_label = gtk.Label('')
+        end_box = Gtk.HBox()
+        empty_label = Gtk.Label(label='')
         empty_label.set_size_request(100,-1)
-        end_box.pack_start(empty_label, False)
-        send_button = gtk.Button("Send")
+        end_box.pack_start(empty_label, False, False, 0)
+        send_button = Gtk.Button("Send")
         send_button.show()
         end_box.pack_start(send_button, False, False, 0)
-        clear_button = gtk.Button("Clear")
+        clear_button = Gtk.Button("Clear")
         clear_button.show()
         end_box.pack_start(clear_button, False, False, 15)
         send_button.connect("clicked", self.do_send, (payto_entry, message_entry, amount_entry, fee_entry))
@@ -702,9 +678,9 @@ class ElectrumWindow:
         vbox.pack_start(end_box, False, False, 5)
 
         # display this line only if there is a signature
-        payto_sig = gtk.HBox()
-        payto_sig_id = gtk.Label('')
-        payto_sig.pack_start(payto_sig_id, False)
+        payto_sig = Gtk.HBox()
+        payto_sig_id = Gtk.Label(label='')
+        payto_sig.pack_start(payto_sig_id, False, False, 0)
         vbox.pack_start(payto_sig, True, True, 5)
         
 
@@ -717,18 +693,19 @@ class ElectrumWindow:
             if not is_fee: fee = None
             if amount is None:
                 return
-            inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee )
+            #assume two outputs - one for change
+            inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee, 2 )
             if not is_fee:
                 fee_entry.set_text( str( Decimal( fee ) / 100000000 ) )
                 self.fee_box.show()
             if inputs:
-                amount_entry.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("#000000"))
-                fee_entry.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("#000000"))
+                amount_entry.modify_text(Gtk.StateType.NORMAL, Gdk.color_parse("#000000"))
+                fee_entry.modify_text(Gtk.StateType.NORMAL, Gdk.color_parse("#000000"))
                 send_button.set_sensitive(True)
             else:
                 send_button.set_sensitive(False)
-                amount_entry.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("#cc0000"))
-                fee_entry.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("#cc0000"))
+                amount_entry.modify_text(Gtk.StateType.NORMAL, Gdk.color_parse("#cc0000"))
+                fee_entry.modify_text(Gtk.StateType.NORMAL, Gdk.color_parse("#cc0000"))
                 self.funds_error = True
 
         amount_entry.connect('changed', entry_changed, False)
@@ -746,37 +723,31 @@ class ElectrumWindow:
         if frozen:
             entry.set_editable(False)
             entry.set_has_frame(False)
-            entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#eeeeee"))
+            entry.modify_base(Gtk.StateType.NORMAL, Gdk.color_parse("#eeeeee"))
         else:
             entry.set_editable(True)
             entry.set_has_frame(True)
-            entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#ffffff"))
+            entry.modify_base(Gtk.StateType.NORMAL, Gdk.color_parse("#ffffff"))
 
     def set_url(self, url):
-        payto, amount, label, message, signature, identity, url = self.wallet.parse_url(url, self.show_message, self.question)
+        payto, amount, label, message, payment_request = parse_URI(url)
         self.notebook.set_current_page(1)
         self.payto_entry.set_text(payto)
         self.message_entry.set_text(message)
         self.amount_entry.set_text(amount)
-        if identity:
-            self.set_frozen(self.payto_entry,True)
-            self.set_frozen(self.amount_entry,True)
-            self.set_frozen(self.message_entry,True)
-            self.payto_sig_id.set_text( '      The bitcoin URI was signed by ' + identity )
-        else:
-            self.payto_sig.set_visible(False)
+        self.payto_sig.set_visible(False)
 
     def create_about_tab(self):
-        import pango
-        page = gtk.VBox()
+        from gi.repository import Pango
+        page = Gtk.VBox()
         page.show()
-        tv = gtk.TextView()
+        tv = Gtk.TextView()
         tv.set_editable(False)
         tv.set_cursor_visible(False)
-        tv.modify_font(pango.FontDescription(MONOSPACE_FONT))
-        scroll = gtk.ScrolledWindow()
+        tv.modify_font(Pango.FontDescription(MONOSPACE_FONT))
+        scroll = Gtk.ScrolledWindow()
         scroll.add(tv)
-        page.pack_start(scroll)
+        page.pack_start(scroll, True, True, 0)
         self.info = tv.get_buffer()
         self.add_tab(page, 'Wall')
 
@@ -788,11 +759,11 @@ class ElectrumWindow:
             entry.set_text('')
 
     def question(self,msg):
-        dialog = gtk.MessageDialog( self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, msg)
+        dialog = Gtk.MessageDialog( self.window, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.QUESTION, Gtk.ButtonsType.OK_CANCEL, msg)
         dialog.show()
         result = dialog.run()
         dialog.destroy()
-        return result == gtk.RESPONSE_OK
+        return result == Gtk.ResponseType.OK
 
     def do_send(self, w, data):
         payto_entry, label_entry, amount_entry, fee_entry = data
@@ -821,12 +792,12 @@ class ElectrumWindow:
 
         try:
             amount = int( Decimal(amount_entry.get_text()) * 100000000 )
-        except:
+        except Exception:
             self.show_message( "invalid amount")
             return
         try:
             fee = int( Decimal(fee_entry.get_text()) * 100000000 )
-        except:
+        except Exception:
             self.show_message( "invalid fee")
             return
 
@@ -839,7 +810,7 @@ class ElectrumWindow:
 
         try:
             tx = self.wallet.mktx( [(to_address, amount)], password, fee )
-        except BaseException, e:
+        except Exception as e:
             self.show_message(str(e))
             return
 
@@ -865,7 +836,7 @@ class ElectrumWindow:
 
 
     def treeview_button_press(self, treeview, event):
-        if event.type == gtk.gdk._2BUTTON_PRESS:
+        if event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
             c = treeview.get_cursor()[0]
             if treeview == self.history_treeview:
                 tx_details = self.history_list.get_value( self.history_list.get_iter(c), 8)
@@ -884,11 +855,11 @@ class ElectrumWindow:
 
     def treeview_key_press(self, treeview, event):
         c = treeview.get_cursor()[0]
-        if event.keyval == gtk.keysyms.Up:
+        if event.keyval == Gdk.KEY_Up:
             if c and c[0] == 0:
                 treeview.parent.grab_focus()
                 treeview.set_cursor((0,))
-        elif event.keyval == gtk.keysyms.Return:
+        elif event.keyval == Gdk.KEY_Return:
             if treeview == self.history_treeview:
                 tx_details = self.history_list.get_value( self.history_list.get_iter(c), 8)
                 self.show_message(tx_details)
@@ -907,36 +878,35 @@ class ElectrumWindow:
 
     def create_history_tab(self):
 
-        self.history_list = gtk.ListStore(str, str, str, str, 'gboolean',  str, str, str, str)
-        treeview = gtk.TreeView(model=self.history_list)
+        self.history_list = Gtk.ListStore(str, str, str, str, 'gboolean',  str, str, str, str)
+        treeview = Gtk.TreeView(model=self.history_list)
         self.history_treeview = treeview
         treeview.set_tooltip_column(7)
         treeview.show()
         treeview.connect('key-press-event', self.treeview_key_press)
         treeview.connect('button-press-event', self.treeview_button_press)
 
-        tvcolumn = gtk.TreeViewColumn('')
+        tvcolumn = Gtk.TreeViewColumn('')
         treeview.append_column(tvcolumn)
-        cell = gtk.CellRendererPixbuf()
+        cell = Gtk.CellRendererPixbuf()
         tvcolumn.pack_start(cell, False)
         tvcolumn.set_attributes(cell, stock_id=1)
 
-        tvcolumn = gtk.TreeViewColumn('Date')
+        tvcolumn = Gtk.TreeViewColumn('Date')
         treeview.append_column(tvcolumn)
-        cell = gtk.CellRendererText()
+        cell = Gtk.CellRendererText()
         tvcolumn.pack_start(cell, False)
         tvcolumn.add_attribute(cell, 'text', 2)
 
-        tvcolumn = gtk.TreeViewColumn('Description')
+        tvcolumn = Gtk.TreeViewColumn('Description')
         treeview.append_column(tvcolumn)
-        cell = gtk.CellRendererText()
+        cell = Gtk.CellRendererText()
         cell.set_property('foreground', 'grey')
         cell.set_property('family', MONOSPACE_FONT)
         cell.set_property('editable', True)
         def edited_cb(cell, path, new_text, h_list):
             tx = h_list.get_value( h_list.get_iter(path), 0)
-            self.wallet.labels[tx] = new_text
-            self.wallet.save() 
+            self.wallet.set_label(tx,new_text)
             self.update_history_tab()
         cell.connect('edited', edited_cb, self.history_list)
         def editing_started(cell, entry, path, h_list):
@@ -947,31 +917,31 @@ class ElectrumWindow:
         tvcolumn.pack_start(cell, True)
         tvcolumn.set_attributes(cell, text=3, foreground_set = 4)
 
-        tvcolumn = gtk.TreeViewColumn('Amount')
+        tvcolumn = Gtk.TreeViewColumn('Amount')
         treeview.append_column(tvcolumn)
-        cell = gtk.CellRendererText()
+        cell = Gtk.CellRendererText()
         cell.set_alignment(1, 0.5)
         cell.set_property('family', MONOSPACE_FONT)
         tvcolumn.pack_start(cell, False)
         tvcolumn.add_attribute(cell, 'text', 5)
 
-        tvcolumn = gtk.TreeViewColumn('Balance')
+        tvcolumn = Gtk.TreeViewColumn('Balance')
         treeview.append_column(tvcolumn)
-        cell = gtk.CellRendererText()
+        cell = Gtk.CellRendererText()
         cell.set_alignment(1, 0.5)
         cell.set_property('family', MONOSPACE_FONT)
         tvcolumn.pack_start(cell, False)
         tvcolumn.add_attribute(cell, 'text', 6)
 
-        tvcolumn = gtk.TreeViewColumn('Tooltip')
+        tvcolumn = Gtk.TreeViewColumn('Tooltip')
         treeview.append_column(tvcolumn)
-        cell = gtk.CellRendererText()
+        cell = Gtk.CellRendererText()
         tvcolumn.pack_start(cell, False)
         tvcolumn.add_attribute(cell, 'text', 7)
         tvcolumn.set_visible(False)
 
-        scroll = gtk.ScrolledWindow()
-        scroll.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+        scroll = Gtk.ScrolledWindow()
+        scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
         scroll.add(treeview)
 
         self.add_tab(scroll, 'History')
@@ -979,40 +949,39 @@ class ElectrumWindow:
 
 
     def create_recv_tab(self):
-        self.recv_list = gtk.ListStore(str, str, str)
+        self.recv_list = Gtk.ListStore(str, str, str, str, str)
         self.add_tab( self.make_address_list(True), 'Receive')
         self.update_receiving_tab()
 
     def create_book_tab(self):
-        self.addressbook_list = gtk.ListStore(str, str, str)
+        self.addressbook_list = Gtk.ListStore(str, str, str)
         self.add_tab( self.make_address_list(False), 'Contacts')
         self.update_sending_tab()
 
     def make_address_list(self, is_recv):
         liststore = self.recv_list if is_recv else self.addressbook_list
-        treeview = gtk.TreeView(model= liststore)
+        treeview = Gtk.TreeView(model= liststore)
         treeview.connect('key-press-event', self.treeview_key_press)
         treeview.connect('button-press-event', self.treeview_button_press)
         treeview.show()
         if not is_recv:
             self.contacts_treeview = treeview
 
-        tvcolumn = gtk.TreeViewColumn('Address')
+        tvcolumn = Gtk.TreeViewColumn('Address')
         treeview.append_column(tvcolumn)
-        cell = gtk.CellRendererText()
+        cell = Gtk.CellRendererText()
         cell.set_property('family', MONOSPACE_FONT)
         tvcolumn.pack_start(cell, True)
         tvcolumn.add_attribute(cell, 'text', 0)
 
-        tvcolumn = gtk.TreeViewColumn('Label')
+        tvcolumn = Gtk.TreeViewColumn('Label')
         tvcolumn.set_expand(True)
         treeview.append_column(tvcolumn)
-        cell = gtk.CellRendererText()
+        cell = Gtk.CellRendererText()
         cell.set_property('editable', True)
         def edited_cb2(cell, path, new_text, liststore):
             address = liststore.get_value( liststore.get_iter(path), 0)
-            self.wallet.labels[address] = new_text
-            self.wallet.save() 
+            self.wallet.set_label(address, new_text)
             self.update_receiving_tab()
             self.update_sending_tab()
             self.update_history_tab()
@@ -1020,54 +989,72 @@ class ElectrumWindow:
         tvcolumn.pack_start(cell, True)
         tvcolumn.add_attribute(cell, 'text', 1)
 
-        tvcolumn = gtk.TreeViewColumn('Tx')
+        tvcolumn = Gtk.TreeViewColumn('Tx')
         treeview.append_column(tvcolumn)
-        cell = gtk.CellRendererText()
+        cell = Gtk.CellRendererText()
         tvcolumn.pack_start(cell, True)
         tvcolumn.add_attribute(cell, 'text', 2)
 
-        scroll = gtk.ScrolledWindow()
-        scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        if is_recv:
+            tvcolumn = Gtk.TreeViewColumn('Balance')
+            treeview.append_column(tvcolumn)
+            cell = Gtk.CellRendererText()
+            tvcolumn.pack_start(cell, True)
+            tvcolumn.add_attribute(cell, 'text', 3)
+            tvcolumn = Gtk.TreeViewColumn('Type')
+            treeview.append_column(tvcolumn)
+            cell = Gtk.CellRendererText()
+            tvcolumn.pack_start(cell, True)
+            tvcolumn.add_attribute(cell, 'text', 4)
+
+        scroll = Gtk.ScrolledWindow()
+        scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
         scroll.add(treeview)
 
-        hbox = gtk.HBox()
+        hbox = Gtk.HBox()
         if not is_recv:
-            button = gtk.Button("New")
+            button = Gtk.Button("New")
             button.connect("clicked", self.newaddress_dialog)
             button.show()
-            hbox.pack_start(button,False)
+            hbox.pack_start(button,False, False, 0)
 
         def showqrcode(w, treeview, liststore):
+            import qrcode
             path, col = treeview.get_cursor()
             if not path: return
             address = liststore.get_value(liststore.get_iter(path), 0)
-            qr = pyqrnative.QRCode(4, pyqrnative.QRErrorCorrectLevel.H)
-            qr.addData(address)
-            qr.make()
+            qr = qrcode.QRCode()
+            qr.add_data(address)
             boxsize = 7
-            size = qr.getModuleCount()*boxsize
-            def area_expose_cb(area, event):
+            matrix = qr.get_matrix()
+            boxcount_row = len(matrix)
+            size = (boxcount_row + 4) * boxsize
+            def area_expose_cb(area, cr):
                 style = area.get_style()
-                k = qr.getModuleCount()
-                for r in range(k):
-                    for c in range(k):
-                        gc = style.black_gc if qr.isDark(r, c) else style.white_gc
-                        area.window.draw_rectangle(gc, True, c*boxsize, r*boxsize, boxsize, boxsize)
-            area = gtk.DrawingArea()
+                Gdk.cairo_set_source_color(cr, style.white)
+                cr.rectangle(0, 0, size, size)
+                cr.fill()
+                Gdk.cairo_set_source_color(cr, style.black)
+                for r in range(boxcount_row):
+                    for c in range(boxcount_row):
+                        if matrix[r][c]:
+                            cr.rectangle((c + 2) * boxsize, (r + 2) * boxsize, boxsize, boxsize)
+                            cr.fill()
+            area = Gtk.DrawingArea()
             area.set_size_request(size, size)
-            area.connect("expose-event", area_expose_cb)
+            area.connect("draw", area_expose_cb)
             area.show()
-            dialog = gtk.Dialog(address, parent=self.window, flags=gtk.DIALOG_MODAL|gtk.DIALOG_NO_SEPARATOR, buttons = ("ok",1))
+            dialog = Gtk.Dialog(address, parent=self.window, flags=Gtk.DialogFlags.MODAL, buttons = ("ok",1))
             dialog.vbox.add(area)
             dialog.run()
             dialog.destroy()
 
-        button = gtk.Button("QR")
+        button = Gtk.Button("QR")
         button.connect("clicked", showqrcode, treeview, liststore)
         button.show()
-        hbox.pack_start(button,False)
+        hbox.pack_start(button,False, False, 0)
 
-        button = gtk.Button("Copy to clipboard")
+        button = Gtk.Button("Copy to clipboard")
         def copy2clipboard(w, treeview, liststore):
             import platform
             path, col =  treeview.get_cursor()
@@ -1081,14 +1068,30 @@ class ElectrumWindow:
                     r.clipboard_append( address )
                     r.destroy()
                 else:
-                    c = gtk.clipboard_get()
-                    c.set_text( address )
+                    atom = Gdk.atom_intern('CLIPBOARD', True)
+                    c = Gtk.Clipboard.get(atom)
+                    c.set_text( address, len(address) )
         button.connect("clicked", copy2clipboard, treeview, liststore)
         button.show()
-        hbox.pack_start(button,False)
+        hbox.pack_start(button,False, False, 0)
+
+        if is_recv:
+            button = Gtk.Button("Freeze")
+            def freeze_address(w, treeview, liststore, wallet):
+                path, col = treeview.get_cursor()
+                if path:
+                    address = liststore.get_value( liststore.get_iter(path), 0)
+                    if address in wallet.frozen_addresses:
+                        wallet.unfreeze(address)
+                    else:
+                        wallet.freeze(address)
+                    self.update_receiving_tab()
+            button.connect("clicked", freeze_address, treeview, liststore, self.wallet)
+            button.show()
+            hbox.pack_start(button,False, False, 0)
 
         if not is_recv:
-            button = gtk.Button("Pay to")
+            button = Gtk.Button("Pay to")
             def payto(w, treeview, liststore):
                 path, col =  treeview.get_cursor()
                 if path:
@@ -1099,30 +1102,30 @@ class ElectrumWindow:
 
             button.connect("clicked", payto, treeview, liststore)
             button.show()
-            hbox.pack_start(button,False)
+            hbox.pack_start(button,False, False, 0)
 
-        vbox = gtk.VBox()
-        vbox.pack_start(scroll,True)
-        vbox.pack_start(hbox, False)
+        vbox = Gtk.VBox()
+        vbox.pack_start(scroll,True, True, 0)
+        vbox.pack_start(hbox, False, False, 0)
         return vbox
 
     def update_status_bar(self):
-        interface = self.wallet.network.interface
+        interface = self.network.interface
         if self.funds_error:
             text = "Not enough funds"
         elif interface and interface.is_connected:
-            self.network_button.set_tooltip_text("Connected to %s:%d.\n%d blocks"%(interface.host, interface.port, self.wallet.network.blockchain.height))
+            self.network_button.set_tooltip_text("Connected to %s:%d.\n%d blocks"%(interface.host, interface.port, self.network.blockchain.height()))
             if not self.wallet.up_to_date:
-                self.status_image.set_from_stock(gtk.STOCK_REFRESH, gtk.ICON_SIZE_MENU)
+                self.status_image.set_from_stock(Gtk.STOCK_REFRESH, Gtk.IconSize.MENU)
                 text = "Synchronizing..."
             else:
-                self.status_image.set_from_stock(gtk.STOCK_YES, gtk.ICON_SIZE_MENU)
-                self.network_button.set_tooltip_text("Connected to %s:%d.\n%d blocks"%(interface.host, interface.port, self.wallet.network.blockchain.height))
+                self.status_image.set_from_stock(Gtk.STOCK_YES, Gtk.IconSize.MENU)
+                self.network_button.set_tooltip_text("Connected to %s:%d.\n%d blocks"%(interface.host, interface.port, self.network.blockchain.height()))
                 c, u = self.wallet.get_balance()
                 text =  "Balance: %s "%( format_satoshis(c,False,self.num_zeros) )
                 if u: text +=  "[%s unconfirmed]"%( format_satoshis(u,True,self.num_zeros).strip() )
         else:
-            self.status_image.set_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_MENU)
+            self.status_image.set_from_stock(Gtk.STOCK_NO, Gtk.IconSize.MENU)
             self.network_button.set_tooltip_text("Not connected.")
             text = "Not connected"
 
@@ -1133,18 +1136,24 @@ class ElectrumWindow:
             self.update_history_tab()
             self.update_receiving_tab()
             # addressbook too...
-            self.info.set_text( self.wallet.network.banner )
+            self.info.set_text( self.network.banner )
             self.wallet_updated = False
 
     def update_receiving_tab(self):
         self.recv_list.clear()
         for address in self.wallet.addresses(True):
-            if self.wallet.is_change(address):continue
+            Type = "R"
+            c = u = 0
+            if self.wallet.is_change(address): Type = "C"
+            if address in self.wallet.imported_keys.keys():
+                Type = "I"
+            c, u = self.wallet.get_addr_balance(address)
+            if address in self.wallet.frozen_addresses: Type = Type + "F"
             label = self.wallet.labels.get(address)
             h = self.wallet.history.get(address,[])
             n = len(h)
-            tx = "None" if n==0 else "%d"%n
-            self.recv_list.append((address, label, tx ))
+            tx = "0" if n==0 else "%d"%n
+            self.recv_list.append((address, label, tx, format_satoshis(c,False,self.num_zeros), Type ))
 
     def update_sending_tab(self):
         # detect addresses that are not mine in history, add them here...
@@ -1168,15 +1177,15 @@ class ElectrumWindow:
             if conf > 0:
                 try:
                     time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3]
-                except:
+                except Exception:
                     time_str = "------"
-                conf_icon = gtk.STOCK_APPLY
+                conf_icon = Gtk.STOCK_APPLY
             elif conf == -1:
                 time_str = 'unverified'
                 conf_icon = None
             else:
                 time_str = 'pending'
-                conf_icon = gtk.STOCK_EXECUTE
+                conf_icon = Gtk.STOCK_EXECUTE
 
             label, is_default_label = self.wallet.get_label(tx_hash)
             tooltip = tx_hash + "\n%d confirmations"%conf if tx_hash else ''
@@ -1201,7 +1210,7 @@ class ElectrumWindow:
             time_str = 'pending'
 
         inputs = map(lambda x: x.get('address'), tx.inputs)
-        outputs = map(lambda x: x.get('address'), tx.d['outputs'])
+        outputs = map(lambda x: x[0], tx.get_outputs())
         tx_details = "Transaction Details" +"\n\n" \
             + "Transaction ID:\n" + tx_hash + "\n\n" \
             + "Status: %d confirmations\n"%conf
@@ -1226,30 +1235,30 @@ class ElectrumWindow:
     def newaddress_dialog(self, w):
 
         title = "New Contact" 
-        dialog = gtk.Dialog(title, parent=self.window, 
-                            flags=gtk.DIALOG_MODAL|gtk.DIALOG_NO_SEPARATOR, 
+        dialog = Gtk.Dialog(title, parent=self.window, 
+                            flags=Gtk.DialogFlags.MODAL,
                             buttons= ("cancel", 0, "ok",1)  )
         dialog.show()
 
-        label = gtk.HBox()
-        label_label = gtk.Label('Label:')
+        label = Gtk.HBox()
+        label_label = Gtk.Label(label='Label:')
         label_label.set_size_request(120,10)
         label_label.show()
-        label.pack_start(label_label)
-        label_entry = gtk.Entry()
+        label.pack_start(label_label, True, True, 0)
+        label_entry = Gtk.Entry()
         label_entry.show()
-        label.pack_start(label_entry)
+        label.pack_start(label_entry, True, True, 0)
         label.show()
         dialog.vbox.pack_start(label, False, True, 5)
 
-        address = gtk.HBox()
-        address_label = gtk.Label('Address:')
+        address = Gtk.HBox()
+        address_label = Gtk.Label(label='Address:')
         address_label.set_size_request(120,10)
         address_label.show()
-        address.pack_start(address_label)
-        address_entry = gtk.Entry()
+        address.pack_start(address_label, True, True, 0)
+        address_entry = Gtk.Entry()
         address_entry.show()
-        address.pack_start(address_entry)
+        address.pack_start(address_entry, True, True, 0)
         address.show()
         dialog.vbox.pack_start(address, False, True, 5)
         
@@ -1263,10 +1272,10 @@ class ElectrumWindow:
                 self.wallet.add_contact(address,label)
                 self.update_sending_tab()
             else:
-                errorDialog = gtk.MessageDialog(
+                errorDialog = Gtk.MessageDialog(
                     parent=self.window,
-                    flags=gtk.DIALOG_MODAL, 
-                    buttons= gtk.BUTTONS_CLOSE, 
+                    flags=Gtk.DialogFlags.MODAL, 
+                    buttons= Gtk.ButtonsType.CLOSE, 
                     message_format = "Invalid address")
                 errorDialog.show()
                 errorDialog.run()
@@ -1279,60 +1288,82 @@ class ElectrumGui():
     def __init__(self, config, network):
         self.network = network
         self.config = config
-        storage = WalletStorage(config)
+
+
+    def main(self, url=None):
+
+        storage = WalletStorage(self.config)
         if not storage.file_exists:
-            print "Wallet not found. try 'electrum create'"
-            exit()
+            action = self.restore_or_create()
+            if not action:
+                exit()
+            self.wallet = wallet = Wallet(storage)
+            gap = self.config.get('gap_limit', 5)
+            if gap != 5:
+                wallet.gap_limit = gap
+                wallet.storage.put('gap_limit', gap, True)
+
+            if action == 'create':
+                seed = wallet.make_seed()
+                show_seed_dialog(seed, None)
+                r = change_password_dialog(False, None)
+                password = r[2] if r else None
+                wallet.add_seed(seed, password)
+                wallet.create_accounts(password)
+                wallet.synchronize()  # generate first addresses offline
+
+            elif action == 'restore':
+                seed = self.seed_dialog()
+                if not seed:
+                    exit()
+                r = change_password_dialog(False, None)
+                password = r[2] if r else None
+                wallet.add_seed(seed, password)
+                wallet.create_accounts(password)
+                
+            else:
+                exit()
+        else:
+            self.wallet = Wallet(storage)
+            action = None
 
-        self.wallet = Wallet(storage)
-        self.wallet.start_threads(network)
+        self.wallet.start_threads(self.network)
 
+        if action == 'restore':
+            self.restore_wallet(wallet)
 
-    def main(self, url=None):
-        ew = ElectrumWindow(self.wallet, self.config)
-        if url: ew.set_url(url)
-        gtk.main()
+        w = ElectrumWindow(self.wallet, self.config, self.network)
+        if url: w.set_url(url)
+        Gtk.main()
 
     def restore_or_create(self):
-        return restore_create_dialog(self.wallet)
+        return restore_create_dialog()
 
     def seed_dialog(self):
-        return run_recovery_dialog( self.wallet )
-
-    def verify_seed(self):
-        self.wallet.save_seed()
-        return True
+        return run_recovery_dialog()
 
     def network_dialog(self):
-        return run_network_dialog( self.wallet, parent=None )
-
-    def show_seed(self):
-        show_seed_dialog(self.wallet, None, None)
+        return run_network_dialog( self.network, parent=None )
 
-    def password_dialog(self):
-        change_password_dialog(self.wallet, None, None)
 
-    def restore_wallet(self):
-        wallet = self.wallet
+    def restore_wallet(self, wallet):
 
-        dialog = gtk.MessageDialog(
+        dialog = Gtk.MessageDialog(
             parent = None,
-            flags = gtk.DIALOG_MODAL, 
-            buttons = gtk.BUTTONS_CANCEL, 
+            flags = Gtk.DialogFlags.MODAL, 
+            buttons = Gtk.ButtonsType.CANCEL, 
             message_format = "Please wait..."  )
         dialog.show()
 
         def recover_thread( wallet, dialog ):
-            while not wallet.is_up_to_date(): 
-                time.sleep(0.1)
-            gobject.idle_add( dialog.destroy )
+            wallet.restore(lambda x:x)
+            GObject.idle_add( dialog.destroy )
 
         thread.start_new_thread( recover_thread, ( wallet, dialog ) )
         r = dialog.run()
         dialog.destroy()
-        if r==gtk.RESPONSE_CANCEL: return False
+        if r==Gtk.ResponseType.CANCEL: return False
         if not wallet.is_found():
             show_message("No transactions found for this seed")
 
-        wallet.save()
         return True