URI: 
       tRework MyTreeWidget editing - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 544b829f6e43de5ab20d8a6c414d7689e13400d9
   DIR parent c481e61417c2c0b9f0432bc662f3dc8453fdacd6
  HTML Author: Neil Booth <kyuupichan@gmail.com>
       Date:   Wed,  9 Sep 2015 07:38:54 +0900
       
       Rework MyTreeWidget editing
       
       Gets rid of need for EditableItem class.
       New callback on_permit_edit to permit widgets to refuse editing.
       Restores popup menu on activating a non-editable column behaviour.
       
       Diffstat:
         M gui/qt/history_widget.py            |       3 +--
         M gui/qt/main_window.py               |      24 +++++++++++++-----------
         M gui/qt/util.py                      |      94 +++++++++++++++++++------------
       
       3 files changed, 72 insertions(+), 49 deletions(-)
       ---
   DIR diff --git a/gui/qt/history_widget.py b/gui/qt/history_widget.py
       t@@ -32,7 +32,6 @@ class HistoryWidget(MyTreeWidget):
                self.refresh_headers()
                self.setColumnHidden(1, True)
                self.config = self.parent.config
       -        self.setSortingEnabled(False)
        
            def refresh_headers(self):
                headers = ['', '', _('Date'), _('Description') , _('Amount'),
       t@@ -72,7 +71,7 @@ class HistoryWidget(MyTreeWidget):
                    label, is_default_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 = EditableItem(entry)
       +            item = QTreeWidgetItem(entry)
                    item.setIcon(0, icon)
                    for i in range(len(entry)):
                        if i>3:
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -855,7 +855,7 @@ class ElectrumWindow(QMainWindow, PrintError):
                    requestor = req.get('name', '')
                    amount_str = self.format_amount(amount) if amount else ""
                    account = ''
       -            item = EditableItem([date, account, address, '', message, amount_str, pr_tooltips.get(status,'')])
       +            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)
       t@@ -921,7 +921,6 @@ class ElectrumWindow(QMainWindow, PrintError):
                self.from_label = QLabel(_('From'))
                grid.addWidget(self.from_label, 3, 0)
                self.from_list = MyTreeWidget(self, self.from_list_menu, ['',''])
       -        self.from_list.setSortingEnabled(False)
                self.from_list.setHeaderHidden(True)
                self.from_list.setMaximumHeight(80)
                grid.addWidget(self.from_list, 3, 1, 1, 3)
       t@@ -1000,6 +999,7 @@ class ElectrumWindow(QMainWindow, PrintError):
                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)
        
       t@@ -1406,14 +1406,15 @@ class ElectrumWindow(QMainWindow, PrintError):
            def create_addresses_tab(self):
                l = MyTreeWidget(self, self.create_receive_menu, [ _('Address'), _('Label'), _('Balance'), _('Tx')], 1)
                l.setSelectionMode(QAbstractItemView.ExtendedSelection)
       -        l.setSortingEnabled(False)
                self.address_list = l
                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.item_edited = self.contact_edited
       +        l.setSortingEnabled(True)
       +        l.on_edited = self.on_contact_edited
       +        l.on_permit_edit = self.on_permit_contact_edit
                self.contacts_list = l
                return self.create_list_tab(l)
        
       t@@ -1427,7 +1428,7 @@ class ElectrumWindow(QMainWindow, PrintError):
                    requestor = pr.get_requestor()
                    exp = pr.get_expiration_date()
                    date_str = util.format_time(exp) if exp else _('Never')
       -            item = EditableItem([date_str, requestor, pr.memo, self.format_amount(pr.get_amount(), whitespaces=True), pr_tooltips.get(status,'')])
       +            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))
       t@@ -1551,7 +1552,11 @@ class ElectrumWindow(QMainWindow, PrintError):
                    self.payto_e.setText(text)
                    self.payto_e.setFocus()
        
       -    def contact_edited(self, item, column, prior):
       +    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)))
       t@@ -1703,7 +1708,7 @@ class ElectrumWindow(QMainWindow, PrintError):
                            label = self.wallet.labels.get(address,'')
                            c, u, x = self.wallet.get_addr_balance(address)
                            balance = self.format_amount(c + u + x)
       -                    item = EditableItem( [ address, label, balance, "%d"%num] )
       +                    item = QTreeWidgetItem([address, label, balance, "%d"%num])
                            item.setFont(0, QFont(MONOSPACE_FONT))
                            item.setData(0, Qt.UserRole, address)
                            item.setData(0, Qt.UserRole+1, True) # label can be edited
       t@@ -1729,10 +1734,7 @@ class ElectrumWindow(QMainWindow, PrintError):
                l.clear()
                for key in sorted(self.contacts.keys()):
                    _type, value = self.contacts[key]
       -            if _type == 'address':
       -                item = EditableItem([key, value, _type])
       -            else: # openalias items shouldn't be editable
       -                item = QTreeWidgetItem([key, value, _type])
       +            item = QTreeWidgetItem([key, value, _type])
                    item.setData(0, Qt.UserRole, key)
                    l.addTopLevelItem(item)
                    if key == current_key:
   DIR diff --git a/gui/qt/util.py b/gui/qt/util.py
       t@@ -283,19 +283,9 @@ def filename_field(parent, config, defaultname, select_msg):
        
            return vbox, filename_e, b1
        
       -class EditableItem(QTreeWidgetItem):
       -    def __init__(self, columns):
       -        QTreeWidgetItem.__init__(self, columns)
       -        self.setFlags(self.flags() | Qt.ItemIsEditable)
       -
       -class EditableItemDelegate(QStyledItemDelegate):
       +class ElectrumItemDelegate(QStyledItemDelegate):
            def createEditor(self, parent, option, index):
       -        if index.column() not in self.parent().editable_columns:
       -            return None
       -        self.parent().editing = (self.parent().currentItem(),
       -                                 index.column(),
       -                                 unicode(index.data().toString()))
       -        return QStyledItemDelegate.createEditor(self, parent, option, index)
       +        return self.parent().createEditor(parent, option, index)
        
        class MyTreeWidget(QTreeWidget):
        
       t@@ -305,23 +295,20 @@ class MyTreeWidget(QTreeWidget):
                self.parent = parent
                self.stretch_column = stretch_column
                self.setContextMenuPolicy(Qt.CustomContextMenu)
       -        self.itemActivated.connect(self.on_activated)
                self.customContextMenuRequested.connect(create_menu)
       +        self.setUniformRowHeights(True)
                # extend the syntax for consistency
                self.addChild = self.addTopLevelItem
                self.insertChild = self.insertTopLevelItem
        
                # Control which columns are editable
       -        self.editing = (None, None, None)
       +        self.editor = None
                if editable_columns is None:
                    editable_columns = [stretch_column]
                self.editable_columns = editable_columns
       -        self.setEditTriggers(QAbstractItemView.DoubleClicked |
       -                             QAbstractItemView.EditKeyPressed)
       -        self.setItemDelegate(EditableItemDelegate(self))
       -        self.itemChanged.connect(self.item_changed)
       +        self.setItemDelegate(ElectrumItemDelegate(self))
       +        self.itemActivated.connect(self.on_activated)
                self.update_headers(headers)
       -        self.setSortingEnabled(True)
        
            def update_headers(self, headers):
                self.setColumnCount(len(headers))
       t@@ -331,26 +318,61 @@ class MyTreeWidget(QTreeWidget):
                    sm = QHeaderView.Stretch if col == self.stretch_column else QHeaderView.ResizeToContents
                    self.header().setResizeMode(col, sm)
        
       -    def on_activated(self, item):
       -        if not item:
       -            return
       -        for i in range(0,self.viewport().height()/5):
       -            if self.itemAt(QPoint(0,i*5)) == item:
       -                break
       +    def editItem(self, item, column):
       +        if column in self.editable_columns:
       +            self.editing_itemcol = (item, column, unicode(item.text(column)))
       +            # Calling setFlags causes on_changed events for some reason
       +            item.setFlags(item.flags() | Qt.ItemIsEditable)
       +            QTreeWidget.editItem(self, item, column)
       +            item.setFlags(item.flags() & ~Qt.ItemIsEditable)
       +
       +    def keyPressEvent(self, event):
       +        if event.key() == Qt.Key_F2:
       +            self.on_activated(self.currentItem(), self.currentColumn())
                else:
       -            return
       -        for j in range(0,30):
       -            if self.itemAt(QPoint(0,i*5 + j)) != item:
       -                break
       -        self.emit(SIGNAL('customContextMenuRequested(const QPoint&)'), QPoint(50, i*5 + j - 1))
       +            QTreeWidget.keyPressEvent(self, event)
        
       -    def item_changed(self, item, column):
       -        '''Called only when the text actually changes'''
       -        # Only pass user edits to item_edited()
       -        if item == self.editing[0] and column == self.editing[1]:
       -            self.item_edited(item, column, self.editing[2])
       +    def permit_edit(self, item, column):
       +        return (column in self.editable_columns
       +                and self.on_permit_edit(item, column))
       +
       +    def on_permit_edit(self, item, column):
       +        return True
        
       -    def item_edited(self, item, column, prior):
       +    def on_activated(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 createEditor(self, parent, option, index):
       +        self.editor = QStyledItemDelegate.createEditor(self.itemDelegate(),
       +                                                       parent, option, index)
       +        self.editor.connect(self.editor, SIGNAL("editingFinished()"),
       +                            self.editing_finished)
       +        return self.editor
       +
       +    def editing_finished(self):
       +        # Long-time QT bug - pressing Enter to finish editing signals
       +        # editingFinished twice.  If the item changed the sequence is
       +        # Enter key:  editingFinished, on_change, editingFinished
       +        # Mouse: on_change, editingFinished
       +        # This mess is the cleanest way to ensure we make the
       +        # on_edited callback with the updated item
       +        if self.editor:
       +            (item, column, prior_text) = self.editing_itemcol
       +            if self.editor.text() == prior_text:
       +                self.editor = None  # Unchanged - ignore any 2nd call
       +            elif item.text(column) == prior_text:
       +                pass # Buggy first call on Enter key, item not yet updated
       +            else:
       +                # What we want - the updated item
       +                self.on_edited(*self.editing_itemcol)
       +                self.editor = None
       +
       +    def on_edited(self, item, column, prior):
                '''Called only when the text actually changes'''
                key = str(item.data(0, Qt.UserRole).toString())
                text = unicode(item.text(column))