network button fix for not connected to a network - GTK gui
[electrum-nvc.git] / gui / gtk.py
index 635deb1..fb0bb5c 100644 (file)
@@ -16,6 +16,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see <http://www.gnu.org/licenses/>.
 
+# use this because this file is named gtk.py
+from __future__ import absolute_import
+
 import datetime
 import thread, time, ast, sys, re
 import socket, traceback
@@ -66,7 +69,7 @@ def show_seed_dialog(wallet, password, parent):
         show_message("No seed")
         return
     try:
-        seed = wallet.decode_seed(password)
+        seed = wallet.get_seed(password)
     except:
         show_message("Incorrect password")
         return
@@ -82,7 +85,7 @@ def show_seed_dialog(wallet, password, parent):
     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
@@ -102,7 +105,7 @@ 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(
         parent = None,
@@ -127,23 +130,8 @@ 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()
 
@@ -151,12 +139,6 @@ def run_recovery_dialog(wallet):
         return False
 
     try:
-        gap = int(gap)
-    except:
-        show_message("error")
-        return False
-
-    try:
         seed.decode('hex')
     except:
         print_error("Warning: Not hex, trying decode")
@@ -165,16 +147,16 @@ def run_recovery_dialog(wallet):
         show_message("no seed")
         return False
         
-    return seed, gap
+    return seed
 
 
 
-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,
+        parent = self.window,
         flags = gtk.DIALOG_MODAL, 
         buttons = gtk.BUTTONS_OK_CANCEL,
         message_format = message)
@@ -194,7 +176,7 @@ def run_settings_dialog(wallet, parent):
     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)
@@ -208,7 +190,7 @@ def run_settings_dialog(wallet, parent):
     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,28 +198,10 @@ 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:
@@ -248,7 +212,7 @@ def run_settings_dialog(wallet, parent):
     except:
         show_message("error")
         return
-    wallet.set_fee(fee)
+    self.wallet.set_fee(fee)
 
     try:
         nz = int( nz )
@@ -256,30 +220,33 @@ def run_settings_dialog(wallet, parent):
     except:
         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 ):
+def run_network_dialog( network, parent ):
     image = gtk.Image()
     image.set_from_stock(gtk.STOCK_NETWORK, gtk.ICON_SIZE_DIALOG)
-    interface = wallet.network.interface
     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)
@@ -295,13 +262,15 @@ def run_network_dialog( wallet, parent ):
     host_box.pack_start(host_label, False, False, 10)
     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.show()
 
@@ -310,33 +279,33 @@ def run_network_dialog( wallet, parent ):
     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.combo_box_new_text()
+    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)
     for host in servers.keys():
@@ -345,25 +314,22 @@ def run_network_dialog( wallet, parent ):
     treeview = gtk.TreeView(model=server_list)
     treeview.show()
 
-    if interface.servers:
-        label = 'Active Servers'
-    else:
-        label = 'Default Servers'
-        
+    label = 'Active Servers' if network.irc_servers else 'Default Servers'
     tvcolumn = gtk.TreeViewColumn(label)
     treeview.append_column(tvcolumn)
     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.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
+    #scroll.add_with_viewport(treeview)
+    #scroll.show()
+    #vbox.pack_start(scroll, True)
+    vbox.pack_start(treeview, True)
 
     def my_treeview_cb(treeview):
         path, view_column = treeview.get_cursor()
@@ -376,11 +342,11 @@ 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()
@@ -389,14 +355,15 @@ def run_network_dialog( wallet, parent ):
         return False
 
     try:
-        interface.set_server(server)
+        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:
         show_message("error:" + server)
         return False
 
-    if parent:
-        wallet.config.set_key("server", server, True)
-    return True
+
 
 
 
@@ -473,7 +440,7 @@ def change_password_dialog(wallet, parent, icon):
         return
 
     try:
-        seed = wallet.decode_seed(password)
+        wallet.get_seed(password)
     except:
         show_message("Incorrect password")
         return
@@ -482,7 +449,7 @@ def change_password_dialog(wallet, parent, icon):
         show_message("passwords do not match")
         return
 
-    wallet.update_password(seed, password, new_password)
+    wallet.update_password(password, new_password)
 
     if icon:
         if wallet.use_encryption:
