WebBrowser/History/HistoryTreeModel.py

branch
QtWebEngine
changeset 4734
ce0b1f024da9
parent 4631
5c1a96925da4
child 5389
9b1c800daff3
equal deleted inserted replaced
4733:ae291a307ea6 4734:ce0b1f024da9
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 - 2016 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the history tree model.
8 """
9
10 from __future__ import unicode_literals
11
12 import bisect
13
14 from PyQt5.QtCore import Qt, QModelIndex, QDate, QAbstractProxyModel
15
16 from .HistoryModel import HistoryModel
17
18 import UI.PixmapCache
19
20
21 class HistoryTreeModel(QAbstractProxyModel):
22 """
23 Class implementing the history tree model.
24 """
25 def __init__(self, sourceModel, parent=None):
26 """
27 Constructor
28
29 @param sourceModel reference to the source model (QAbstractItemModel)
30 @param parent reference to the parent object (QObject)
31 """
32 super(HistoryTreeModel, self).__init__(parent)
33
34 self.__sourceRowCache = []
35 self.__removingDown = False
36
37 self.setSourceModel(sourceModel)
38
39 def headerData(self, section, orientation, role=Qt.DisplayRole):
40 """
41 Public method to get the header data.
42
43 @param section section number (integer)
44 @param orientation header orientation (Qt.Orientation)
45 @param role data role (integer)
46 @return header data
47 """
48 return self.sourceModel().headerData(section, orientation, role)
49
50 def data(self, index, role=Qt.DisplayRole):
51 """
52 Public method to get data from the model.
53
54 @param index index of history entry to get data for (QModelIndex)
55 @param role data role (integer)
56 @return history entry data
57 """
58 if role in [Qt.DisplayRole, Qt.EditRole]:
59 start = index.internalId()
60 if start == 0:
61 offset = self.__sourceDateRow(index.row())
62 if index.column() == 0:
63 idx = self.sourceModel().index(offset, 0)
64 date = idx.data(HistoryModel.DateRole)
65 if date == QDate.currentDate():
66 return self.tr("Earlier Today")
67 return date.toString("yyyy-MM-dd")
68 if index.column() == 1:
69 return self.tr(
70 "%n item(s)", "",
71 self.rowCount(index.sibling(index.row(), 0)))
72
73 elif role == Qt.DecorationRole:
74 if index.column() == 0 and not index.parent().isValid():
75 return UI.PixmapCache.getIcon("history.png")
76
77 elif role == HistoryModel.DateRole:
78 if index.column() == 0 and index.internalId() == 0:
79 offset = self.__sourceDateRow(index.row())
80 idx = self.sourceModel().index(offset, 0)
81 return idx.data(HistoryModel.DateRole)
82
83 return QAbstractProxyModel.data(self, index, role)
84
85 def columnCount(self, parent=QModelIndex()):
86 """
87 Public method to get the number of columns.
88
89 @param parent index of parent (QModelIndex)
90 @return number of columns (integer)
91 """
92 return self.sourceModel().columnCount(self.mapToSource(parent))
93
94 def rowCount(self, parent=QModelIndex()):
95 """
96 Public method to determine the number of rows.
97
98 @param parent index of parent (QModelIndex)
99 @return number of rows (integer)
100 """
101 if parent.internalId() != 0 or \
102 parent.column() > 0 or \
103 self.sourceModel() is None:
104 return 0
105
106 # row count OF dates
107 if not parent.isValid():
108 if self.__sourceRowCache:
109 return len(self.__sourceRowCache)
110
111 currentDate = QDate()
112 rows = 0
113 totalRows = self.sourceModel().rowCount()
114
115 for row in range(totalRows):
116 rowDate = self.sourceModel().index(row, 0)\
117 .data(HistoryModel.DateRole)
118 if rowDate != currentDate:
119 self.__sourceRowCache.append(row)
120 currentDate = rowDate
121 rows += 1
122 return rows
123
124 # row count FOR a date
125 start = self.__sourceDateRow(parent.row())
126 end = self.__sourceDateRow(parent.row() + 1)
127 return end - start
128
129 def __sourceDateRow(self, row):
130 """
131 Private method to translate the top level date row into the offset
132 where that date starts.
133
134 @param row row number of the date (integer)
135 @return offset where that date starts (integer)
136 """
137 if row <= 0:
138 return 0
139
140 if len(self.__sourceRowCache) == 0:
141 self.rowCount(QModelIndex())
142
143 if row >= len(self.__sourceRowCache):
144 if self.sourceModel() is None:
145 return 0
146 return self.sourceModel().rowCount()
147
148 return self.__sourceRowCache[row]
149
150 def mapToSource(self, proxyIndex):
151 """
152 Public method to map an index to the source model index.
153
154 @param proxyIndex reference to a proxy model index (QModelIndex)
155 @return source model index (QModelIndex)
156 """
157 offset = proxyIndex.internalId()
158 if offset == 0:
159 return QModelIndex()
160 startDateRow = self.__sourceDateRow(offset - 1)
161 return self.sourceModel().index(
162 startDateRow + proxyIndex.row(), proxyIndex.column())
163
164 def index(self, row, column, parent=QModelIndex()):
165 """
166 Public method to create an index.
167
168 @param row row number for the index (integer)
169 @param column column number for the index (integer)
170 @param parent index of the parent item (QModelIndex)
171 @return requested index (QModelIndex)
172 """
173 if row < 0 or \
174 column < 0 or \
175 column >= self.columnCount(parent) or \
176 parent.column() > 0:
177 return QModelIndex()
178
179 if not parent.isValid():
180 return self.createIndex(row, column, 0)
181 return self.createIndex(row, column, parent.row() + 1)
182
183 def parent(self, index):
184 """
185 Public method to get the parent index.
186
187 @param index index of item to get parent (QModelIndex)
188 @return index of parent (QModelIndex)
189 """
190 offset = index.internalId()
191 if offset == 0 or not index.isValid():
192 return QModelIndex()
193 return self.createIndex(offset - 1, 0, 0)
194
195 def hasChildren(self, parent=QModelIndex()):
196 """
197 Public method to check, if an entry has some children.
198
199 @param parent index of the entry to check (QModelIndex)
200 @return flag indicating the presence of children (boolean)
201 """
202 grandparent = parent.parent()
203 if not grandparent.isValid():
204 return True
205 return False
206
207 def flags(self, index):
208 """
209 Public method to get the item flags.
210
211 @param index index of the item (QModelIndex)
212 @return flags (Qt.ItemFlags)
213 """
214 if not index.isValid():
215 return Qt.ItemFlags(Qt.NoItemFlags)
216 return Qt.ItemFlags(
217 Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
218
219 def setSourceModel(self, sourceModel):
220 """
221 Public method to set the source model.
222
223 @param sourceModel reference to the source model (QAbstractItemModel)
224 """
225 if self.sourceModel() is not None:
226 self.sourceModel().modelReset.disconnect(self.__sourceReset)
227 self.sourceModel().layoutChanged.disconnect(self.__sourceReset)
228 self.sourceModel().rowsInserted.disconnect(
229 self.__sourceRowsInserted)
230 self.sourceModel().rowsRemoved.disconnect(self.__sourceRowsRemoved)
231
232 super(HistoryTreeModel, self).setSourceModel(sourceModel)
233
234 if self.sourceModel() is not None:
235 self.__loaded = False
236 self.sourceModel().modelReset.connect(self.__sourceReset)
237 self.sourceModel().layoutChanged.connect(self.__sourceReset)
238 self.sourceModel().rowsInserted.connect(self.__sourceRowsInserted)
239 self.sourceModel().rowsRemoved.connect(self.__sourceRowsRemoved)
240
241 self.beginResetModel()
242 self.endResetModel()
243
244 def __sourceReset(self):
245 """
246 Private slot to handle a reset of the source model.
247 """
248 self.beginResetModel()
249 self.__sourceRowCache = []
250 self.endResetModel()
251
252 def __sourceRowsInserted(self, parent, start, end):
253 """
254 Private slot to handle the insertion of data in the source model.
255
256 @param parent reference to the parent index (QModelIndex)
257 @param start start row (integer)
258 @param end end row (integer)
259 """
260 if not parent.isValid():
261 if start != 0 or start != end:
262 self.beginResetModel()
263 self.__sourceRowCache = []
264 self.endResetModel()
265 return
266
267 self.__sourceRowCache = []
268 treeIndex = self.mapFromSource(self.sourceModel().index(start, 0))
269 treeParent = treeIndex.parent()
270 if self.rowCount(treeParent) == 1:
271 self.beginInsertRows(QModelIndex(), 0, 0)
272 self.endInsertRows()
273 else:
274 self.beginInsertRows(treeParent, treeIndex.row(),
275 treeIndex.row())
276 self.endInsertRows()
277
278 def mapFromSource(self, sourceIndex):
279 """
280 Public method to map an index to the proxy model index.
281
282 @param sourceIndex reference to a source model index (QModelIndex)
283 @return proxy model index (QModelIndex)
284 """
285 if not sourceIndex.isValid():
286 return QModelIndex()
287
288 if len(self.__sourceRowCache) == 0:
289 self.rowCount(QModelIndex())
290
291 try:
292 row = self.__sourceRowCache.index(sourceIndex.row())
293 except ValueError:
294 row = bisect.bisect_left(self.__sourceRowCache, sourceIndex.row())
295 if row == len(self.__sourceRowCache) or \
296 self.__sourceRowCache[row] != sourceIndex.row():
297 row -= 1
298 dateRow = max(0, row)
299 row = sourceIndex.row() - self.__sourceRowCache[dateRow]
300 return self.createIndex(row, sourceIndex.column(), dateRow + 1)
301
302 def removeRows(self, row, count, parent=QModelIndex()):
303 """
304 Public method to remove entries from the model.
305
306 @param row row of the first entry to remove (integer)
307 @param count number of entries to remove (integer)
308 @param parent index of the parent entry (QModelIndex)
309 @return flag indicating successful removal (boolean)
310 """
311 if row < 0 or \
312 count <= 0 or \
313 row + count > self.rowCount(parent):
314 return False
315
316 self.__removingDown = True
317 if parent.isValid() and self.rowCount(parent) == count - row:
318 self.beginRemoveRows(QModelIndex(), parent.row(), parent.row())
319 else:
320 self.beginRemoveRows(parent, row, row + count - 1)
321 if parent.isValid():
322 # removing pages
323 offset = self.__sourceDateRow(parent.row())
324 return self.sourceModel().removeRows(offset + row, count)
325 else:
326 # removing whole dates
327 for i in range(row + count - 1, row - 1, -1):
328 dateParent = self.index(i, 0)
329 offset = self.__sourceDateRow(dateParent.row())
330 if not self.sourceModel().removeRows(
331 offset, self.rowCount(dateParent)):
332 return False
333 return True
334
335 def __sourceRowsRemoved(self, parent, start, end):
336 """
337 Private slot to handle the removal of data in the source model.
338
339 @param parent reference to the parent index (QModelIndex)
340 @param start start row (integer)
341 @param end end row (integer)
342 """
343 if not self.__removingDown:
344 self.beginResetModel()
345 self.__sourceRowCache = []
346 self.endResetModel()
347 return
348
349 if not parent.isValid():
350 if self.__sourceRowCache:
351 i = end
352 while i >= start:
353 try:
354 ind = self.__sourceRowCache.index(i)
355 except ValueError:
356 ind = bisect.bisect_left(self.__sourceRowCache, i)
357 if ind == len(self.__sourceRowCache) or \
358 self.__sourceRowCache[ind] != i:
359 ind -= 1
360 row = max(0, ind)
361 offset = self.__sourceRowCache[row]
362 dateParent = self.index(row, 0)
363 # If we can remove all the rows in the date do that
364 # and skip over them.
365 rc = self.rowCount(dateParent)
366 if i - rc + 1 == offset and start <= i - rc + 1:
367 del self.__sourceRowCache[row]
368 i -= rc + 1
369 else:
370 row += 1
371 i -= 1
372 for j in range(row, len(self.__sourceRowCache)):
373 self.__sourceRowCache[j] -= 1
374
375 if self.__removingDown:
376 self.endRemoveRows()
377 self.__removingDown = False

eric ide

mercurial