qutebrowser/qutebrowser/utils/completion.py

192 lines
5.6 KiB
Python
Raw Normal View History

2014-01-27 21:42:00 +01:00
from collections import OrderedDict
from PyQt5.QtCore import (QAbstractItemModel, Qt, QModelIndex, QVariant,
QSortFilterProxyModel, pyqtSignal)
class CompletionModel(QAbstractItemModel):
def __init__(self, parent=None):
super().__init__(parent)
self._data = OrderedDict()
self.parents = []
self.root = CompletionItem([""] * 2)
def removeRows(self, position=0, count=1, parent=QModelIndex()):
2014-01-28 19:52:09 +01:00
node = self.node(parent)
self.beginRemoveRows(parent, position, position + count - 1)
node.children.pop(position)
self.endRemoveRows()
2014-01-27 21:42:00 +01:00
def node(self, index):
if index.isValid():
return index.internalPointer()
else:
return self.root
def columnCount(self, parent=QModelIndex()):
2014-01-28 19:52:09 +01:00
# pylint: disable=unused-argument
2014-01-27 21:42:00 +01:00
return self.root.column_count()
def data(self, index, role=Qt.DisplayRole):
if not index.isValid():
return QVariant()
item = index.internalPointer()
try:
return QVariant(item.data(index.column(), role))
except (IndexError, ValueError):
return QVariant()
def flags(self, index):
# FIXME categories are not selectable, but moving via arrow keys still
# tries to select them
if not index.isValid():
return Qt.NoItemFlags
flags = Qt.ItemIsEnabled
if len(index.internalPointer().children) > 0:
return flags
else:
return flags | Qt.ItemIsSelectable
def headerData(self, section, orientation, role=Qt.DisplayRole):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return QVariant(self.root.data(section))
return None
def setData(self, index, value, role=Qt.EditRole):
if not index.isValid():
return False
item = index.internalPointer()
try:
item.setdata(index.column(), value, role)
except (IndexError, ValueError):
return False
self.dataChanged.emit(index, index)
return True
def index(self, row, column, parent=QModelIndex()):
if (0 <= row < self.rowCount(parent) and
0 <= column < self.columnCount(parent)):
pass
else:
return QModelIndex()
if not parent.isValid():
parent_item = self.root
else:
parent_item = parent.internalPointer()
child_item = parent_item.children[row]
if child_item:
return self.createIndex(row, column, child_item)
else:
return QModelIndex()
def parent(self, index):
if not index.isValid():
return QModelIndex()
item = index.internalPointer().parent
if item == self.root or item is None:
return QModelIndex()
return self.createIndex(item.row(), 0, item)
def rowCount(self, parent=QModelIndex()):
if parent.column() > 0:
return 0
if not parent.isValid():
pitem = self.root
else:
pitem = parent.internalPointer()
return len(pitem.children)
2014-01-27 22:18:12 +01:00
def sort(self, column, order=Qt.AscendingOrder):
raise NotImplementedError
2014-01-27 21:42:00 +01:00
def init_data(self):
for (cat, items) in self._data.items():
newcat = CompletionItem([cat], self.root)
self.root.children.append(newcat)
for item in items:
newitem = CompletionItem(item, newcat)
newcat.children.append(newitem)
class CompletionItem():
parent = None
_data = None
children = None
_marks = None
def __init__(self, data, parent=None):
self.parent = parent
self._data = data
self.children = []
self._marks = []
def data(self, column, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
return self._data[column]
elif role == Qt.UserRole:
return self._marks
else:
raise ValueError
def setdata(self, column, value, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
self._data[column] = value
elif role == Qt.UserRole:
self._marks = value
else:
raise ValueError
def column_count(self):
return len(self._data)
def row(self):
if self.parent:
return self.parent.children.index(self)
return 0
class CompletionFilterModel(QSortFilterProxyModel):
_pattern = None
2014-01-27 22:18:12 +01:00
pattern_changed = pyqtSignal(str)
2014-01-27 21:42:00 +01:00
@property
def pattern(self):
return self._pattern
@pattern.setter
def pattern(self, val):
self._pattern = val
2014-01-27 22:18:12 +01:00
self.invalidate()
self.pattern_changed.emit(val)
2014-01-27 21:42:00 +01:00
def __init__(self, parent=None):
super().__init__(parent)
self.pattern = ''
def filterAcceptsRow(self, row, parent):
if parent == QModelIndex():
return True
2014-01-28 19:52:09 +01:00
idx = self.sourceModel().index(row, 0, parent)
2014-01-27 21:42:00 +01:00
data = self.sourceModel().data(idx).value()
# TODO more sophisticated filtering
if not self.pattern:
return True
return self.pattern in data
2014-01-27 22:18:12 +01:00
def lessThan(self, lindex, rindex):
left = self.sourceModel().data(lindex).value()
right = self.sourceModel().data(rindex).value()
leftstart = left.startswith(self.pattern)
rightstart = right.startswith(self.pattern)
if leftstart and rightstart:
return left < right
elif leftstart:
return True
elif rightstart:
return False
else:
return left < right