eric6/Helpviewer/Bookmarks/BookmarksModel.py

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

eric ide

mercurial