URI: 
       tDefine TreeWidget subclasses for lists * move class code in separate files * make menu column-dependent (fixes #1734) - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 12dfccb3ab199c59d767f729f332c24f0ba1dde9
   DIR parent 0273936b07d21f09be7c91bb3b6640320fdd340c
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Fri, 27 May 2016 09:56:53 +0200
       
       Define TreeWidget subclasses for lists
       * move class code in separate files
       * make menu column-dependent (fixes #1734)
       
       Diffstat:
         A gui/qt/address_list.py              |     157 +++++++++++++++++++++++++++++++
         A gui/qt/contact_list.py              |      93 +++++++++++++++++++++++++++++++
         A gui/qt/history_list.py              |     142 +++++++++++++++++++++++++++++++
         D gui/qt/history_widget.py            |     132 -------------------------------
         A gui/qt/invoice_list.py              |      77 +++++++++++++++++++++++++++++++
         M gui/qt/main_window.py               |     379 +++----------------------------
         A gui/qt/request_list.py              |     124 +++++++++++++++++++++++++++++++
         M gui/qt/util.py                      |      39 +++++++++++++++++++++++++------
       
       8 files changed, 660 insertions(+), 483 deletions(-)
       ---
   DIR diff --git a/gui/qt/address_list.py b/gui/qt/address_list.py
       t@@ -0,0 +1,157 @@
       +#!/usr/bin/env python
       +#
       +# Electrum - lightweight Bitcoin client
       +# Copyright (C) 2015 Thomas Voegtlin
       +#
       +# Permission is hereby granted, free of charge, to any person
       +# obtaining a copy of this software and associated documentation files
       +# (the "Software"), to deal in the Software without restriction,
       +# including without limitation the rights to use, copy, modify, merge,
       +# publish, distribute, sublicense, and/or sell copies of the Software,
       +# and to permit persons to whom the Software is furnished to do so,
       +# subject to the following conditions:
       +#
       +# The above copyright notice and this permission notice shall be
       +# included in all copies or substantial portions of the Software.
       +#
       +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
       +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
       +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
       +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
       +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       +# SOFTWARE.
       +
       +
       +import webbrowser
       +
       +from util import *
       +from electrum.i18n import _
       +from electrum.util import block_explorer_URL, format_satoshis, format_time
       +from electrum.plugins import run_hook
       +from electrum.bitcoin import is_address
       +
       +
       +class AddressList(MyTreeWidget):
       +
       +    def __init__(self, parent=None):
       +        MyTreeWidget.__init__(self, parent, self.create_menu, [ _('Address'), _('Label'), _('Balance'), _('Tx')], 1)
       +        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
       +
       +    def on_update(self):
       +        self.wallet = self.parent.wallet
       +        item = self.currentItem()
       +        current_address = item.data(0, Qt.UserRole).toString() if item else None
       +        self.clear()
       +        accounts = self.wallet.get_accounts()
       +        if self.parent.current_account is None:
       +            account_items = sorted(accounts.items())
       +        else:
       +            account_items = [(self.parent.current_account, accounts.get(self.parent.current_account))]
       +        for k, account in account_items:
       +            if len(accounts) > 1:
       +                name = self.wallet.get_account_name(k)
       +                c, u, x = self.wallet.get_account_balance(k)
       +                account_item = QTreeWidgetItem([ name, '', self.parent.format_amount(c + u + x), ''])
       +                account_item.setExpanded(self.accounts_expanded.get(k, True))
       +                account_item.setData(0, Qt.UserRole, k)
       +                self.addTopLevelItem(account_item)
       +            else:
       +                account_item = self
       +            sequences = [0,1] if account.has_change() else [0]
       +            for is_change in sequences:
       +                if len(sequences) > 1:
       +                    name = _("Receiving") if not is_change else _("Change")
       +                    seq_item = QTreeWidgetItem( [ name, '', '', '', ''] )
       +                    account_item.addChild(seq_item)
       +                    if not is_change:
       +                        seq_item.setExpanded(True)
       +                else:
       +                    seq_item = account_item
       +                used_item = QTreeWidgetItem( [ _("Used"), '', '', '', ''] )
       +                used_flag = False
       +                addr_list = account.get_addresses(is_change)
       +                for address in addr_list:
       +                    num = len(self.wallet.history.get(address,[]))
       +                    is_used = self.wallet.is_used(address)
       +                    label = self.wallet.labels.get(address,'')
       +                    c, u, x = self.wallet.get_addr_balance(address)
       +                    balance = self.parent.format_amount(c + u + x)
       +                    address_item = QTreeWidgetItem([address, label, balance, "%d"%num])
       +                    address_item.setFont(0, QFont(MONOSPACE_FONT))
       +                    address_item.setData(0, Qt.UserRole, address)
       +                    address_item.setData(0, Qt.UserRole+1, True) # label can be edited
       +                    if self.wallet.is_frozen(address):
       +                        address_item.setBackgroundColor(0, QColor('lightblue'))
       +                    if self.wallet.is_beyond_limit(address, account, is_change):
       +                        address_item.setBackgroundColor(0, QColor('red'))
       +                    if is_used:
       +                        if not used_flag:
       +                            seq_item.insertChild(0, used_item)
       +                            used_flag = True
       +                        used_item.addChild(address_item)
       +                    else:
       +                        seq_item.addChild(address_item)
       +                    if address == current_address:
       +                        self.setCurrentItem(address_item)
       +                    # add utxos
       +                    utxos = self.wallet.get_addr_utxo(address)
       +                    for x in utxos:
       +                        h = x.get('prevout_hash')
       +                        s = h + ":%d"%x.get('prevout_n')
       +                        label = self.wallet.get_label(h)
       +                        utxo_item = QTreeWidgetItem([s, label, self.parent.format_amount(x['value'])])
       +                        utxo_item.setFont(0, QFont(MONOSPACE_FONT))
       +                        address_item.addChild(utxo_item)
       +
       +    def create_menu(self, position):
       +        selected = self.selectedItems()
       +        multi_select = len(selected) > 1
       +        addrs = [unicode(item.text(0)) for item in selected]
       +        if not multi_select:
       +            item = self.itemAt(position)
       +            col = self.currentColumn()
       +            if not item:
       +                return
       +            addr = addrs[0]
       +            if not is_address(addr):
       +                k = str(item.data(0,32).toString())
       +                if k:
       +                    self.create_account_menu(position, k, item)
       +                else:
       +                    item.setExpanded(not item.isExpanded())
       +                return
       +
       +        menu = QMenu()
       +        if not multi_select:
       +            column_title = self.headerItem().text(col)
       +            menu.addAction(_("Copy %s")%column_title, lambda: self.parent.app.clipboard().setText(item.text(col)))
       +            if col in self.editable_columns:
       +                menu.addAction(_("Edit %s")%column_title, lambda: self.editItem(item, col))
       +            menu.addAction(_("Request payment"), lambda: self.parent.receive_at(addr))
       +            menu.addAction(_('History'), lambda: self.parent.show_address(addr))
       +            menu.addAction(_('Public Keys'), lambda: self.parent.show_public_keys(addr))
       +            if self.wallet.can_export():
       +                menu.addAction(_("Private key"), lambda: self.parent.show_private_key(addr))
       +            if not self.wallet.is_watching_only():
       +                menu.addAction(_("Sign/verify message"), lambda: self.parent.sign_verify_message(addr))
       +                menu.addAction(_("Encrypt/decrypt message"), lambda: self.parent.encrypt_message(addr))
       +            if self.wallet.is_imported(addr):
       +                menu.addAction(_("Remove from wallet"), lambda: self.parent.delete_imported_key(addr))
       +            addr_URL = block_explorer_URL(self.config, 'addr', addr)
       +            if addr_URL:
       +                menu.addAction(_("View on block explorer"), lambda: webbrowser.open(addr_URL))
       +
       +        if any(not self.wallet.is_frozen(addr) for addr in addrs):
       +            menu.addAction(_("Freeze"), lambda: self.parent.set_frozen_state(addrs, True))
       +        if any(self.wallet.is_frozen(addr) for addr in addrs):
       +            menu.addAction(_("Unfreeze"), lambda: self.parent.set_frozen_state(addrs, False))
       +
       +        def can_send(addr):
       +            return not self.wallet.is_frozen(addr) and sum(self.wallet.get_addr_balance(addr)[:2])
       +        if any(can_send(addr) for addr in addrs):
       +            menu.addAction(_("Send From"), lambda: self.parent.send_from_addresses(addrs))
       +
       +        run_hook('receive_menu', menu, addrs, self.wallet)
       +        menu.exec_(self.viewport().mapToGlobal(position))
   DIR diff --git a/gui/qt/contact_list.py b/gui/qt/contact_list.py
       t@@ -0,0 +1,93 @@
       +#!/usr/bin/env python
       +#
       +# Electrum - lightweight Bitcoin client
       +# Copyright (C) 2015 Thomas Voegtlin
       +#
       +# Permission is hereby granted, free of charge, to any person
       +# obtaining a copy of this software and associated documentation files
       +# (the "Software"), to deal in the Software without restriction,
       +# including without limitation the rights to use, copy, modify, merge,
       +# publish, distribute, sublicense, and/or sell copies of the Software,
       +# and to permit persons to whom the Software is furnished to do so,
       +# subject to the following conditions:
       +#
       +# The above copyright notice and this permission notice shall be
       +# included in all copies or substantial portions of the Software.
       +#
       +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
       +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
       +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
       +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
       +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       +# SOFTWARE.
       +
       +
       +from electrum.i18n import _
       +from electrum.util import block_explorer_URL, format_satoshis, format_time, age
       +from electrum.plugins import run_hook
       +from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED
       +from PyQt4.QtGui import *
       +from PyQt4.QtCore import *
       +from util import MyTreeWidget, pr_tooltips, pr_icons
       +
       +
       +class ContactList(MyTreeWidget):
       +
       +    def __init__(self, parent):
       +        MyTreeWidget.__init__(self, parent, self.create_menu, [_('Name'), _('Value'), _('Type')], 1, [0, 1])
       +        self.setSelectionMode(QAbstractItemView.ExtendedSelection)
       +        self.setSortingEnabled(True)
       +
       +    def on_permit_edit(self, item, column):
       +        # openalias items shouldn't be editable
       +        return item.text(2) != "openalias"
       +
       +    def on_edited(self, item, column, prior):
       +        if column == 0:  # Remove old contact if renamed
       +            self.parent.contacts.pop(prior)
       +        self.parent.set_contact(unicode(item.text(0)), unicode(item.text(1)))
       +
       +    def create_menu(self, position):
       +        menu = QMenu()
       +        selected = self.selectedItems()
       +        if not selected:
       +            menu.addAction(_("New contact"), lambda: self.parent.new_contact_dialog())
       +        else:
       +            labels = [unicode(item.text(0)) for item in selected]
       +            addrs = [unicode(item.text(1)) for item in selected]
       +            types = [unicode(item.text(2)) for item in selected]
       +            column = self.currentColumn()
       +            column_title = self.headerItem().text(column)
       +            column_data = '\n'.join([unicode(item.text(column)) for item in selected])
       +
       +            menu.addAction(_("Copy %s")%column_title, lambda: self.parent.app.clipboard().setText(column_data))
       +            if column in self.editable_columns:
       +                menu.addAction(_("Edit %s")%column_title, lambda: self.editItem(item, column))
       +
       +            menu.addAction(_("Pay to"), lambda: self.parent.payto_contacts(labels))
       +            menu.addAction(_("Delete"), lambda: self.parent.delete_contacts(labels))
       +            URLs = []
       +            for (addr, _type) in zip(addrs, types):
       +                if _type == 'address':
       +                    URLs.append(block_explorer_URL(self.config, 'addr', addr))
       +            if URLs:
       +                menu.addAction(_("View on block explorer"),
       +                               lambda: map(webbrowser.open, URLs))
       +
       +        run_hook('create_contact_menu', menu, selected)
       +        menu.exec_(self.viewport().mapToGlobal(position))
       +
       +    def on_update(self):
       +        item = self.currentItem()
       +        current_key = item.data(0, Qt.UserRole).toString() if item else None
       +        self.clear()
       +        for key in sorted(self.parent.contacts.keys()):
       +            _type, value = self.parent.contacts[key]
       +            item = QTreeWidgetItem([key, value, _type])
       +            item.setData(0, Qt.UserRole, key)
       +            self.addTopLevelItem(item)
       +            if key == current_key:
       +                self.setCurrentItem(item)
       +        run_hook('update_contacts_tab', self)
   DIR diff --git a/gui/qt/history_list.py b/gui/qt/history_list.py
       t@@ -0,0 +1,142 @@
       +#!/usr/bin/env python
       +#
       +# Electrum - lightweight Bitcoin client
       +# Copyright (C) 2015 Thomas Voegtlin
       +#
       +# Permission is hereby granted, free of charge, to any person
       +# obtaining a copy of this software and associated documentation files
       +# (the "Software"), to deal in the Software without restriction,
       +# including without limitation the rights to use, copy, modify, merge,
       +# publish, distribute, sublicense, and/or sell copies of the Software,
       +# and to permit persons to whom the Software is furnished to do so,
       +# subject to the following conditions:
       +#
       +# The above copyright notice and this permission notice shall be
       +# included in all copies or substantial portions of the Software.
       +#
       +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
       +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
       +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
       +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
       +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       +# SOFTWARE.
       +
       +
       +import webbrowser
       +
       +from util import *
       +from electrum.i18n import _
       +from electrum.util import block_explorer_URL, format_satoshis, format_time
       +from electrum.plugins import run_hook
       +
       +
       +class HistoryList(MyTreeWidget):
       +
       +    def __init__(self, parent=None):
       +        MyTreeWidget.__init__(self, parent, self.create_menu, [], 3)
       +        self.refresh_headers()
       +        self.setColumnHidden(1, True)
       +
       +    def refresh_headers(self):
       +        headers = ['', '', _('Date'), _('Description') , _('Amount'),
       +                   _('Balance')]
       +        run_hook('history_tab_headers', headers)
       +        self.update_headers(headers)
       +
       +    def get_icon(self, conf, timestamp):
       +        time_str = _("unknown")
       +        if conf > 0:
       +            time_str = format_time(timestamp)
       +        if conf == -1:
       +            time_str = _('Not Verified')
       +            icon = QIcon(":icons/unconfirmed.png")
       +        elif conf == 0:
       +            time_str = _('Unconfirmed')
       +            icon = QIcon(":icons/unconfirmed.png")
       +        elif conf < 6:
       +            icon = QIcon(":icons/clock%d.png"%conf)
       +        else:
       +            icon = QIcon(":icons/confirmed.png")
       +        return icon, time_str
       +
       +    def get_domain(self):
       +        '''Replaced in address_dialog.py'''
       +        return self.wallet.get_account_addresses(self.parent.current_account)
       +
       +    def on_update(self):
       +        self.wallet = self.parent.wallet
       +        h = self.wallet.get_history(self.get_domain())
       +
       +        item = self.currentItem()
       +        current_tx = item.data(0, Qt.UserRole).toString() if item else None
       +        self.clear()
       +        run_hook('history_tab_update_begin')
       +        for tx in h:
       +            tx_hash, conf, value, timestamp, balance = tx
       +            if conf is None and timestamp is None:
       +                continue  # skip history in offline mode
       +            icon, time_str = self.get_icon(conf, timestamp)
       +            v_str = self.parent.format_amount(value, True, whitespaces=True)
       +            balance_str = self.parent.format_amount(balance, whitespaces=True)
       +            label = self.wallet.get_label(tx_hash)
       +            entry = ['', tx_hash, time_str, label, v_str, balance_str]
       +            run_hook('history_tab_update', tx, entry)
       +            item = QTreeWidgetItem(entry)
       +            item.setIcon(0, icon)
       +            for i in range(len(entry)):
       +                if i>3:
       +                    item.setTextAlignment(i, Qt.AlignRight)
       +                if i!=2:
       +                    item.setFont(i, QFont(MONOSPACE_FONT))
       +            if value < 0:
       +                item.setForeground(3, QBrush(QColor("#BC1E1E")))
       +                item.setForeground(4, QBrush(QColor("#BC1E1E")))
       +            if tx_hash:
       +                item.setData(0, Qt.UserRole, tx_hash)
       +            self.insertTopLevelItem(0, item)
       +            if current_tx == tx_hash:
       +                self.setCurrentItem(item)
       +
       +    def update_item(self, tx_hash, conf, timestamp):
       +        icon, time_str = self.get_icon(conf, timestamp)
       +        items = self.findItems(tx_hash, Qt.UserRole|Qt.MatchContains|Qt.MatchRecursive, column=1)
       +        if items:
       +            item = items[0]
       +            item.setIcon(0, icon)
       +            item.setText(2, time_str)
       +
       +    def create_menu(self, position):
       +        self.selectedIndexes()
       +        item = self.currentItem()
       +        if not item:
       +            return
       +        column = self.currentColumn()
       +        tx_hash = str(item.data(0, Qt.UserRole).toString())
       +        if not tx_hash:
       +            return
       +        if column is 0:
       +            column_title = "ID"
       +            column_data = tx_hash
       +        else:
       +            column_title = self.headerItem().text(column)
       +            column_data = item.text(column)
       +
       +        tx_URL = block_explorer_URL(self.config, 'tx', tx_hash)
       +        conf, timestamp = self.wallet.get_confirmations(tx_hash)
       +        tx = self.wallet.transactions.get(tx_hash)
       +        is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx)
       +        rbf = is_mine and (conf == 0) and tx and not tx.is_final()
       +        menu = QMenu()
       +
       +        menu.addAction(_("Copy %s")%column_title, lambda: self.parent.app.clipboard().setText(column_data))
       +        if column in self.editable_columns:
       +            menu.addAction(_("Edit %s")%column_title, lambda: self.editItem(item, column))
       +
       +        menu.addAction(_("Details"), lambda: self.parent.show_transaction(tx))
       +        if rbf:
       +            menu.addAction(_("Increase fee"), lambda: self.parent.bump_fee_dialog(tx))
       +        if tx_URL:
       +            menu.addAction(_("View on block explorer"), lambda: webbrowser.open(tx_URL))
       +        menu.exec_(self.viewport().mapToGlobal(position))
   DIR diff --git a/gui/qt/history_widget.py b/gui/qt/history_widget.py
       t@@ -1,132 +0,0 @@
       -#!/usr/bin/env python
       -#
       -# Electrum - lightweight Bitcoin client
       -# Copyright (C) 2015 Thomas Voegtlin
       -#
       -# Permission is hereby granted, free of charge, to any person
       -# obtaining a copy of this software and associated documentation files
       -# (the "Software"), to deal in the Software without restriction,
       -# including without limitation the rights to use, copy, modify, merge,
       -# publish, distribute, sublicense, and/or sell copies of the Software,
       -# and to permit persons to whom the Software is furnished to do so,
       -# subject to the following conditions:
       -#
       -# The above copyright notice and this permission notice shall be
       -# included in all copies or substantial portions of the Software.
       -#
       -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
       -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
       -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
       -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
       -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       -# SOFTWARE.
       -
       -
       -import webbrowser
       -
       -from util import *
       -from electrum.i18n import _
       -from electrum.util import block_explorer_URL, format_satoshis, format_time
       -from electrum.plugins import run_hook
       -
       -
       -class HistoryWidget(MyTreeWidget):
       -
       -    def __init__(self, parent=None):
       -        MyTreeWidget.__init__(self, parent, self.create_menu, [], 3)
       -        self.refresh_headers()
       -        self.setColumnHidden(1, True)
       -        self.config = self.parent.config
       -
       -    def refresh_headers(self):
       -        headers = ['', '', _('Date'), _('Description') , _('Amount'),
       -                   _('Balance')]
       -        run_hook('history_tab_headers', headers)
       -        self.update_headers(headers)
       -
       -    def get_icon(self, conf, timestamp):
       -        time_str = _("unknown")
       -        if conf > 0:
       -            time_str = format_time(timestamp)
       -        if conf == -1:
       -            time_str = _('Not Verified')
       -            icon = QIcon(":icons/unconfirmed.png")
       -        elif conf == 0:
       -            time_str = _('Unconfirmed')
       -            icon = QIcon(":icons/unconfirmed.png")
       -        elif conf < 6:
       -            icon = QIcon(":icons/clock%d.png"%conf)
       -        else:
       -            icon = QIcon(":icons/confirmed.png")
       -        return icon, time_str
       -
       -    def get_domain(self):
       -        '''Replaced in address_dialog.py'''
       -        return self.wallet.get_account_addresses(self.parent.current_account)
       -
       -    def on_update(self):
       -        self.wallet = self.parent.wallet
       -        h = self.wallet.get_history(self.get_domain())
       -
       -        item = self.currentItem()
       -        current_tx = item.data(0, Qt.UserRole).toString() if item else None
       -        self.clear()
       -        run_hook('history_tab_update_begin')
       -        for tx in h:
       -            tx_hash, conf, value, timestamp, balance = tx
       -            if conf is None and timestamp is None:
       -                continue  # skip history in offline mode
       -            icon, time_str = self.get_icon(conf, timestamp)
       -            v_str = self.parent.format_amount(value, True, whitespaces=True)
       -            balance_str = self.parent.format_amount(balance, whitespaces=True)
       -            label = self.wallet.get_label(tx_hash)
       -            entry = ['', tx_hash, time_str, label, v_str, balance_str]
       -            run_hook('history_tab_update', tx, entry)
       -            item = QTreeWidgetItem(entry)
       -            item.setIcon(0, icon)
       -            for i in range(len(entry)):
       -                if i>3:
       -                    item.setTextAlignment(i, Qt.AlignRight)
       -                if i!=2:
       -                    item.setFont(i, QFont(MONOSPACE_FONT))
       -            if value < 0:
       -                item.setForeground(3, QBrush(QColor("#BC1E1E")))
       -                item.setForeground(4, QBrush(QColor("#BC1E1E")))
       -            if tx_hash:
       -                item.setData(0, Qt.UserRole, tx_hash)
       -            self.insertTopLevelItem(0, item)
       -            if current_tx == tx_hash:
       -                self.setCurrentItem(item)
       -
       -    def update_item(self, tx_hash, conf, timestamp):
       -        icon, time_str = self.get_icon(conf, timestamp)
       -        items = self.findItems(tx_hash, Qt.UserRole|Qt.MatchContains|Qt.MatchRecursive, column=1)
       -        if items:
       -            item = items[0]
       -            item.setIcon(0, icon)
       -            item.setText(2, time_str)
       -
       -    def create_menu(self, position):
       -        self.selectedIndexes()
       -        item = self.currentItem()
       -        if not item:
       -            return
       -        tx_hash = str(item.data(0, Qt.UserRole).toString())
       -        if not tx_hash:
       -            return
       -        tx_URL = block_explorer_URL(self.config, 'tx', tx_hash)
       -        conf, timestamp = self.wallet.get_confirmations(tx_hash)
       -        tx = self.wallet.transactions.get(tx_hash)
       -        is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx)
       -        rbf = is_mine and (conf == 0) and tx and not tx.is_final()
       -        menu = QMenu()
       -        menu.addAction(_("Copy ID to Clipboard"), lambda: self.parent.app.clipboard().setText(tx_hash))
       -        menu.addAction(_("Details"), lambda: self.parent.show_transaction(tx))
       -        menu.addAction(_("Edit description"), lambda: self.editItem(item, self.editable_columns[0]))
       -        if rbf:
       -            menu.addAction(_("Increase fee"), lambda: self.parent.bump_fee_dialog(tx))
       -        if tx_URL:
       -            menu.addAction(_("View on block explorer"), lambda: webbrowser.open(tx_URL))
       -        menu.exec_(self.viewport().mapToGlobal(position))
   DIR diff --git a/gui/qt/invoice_list.py b/gui/qt/invoice_list.py
       t@@ -0,0 +1,77 @@
       +#!/usr/bin/env python
       +#
       +# Electrum - lightweight Bitcoin client
       +# Copyright (C) 2015 Thomas Voegtlin
       +#
       +# Permission is hereby granted, free of charge, to any person
       +# obtaining a copy of this software and associated documentation files
       +# (the "Software"), to deal in the Software without restriction,
       +# including without limitation the rights to use, copy, modify, merge,
       +# publish, distribute, sublicense, and/or sell copies of the Software,
       +# and to permit persons to whom the Software is furnished to do so,
       +# subject to the following conditions:
       +#
       +# The above copyright notice and this permission notice shall be
       +# included in all copies or substantial portions of the Software.
       +#
       +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
       +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
       +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
       +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
       +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       +# SOFTWARE.
       +
       +
       +from util import *
       +from electrum.i18n import _
       +from electrum.util import block_explorer_URL, format_satoshis, format_time
       +from electrum.plugins import run_hook
       +
       +
       +class InvoiceList(MyTreeWidget):
       +
       +    def __init__(self, parent):
       +        MyTreeWidget.__init__(self, parent, self.create_menu, [_('Expires'), _('Requestor'), _('Description'), _('Amount'), _('Status')], 2)
       +        self.setSortingEnabled(True)
       +        self.header().setResizeMode(1, QHeaderView.Interactive)
       +        self.setColumnWidth(1, 200)
       +
       +    def on_update(self):
       +        inv_list = self.parent.invoices.sorted_list()
       +        self.clear()
       +        for pr in inv_list:
       +            key = pr.get_id()
       +            status = self.parent.invoices.get_status(key)
       +            requestor = pr.get_requestor()
       +            exp = pr.get_expiration_date()
       +            date_str = format_time(exp) if exp else _('Never')
       +            item = QTreeWidgetItem([date_str, requestor, pr.memo, self.parent.format_amount(pr.get_amount(), whitespaces=True), pr_tooltips.get(status,'')])
       +            item.setIcon(4, QIcon(pr_icons.get(status)))
       +            item.setData(0, Qt.UserRole, key)
       +            item.setFont(1, QFont(MONOSPACE_FONT))
       +            item.setFont(3, QFont(MONOSPACE_FONT))
       +            self.addTopLevelItem(item)
       +        self.setCurrentItem(self.topLevelItem(0))
       +        self.setVisible(len(inv_list))
       +        self.parent.invoices_label.setVisible(len(inv_list))
       +
       +    def create_menu(self, position):
       +        item = self.itemAt(position)
       +        if not item:
       +            return
       +        key = str(item.data(0, 32).toString())
       +        column = self.currentColumn()        
       +        column_title = self.headerItem().text(column)
       +        column_data = item.text(column)
       +        pr = self.parent.invoices.get(key)
       +        status = self.parent.invoices.get_status(key)
       +        menu = QMenu()
       +        if column_data:
       +            menu.addAction(_("Copy %s")%column_title, lambda: self.parent.app.clipboard().setText(column_data))
       +        menu.addAction(_("Details"), lambda: self.parent.show_invoice(key))
       +        if status == PR_UNPAID:
       +            menu.addAction(_("Pay Now"), lambda: self.parent.do_pay_invoice(key))
       +        menu.addAction(_("Delete"), lambda: self.parent.delete_invoice(key))
       +        menu.exec_(self.viewport().mapToGlobal(position))
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -88,26 +88,6 @@ class StatusBarButton(QPushButton):
        
        from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED
        
       -pr_icons = {
       -    PR_UNPAID:":icons/unpaid.png",
       -    PR_PAID:":icons/confirmed.png",
       -    PR_EXPIRED:":icons/expired.png"
       -}
       -
       -pr_tooltips = {
       -    PR_UNPAID:_('Pending'),
       -    PR_PAID:_('Paid'),
       -    PR_EXPIRED:_('Expired')
       -}
       -
       -expiration_values = [
       -    (_('1 hour'), 60*60),
       -    (_('1 day'), 24*60*60),
       -    (_('1 week'), 7*24*60*60),
       -    (_('Never'), None)
       -]
       -
       -
        
        class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
        
       t@@ -305,7 +285,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                self.update_buttons_on_seed()
                self.update_console()
                self.clear_receive_tab()
       -        self.receive_list.update()
       +        self.request_list.update()
                self.tabs.show()
        
                try:
       t@@ -628,15 +608,15 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
        
            def update_tabs(self):
                self.history_list.update()
       -        self.receive_list.update()
       +        self.request_list.update()
                self.address_list.update()
       -        self.contacts_list.update()
       -        self.invoices_list.update()
       +        self.contact_list.update()
       +        self.invoice_list.update()
                self.update_completions()
        
            def create_history_tab(self):
       -        from history_widget import HistoryWidget
       -        self.history_list = l = HistoryWidget(self)
       +        from history_list import HistoryList
       +        self.history_list = l = HistoryList(self)
                return l
        
            def show_address(self, addr):
       t@@ -711,14 +691,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                grid.addLayout(buttons, 4, 1, 1, 2)
        
                self.receive_requests_label = QLabel(_('Requests'))
       -        self.receive_list = MyTreeWidget(self, self.receive_list_menu, [_('Date'), _('Account'), _('Address'), '', _('Description'), _('Amount'), _('Status')], 4)
       -        self.receive_list.currentItemChanged.connect(self.receive_item_changed)
       -        self.receive_list.itemClicked.connect(self.receive_item_changed)
       -        self.receive_list.setSortingEnabled(True)
       -        self.receive_list.setColumnWidth(0, 180)
       -        self.receive_list.hideColumn(1)
       -        self.receive_list.hideColumn(2)
       -        self.receive_list.on_update = self.update_receive_tab
       +
       +        from request_list import RequestList
       +        self.request_list = RequestList(self)
        
                # layout
                vbox_g = QVBoxLayout()
       t@@ -734,33 +709,16 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                vbox.addLayout(hbox)
                vbox.addStretch(1)
                vbox.addWidget(self.receive_requests_label)
       -        vbox.addWidget(self.receive_list)
       -        vbox.setStretchFactor(self.receive_list, 1000)
       +        vbox.addWidget(self.request_list)
       +        vbox.setStretchFactor(self.request_list, 1000)
        
                return w
        
       -    def receive_item_changed(self, item):
       -        if item is None:
       -            return
       -        if not self.receive_list.isItemSelected(item):
       -            return
       -        addr = str(item.text(2))
       -        req = self.wallet.receive_requests[addr]
       -        expires = util.age(req['time'] + req['exp']) if req.get('exp') else _('Never')
       -        amount = req['amount']
       -        message = self.wallet.labels.get(addr, '')
       -        self.receive_address_e.setText(addr)
       -        self.receive_message_e.setText(message)
       -        self.receive_amount_e.setAmount(amount)
       -        self.expires_combo.hide()
       -        self.expires_label.show()
       -        self.expires_label.setText(expires)
       -        self.new_request_button.setEnabled(True)
        
            def delete_payment_request(self, item):
                addr = str(item.text(2))
                self.wallet.remove_payment_request(addr, self.config)
       -        self.receive_list.update()
       +        self.request_list.update()
                self.clear_receive_tab()
        
            def get_request_URI(self, addr):
       t@@ -778,17 +736,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                    URI += "&name=" + req['name'] + "&sig="+sig
                return str(URI)
        
       -    def receive_list_menu(self, position):
       -        item = self.receive_list.itemAt(position)
       -        addr = str(item.text(2))
       -        req = self.wallet.receive_requests[addr]
       -        menu = QMenu(self)
       -        menu.addAction(_("Copy Address"), lambda: self.view_and_paste(_('Address'), '', addr))
       -        menu.addAction(_("Copy URI"), lambda: self.view_and_paste('URI', '', self.get_request_URI(addr)))
       -        menu.addAction(_("Save as BIP70 file"), lambda: self.export_payment_request(addr))
       -        menu.addAction(_("Delete"), lambda: self.delete_payment_request(item))
       -        run_hook('receive_list_menu', menu, addr)
       -        menu.exec_(self.receive_list.viewport().mapToGlobal(position))
        
            def sign_payment_request(self, addr):
                alias = self.config.get('alias')
       t@@ -823,7 +770,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                req = self.wallet.make_payment_request(addr, amount, message, expiration)
                self.wallet.add_payment_request(req, self.config)
                self.sign_payment_request(addr)
       -        self.receive_list.update()
       +        self.request_list.update()
                self.address_list.update()
                self.save_request_button.setEnabled(False)
        
       t@@ -901,52 +848,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                self.receive_address_e.setText(addr)
                self.new_request_button.setEnabled(True)
        
       -    def update_receive_tab(self):
       -
       -        # hide receive tab if no receive requests available
       -        b = len(self.wallet.receive_requests) > 0
       -        self.receive_list.setVisible(b)
       -        self.receive_requests_label.setVisible(b)
       -        if not b:
       -            self.expires_label.hide()
       -            self.expires_combo.show()
       -
       -        # check if it is necessary to show the account
       -        self.receive_list.setColumnHidden(1, len(self.wallet.get_accounts()) == 1)
       -
       -        # update the receive address if necessary
       -        current_address = self.receive_address_e.text()
       -        domain = self.wallet.get_account_addresses(self.current_account, include_change=False)
       -        addr = self.wallet.get_unused_address(self.current_account)
       -        if not current_address in domain and addr:
       -            self.set_receive_address(addr)
       -        self.new_request_button.setEnabled(addr != current_address)
       -
       -        # clear the list and fill it again
       -        self.receive_list.clear()
       -        for req in self.wallet.get_sorted_requests(self.config):
       -            address = req['address']
       -            if address not in domain:
       -                continue
       -            timestamp = req.get('time', 0)
       -            amount = req.get('amount')
       -            expiration = req.get('exp', None)
       -            message = req.get('memo', '')
       -            date = format_time(timestamp)
       -            status = req.get('status')
       -            signature = req.get('sig')
       -            requestor = req.get('name', '')
       -            amount_str = self.format_amount(amount) if amount else ""
       -            account = ''
       -            item = QTreeWidgetItem([date, account, address, '', message, amount_str, pr_tooltips.get(status,'')])
       -            if signature is not None:
       -                item.setIcon(3, QIcon(":icons/seal.png"))
       -                item.setToolTip(3, 'signed by '+ requestor)
       -            if status is not PR_UNKNOWN:
       -                item.setIcon(6, QIcon(pr_icons.get(status)))
       -            self.receive_list.addTopLevelItem(item)
       -
       -
            def update_receive_qr(self):
                addr = str(self.receive_address_e.text())
                amount = self.receive_amount_e.get_amount()
       t@@ -1101,12 +1002,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                self.fee_e.textChanged.connect(entry_changed)
        
                self.invoices_label = QLabel(_('Invoices'))
       -        self.invoices_list = MyTreeWidget(self, self.invoices_list_menu,
       -                                          [_('Expires'), _('Requestor'), _('Description'), _('Amount'), _('Status')], 2)
       -        self.invoices_list.setSortingEnabled(True)
       -        self.invoices_list.header().setResizeMode(1, QHeaderView.Interactive)
       -        self.invoices_list.setColumnWidth(1, 200)
       -        self.invoices_list.on_update = self.update_invoices_list
       +        from invoice_list import InvoiceList
       +        self.invoice_list = InvoiceList(self)
        
                vbox0 = QVBoxLayout()
                vbox0.addLayout(grid)
       t@@ -1117,8 +1014,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                vbox.addLayout(hbox)
                vbox.addStretch(1)
                vbox.addWidget(self.invoices_label)
       -        vbox.addWidget(self.invoices_list)
       -        vbox.setStretchFactor(self.invoices_list, 1000)
       +        vbox.addWidget(self.invoice_list)
       +        vbox.setStretchFactor(self.invoice_list, 1000)
        
                # Defer this until grid is parented to avoid ugly flash during startup
                self.update_fee_edit()
       t@@ -1402,7 +1299,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                            if tx_desc is not None and tx.is_complete():
                                self.wallet.set_label(tx.hash(), tx_desc)
                                parent.show_message(_('Payment sent.') + '\n' + msg)
       -                        self.invoices_list.update()
       +                        self.invoice_list.update()
                                self.do_clear()
                        else:
                            parent.show_error(msg)
       t@@ -1428,23 +1325,25 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                self.payto_e.setText(_("please wait..."))
                return True
        
       +    def delete_invoice(self, key):
       +        self.invoices.remove(key)
       +        self.invoice_list.update()
       +
            def payment_request_ok(self):
                pr = self.payment_request
                key = self.invoices.add(pr)
                status = self.invoices.get_status(key)
       -        self.invoices_list.update()
       +        self.invoice_list.update()
                if status == PR_PAID:
                    self.show_message("invoice already paid")
                    self.do_clear()
                    self.payment_request = None
                    return
       -
                self.payto_e.is_pr = True
                if not pr.has_expired():
                    self.payto_e.setGreen()
                else:
                    self.payto_e.setExpired()
       -
                self.payto_e.setText(pr.get_requestor())
                self.amount_e.setText(format_satoshis_plain(pr.get_amount(), self.decimal_point))
                self.message_e.setText(pr.get_memo())
       t@@ -1524,42 +1423,15 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                return w
        
            def create_addresses_tab(self):
       -        l = MyTreeWidget(self, self.create_address_menu, [ _('Address'), _('Label'), _('Balance'), _('Tx')], 1)
       -        l.setSelectionMode(QAbstractItemView.ExtendedSelection)
       -        l.on_update = self.update_address_tab
       -        self.address_list = l
       +        from address_list import AddressList
       +        self.address_list = l = AddressList(self)
                return self.create_list_tab(l)
        
            def create_contacts_tab(self):
       -        l = MyTreeWidget(self, self.create_contact_menu, [_('Name'), _('Value'), _('Type')], 1, [0, 1])
       -        l.setSelectionMode(QAbstractItemView.ExtendedSelection)
       -        l.setSortingEnabled(True)
       -        l.on_edited = self.on_contact_edited
       -        l.on_permit_edit = self.on_permit_contact_edit
       -        l.on_update = self.update_contacts_tab
       -        self.contacts_list = l
       +        from contact_list import ContactList
       +        self.contact_list = l = ContactList(self)
                return self.create_list_tab(l)
        
       -    def update_invoices_list(self):
       -        inv_list = self.invoices.sorted_list()
       -        l = self.invoices_list
       -        l.clear()
       -        for pr in inv_list:
       -            key = pr.get_id()
       -            status = self.invoices.get_status(key)
       -            requestor = pr.get_requestor()
       -            exp = pr.get_expiration_date()
       -            date_str = util.format_time(exp) if exp else _('Never')
       -            item = QTreeWidgetItem([date_str, requestor, pr.memo, self.format_amount(pr.get_amount(), whitespaces=True), pr_tooltips.get(status,'')])
       -            item.setIcon(4, QIcon(pr_icons.get(status)))
       -            item.setData(0, Qt.UserRole, key)
       -            item.setFont(1, QFont(MONOSPACE_FONT))
       -            item.setFont(3, QFont(MONOSPACE_FONT))
       -            l.addTopLevelItem(item)
       -        l.setCurrentItem(l.topLevelItem(0))
       -        self.invoices_list.setVisible(len(inv_list))
       -        self.invoices_label.setVisible(len(inv_list))
       -
            def delete_imported_key(self, addr):
                if self.question(_("Do you want to remove")+" %s "%addr +_("from your wallet?")):
                    self.wallet.delete_imported_key(addr)
       t@@ -1586,53 +1458,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                    menu.addAction(_("View details"), lambda: self.show_account_details(k))
                menu.exec_(self.address_list.viewport().mapToGlobal(position))
        
       -    def create_address_menu(self, position):
       -        selected = self.address_list.selectedItems()
       -        multi_select = len(selected) > 1
       -        addrs = [unicode(item.text(0)) for item in selected]
       -        if not multi_select:
       -            item = self.address_list.itemAt(position)
       -            if not item:
       -                return
       -            addr = addrs[0]
       -            if not is_valid(addr):
       -                k = str(item.data(0,32).toString())
       -                if k:
       -                    self.create_account_menu(position, k, item)
       -                else:
       -                    item.setExpanded(not item.isExpanded())
       -                return
       -        menu = QMenu()
       -        if not multi_select:
       -            menu.addAction(_("Copy to clipboard"), lambda: self.app.clipboard().setText(addr))
       -            menu.addAction(_("Request payment"), lambda: self.receive_at(addr))
       -            menu.addAction(_("Edit label"), lambda: self.address_list.editItem(item, self.address_list.editable_columns[0]))
       -            menu.addAction(_('History'), lambda: self.show_address(addr))
       -            menu.addAction(_('Public Keys'), lambda: self.show_public_keys(addr))
       -            if self.wallet.can_export():
       -                menu.addAction(_("Private key"), lambda: self.show_private_key(addr))
       -            if not self.wallet.is_watching_only():
       -                menu.addAction(_("Sign/verify message"), lambda: self.sign_verify_message(addr))
       -                menu.addAction(_("Encrypt/decrypt message"), lambda: self.encrypt_message(addr))
       -            if self.wallet.is_imported(addr):
       -                menu.addAction(_("Remove from wallet"), lambda: self.delete_imported_key(addr))
       -            addr_URL = block_explorer_URL(self.config, 'addr', addr)
       -            if addr_URL:
       -                menu.addAction(_("View on block explorer"), lambda: webbrowser.open(addr_URL))
       -
       -        if any(not self.wallet.is_frozen(addr) for addr in addrs):
       -            menu.addAction(_("Freeze"), lambda: self.set_frozen_state(addrs, True))
       -        if any(self.wallet.is_frozen(addr) for addr in addrs):
       -            menu.addAction(_("Unfreeze"), lambda: self.set_frozen_state(addrs, False))
       -
       -        def can_send(addr):
       -            return not self.wallet.is_frozen(addr) and sum(self.wallet.get_addr_balance(addr)[:2])
       -        if any(can_send(addr) for addr in addrs):
       -            menu.addAction(_("Send From"), lambda: self.send_from_addresses(addrs))
       -
       -        run_hook('receive_menu', menu, addrs, self.wallet)
       -        menu.exec_(self.address_list.viewport().mapToGlobal(position))
       -
        
            def get_coins(self):
                if self.pay_from:
       t@@ -1669,22 +1494,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                    self.payto_e.setText(text)
                    self.payto_e.setFocus()
        
       -    def on_permit_contact_edit(self, item, column):
       -        # openalias items shouldn't be editable
       -        return item.text(2) != "openalias"
       -
       -    def on_contact_edited(self, item, column, prior):
       -        if column == 0:  # Remove old contact if renamed
       -            self.contacts.pop(prior)
       -        self.set_contact(unicode(item.text(0)), unicode(item.text(1)))
       -
            def set_contact(self, label, address):
                if not is_valid(address):
                    self.show_error(_('Invalid Address'))
       -            self.contacts_list.update()  # Displays original unchanged value
       +            self.contact_list.update()  # Displays original unchanged value
                    return False
                self.contacts[label] = ('address', address)
       -        self.contacts_list.update()
       +        self.contact_list.update()
                self.history_list.update()
                self.update_completions()
                return True
       t@@ -1696,33 +1512,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                for label in labels:
                    self.contacts.pop(label)
                self.history_list.update()
       -        self.contacts_list.update()
       +        self.contact_list.update()
                self.update_completions()
        
       -    def create_contact_menu(self, position):
       -        menu = QMenu()
       -        selected = self.contacts_list.selectedItems()
       -        if not selected:
       -            menu.addAction(_("New contact"), lambda: self.new_contact_dialog())
       -        else:
       -            labels = [unicode(item.text(0)) for item in selected]
       -            addrs = [unicode(item.text(1)) for item in selected]
       -            types = [unicode(item.text(2)) for item in selected]
       -            menu.addAction(_("Copy to Clipboard"), lambda:
       -                           self.app.clipboard().setText('\n'.join(labels)))
       -            menu.addAction(_("Pay to"), lambda: self.payto_contacts(labels))
       -            menu.addAction(_("Delete"), lambda: self.delete_contacts(labels))
       -            URLs = []
       -            for (addr, _type) in zip(addrs, types):
       -                if _type == 'address':
       -                    URLs.append(block_explorer_URL(self.config, 'addr', addr))
       -            if URLs:
       -                menu.addAction(_("View on block explorer"),
       -                               lambda: map(webbrowser.open, URLs))
       -
       -        run_hook('create_contact_menu', menu, selected)
       -        menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
       -
        
            def show_invoice(self, key):
                pr = self.invoices.get(key)
       t@@ -1766,107 +1558,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                else:
                    self.payment_request_error()
        
       -
       -    def invoices_list_menu(self, position):
       -        item = self.invoices_list.itemAt(position)
       -        if not item:
       -            return
       -        key = str(item.data(0, 32).toString())
       -        pr = self.invoices.get(key)
       -        status = self.invoices.get_status(key)
       -        menu = QMenu()
       -        menu.addAction(_("Details"), lambda: self.show_invoice(key))
       -        if status == PR_UNPAID:
       -            menu.addAction(_("Pay Now"), lambda: self.do_pay_invoice(key))
       -        def delete_invoice(key):
       -            self.invoices.remove(key)
       -            self.invoices_list.update()
       -        menu.addAction(_("Delete"), lambda: delete_invoice(key))
       -        menu.exec_(self.invoices_list.viewport().mapToGlobal(position))
       -
       -
       -    def update_address_tab(self):
       -        l = self.address_list
       -        item = l.currentItem()
       -        current_address = item.data(0, Qt.UserRole).toString() if item else None
       -        l.clear()
       -        accounts = self.wallet.get_accounts()
       -        if self.current_account is None:
       -            account_items = sorted(accounts.items())
       -        else:
       -            account_items = [(self.current_account, accounts.get(self.current_account))]
       -        for k, account in account_items:
       -            if len(accounts) > 1:
       -                name = self.wallet.get_account_name(k)
       -                c, u, x = self.wallet.get_account_balance(k)
       -                account_item = QTreeWidgetItem([ name, '', self.format_amount(c + u + x), ''])
       -                account_item.setExpanded(self.accounts_expanded.get(k, True))
       -                account_item.setData(0, Qt.UserRole, k)
       -                l.addTopLevelItem(account_item)
       -            else:
       -                account_item = l
       -            sequences = [0,1] if account.has_change() else [0]
       -            for is_change in sequences:
       -                if len(sequences) > 1:
       -                    name = _("Receiving") if not is_change else _("Change")
       -                    seq_item = QTreeWidgetItem( [ name, '', '', '', ''] )
       -                    account_item.addChild(seq_item)
       -                    if not is_change:
       -                        seq_item.setExpanded(True)
       -                else:
       -                    seq_item = account_item
       -                used_item = QTreeWidgetItem( [ _("Used"), '', '', '', ''] )
       -                used_flag = False
       -                addr_list = account.get_addresses(is_change)
       -                for address in addr_list:
       -                    num = len(self.wallet.history.get(address,[]))
       -                    is_used = self.wallet.is_used(address)
       -                    label = self.wallet.labels.get(address,'')
       -                    c, u, x = self.wallet.get_addr_balance(address)
       -                    balance = self.format_amount(c + u + x)
       -                    address_item = QTreeWidgetItem([address, label, balance, "%d"%num])
       -                    address_item.setFont(0, QFont(MONOSPACE_FONT))
       -                    address_item.setData(0, Qt.UserRole, address)
       -                    address_item.setData(0, Qt.UserRole+1, True) # label can be edited
       -                    if self.wallet.is_frozen(address):
       -                        address_item.setBackgroundColor(0, QColor('lightblue'))
       -                    if self.wallet.is_beyond_limit(address, account, is_change):
       -                        address_item.setBackgroundColor(0, QColor('red'))
       -                    if is_used:
       -                        if not used_flag:
       -                            seq_item.insertChild(0, used_item)
       -                            used_flag = True
       -                        used_item.addChild(address_item)
       -                    else:
       -                        seq_item.addChild(address_item)
       -                    if address == current_address:
       -                        l.setCurrentItem(address_item)
       -                    # add utxos
       -                    utxos = self.wallet.get_addr_utxo(address)
       -                    for x in utxos:
       -                        h = x.get('prevout_hash')
       -                        s = h + ":%d"%x.get('prevout_n')
       -                        label = self.wallet.get_label(h)
       -                        utxo_item = QTreeWidgetItem([s, label, self.format_amount(x['value'])])
       -                        utxo_item.setFont(0, QFont(MONOSPACE_FONT))
       -                        address_item.addChild(utxo_item)
       -
       -
       -    def update_contacts_tab(self):
       -        l = self.contacts_list
       -        item = l.currentItem()
       -        current_key = item.data(0, Qt.UserRole).toString() if item else None
       -        l.clear()
       -        for key in sorted(self.contacts.keys()):
       -            _type, value = self.contacts[key]
       -            item = QTreeWidgetItem([key, value, _type])
       -            item.setData(0, Qt.UserRole, key)
       -            l.addTopLevelItem(item)
       -            if key == current_key:
       -                l.setCurrentItem(item)
       -        run_hook('update_contacts_tab', l)
       -
       -
            def create_console_tab(self):
                from console import Console
                self.console = console = Console()
       t@@ -1906,7 +1597,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                self.history_list.update()
                self.update_status()
                self.address_list.update()
       -        self.receive_list.update()
       +        self.request_list.update()
        
            def create_status_bar(self):
        
       t@@ -1993,13 +1684,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                if i == 0:
                    self.history_list.filter(t, [2, 3, 4])  # Date, Description, Amount
                elif i == 1:
       -            self.invoices_list.filter(t, [0, 1, 2, 3]) # Date, Requestor, Description, Amount
       +            self.invoice_list.filter(t, [0, 1, 2, 3]) # Date, Requestor, Description, Amount
                elif i == 2:
       -            self.receive_list.filter(t, [0, 1, 2, 3, 4]) # Date, Account, Address, Description, Amount
       +            self.request_list.filter(t, [0, 1, 2, 3, 4]) # Date, Account, Address, Description, Amount
                elif i == 3:
                    self.address_list.filter(t, [0,1, 2])  # Address, Label, Balance
                elif i == 4:
       -            self.contacts_list.filter(t, [0, 1])  # Key, Value
       +            self.contact_list.filter(t, [0, 1])  # Key, Value
        
        
            def new_contact_dialog(self):
       t@@ -2795,7 +2486,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                        raise Exception('Unknown base unit')
                    self.config.set_key('decimal_point', self.decimal_point, True)
                    self.history_list.update()
       -            self.receive_list.update()
       +            self.request_list.update()
                    self.address_list.update()
                    for edit, amount in zip(edits, amounts):
                        edit.setAmount(amount)
   DIR diff --git a/gui/qt/request_list.py b/gui/qt/request_list.py
       t@@ -0,0 +1,124 @@
       +#!/usr/bin/env python
       +#
       +# Electrum - lightweight Bitcoin client
       +# Copyright (C) 2015 Thomas Voegtlin
       +#
       +# Permission is hereby granted, free of charge, to any person
       +# obtaining a copy of this software and associated documentation files
       +# (the "Software"), to deal in the Software without restriction,
       +# including without limitation the rights to use, copy, modify, merge,
       +# publish, distribute, sublicense, and/or sell copies of the Software,
       +# and to permit persons to whom the Software is furnished to do so,
       +# subject to the following conditions:
       +#
       +# The above copyright notice and this permission notice shall be
       +# included in all copies or substantial portions of the Software.
       +#
       +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
       +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
       +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
       +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
       +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
       +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
       +# SOFTWARE.
       +
       +
       +from electrum.i18n import _
       +from electrum.util import block_explorer_URL, format_satoshis, format_time, age
       +from electrum.plugins import run_hook
       +from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED
       +from PyQt4.QtGui import *
       +from PyQt4.QtCore import *
       +from util import MyTreeWidget, pr_tooltips, pr_icons
       +
       +
       +class RequestList(MyTreeWidget):
       +
       +    def __init__(self, parent):
       +        MyTreeWidget.__init__(self, parent, self.create_menu, [_('Date'), _('Account'), _('Address'), '', _('Description'), _('Amount'), _('Status')], 4)
       +        self.currentItemChanged.connect(self.item_changed)
       +        self.itemClicked.connect(self.item_changed)
       +        self.setSortingEnabled(True)
       +        self.setColumnWidth(0, 180)
       +        self.hideColumn(1)
       +        self.hideColumn(2)
       +
       +    def item_changed(self, item):
       +        if item is None:
       +            return
       +        if not self.isItemSelected(item):
       +            return
       +        addr = str(item.text(2))
       +        req = self.wallet.receive_requests[addr]
       +        expires = age(req['time'] + req['exp']) if req.get('exp') else _('Never')
       +        amount = req['amount']
       +        message = self.wallet.labels.get(addr, '')
       +        self.parent.receive_address_e.setText(addr)
       +        self.parent.receive_message_e.setText(message)
       +        self.parent.receive_amount_e.setAmount(amount)
       +        self.parent.expires_combo.hide()
       +        self.parent.expires_label.show()
       +        self.parent.expires_label.setText(expires)
       +        self.parent.new_request_button.setEnabled(True)
       +
       +    def on_update(self):
       +        self.wallet = self.parent.wallet
       +        # hide receive tab if no receive requests available
       +        b = len(self.wallet.receive_requests) > 0
       +        self.setVisible(b)
       +        self.parent.receive_requests_label.setVisible(b)
       +        if not b:
       +            self.parent.expires_label.hide()
       +            self.parent.expires_combo.show()
       +
       +        # check if it is necessary to show the account
       +        self.setColumnHidden(1, len(self.wallet.get_accounts()) == 1)
       +
       +        # update the receive address if necessary
       +        current_address = self.parent.receive_address_e.text()
       +        domain = self.wallet.get_account_addresses(self.parent.current_account, include_change=False)
       +        addr = self.wallet.get_unused_address(self.parent.current_account)
       +        if not current_address in domain and addr:
       +            self.parent.set_receive_address(addr)
       +        self.parent.new_request_button.setEnabled(addr != current_address)
       +
       +        # clear the list and fill it again
       +        self.clear()
       +        for req in self.wallet.get_sorted_requests(self.config):
       +            address = req['address']
       +            if address not in domain:
       +                continue
       +            timestamp = req.get('time', 0)
       +            amount = req.get('amount')
       +            expiration = req.get('exp', None)
       +            message = req.get('memo', '')
       +            date = format_time(timestamp)
       +            status = req.get('status')
       +            signature = req.get('sig')
       +            requestor = req.get('name', '')
       +            amount_str = self.parent.format_amount(amount) if amount else ""
       +            account = ''
       +            item = QTreeWidgetItem([date, account, address, '', message, amount_str, pr_tooltips.get(status,'')])
       +            if signature is not None:
       +                item.setIcon(3, QIcon(":icons/seal.png"))
       +                item.setToolTip(3, 'signed by '+ requestor)
       +            if status is not PR_UNKNOWN:
       +                item.setIcon(6, QIcon(pr_icons.get(status)))
       +            self.addTopLevelItem(item)
       +
       +
       +    def create_menu(self, position):
       +        item = self.itemAt(position)
       +        addr = str(item.text(2))
       +        req = self.wallet.receive_requests[addr]
       +        column = self.currentColumn()
       +        column_title = self.headerItem().text(column)
       +        column_data = item.text(column)
       +        menu = QMenu(self)
       +        menu.addAction(_("Copy %s")%column_title, lambda: self.parent.app.clipboard().setText(column_data))
       +        menu.addAction(_("Copy URI"), lambda: self.parent.view_and_paste('URI', '', self.parent.get_request_URI(addr)))
       +        menu.addAction(_("Save as BIP70 file"), lambda: self.parent.export_payment_request(addr))
       +        menu.addAction(_("Delete"), lambda: self.parent.delete_payment_request(item))
       +        run_hook('receive_list_menu', menu, addr)
       +        menu.exec_(self.viewport().mapToGlobal(position))
   DIR diff --git a/gui/qt/util.py b/gui/qt/util.py
       t@@ -27,6 +27,28 @@ BLACK_FG = "QWidget {color:black;}"
        
        dialogs = []
        
       +from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED
       +
       +pr_icons = {
       +    PR_UNPAID:":icons/unpaid.png",
       +    PR_PAID:":icons/confirmed.png",
       +    PR_EXPIRED:":icons/expired.png"
       +}
       +
       +pr_tooltips = {
       +    PR_UNPAID:_('Pending'),
       +    PR_PAID:_('Paid'),
       +    PR_EXPIRED:_('Expired')
       +}
       +
       +expiration_values = [
       +    (_('1 hour'), 60*60),
       +    (_('1 day'), 24*60*60),
       +    (_('1 week'), 7*24*60*60),
       +    (_('Never'), None)
       +]
       +
       +
        class Timer(QThread):
            stopped = False
        
       t@@ -351,6 +373,7 @@ class MyTreeWidget(QTreeWidget):
                         editable_columns=None):
                QTreeWidget.__init__(self, parent)
                self.parent = parent
       +        self.config = self.parent.config
                self.stretch_column = stretch_column
                self.setContextMenuPolicy(Qt.CustomContextMenu)
                self.customContextMenuRequested.connect(create_menu)
       t@@ -366,7 +389,7 @@ class MyTreeWidget(QTreeWidget):
                    editable_columns = [stretch_column]
                self.editable_columns = editable_columns
                self.setItemDelegate(ElectrumItemDelegate(self))
       -        self.itemActivated.connect(self.on_activated)
       +        self.itemDoubleClicked.connect(self.on_doubleclick)
                self.update_headers(headers)
        
            def update_headers(self, headers):
       t@@ -386,7 +409,7 @@ class MyTreeWidget(QTreeWidget):
                    item.setFlags(item.flags() & ~Qt.ItemIsEditable)
        
            def keyPressEvent(self, event):
       -        if event.key() == Qt.Key_F2:
       +        if event.key() in [ Qt.Key_F2, Qt.Key_Return ] and self.editor is None:
                    self.on_activated(self.currentItem(), self.currentColumn())
                else:
                    QTreeWidget.keyPressEvent(self, event)
       t@@ -398,13 +421,15 @@ class MyTreeWidget(QTreeWidget):
            def on_permit_edit(self, item, column):
                return True
        
       -    def on_activated(self, item, column):
       +    def on_doubleclick(self, item, column):
                if self.permit_edit(item, column):
                    self.editItem(item, column)
       -        else:
       -            pt = self.visualItemRect(item).bottomLeft()
       -            pt.setX(50)
       -            self.emit(SIGNAL('customContextMenuRequested(const QPoint&)'), pt)
       +
       +    def on_activated(self, item, column):
       +        # on 'enter' we show the menu
       +        pt = self.visualItemRect(item).bottomLeft()
       +        pt.setX(50)
       +        self.emit(SIGNAL('customContextMenuRequested(const QPoint&)'), pt)
        
            def createEditor(self, parent, option, index):
                self.editor = QStyledItemDelegate.createEditor(self.itemDelegate(),