URI: 
       tstore contacts in a separate file, shared between wallets - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit e8189490e9a2ac6805c5800526bd1ea582e97daa
   DIR parent 065145e5572f9ddb06967ce21a9e9b50bb22d484
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Thu, 23 Apr 2015 12:16:46 +0200
       
       store contacts in a separate file, shared between wallets
       
       Diffstat:
         M gui/qt/main_window.py               |      87 ++++++++++++++-----------------
         M lib/util.py                         |      30 ++++++++++++++++++++++++++++++
         M lib/wallet.py                       |      23 -----------------------
         M plugins/openalias.py                |       2 +-
       
       4 files changed, 70 insertions(+), 72 deletions(-)
       ---
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -34,7 +34,7 @@ from electrum.plugins import run_hook
        
        import icons_rc
        
       -from electrum.util import format_satoshis, format_time, NotEnoughFunds
       +from electrum.util import format_satoshis, format_time, NotEnoughFunds, StoreDict
        from electrum import Transaction
        from electrum import mnemonic
        from electrum import util, bitcoin, commands, Interface, Wallet
       t@@ -117,6 +117,7 @@ class ElectrumWindow(QMainWindow):
                self.app = gui_object.app
        
                self.invoices = InvoiceStore(self.config)
       +        self.contacts = StoreDict(self.config, 'contacts')
        
                self.create_status_bar()
                self.need_update = threading.Event()
       t@@ -201,11 +202,12 @@ class ElectrumWindow(QMainWindow):
            def load_wallet(self, wallet):
                import electrum
                self.wallet = wallet
       +        # backward compatibility
                self.update_wallet_format()
       +        self.import_old_contacts()
                # address used to create a dummy transaction and estimate transaction fee
                a = self.wallet.addresses(False)
                self.dummy_address = a[0] if a else None
       -
                self.accounts_expanded = self.wallet.storage.get('accounts_expanded',{})
                self.current_account = self.wallet.storage.get("current_account", None)
                title = 'Electrum ' + self.wallet.electrum_version + '  -  ' + os.path.basename(self.wallet.storage.path)
       t@@ -224,16 +226,22 @@ class ElectrumWindow(QMainWindow):
                self.mpk_menu.setEnabled(self.wallet.is_deterministic())
                self.import_menu.setVisible(self.wallet.can_import())
                self.export_menu.setEnabled(self.wallet.can_export())
       -
                self.update_lock_icon()
                self.update_buttons_on_seed()
                self.update_console()
       -
                self.clear_receive_tab()
                self.update_receive_tab()
                self.show()
                run_hook('load_wallet', wallet)
        
       +    def import_old_contacts(self):
       +        # backward compatibility: import contacts
       +        addressbook = set(self.wallet.storage.get('contacts', []))
       +        for k in addressbook:
       +            l = self.wallet.labels.get(k)
       +            if bitcoin.is_address(k) and l:
       +                self.contacts[l] = ('address', k)
       +        self.wallet.storage.put('contacts', None)
        
            def update_wallet_format(self):
                # convert old-format imported keys
       t@@ -248,7 +256,6 @@ class ElectrumWindow(QMainWindow):
                if self.wallet.get_master_public_keys() and self.wallet.addresses() == []:
                    self.wallet.synchronize()
        
       -
            def open_wallet(self):
                wallet_folder = self.wallet.storage.path
                filename = unicode( QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder) )
       t@@ -1032,21 +1039,21 @@ class ElectrumWindow(QMainWindow):
                for item in self.pay_from:
                    self.from_list.addTopLevelItem(QTreeWidgetItem( [format(item), self.format_amount(item['value']) ]))
        
       +    def get_contact_payto(self, key):
       +        _type, value = self.contacts.get(key)
       +        return key + '  <' + value + '>' if _type == 'address' else key
       +
            def update_completions(self):
       -        l = self.wallet.get_completions()
       +        l = [self.get_contact_payto(key) for key in self.contacts.keys()]
                self.completions.setStringList(l)
        
       -
            def protected(func):
                return lambda s, *args: s.do_protect(func, args)
        
       -
            def read_send_tab(self):
       -
                if self.payment_request and self.payment_request.has_expired():
                    QMessageBox.warning(self, _('Error'), _('Payment request has expired'), _('OK'))
                    return
       -
                label = unicode( self.message_e.text() )
        
                if self.payment_request:
       t@@ -1304,7 +1311,7 @@ class ElectrumWindow(QMainWindow):
                return self.create_list_tab(l)
        
            def create_contacts_tab(self):
       -        l = MyTreeWidget(self, self.create_contact_menu, [_('Address'), _('Label'), _('Tx')], [350, None])
       +        l = MyTreeWidget(self, self.create_contact_menu, [_('Key'), _('Value'), _('Type')], [350, None, 130])
                self.contacts_list = l
                return self.create_list_tab(l)
        
       t@@ -1430,22 +1437,19 @@ class ElectrumWindow(QMainWindow):
        
        
            def payto(self, addr):
       -        if not addr: return
       -        label = self.wallet.labels.get(addr)
       -        m_addr = label + '  <' + addr + '>' if label else addr
       +        if not addr:
       +            return
                self.tabs.setCurrentIndex(1)
       -        self.payto_e.setText(m_addr)
       +        self.payto_e.setText(addr)
                self.amount_e.setFocus()
        
       -
            def delete_contact(self, x):
       -        if self.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")):
       -            self.wallet.delete_contact(x)
       -            self.wallet.set_label(x, None)
       -            self.update_history_tab()
       -            self.update_contacts_tab()
       -            self.update_completions()
       -
       +        if not self.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")):
       +            return
       +        self.contacts.pop(x)
       +        self.update_history_tab()
       +        self.update_contacts_tab()
       +        self.update_completions()
        
            def create_contact_menu(self, position):
                item = self.contacts_list.itemAt(position)
       t@@ -1453,16 +1457,10 @@ class ElectrumWindow(QMainWindow):
                if not item:
                    menu.addAction(_("New contact"), lambda: self.new_contact_dialog())
                else:
       -            addr = unicode(item.text(0))
       -            label = unicode(item.text(1))
       -            is_editable = item.data(0,32).toBool()
       -            payto_addr = item.data(0,33).toString()
       -            menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
       -            menu.addAction(_("Pay to"), lambda: self.payto(payto_addr))
       -            menu.addAction(_("QR code"), lambda: self.show_qrcode("bitcoin:" + addr, _("Address")))
       -            if is_editable:
       -                menu.addAction(_("Edit label"), lambda: self.contacts_list.edit_label(item))
       -                menu.addAction(_("Delete"), lambda: self.delete_contact(addr))
       +            key = unicode(item.text(0))
       +            menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(key))
       +            menu.addAction(_("Pay to"), lambda: self.payto(self.get_contact_payto(key)))
       +            menu.addAction(_("Delete"), lambda: self.delete_contact(key))
        
                run_hook('create_contact_menu', menu, item)
                menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
       t@@ -1590,17 +1588,14 @@ class ElectrumWindow(QMainWindow):
            def update_contacts_tab(self):
                l = self.contacts_list
                item = l.currentItem()
       -        current_address = item.data(0, Qt.UserRole).toString() if item else None
       +        current_key = item.data(0, Qt.UserRole).toString() if item else None
                l.clear()
       -        for address in self.wallet.addressbook:
       -            label = self.wallet.labels.get(address,'')
       -            n = self.wallet.get_num_tx(address)
       -            item = QTreeWidgetItem( [ address, label, "%d"%n] )
       -            item.setFont(0, QFont(MONOSPACE_FONT))
       -            item.setData(0, Qt.UserRole, address)
       -            item.setData(0, Qt.UserRole+1, True)
       +        for key, v in self.contacts.items():
       +            _type, value = v
       +            item = QTreeWidgetItem([key, value, _type])
       +            item.setData(0, Qt.UserRole, key)
                    l.addTopLevelItem(item)
       -            if address == current_address:
       +            if key == current_key:
                        l.setCurrentItem(item)
                run_hook('update_contacts_tab', l)
        
       t@@ -1697,12 +1692,10 @@ class ElectrumWindow(QMainWindow):
        
        
            def new_contact_dialog(self):
       -
                d = QDialog(self)
                d.setWindowTitle(_("New Contact"))
                vbox = QVBoxLayout(d)
       -        vbox.addWidget(QLabel(_('New Contact')+':'))
       -
       +        vbox.addWidget(QLabel(_('New Contact') + ':'))
                grid = QGridLayout()
                line1 = QLineEdit()
                line2 = QLineEdit()
       t@@ -1724,9 +1717,7 @@ class ElectrumWindow(QMainWindow):
                    QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
                    return
        
       -        self.wallet.add_contact(address)
       -        if label:
       -            self.wallet.set_label(address, label)
       +        self.contacts[label] = ('address', address)
        
                self.update_contacts_tab()
                self.update_history_tab()
   DIR diff --git a/lib/util.py b/lib/util.py
       t@@ -397,3 +397,33 @@ class QueuePipe:
            def send_all(self, requests):
                for request in requests:
                    self.send(request)
       +
       +
       +
       +class StoreDict(dict):
       +
       +    def __init__(self, config, name):
       +        self.config = config
       +        self.path = os.path.join(self.config.path, name)
       +        self.load()
       +
       +    def load(self):
       +        try:
       +            with open(self.path, 'r') as f:
       +                self.update(json.loads(f.read()))
       +        except:
       +            pass
       +
       +    def save(self):
       +        with open(self.path, 'w') as f:
       +            s = json.dumps(self, indent=4, sort_keys=True)
       +            r = f.write(s)
       +
       +    def __setitem__(self, key, value):
       +        dict.__setitem__(self, key, value)
       +        self.save()
       +
       +    def pop(self, key):
       +        if key in self.keys():
       +            dict.pop(self, key)
       +            self.save()
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -159,7 +159,6 @@ class Abstract_Wallet(object):
                self.seed                  = storage.get('seed', '')               # encrypted
                self.labels                = storage.get('labels', {})
                self.frozen_addresses      = storage.get('frozen_addresses',[])
       -        self.addressbook           = set(storage.get('contacts', []))
        
                self.history               = storage.get('addr_history',{})        # address -> list(txid, height)
                self.fee_per_kb            = int(storage.get('fee_per_kb', RECOMMENDED_FEE))
       t@@ -380,28 +379,6 @@ class Abstract_Wallet(object):
            def is_found(self):
                return self.history.values() != [[]] * len(self.history)
        
       -    def add_contact(self, address, label=None):
       -        self.addressbook.add(address)
       -        self.storage.put('contacts', list(self.addressbook), True)
       -        if label:
       -            self.set_label(address, label)
       -
       -    def delete_contact(self, addr):
       -        if addr in self.addressbook:
       -            self.addressbook.remove(addr)
       -            self.storage.put('contacts', list(self.addressbook), True)
       -
       -    def get_completions(self):
       -        l = []
       -        for x in self.addressbook:
       -            if bitcoin.is_address(x):
       -                label = self.labels.get(x)
       -                if label:
       -                    l.append( label + '  <' + x + '>')
       -            else:
       -                l.append(x)
       -        return l
       -
            def get_num_tx(self, address):
                """ return number of transactions where address is involved """
                return len(self.history.get(address, []))
   DIR diff --git a/plugins/openalias.py b/plugins/openalias.py
       t@@ -110,7 +110,7 @@ class Plugin(BasePlugin):
                self.win.previous_payto_e = new_url
        
                if self.config.get('openalias_autoadd') == 'checked':
       -            self.win.wallet.add_contact(url, name)
       +            self.win.contacts[url] = ('openalias', name)
                    self.win.update_contacts_tab()
        
                self.win.payto_e.setFrozen(True)