src/eric7/WebBrowser/History/HistoryTreeModel.py

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

eric ide

mercurial