@@ -510,11 +477,12 @@ 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)
         title = 'Electrum ' + self.wallet.electrum_version + '  -  ' + self.config.path
@@ -547,7 +515,7 @@ class ElectrumWindow:
         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.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.show()
@@ -573,7 +541,7 @@ class ElectrumWindow:
         settings_icon.show()
 
         prefs_button = gtk.Button()
-        prefs_button.connect("clicked", lambda x: run_settings_dialog(self.wallet, self.window) )
+        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)
@@ -601,7 +569,7 @@ 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():
@@ -935,8 +903,7 @@ class ElectrumWindow:
         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):
@@ -979,7 +946,7 @@ class ElectrumWindow:
 
 
     def create_recv_tab(self):
-        self.recv_list = gtk.ListStore(str, str, str)
+        self.recv_list = gtk.ListStore(str, str, str, str)
         self.add_tab( self.make_address_list(True), 'Receive')
         self.update_receiving_tab()
 
@@ -1011,8 +978,7 @@ class ElectrumWindow:
         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()
@@ -1026,6 +992,13 @@ class ElectrumWindow:
         tvcolumn.pack_start(cell, True)
         tvcolumn.add_attribute(cell, 'text', 2)
 
+        if is_recv:
+            tvcolumn = gtk.TreeViewColumn('Type')
+            treeview.append_column(tvcolumn)
+            cell = gtk.CellRendererText()
+            tvcolumn.pack_start(cell, True)
+            tvcolumn.add_attribute(cell, 'text', 3)
+
         scroll = gtk.ScrolledWindow()
         scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
         scroll.add(treeview)
@@ -1107,17 +1080,17 @@ class ElectrumWindow:
         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)
                 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.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() )
@@ -1133,18 +1106,20 @@ 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"
+            if self.wallet.is_change(address): Type = "C"
+            if address in self.wallet.imported_keys.keys(): Type = "I"
             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, Type ))
 
     def update_sending_tab(self):
         # detect addresses that are not mine in history, add them here...
@@ -1279,32 +1254,56 @@ class ElectrumGui():
     def __init__(self, config, network):
         self.network = network
         self.config = config
-        storage = WalletStorage(config)
-        if not storage.file_exists:
-            print "Wallet not found. try 'electrum create'"
-            exit()
-
-        self.wallet = Wallet(storage)
-        self.wallet.start_threads(network)
 
 
     def main(self, url=None):
-        ew = ElectrumWindow(self.wallet, self.config)
-        if url: ew.set_url(url)
+
+        storage = WalletStorage(self.config)
+        if not storage.file_exists:
+            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)
+
+            self.wallet.start_threads(self.network)
+
+            if action == 'create':
+                wallet.init_seed(None)
+                wallet.save_seed()
+                wallet.create_accounts()
+                wallet.synchronize()  # generate first addresses offline
+            elif action == 'restore':
+                seed = self.seed_dialog()
+                wallet.init_seed(seed)
+                wallet.save_seed()
+                self.restore_wallet(wallet)
+                
+            else:
+                exit()
+        else:
+            self.wallet = Wallet(storage)
+            self.wallet.start_threads(self.network)
+
+        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 )
+        return run_recovery_dialog()
 
     def verify_seed(self):
         self.wallet.save_seed()
         return True
 
     def network_dialog(self):
-        return run_network_dialog( self.wallet, parent=None )
+        return run_network_dialog( self.network, parent=None )
 
     def show_seed(self):
         show_seed_dialog(self.wallet, None, None)
@@ -1312,8 +1311,7 @@ class ElectrumGui():
     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(
             parent = None,
@@ -1323,8 +1321,7 @@ class ElectrumGui():
         dialog.show()
 
         def recover_thread( wallet, dialog ):
-            while not wallet.is_up_to_date(): 
-                time.sleep(0.1)
+            wallet.restore(lambda x:x)
             gobject.idle_add( dialog.destroy )
 
         thread.start_new_thread( recover_thread, ( wallet, dialog ) )
@@ -1334,5 +1331,4 @@ class ElectrumGui():
         if not wallet.is_found():
             show_message("No transactions found for this seed")
 
-        wallet.save()
         return True