eric6/WebBrowser/Bookmarks/BookmarksModel.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 7229
53054eb5b15a
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2009 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the bookmark model class.
8 """
9
10 from __future__ import unicode_literals
11
12 from PyQt5.QtCore import Qt, QAbstractItemModel, QModelIndex, QUrl, \
13 QByteArray, QDataStream, QIODevice, QBuffer, QMimeData
14
15 import UI.PixmapCache
16
17
18 class BookmarksModel(QAbstractItemModel):
19 """
20 Class implementing the bookmark model.
21 """
22 TypeRole = Qt.UserRole + 1
23 UrlRole = Qt.UserRole + 2
24 UrlStringRole = Qt.UserRole + 3
25 VisitCountRole = Qt.UserRole + 4
26 SeparatorRole = Qt.UserRole + 5
27
28 MIMETYPE = "application/bookmarks.xbel"
29
30 def __init__(self, manager, parent=None):
31 """
32 Constructor
33
34 @param manager reference to the bookmark manager object
35 (BookmarksManager)
36 @param parent reference to the parent object (QObject)
37 """
38 super(BookmarksModel, self).__init__(parent)
39
40 self.__endMacro = False
41 self.__bookmarksManager = manager
42
43 manager.entryAdded.connect(self.entryAdded)
44 manager.entryRemoved.connect(self.entryRemoved)
45 manager.entryChanged.connect(self.entryChanged)
46
47 self.__headers = [
48 self.tr("Title"),
49 self.tr("Address"),
50 ]
51
52 def bookmarksManager(self):
53 """
54 Public method to get a reference to the bookmarks manager.
55
56 @return reference to the bookmarks manager object (BookmarksManager)
57 """
58 return self.__bookmarksManager
59
60 def nodeIndex(self, node):
61 """
62 Public method to get a model index.
63
64 @param node reference to the node to get the index for (BookmarkNode)
65 @return model index (QModelIndex)
66 """
67 parent = node.parent()
68 if parent is None:
69 return QModelIndex()
70 return self.createIndex(parent.children().index(node), 0, node)
71
72 def entryAdded(self, node):
73 """
74 Public slot to add a bookmark node.
75
76 @param node reference to the bookmark node to add (BookmarkNode)
77 """
78 if node is None or node.parent() is None:
79 return
80
81 parent = node.parent()
82 row = parent.children().index(node)
83 # node was already added so remove before beginInsertRows is called
84 parent.remove(node)
85 self.beginInsertRows(self.nodeIndex(parent), row, row)
86 parent.add(node, row)
87 self.endInsertRows()
88
89 def entryRemoved(self, parent, row, node):
90 """
91 Public slot to remove a bookmark node.
92
93 @param parent reference to the parent bookmark node (BookmarkNode)
94 @param row row number of the node (integer)
95 @param node reference to the bookmark node to remove (BookmarkNode)
96 """
97 # node was already removed, re-add so beginRemoveRows works
98 parent.add(node, row)
99 self.beginRemoveRows(self.nodeIndex(parent), row, row)
100 parent.remove(node)
101 self.endRemoveRows()
102
103 def entryChanged(self, node):
104 """
105 Public method to change a node.
106
107 @param node reference to the bookmark node to change (BookmarkNode)
108 """
109 idx = self.nodeIndex(node)
110 self.dataChanged.emit(idx, idx)
111
112 def removeRows(self, row, count, parent=None):
113 """
114 Public method to remove bookmarks from the model.
115
116 @param row row of the first bookmark to remove (integer)
117 @param count number of bookmarks to remove (integer)
118 @param parent index of the parent bookmark node (QModelIndex)
119 @return flag indicating successful removal (boolean)
120 """
121 if parent is None:
122 parent = QModelIndex()
123
124 if row < 0 or count <= 0 or row + count > self.rowCount(parent):
125 return False
126
127 bookmarkNode = self.node(parent)
128 children = bookmarkNode.children()[row:(row + count)]
129 for node in children:
130 if node == self.__bookmarksManager.menu() or \
131 node == self.__bookmarksManager.toolbar():
132 continue
133 self.__bookmarksManager.removeBookmark(node)
134
135 if self.__endMacro:
136 self.__bookmarksManager.undoRedoStack().endMacro()
137 self.__endMacro = False
138
139 return True
140
141 def headerData(self, section, orientation, role=Qt.DisplayRole):
142 """
143 Public method to get the header data.
144
145 @param section section number (integer)
146 @param orientation header orientation (Qt.Orientation)
147 @param role data role (integer)
148 @return header data
149 """
150 if orientation == Qt.Horizontal and role == Qt.DisplayRole:
151 try:
152 return self.__headers[section]
153 except IndexError:
154 pass
155 return QAbstractItemModel.headerData(self, section, orientation, role)
156
157 def data(self, index, role=Qt.DisplayRole):
158 """
159 Public method to get data from the model.
160
161 @param index index of bookmark to get data for (QModelIndex)
162 @param role data role (integer)
163 @return bookmark data
164 """
165 if not index.isValid() or index.model() != self:
166 return None
167
168 from .BookmarkNode import BookmarkNode
169
170 bookmarkNode = self.node(index)
171 if role in [Qt.EditRole, Qt.DisplayRole]:
172 if bookmarkNode.type() == BookmarkNode.Separator:
173 if index.column() == 0:
174 return 50 * '\xB7'
175 elif index.column() == 1:
176 return ""
177
178 if index.column() == 0:
179 return bookmarkNode.title
180 elif index.column() == 1:
181 return bookmarkNode.url
182
183 elif role == BookmarksModel.UrlRole:
184 return QUrl(bookmarkNode.url)
185
186 elif role == BookmarksModel.UrlStringRole:
187 return bookmarkNode.url
188
189 elif role == BookmarksModel.VisitCountRole:
190 return bookmarkNode.visitCount
191
192 elif role == BookmarksModel.TypeRole:
193 return bookmarkNode.type()
194
195 elif role == BookmarksModel.SeparatorRole:
196 return bookmarkNode.type() == BookmarkNode.Separator
197
198 elif role == Qt.DecorationRole:
199 if index.column() == 0:
200 if bookmarkNode.type() == BookmarkNode.Folder:
201 return UI.PixmapCache.getIcon("dirOpen.png")
202 import WebBrowser.WebBrowserWindow
203 return WebBrowser.WebBrowserWindow.WebBrowserWindow.icon(
204 QUrl(bookmarkNode.url))
205
206 return None
207
208 def columnCount(self, parent=None):
209 """
210 Public method to get the number of columns.
211
212 @param parent index of parent (QModelIndex)
213 @return number of columns (integer)
214 """
215 if parent is None:
216 parent = QModelIndex()
217
218 if parent.column() > 0:
219 return 0
220 else:
221 return len(self.__headers)
222
223 def rowCount(self, parent=None):
224 """
225 Public method to determine the number of rows.
226
227 @param parent index of parent (QModelIndex)
228 @return number of rows (integer)
229 """
230 if parent is None:
231 parent = QModelIndex()
232
233 if parent.column() > 0:
234 return 0
235
236 if not parent.isValid():
237 return len(self.__bookmarksManager.bookmarks().children())
238
239 itm = parent.internalPointer()
240 return len(itm.children())
241
242 def index(self, row, column, parent=None):
243 """
244 Public method to get a model index for a node cell.
245
246 @param row row number (integer)
247 @param column column number (integer)
248 @param parent index of the parent (QModelIndex)
249 @return index (QModelIndex)
250 """
251 if parent is None:
252 parent = QModelIndex()
253
254 if row < 0 or column < 0 or \
255 row >= self.rowCount(parent) or column >= self.columnCount(parent):
256 return QModelIndex()
257
258 parentNode = self.node(parent)
259 return self.createIndex(row, column, parentNode.children()[row])
260
261 def parent(self, index=None):
262 """
263 Public method to get the index of the parent node.
264
265 @param index index of the child node (QModelIndex)
266 @return index of the parent node (QModelIndex)
267 """
268 if index is None:
269 index = QModelIndex()
270
271 if not index.isValid():
272 return QModelIndex()
273
274 itemNode = self.node(index)
275 if itemNode is None:
276 parentNode = None
277 else:
278 parentNode = itemNode.parent()
279
280 if parentNode is None or \
281 parentNode == self.__bookmarksManager.bookmarks():
282 return QModelIndex()
283
284 # get the parent's row
285 grandParentNode = parentNode.parent()
286 parentRow = grandParentNode.children().index(parentNode)
287 return self.createIndex(parentRow, 0, parentNode)
288
289 def hasChildren(self, parent=None):
290 """
291 Public method to check, if a parent node has some children.
292
293 @param parent index of the parent node (QModelIndex)
294 @return flag indicating the presence of children (boolean)
295 """
296 if parent is None:
297 parent = QModelIndex()
298
299 if not parent.isValid():
300 return True
301
302 from .BookmarkNode import BookmarkNode
303 parentNode = self.node(parent)
304 return parentNode.type() == BookmarkNode.Folder
305
306 def flags(self, index):
307 """
308 Public method to get flags for a node cell.
309
310 @param index index of the node cell (QModelIndex)
311 @return flags (Qt.ItemFlags)
312 """
313 if not index.isValid():
314 return Qt.NoItemFlags
315
316 node = self.node(index)
317 type_ = node.type()
318 flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
319
320 if self.hasChildren(index):
321 flags |= Qt.ItemIsDropEnabled
322
323 if node == self.__bookmarksManager.menu() or \
324 node == self.__bookmarksManager.toolbar():
325 return flags
326
327 flags |= Qt.ItemIsDragEnabled
328
329 from .BookmarkNode import BookmarkNode
330 if (index.column() == 0 and type_ != BookmarkNode.Separator) or \
331 (index.column() == 1 and type_ == BookmarkNode.Bookmark):
332 flags |= Qt.ItemIsEditable
333
334 return flags
335
336 def supportedDropActions(self):
337 """
338 Public method to report the supported drop actions.
339
340 @return supported drop actions (Qt.DropAction)
341 """
342 return Qt.CopyAction | Qt.MoveAction
343
344 def mimeTypes(self):
345 """
346 Public method to report the supported mime types.
347
348 @return supported mime types (list of strings)
349 """
350 return [self.MIMETYPE, "text/uri-list"]
351
352 def mimeData(self, indexes):
353 """
354 Public method to return the mime data.
355
356 @param indexes list of indexes (QModelIndexList)
357 @return mime data (QMimeData)
358 """
359 from .XbelWriter import XbelWriter
360
361 data = QByteArray()
362 stream = QDataStream(data, QIODevice.WriteOnly)
363 urls = []
364
365 for index in indexes:
366 if index.column() != 0 or not index.isValid():
367 continue
368
369 encodedData = QByteArray()
370 buffer = QBuffer(encodedData)
371 buffer.open(QIODevice.ReadWrite)
372 writer = XbelWriter()
373 parentNode = self.node(index)
374 writer.write(buffer, parentNode)
375 stream << encodedData
376 urls.append(index.data(self.UrlRole))
377
378 mdata = QMimeData()
379 mdata.setData(self.MIMETYPE, data)
380 mdata.setUrls(urls)
381 return mdata
382
383 def dropMimeData(self, data, action, row, column, parent):
384 """
385 Public method to accept the mime data of a drop action.
386
387 @param data reference to the mime data (QMimeData)
388 @param action drop action requested (Qt.DropAction)
389 @param row row number (integer)
390 @param column column number (integer)
391 @param parent index of the parent node (QModelIndex)
392 @return flag indicating successful acceptance of the data (boolean)
393 """
394 if action == Qt.IgnoreAction:
395 return True
396
397 if column > 0:
398 return False
399
400 parentNode = self.node(parent)
401
402 if not data.hasFormat(self.MIMETYPE):
403 if not data.hasUrls():
404 return False
405
406 from .BookmarkNode import BookmarkNode
407 node = BookmarkNode(BookmarkNode.Bookmark, parentNode)
408 node.url = bytes(data.urls()[0].toEncoded()).decode()
409
410 if data.hasText():
411 node.title = data.text()
412 else:
413 node.title = node.url
414
415 self.__bookmarksManager.addBookmark(parentNode, node, row)
416 return True
417
418 ba = data.data(self.MIMETYPE)
419 stream = QDataStream(ba, QIODevice.ReadOnly)
420 if stream.atEnd():
421 return False
422
423 undoStack = self.__bookmarksManager.undoRedoStack()
424 undoStack.beginMacro("Move Bookmarks")
425
426 from .XbelReader import XbelReader
427 while not stream.atEnd():
428 encodedData = QByteArray()
429 stream >> encodedData
430 buffer = QBuffer(encodedData)
431 buffer.open(QIODevice.ReadOnly)
432
433 reader = XbelReader()
434 rootNode = reader.read(buffer)
435 for bookmarkNode in rootNode.children():
436 rootNode.remove(bookmarkNode)
437 row = max(0, row)
438 self.__bookmarksManager.addBookmark(
439 parentNode, bookmarkNode, row)
440 self.__endMacro = True
441
442 return True
443
444 def setData(self, index, value, role=Qt.EditRole):
445 """
446 Public method to set the data of a node cell.
447
448 @param index index of the node cell (QModelIndex)
449 @param value value to be set
450 @param role role of the data (integer)
451 @return flag indicating success (boolean)
452 """
453 if not index.isValid() or (self.flags(index) & Qt.ItemIsEditable) == 0:
454 return False
455
456 item = self.node(index)
457
458 if role in (Qt.EditRole, Qt.DisplayRole):
459 if index.column() == 0:
460 self.__bookmarksManager.setTitle(item, value)
461 elif index.column() == 1:
462 self.__bookmarksManager.setUrl(item, value)
463 else:
464 return False
465
466 elif role == BookmarksModel.UrlRole:
467 self.__bookmarksManager.setUrl(item, value.toString())
468
469 elif role == BookmarksModel.UrlStringRole:
470 self.__bookmarksManager.setUrl(item, value)
471
472 elif role == BookmarksModel.VisitCountRole:
473 self.__bookmarksManager.setVisitCount(item, value)
474
475 else:
476 return False
477
478 return True
479
480 def node(self, index):
481 """
482 Public method to get a bookmark node given its index.
483
484 @param index index of the node (QModelIndex)
485 @return bookmark node (BookmarkNode)
486 """
487 itemNode = index.internalPointer()
488 if itemNode is None:
489 return self.__bookmarksManager.bookmarks()
490 else:
491 return itemNode

eric ide

mercurial