|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2009 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing the history menu. |
|
8 """ |
|
9 |
|
10 import sys |
|
11 |
|
12 from PyQt4.QtCore import * |
|
13 from PyQt4.QtGui import * |
|
14 |
|
15 from E4Gui.E4ModelMenu import E4ModelMenu |
|
16 |
|
17 import Helpviewer.HelpWindow |
|
18 |
|
19 from HistoryModel import HistoryModel |
|
20 from HistoryDialog import HistoryDialog |
|
21 |
|
22 import UI.PixmapCache |
|
23 |
|
24 class HistoryMenuModel(QAbstractProxyModel): |
|
25 """ |
|
26 Class implementing a model for the history menu. |
|
27 |
|
28 It maps the first bunch of items of the source model to the root. |
|
29 """ |
|
30 MOVEDROWS = 15 |
|
31 |
|
32 def __init__(self, sourceModel, parent = None): |
|
33 """ |
|
34 Constructor |
|
35 |
|
36 @param sourceModel reference to the source model (QAbstractItemModel) |
|
37 @param parent reference to the parent object (QObject) |
|
38 """ |
|
39 QAbstractProxyModel.__init__(self, parent) |
|
40 |
|
41 self.__treeModel = sourceModel |
|
42 |
|
43 self.setSourceModel(sourceModel) |
|
44 |
|
45 def bumpedRows(self): |
|
46 """ |
|
47 Public method to determine the number of rows moved to the root. |
|
48 |
|
49 @return number of rows moved to the root (integer) |
|
50 """ |
|
51 first = self.__treeModel.index(0, 0) |
|
52 if not first.isValid(): |
|
53 return 0 |
|
54 return min(self.__treeModel.rowCount(first), self.MOVEDROWS) |
|
55 |
|
56 def columnCount(self, parent = QModelIndex()): |
|
57 """ |
|
58 Public method to get the number of columns. |
|
59 |
|
60 @param parent index of parent (QModelIndex) |
|
61 @return number of columns (integer) |
|
62 """ |
|
63 return self.__treeModel.columnCount(self.mapToSource(parent)) |
|
64 |
|
65 def rowCount(self, parent = QModelIndex()): |
|
66 """ |
|
67 Public method to determine the number of rows. |
|
68 |
|
69 @param parent index of parent (QModelIndex) |
|
70 @return number of rows (integer) |
|
71 """ |
|
72 if parent.column() > 0: |
|
73 return 0 |
|
74 |
|
75 if not parent.isValid(): |
|
76 folders = self.sourceModel().rowCount() |
|
77 bumpedItems = self.bumpedRows() |
|
78 if bumpedItems <= self.MOVEDROWS and \ |
|
79 bumpedItems == self.sourceModel().rowCount(self.sourceModel().index(0, 0)): |
|
80 folders -= 1 |
|
81 return bumpedItems + folders |
|
82 |
|
83 if parent.internalId() == sys.maxint: |
|
84 if parent.row() < self.bumpedRows(): |
|
85 return 0 |
|
86 |
|
87 idx = self.mapToSource(parent) |
|
88 defaultCount = self.sourceModel().rowCount(idx) |
|
89 if idx == self.sourceModel().index(0, 0): |
|
90 return defaultCount - self.bumpedRows() |
|
91 |
|
92 return defaultCount |
|
93 |
|
94 def mapFromSource(self, sourceIndex): |
|
95 """ |
|
96 Public method to map an index to the proxy model index. |
|
97 |
|
98 @param sourceIndex reference to a source model index (QModelIndex) |
|
99 @return proxy model index (QModelIndex) |
|
100 """ |
|
101 assert False |
|
102 sourceRow = self.__treeModel.mapToSource(sourceIndex).row() |
|
103 return self.createIndex(sourceIndex.row(), sourceIndex.column(), sourceRow) |
|
104 |
|
105 def mapToSource(self, proxyIndex): |
|
106 """ |
|
107 Public method to map an index to the source model index. |
|
108 |
|
109 @param proxyIndex reference to a proxy model index (QModelIndex) |
|
110 @return source model index (QModelIndex) |
|
111 """ |
|
112 if not proxyIndex.isValid(): |
|
113 return QModelIndex() |
|
114 |
|
115 if proxyIndex.internalId() == sys.maxint: |
|
116 bumpedItems = self.bumpedRows() |
|
117 if proxyIndex.row() < bumpedItems: |
|
118 return self.__treeModel.index(proxyIndex.row(), proxyIndex.column(), |
|
119 self.__treeModel.index(0, 0)) |
|
120 if bumpedItems <= self.MOVEDROWS and \ |
|
121 bumpedItems == self.sourceModel().rowCount(self.__treeModel.index(0, 0)): |
|
122 bumpedItems -= 1 |
|
123 return self.__treeModel.index(proxyIndex.row() - bumpedItems, |
|
124 proxyIndex.column()) |
|
125 |
|
126 historyIndex = self.__treeModel.sourceModel()\ |
|
127 .index(proxyIndex.internalId(), proxyIndex.column()) |
|
128 treeIndex = self.__treeModel.mapFromSource(historyIndex) |
|
129 return treeIndex |
|
130 |
|
131 def index(self, row, column, parent = QModelIndex()): |
|
132 """ |
|
133 Public method to create an index. |
|
134 |
|
135 @param row row number for the index (integer) |
|
136 @param column column number for the index (integer) |
|
137 @param parent index of the parent item (QModelIndex) |
|
138 @return requested index (QModelIndex) |
|
139 """ |
|
140 if row < 0 or \ |
|
141 column < 0 or \ |
|
142 column >= self.columnCount(parent) or \ |
|
143 parent.column() > 0: |
|
144 return QModelIndex() |
|
145 |
|
146 if not parent.isValid(): |
|
147 return self.createIndex(row, column, sys.maxint) |
|
148 |
|
149 treeIndexParent = self.mapToSource(parent) |
|
150 |
|
151 bumpedItems = 0 |
|
152 if treeIndexParent == self.sourceModel().index(0, 0): |
|
153 bumpedItems = self.bumpedRows() |
|
154 treeIndex = self.__treeModel.index(row + bumpedItems, column, treeIndexParent) |
|
155 historyIndex = self.__treeModel.mapToSource(treeIndex) |
|
156 historyRow = historyIndex.row() |
|
157 if historyRow == -1: |
|
158 historyRow = treeIndex.row() |
|
159 return self.createIndex(row, column, historyRow) |
|
160 |
|
161 def parent(self, index): |
|
162 """ |
|
163 Public method to get the parent index. |
|
164 |
|
165 @param index index of item to get parent (QModelIndex) |
|
166 @return index of parent (QModelIndex) |
|
167 """ |
|
168 offset = index.internalId() |
|
169 if offset == sys.maxint or not index.isValid(): |
|
170 return QModelIndex() |
|
171 |
|
172 historyIndex = self.__treeModel.sourceModel().index(index.internalId(), 0) |
|
173 treeIndex = self.__treeModel.mapFromSource(historyIndex) |
|
174 treeIndexParent = treeIndex.parent() |
|
175 |
|
176 soureRow = self.sourceModel().mapToSource(treeIndexParent).row() |
|
177 bumpedItems = self.bumpedRows() |
|
178 if bumpedItems <= self.MOVEDROWS and \ |
|
179 bumpedItems == self.sourceModel().rowCount(self.sourceModel().index(0, 0)): |
|
180 bumpedItems -= 1 |
|
181 |
|
182 return self.createIndex(bumpedItems + treeIndexParent.row(), |
|
183 treeIndexParent.column(), |
|
184 sourceRow) |
|
185 |
|
186 def mimeData(self, indexes): |
|
187 """ |
|
188 Public method to return the mime data. |
|
189 |
|
190 @param indexes list of indexes (QModelIndexList) |
|
191 @return mime data (QMimeData) |
|
192 """ |
|
193 urls = [] |
|
194 for index in indexes: |
|
195 url = index.data(HistoryModel.UrlRole).toUrl() |
|
196 urls.append(url) |
|
197 |
|
198 mdata = QMimeData() |
|
199 mdata.setUrls(urls) |
|
200 return mdata |
|
201 |
|
202 class HistoryMenu(E4ModelMenu): |
|
203 """ |
|
204 Class implementing the history menu. |
|
205 |
|
206 @signal openUrl(const QUrl&, const QString&) emitted to open a URL in the current |
|
207 tab |
|
208 @signal newUrl(const QUrl&, const QString&) emitted to open a URL in a new tab |
|
209 """ |
|
210 def __init__(self, parent = None): |
|
211 """ |
|
212 Constructor |
|
213 |
|
214 @param parent reference to the parent widget (QWidget) |
|
215 """ |
|
216 E4ModelMenu.__init__(self, parent) |
|
217 |
|
218 self.__historyManager = None |
|
219 self.__historyMenuModel = None |
|
220 self.__initialActions = [] |
|
221 |
|
222 self.setMaxRows(7) |
|
223 |
|
224 self.connect(self, SIGNAL("activated(const QModelIndex&)"), self.__activated) |
|
225 self.setStatusBarTextRole(HistoryModel.UrlStringRole) |
|
226 |
|
227 def __activated(self, idx): |
|
228 """ |
|
229 Private slot handling the activated signal. |
|
230 |
|
231 @param idx index of the activated item (QModelIndex) |
|
232 """ |
|
233 if self._keyboardModifiers & Qt.ControlModifier: |
|
234 self.emit(SIGNAL("newUrl(const QUrl&, const QString&)"), |
|
235 idx.data(HistoryModel.UrlRole).toUrl(), |
|
236 idx.data(HistoryModel.TitleRole).toString()) |
|
237 else: |
|
238 self.emit(SIGNAL("openUrl(const QUrl&, const QString&)"), |
|
239 idx.data(HistoryModel.UrlRole).toUrl(), |
|
240 idx.data(HistoryModel.TitleRole).toString()) |
|
241 |
|
242 def prePopulated(self): |
|
243 """ |
|
244 Public method to add any actions before the tree. |
|
245 |
|
246 @return flag indicating if any actions were added (boolean) |
|
247 """ |
|
248 if self.__historyManager is None: |
|
249 self.__historyManager = Helpviewer.HelpWindow.HelpWindow.historyManager() |
|
250 self.__historyMenuModel = \ |
|
251 HistoryMenuModel(self.__historyManager.historyTreeModel(), self) |
|
252 self.setModel(self.__historyMenuModel) |
|
253 |
|
254 # initial actions |
|
255 for act in self.__initialActions: |
|
256 self.addAction(act) |
|
257 if len(self.__initialActions) != 0: |
|
258 self.addSeparator() |
|
259 self.setFirstSeparator(self.__historyMenuModel.bumpedRows()) |
|
260 |
|
261 return False |
|
262 |
|
263 def postPopulated(self): |
|
264 """ |
|
265 Public method to add any actions after the tree. |
|
266 """ |
|
267 if len(self.__historyManager.history()) > 0: |
|
268 self.addSeparator() |
|
269 |
|
270 act = self.addAction(UI.PixmapCache.getIcon("history.png"), |
|
271 self.trUtf8("Show All History...")) |
|
272 self.connect(act, SIGNAL("triggered()"), self.__showHistoryDialog) |
|
273 act = self.addAction(UI.PixmapCache.getIcon("historyClear.png"), |
|
274 self.trUtf8("Clear History...")) |
|
275 self.connect(act, SIGNAL("triggered()"), self.__clearHistoryDialog) |
|
276 |
|
277 def setInitialActions(self, actions): |
|
278 """ |
|
279 Public method to set the list of actions that should appear first in the menu. |
|
280 |
|
281 @param actions list of initial actions (list of QAction) |
|
282 """ |
|
283 self.__initialActions = actions[:] |
|
284 for act in self.__initialActions: |
|
285 self.addAction(act) |
|
286 |
|
287 def __showHistoryDialog(self): |
|
288 """ |
|
289 Private slot to show the history dialog. |
|
290 """ |
|
291 dlg = HistoryDialog(self) |
|
292 dlg.setAttribute(Qt.WA_DeleteOnClose) |
|
293 self.connect(dlg, SIGNAL("newUrl(const QUrl&, const QString&)"), |
|
294 self, SIGNAL("newUrl(const QUrl&, const QString&)")) |
|
295 self.connect(dlg, SIGNAL("openUrl(const QUrl&, const QString&)"), |
|
296 self, SIGNAL("openUrl(const QUrl&, const QString&)")) |
|
297 dlg.show() |
|
298 |
|
299 def __clearHistoryDialog(self): |
|
300 """ |
|
301 Private slot to clear the history. |
|
302 """ |
|
303 if self.__historyManager is not None and \ |
|
304 QMessageBox.question(None, |
|
305 self.trUtf8("Clear History"), |
|
306 self.trUtf8("""Do you want to clear the history?"""), |
|
307 QMessageBox.StandardButtons(\ |
|
308 QMessageBox.No | \ |
|
309 QMessageBox.Yes), |
|
310 QMessageBox.No) == QMessageBox.Yes: |
|
311 self.__historyManager.clear() |