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