Helpviewer/Bookmarks/BookmarksModel.py

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

eric ide

mercurial