WebBrowser/History/HistoryMenu.py

branch
QtWebEngine
changeset 4734
ce0b1f024da9
parent 4631
5c1a96925da4
child 4735
84e78ee0f361
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 menu.
8 """
9
10 from __future__ import unicode_literals
11
12 import sys
13
14 from PyQt5.QtCore import pyqtSignal, Qt, QMimeData, QUrl, QModelIndex, \
15 QSortFilterProxyModel, QAbstractProxyModel
16 from PyQt5.QtWidgets import QMenu
17
18 from E5Gui.E5ModelMenu import E5ModelMenu
19 from E5Gui import E5MessageBox
20
21 from .HistoryModel import HistoryModel
22
23 import UI.PixmapCache
24
25
26 class HistoryMenuModel(QAbstractProxyModel):
27 """
28 Class implementing a model for the history menu.
29
30 It maps the first bunch of items of the source model to the root.
31 """
32 MOVEDROWS = 15
33
34 def __init__(self, sourceModel, parent=None):
35 """
36 Constructor
37
38 @param sourceModel reference to the source model (QAbstractItemModel)
39 @param parent reference to the parent object (QObject)
40 """
41 super(HistoryMenuModel, self).__init__(parent)
42
43 self.__treeModel = sourceModel
44
45 self.setSourceModel(sourceModel)
46
47 def bumpedRows(self):
48 """
49 Public method to determine the number of rows moved to the root.
50
51 @return number of rows moved to the root (integer)
52 """
53 first = self.__treeModel.index(0, 0)
54 if not first.isValid():
55 return 0
56 return min(self.__treeModel.rowCount(first), self.MOVEDROWS)
57
58 def columnCount(self, parent=QModelIndex()):
59 """
60 Public method to get the number of columns.
61
62 @param parent index of parent (QModelIndex)
63 @return number of columns (integer)
64 """
65 return self.__treeModel.columnCount(self.mapToSource(parent))
66
67 def rowCount(self, parent=QModelIndex()):
68 """
69 Public method to determine the number of rows.
70
71 @param parent index of parent (QModelIndex)
72 @return number of rows (integer)
73 """
74 if parent.column() > 0:
75 return 0
76
77 if not parent.isValid():
78 folders = self.sourceModel().rowCount()
79 bumpedItems = self.bumpedRows()
80 if bumpedItems <= self.MOVEDROWS and \
81 bumpedItems == self.sourceModel().rowCount(
82 self.sourceModel().index(0, 0)):
83 folders -= 1
84 return bumpedItems + folders
85
86 if parent.internalId() == sys.maxsize:
87 if parent.row() < self.bumpedRows():
88 return 0
89
90 idx = self.mapToSource(parent)
91 defaultCount = self.sourceModel().rowCount(idx)
92 if idx == self.sourceModel().index(0, 0):
93 return defaultCount - self.bumpedRows()
94
95 return defaultCount
96
97 def mapFromSource(self, sourceIndex):
98 """
99 Public method to map an index to the proxy model index.
100
101 @param sourceIndex reference to a source model index (QModelIndex)
102 @return proxy model index (QModelIndex)
103 """
104 sourceRow = self.__treeModel.mapToSource(sourceIndex).row()
105 return self.createIndex(
106 sourceIndex.row(), sourceIndex.column(), sourceRow)
107
108 def mapToSource(self, proxyIndex):
109 """
110 Public method to map an index to the source model index.
111
112 @param proxyIndex reference to a proxy model index (QModelIndex)
113 @return source model index (QModelIndex)
114 """
115 if not proxyIndex.isValid():
116 return QModelIndex()
117
118 if proxyIndex.internalId() == sys.maxsize:
119 bumpedItems = self.bumpedRows()
120 if proxyIndex.row() < bumpedItems:
121 return self.__treeModel.index(
122 proxyIndex.row(), proxyIndex.column(),
123 self.__treeModel.index(0, 0))
124 if bumpedItems <= self.MOVEDROWS and \
125 bumpedItems == self.sourceModel().rowCount(
126 self.__treeModel.index(0, 0)):
127 bumpedItems -= 1
128 return self.__treeModel.index(proxyIndex.row() - bumpedItems,
129 proxyIndex.column())
130
131 historyIndex = self.__treeModel.sourceModel()\
132 .index(proxyIndex.internalId(), proxyIndex.column())
133 treeIndex = self.__treeModel.mapFromSource(historyIndex)
134 return treeIndex
135
136 def index(self, row, column, parent=QModelIndex()):
137 """
138 Public method to create an index.
139
140 @param row row number for the index (integer)
141 @param column column number for the index (integer)
142 @param parent index of the parent item (QModelIndex)
143 @return requested index (QModelIndex)
144 """
145 if row < 0 or \
146 column < 0 or \
147 column >= self.columnCount(parent) or \
148 parent.column() > 0:
149 return QModelIndex()
150
151 if not parent.isValid():
152 return self.createIndex(row, column, sys.maxsize)
153
154 treeIndexParent = self.mapToSource(parent)
155
156 bumpedItems = 0
157 if treeIndexParent == self.sourceModel().index(0, 0):
158 bumpedItems = self.bumpedRows()
159 treeIndex = self.__treeModel.index(
160 row + bumpedItems, column, treeIndexParent)
161 historyIndex = self.__treeModel.mapToSource(treeIndex)
162 historyRow = historyIndex.row()
163 if historyRow == -1:
164 historyRow = treeIndex.row()
165 return self.createIndex(row, column, historyRow)
166
167 def parent(self, index):
168 """
169 Public method to get the parent index.
170
171 @param index index of item to get parent (QModelIndex)
172 @return index of parent (QModelIndex)
173 """
174 offset = index.internalId()
175 if offset == sys.maxsize or not index.isValid():
176 return QModelIndex()
177
178 historyIndex = self.__treeModel.sourceModel().index(
179 index.internalId(), 0)
180 treeIndex = self.__treeModel.mapFromSource(historyIndex)
181 treeIndexParent = treeIndex.parent()
182
183 sourceRow = self.sourceModel().mapToSource(treeIndexParent).row()
184 bumpedItems = self.bumpedRows()
185 if bumpedItems <= self.MOVEDROWS and \
186 bumpedItems == self.sourceModel().rowCount(
187 self.sourceModel().index(0, 0)):
188 bumpedItems -= 1
189
190 return self.createIndex(bumpedItems + treeIndexParent.row(),
191 treeIndexParent.column(),
192 sourceRow)
193
194 def mimeData(self, indexes):
195 """
196 Public method to return the mime data.
197
198 @param indexes list of indexes (QModelIndexList)
199 @return mime data (QMimeData)
200 """
201 urls = []
202 for index in indexes:
203 url = index.data(HistoryModel.UrlRole)
204 urls.append(url)
205
206 mdata = QMimeData()
207 mdata.setUrls(urls)
208 return mdata
209
210
211 class HistoryMostVisitedMenuModel(QSortFilterProxyModel):
212 """
213 Class implementing a model to show the most visited history entries.
214 """
215 def __init__(self, sourceModel, parent=None):
216 """
217 Constructor
218
219 @param sourceModel reference to the source model (QAbstractItemModel)
220 @param parent reference to the parent object (QObject)
221 """
222 super(HistoryMostVisitedMenuModel, self).__init__(parent)
223
224 self.setDynamicSortFilter(True)
225 self.setSourceModel(sourceModel)
226
227 def lessThan(self, left, right):
228 """
229 Public method used to sort the displayed items.
230
231 @param left index of left item (QModelIndex)
232 @param right index of right item (QModelIndex)
233 @return true, if left is less than right (boolean)
234 """
235 from .HistoryFilterModel import HistoryFilterModel
236 frequency_L = \
237 self.sourceModel().data(left, HistoryFilterModel.FrequencyRole)
238 dateTime_L = \
239 self.sourceModel().data(left, HistoryModel.DateTimeRole)
240 frequency_R = \
241 self.sourceModel().data(right, HistoryFilterModel.FrequencyRole)
242 dateTime_R = \
243 self.sourceModel().data(right, HistoryModel.DateTimeRole)
244
245 # Sort results in descending frequency-derived score. If frequencies
246 # are equal, sort on most recently viewed
247 if frequency_R == frequency_L:
248 return dateTime_R < dateTime_L
249
250 return frequency_R < frequency_L
251
252
253 class HistoryMenu(E5ModelMenu):
254 """
255 Class implementing the history menu.
256
257 @signal openUrl(QUrl, str) emitted to open a URL in the current tab
258 @signal newUrl(QUrl, str) emitted to open a URL in a new tab
259 """
260 openUrl = pyqtSignal(QUrl, str)
261 newUrl = pyqtSignal(QUrl, str)
262
263 def __init__(self, parent=None, tabWidget=None):
264 """
265 Constructor
266
267 @param parent reference to the parent widget (QWidget)
268 @param tabWidget reference to the tab widget managing the browser
269 tabs (HelpTabWidget
270 """
271 E5ModelMenu.__init__(self, parent)
272
273 self.__tabWidget = tabWidget
274
275 self.__historyManager = None
276 self.__historyMenuModel = None
277 self.__initialActions = []
278 self.__mostVisitedMenu = None
279
280 # TODO: Closed Tabs Manager
281 ## self.__closedTabsMenu = QMenu(self.tr("Closed Tabs"))
282 ## self.__closedTabsMenu.aboutToShow.connect(
283 ## self.__aboutToShowClosedTabsMenu)
284 ## self.__tabWidget.closedTabsManager().closedTabAvailable.connect(
285 ## self.__closedTabAvailable)
286
287 self.setMaxRows(7)
288
289 self.activated.connect(self.__activated)
290 self.setStatusBarTextRole(HistoryModel.UrlStringRole)
291
292 def __activated(self, idx):
293 """
294 Private slot handling the activated signal.
295
296 @param idx index of the activated item (QModelIndex)
297 """
298 if self._keyboardModifiers & Qt.ControlModifier:
299 self.newUrl.emit(
300 idx.data(HistoryModel.UrlRole),
301 idx.data(HistoryModel.TitleRole))
302 else:
303 self.openUrl.emit(
304 idx.data(HistoryModel.UrlRole),
305 idx.data(HistoryModel.TitleRole))
306
307 def prePopulated(self):
308 """
309 Public method to add any actions before the tree.
310
311 @return flag indicating if any actions were added (boolean)
312 """
313 if self.__historyManager is None:
314 import WebBrowser.WebBrowserWindow
315 self.__historyManager = \
316 WebBrowser.WebBrowserWindow.WebBrowserWindow.historyManager()
317 self.__historyMenuModel = HistoryMenuModel(
318 self.__historyManager.historyTreeModel(), self)
319 self.setModel(self.__historyMenuModel)
320
321 # initial actions
322 for act in self.__initialActions:
323 self.addAction(act)
324 if len(self.__initialActions) != 0:
325 self.addSeparator()
326 self.setFirstSeparator(self.__historyMenuModel.bumpedRows())
327
328 return False
329
330 def postPopulated(self):
331 """
332 Public method to add any actions after the tree.
333 """
334 if len(self.__historyManager.history()) > 0:
335 self.addSeparator()
336
337 if self.__mostVisitedMenu is None:
338 self.__mostVisitedMenu = HistoryMostVisitedMenu(10, self)
339 self.__mostVisitedMenu.setTitle(self.tr("Most Visited"))
340 self.__mostVisitedMenu.openUrl.connect(self.openUrl)
341 self.__mostVisitedMenu.newUrl.connect(self.newUrl)
342 self.addMenu(self.__mostVisitedMenu)
343 # TODO: Closed Tabs Manager
344 ## act = self.addMenu(self.__closedTabsMenu)
345 ## act.setIcon(UI.PixmapCache.getIcon("trash.png"))
346 ## act.setEnabled(self.__tabWidget.canRestoreClosedTab())
347 self.addSeparator()
348
349 act = self.addAction(UI.PixmapCache.getIcon("history.png"),
350 self.tr("Show All History..."))
351 act.triggered.connect(self.__showHistoryDialog)
352 act = self.addAction(UI.PixmapCache.getIcon("historyClear.png"),
353 self.tr("Clear History..."))
354 act.triggered.connect(self.__clearHistoryDialog)
355
356 def setInitialActions(self, actions):
357 """
358 Public method to set the list of actions that should appear first in
359 the menu.
360
361 @param actions list of initial actions (list of QAction)
362 """
363 self.__initialActions = actions[:]
364 for act in self.__initialActions:
365 self.addAction(act)
366
367 def __showHistoryDialog(self):
368 """
369 Private slot to show the history dialog.
370 """
371 from .HistoryDialog import HistoryDialog
372 dlg = HistoryDialog(self)
373 dlg.newUrl.connect(self.newUrl)
374 dlg.openUrl.connect(self.openUrl)
375 dlg.show()
376
377 def __clearHistoryDialog(self):
378 """
379 Private slot to clear the history.
380 """
381 if self.__historyManager is not None and E5MessageBox.yesNo(
382 self,
383 self.tr("Clear History"),
384 self.tr("""Do you want to clear the history?""")):
385 self.__historyManager.clear()
386 # TODO: Closed Tabs Manager
387 ## self.__tabWidget.clearClosedTabsList()
388
389 # TODO: Closed Tabs Manager
390 ## def __aboutToShowClosedTabsMenu(self):
391 ## """
392 ## Private slot to populate the closed tabs menu.
393 ## """
394 ## fm = self.__closedTabsMenu.fontMetrics()
395 ## maxWidth = fm.width('m') * 40
396 ##
397 ## import WebBrowser.WebBrowserWindow
398 ## self.__closedTabsMenu.clear()
399 ## index = 0
400 ## for tab in self.__tabWidget.closedTabsManager().allClosedTabs():
401 ## title = fm.elidedText(tab.title, Qt.ElideRight, maxWidth)
402 ## self.__closedTabsMenu.addAction(
403 ## WebBrowser.WebBrowserWindow.WebBrowserWindow.icon(tab.url),
404 ## title,
405 ## self.__tabWidget.restoreClosedTab).setData(index)
406 ## index += 1
407 ## self.__closedTabsMenu.addSeparator()
408 ## self.__closedTabsMenu.addAction(
409 ## self.tr("Restore All Closed Tabs"),
410 ## self.__tabWidget.restoreAllClosedTabs)
411 ## self.__closedTabsMenu.addAction(
412 ## self.tr("Clear List"),
413 ## self.__tabWidget.clearClosedTabsList)
414 ##
415 ## def __closedTabAvailable(self, avail):
416 ## """
417 ## Private slot to handle changes of the availability of closed tabs.
418 ##
419 ## @param avail flag indicating the availability of closed tabs (boolean)
420 ## """
421 ## self.__closedTabsMenu.setEnabled(avail)
422
423
424 class HistoryMostVisitedMenu(E5ModelMenu):
425 """
426 Class implementing the most visited history menu.
427
428 @signal openUrl(QUrl, str) emitted to open a URL in the current tab
429 @signal newUrl(QUrl, str) emitted to open a URL in a new tab
430 """
431 openUrl = pyqtSignal(QUrl, str)
432 newUrl = pyqtSignal(QUrl, str)
433
434 def __init__(self, count, parent=None):
435 """
436 Constructor
437
438 @param count maximum number of entries to be shown (integer)
439 @param parent reference to the parent widget (QWidget)
440 """
441 E5ModelMenu.__init__(self, parent)
442
443 self.__historyMenuModel = None
444
445 self.setMaxRows(count + 1)
446
447 self.activated.connect(self.__activated)
448 self.setStatusBarTextRole(HistoryModel.UrlStringRole)
449
450 def __activated(self, idx):
451 """
452 Private slot handling the activated signal.
453
454 @param idx index of the activated item (QModelIndex)
455 """
456 if self._keyboardModifiers & Qt.ControlModifier:
457 self.newUrl.emit(
458 idx.data(HistoryModel.UrlRole),
459 idx.data(HistoryModel.TitleRole))
460 else:
461 self.openUrl.emit(
462 idx.data(HistoryModel.UrlRole),
463 idx.data(HistoryModel.TitleRole))
464
465 def prePopulated(self):
466 """
467 Public method to add any actions before the tree.
468
469 @return flag indicating if any actions were added (boolean)
470 """
471 if self.__historyMenuModel is None:
472 import WebBrowser.WebBrowserWindow
473 historyManager = \
474 WebBrowser.WebBrowserWindow.WebBrowserWindow.historyManager()
475 self.__historyMenuModel = HistoryMostVisitedMenuModel(
476 historyManager.historyFilterModel(), self)
477 self.setModel(self.__historyMenuModel)
478 self.__historyMenuModel.sort(0)
479
480 return False

eric ide

mercurial