1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the demonstration applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #ifndef HISTORY_H
43 #define HISTORY_H
44 
45 #include "modelmenu.h"
46 
47 #include <QtCore/QDateTime>
48 #include <QtCore/QHash>
49 #include <QtCore/QObject>
50 #include <QtCore/QTimer>
51 #include <QtCore/QUrl>
52 
53 #include <QtGui/QSortFilterProxyModel>
54 
55 #include <QWebHistoryInterface>
56 
57 class HistoryItem
58 {
59 public:
HistoryItem()60     HistoryItem() {}
61     HistoryItem(const QString &u,
62                 const QDateTime &d = QDateTime(), const QString &t = QString())
title(t)63         : title(t), url(u), dateTime(d) {}
64 
65     inline bool operator==(const HistoryItem &other) const
66         { return other.title == title
67           && other.url == url && other.dateTime == dateTime; }
68 
69     // history is sorted in reverse
70     inline bool operator <(const HistoryItem &other) const
71         { return dateTime > other.dateTime; }
72 
73     QString title;
74     QString url;
75     QDateTime dateTime;
76 };
77 
78 class AutoSaver;
79 class HistoryModel;
80 class HistoryFilterModel;
81 class HistoryTreeModel;
82 class HistoryManager : public QWebHistoryInterface
83 {
84     Q_OBJECT
85     Q_PROPERTY(int historyLimit READ historyLimit WRITE setHistoryLimit)
86 
87 signals:
88     void historyReset();
89     void entryAdded(const HistoryItem &item);
90     void entryRemoved(const HistoryItem &item);
91     void entryUpdated(int offset);
92 
93 public:
94     HistoryManager(QObject *parent = 0);
95     ~HistoryManager();
96 
97     bool historyContains(const QString &url) const;
98     void addHistoryEntry(const QString &url);
99 
100     void updateHistoryItem(const QUrl &url, const QString &title);
101 
102     int historyLimit() const;
103     void setHistoryLimit(int limit);
104 
105     QList<HistoryItem> history() const;
106     void setHistory(const QList<HistoryItem> &history, bool loadedAndSorted = false);
107 
108     // History manager keeps around these models for use by the completer and other classes
109     HistoryModel *historyModel() const;
110     HistoryFilterModel *historyFilterModel() const;
111     HistoryTreeModel *historyTreeModel() const;
112 
113 public slots:
114     void clear();
115     void loadSettings();
116 
117 private slots:
118     void save();
119     void checkForExpired();
120 
121 protected:
122     void addHistoryItem(const HistoryItem &item);
123 
124 private:
125     void load();
126 
127     AutoSaver *m_saveTimer;
128     int m_historyLimit;
129     QTimer m_expiredTimer;
130     QList<HistoryItem> m_history;
131     QString m_lastSavedUrl;
132 
133     HistoryModel *m_historyModel;
134     HistoryFilterModel *m_historyFilterModel;
135     HistoryTreeModel *m_historyTreeModel;
136 };
137 
138 class HistoryModel : public QAbstractTableModel
139 {
140     Q_OBJECT
141 
142 public slots:
143     void historyReset();
144     void entryAdded();
145     void entryUpdated(int offset);
146 
147 public:
148     enum Roles {
149         DateRole = Qt::UserRole + 1,
150         DateTimeRole = Qt::UserRole + 2,
151         UrlRole = Qt::UserRole + 3,
152         UrlStringRole = Qt::UserRole + 4
153     };
154 
155     HistoryModel(HistoryManager *history, QObject *parent = 0);
156     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
157     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
158     int columnCount(const QModelIndex &parent = QModelIndex()) const;
159     int rowCount(const QModelIndex &parent = QModelIndex()) const;
160     bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
161 
162 private:
163     HistoryManager *m_history;
164 };
165 
166 /*!
167     Proxy model that will remove any duplicate entries.
168     Both m_sourceRow and m_historyHash store their offsets not from
169     the front of the list, but as offsets from the back.
170   */
171 class HistoryFilterModel : public QAbstractProxyModel
172 {
173     Q_OBJECT
174 
175 public:
176     HistoryFilterModel(QAbstractItemModel *sourceModel, QObject *parent = 0);
177 
historyContains(const QString & url)178     inline bool historyContains(const QString &url) const
179         { load(); return m_historyHash.contains(url); }
180     int historyLocation(const QString &url) const;
181 
182     QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
183     QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
184     void setSourceModel(QAbstractItemModel *sourceModel);
185     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
186     int rowCount(const QModelIndex &parent = QModelIndex()) const;
187     int columnCount(const QModelIndex &parent = QModelIndex()) const;
188     QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const;
189     QModelIndex parent(const QModelIndex& index= QModelIndex()) const;
190     bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
191     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
192 
193 private slots:
194     void sourceReset();
195     void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
196     void sourceRowsInserted(const QModelIndex &parent, int start, int end);
197     void sourceRowsRemoved(const QModelIndex &, int, int);
198 
199 private:
200     void load() const;
201 
202     mutable QList<int> m_sourceRow;
203     mutable QHash<QString, int> m_historyHash;
204     mutable bool m_loaded;
205 };
206 
207 /*
208     The history menu
209     - Removes the first twenty entries and puts them as children of the top level.
210     - If there are less then twenty entries then the first folder is also removed.
211 
212     The mapping is done by knowing that HistoryTreeModel is over a table
213     We store that row offset in our index's private data.
214 */
215 class HistoryMenuModel : public QAbstractProxyModel
216 {
217     Q_OBJECT
218 
219 public:
220     HistoryMenuModel(HistoryTreeModel *sourceModel, QObject *parent = 0);
221     int columnCount(const QModelIndex &parent) const;
222     int rowCount(const QModelIndex &parent = QModelIndex()) const;
223     QModelIndex mapFromSource(const QModelIndex & sourceIndex) const;
224     QModelIndex mapToSource(const QModelIndex & proxyIndex) const;
225     QModelIndex index(int, int, const QModelIndex &parent = QModelIndex()) const;
226     QModelIndex parent(const QModelIndex &index = QModelIndex()) const;
227 
228     int bumpedRows() const;
229 
230 private:
231     HistoryTreeModel *m_treeModel;
232 };
233 
234 // Menu that is dynamically populated from the history
235 class HistoryMenu : public ModelMenu
236 {
237     Q_OBJECT
238 
239 signals:
240     void openUrl(const QUrl &url);
241 
242 public:
243      HistoryMenu(QWidget *parent = 0);
244      void setInitialActions(QList<QAction*> actions);
245 
246 protected:
247     bool prePopulated();
248     void postPopulated();
249 
250 private slots:
251     void activated(const QModelIndex &index);
252     void showHistoryDialog();
253 
254 private:
255     HistoryManager *m_history;
256     HistoryMenuModel *m_historyMenuModel;
257     QList<QAction*> m_initialActions;
258 };
259 
260 // proxy model for the history model that
261 // exposes each url http://www.foo.com and it url starting at the host www.foo.com
262 class HistoryCompletionModel : public QAbstractProxyModel
263 {
264     Q_OBJECT
265 
266 public:
267     HistoryCompletionModel(QObject *parent = 0);
268     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
269     int rowCount(const QModelIndex &parent = QModelIndex()) const;
270     int columnCount(const QModelIndex &parent = QModelIndex()) const;
271     QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
272     QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
273     QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const;
274     QModelIndex parent(const QModelIndex& index= QModelIndex()) const;
275     void setSourceModel(QAbstractItemModel *sourceModel);
276 
277 private slots:
278     void sourceReset();
279 
280 };
281 
282 // proxy model for the history model that converts the list
283 // into a tree, one top level node per day.
284 // Used in the HistoryDialog.
285 class HistoryTreeModel : public QAbstractProxyModel
286 {
287     Q_OBJECT
288 
289 public:
290     HistoryTreeModel(QAbstractItemModel *sourceModel, QObject *parent = 0);
291     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
292     int columnCount(const QModelIndex &parent) const;
293     int rowCount(const QModelIndex &parent = QModelIndex()) const;
294     QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
295     QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
296     QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
297     QModelIndex parent(const QModelIndex &index= QModelIndex()) const;
298     bool hasChildren(const QModelIndex &parent = QModelIndex()) const;
299     Qt::ItemFlags flags(const QModelIndex &index) const;
300     bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex());
301     QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
302 
303     void setSourceModel(QAbstractItemModel *sourceModel);
304 
305 private slots:
306     void sourceReset();
307     void sourceRowsInserted(const QModelIndex &parent, int start, int end);
308     void sourceRowsRemoved(const QModelIndex &parent, int start, int end);
309 
310 private:
311     int sourceDateRow(int row) const;
312     mutable QList<int> m_sourceRowCache;
313 
314 };
315 
316 // A modified QSortFilterProxyModel that always accepts the root nodes in the tree
317 // so filtering is only done on the children.
318 // Used in the HistoryDialog
319 class TreeProxyModel : public QSortFilterProxyModel
320 {
321     Q_OBJECT
322 
323 public:
324     TreeProxyModel(QObject *parent = 0);
325 
326 protected:
327     bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
328 };
329 
330 #include "ui_history.h"
331 
332 class HistoryDialog : public QDialog, public Ui_HistoryDialog
333 {
334     Q_OBJECT
335 
336 signals:
337     void openUrl(const QUrl &url);
338 
339 public:
340     HistoryDialog(QWidget *parent = 0, HistoryManager *history = 0);
341 
342 private slots:
343     void customContextMenuRequested(const QPoint &pos);
344     void open();
345     void copy();
346 
347 };
348 
349 #endif // HISTORY_H
350 
351