/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef HISTORY_H#define HISTORY_H#include "modelmenu.h"#include <QtCore/QDateTime>#include <QtCore/QHash>#include <QtCore/QObject>#include <QtCore/QTimer>#include <QtCore/QUrl>#include <QtCore/QSortFilterProxyModel>class HistoryItem
{
public:
HistoryItem() {}
HistoryItem(constQString&u,constQDateTime&d =QDateTime(),constQString&t =QString())
: title(t), url(u), dateTime(d) {}
inline bool operator==(const HistoryItem &other) const
{ return other.title == title
&& other.url == url && other.dateTime == dateTime; }
// history is sorted in reverseinline bool operator<(const HistoryItem &other) const
{ return dateTime > other.dateTime; }
QString title;
QString url;
QDateTime dateTime;
};
class AutoSaver;
class HistoryModel;
class HistoryFilterModel;
class HistoryTreeModel;
class HistoryManager : publicQObject
{
Q_OBJECT
Q_PROPERTY(int historyLimit READ historyLimit WRITE setHistoryLimit)
signals:
void historyReset();
void entryAdded(const HistoryItem &item);
void entryRemoved(const HistoryItem &item);
void entryUpdated(int offset);
public:
HistoryManager(QObject*parent =0);
~HistoryManager();
bool historyContains(constQString&url) const;
void addHistoryEntry(constQString&url);
void removeHistoryEntry(constQString&url);
void updateHistoryItem(constQUrl&url,constQString&title);
int historyLimit() const;
void setHistoryLimit(int limit);
QList<HistoryItem>& history();
void setHistory(constQList<HistoryItem>&history, bool loadedAndSorted =false);
// History manager keeps around these models for use by the completer and other classes
HistoryModel *historyModel() const;
HistoryFilterModel *historyFilterModel() const;
HistoryTreeModel *historyTreeModel() const;
publicslots:
void clear();
void loadSettings();
privateslots:
void save();
void checkForExpired(bool removeExpiredEntriesDirectly =false);
protected:
void addHistoryItem(const HistoryItem &item);
void removeHistoryItem(const HistoryItem &item);
private:
void load();
AutoSaver *m_saveTimer;
int m_historyLimit;
QTimer m_expiredTimer;
QList<HistoryItem> m_history;
QString m_lastSavedUrl;
HistoryModel *m_historyModel;
HistoryFilterModel *m_historyFilterModel;
HistoryTreeModel *m_historyTreeModel;
};
class HistoryModel : publicQAbstractTableModel
{
Q_OBJECT
publicslots:
void historyReset();
void entryAdded(const HistoryItem &item);
void entryRemoved(const HistoryItem &item);
void entryUpdated(int offset);
public:
enum Roles {
DateRole =Qt::UserRole +1,
DateTimeRole =Qt::UserRole +2,
UrlRole =Qt::UserRole +3,
UrlStringRole =Qt::UserRole +4
};
HistoryModel(HistoryManager *history,QObject*parent =0);
QVariant headerData(int section,Qt::Orientation orientation,int role =Qt::DisplayRole) const;
QVariant data(constQModelIndex&index,int role =Qt::DisplayRole) const;
int columnCount(constQModelIndex&parent =QModelIndex()) const;
int rowCount(constQModelIndex&parent =QModelIndex()) const;
bool removeRows(int row,int count,constQModelIndex&parent =QModelIndex());
private:
HistoryManager *m_history;
};
/*!
Proxy model that will remove any duplicate entries.
Both m_sourceRow and m_historyHash store their offsets not from
the front of the list, but as offsets from the back.
*/class HistoryFilterModel : publicQAbstractProxyModel
{
Q_OBJECT
public:
HistoryFilterModel(QAbstractItemModel*sourceModel,QObject*parent =0);
inline bool historyContains(constQString&url) const
{ load(); return m_historyHash.contains(url); }
QModelIndex mapFromSource(constQModelIndex&sourceIndex) const;
QModelIndex mapToSource(constQModelIndex&proxyIndex) const;
void setSourceModel(QAbstractItemModel*sourceModel);
QVariant headerData(int section,Qt::Orientation orientation,int role =Qt::DisplayRole) const;
int rowCount(constQModelIndex&parent =QModelIndex()) const;
int columnCount(constQModelIndex&parent =QModelIndex()) const;
QModelIndex index(int,int,constQModelIndex&=QModelIndex()) const;
QModelIndex parent(constQModelIndex& index=QModelIndex()) const;
bool removeRows(int row,int count,constQModelIndex&parent =QModelIndex());
privateslots:
void sourceReset();
void sourceDataChanged(constQModelIndex&topLeft,constQModelIndex&bottomRight);
void sourceRowsInserted(constQModelIndex&parent,int start,int end);
void sourceRowsRemoved(constQModelIndex&,int,int);
private:
void load() const;
mutableQList<int> m_sourceRow;
mutableQHash<QString,int> m_historyHash;
mutable bool m_loaded;
};
/*
The history menu
- Removes the first twenty entries and puts them as children of the top level.
- If there are less then twenty entries then the first folder is also removed.
The mapping is done by knowing that HistoryTreeModel is over a table
We store that row offset in our index's private data.
*/class HistoryMenuModel : publicQAbstractProxyModel
{
Q_OBJECT
public:
HistoryMenuModel(HistoryTreeModel *sourceModel,QObject*parent =0);
int columnCount(constQModelIndex&parent) const;
int rowCount(constQModelIndex&parent =QModelIndex()) const;
QModelIndex mapFromSource(constQModelIndex& sourceIndex) const;
QModelIndex mapToSource(constQModelIndex& proxyIndex) const;
QModelIndex index(int,int,constQModelIndex&parent =QModelIndex()) const;
QModelIndex parent(constQModelIndex&index =QModelIndex()) const;
int bumpedRows() const;
private:
HistoryTreeModel *m_treeModel;
};
// Menu that is dynamically populated from the historyclass HistoryMenu : public ModelMenu
{
Q_OBJECT
signals:
void openUrl(constQUrl&url);
public:
HistoryMenu(QWidget*parent =0);
void setInitialActions(QList<QAction*> actions);
protected:
bool prePopulated();
void postPopulated();
privateslots:
void activated(constQModelIndex&index);
void showHistoryDialog();
private:
HistoryManager *m_history;
HistoryMenuModel *m_historyMenuModel;
QList<QAction*> m_initialActions;
};
// proxy model for the history model that// exposes each url http://www.foo.com and it url starting at the host www.foo.comclass HistoryCompletionModel : publicQAbstractProxyModel
{
Q_OBJECT
public:
HistoryCompletionModel(QObject*parent =0);
QVariant data(constQModelIndex&index,int role =Qt::DisplayRole) const;
int rowCount(constQModelIndex&parent =QModelIndex()) const;
int columnCount(constQModelIndex&parent =QModelIndex()) const;
QModelIndex mapFromSource(constQModelIndex&sourceIndex) const;
QModelIndex mapToSource(constQModelIndex&proxyIndex) const;
QModelIndex index(int,int,constQModelIndex&=QModelIndex()) const;
QModelIndex parent(constQModelIndex& index=QModelIndex()) const;
void setSourceModel(QAbstractItemModel*sourceModel);
privateslots:
void sourceReset();
};
// proxy model for the history model that converts the list// into a tree, one top level node per day.// Used in the HistoryDialog.class HistoryTreeModel : publicQAbstractProxyModel
{
Q_OBJECT
public:
HistoryTreeModel(QAbstractItemModel*sourceModel,QObject*parent =0);
QVariant data(constQModelIndex&index,int role =Qt::DisplayRole) const;
int columnCount(constQModelIndex&parent) const;
int rowCount(constQModelIndex&parent =QModelIndex()) const;
QModelIndex mapFromSource(constQModelIndex&sourceIndex) const;
QModelIndex mapToSource(constQModelIndex&proxyIndex) const;
QModelIndex index(int row,int column,constQModelIndex&parent =QModelIndex()) const;
QModelIndex parent(constQModelIndex&index=QModelIndex()) const;
bool hasChildren(constQModelIndex&parent =QModelIndex()) const;
Qt::ItemFlags flags(constQModelIndex&index) const;
bool removeRows(int row,int count,constQModelIndex&parent =QModelIndex());
QVariant headerData(int section,Qt::Orientation orientation,int role =Qt::DisplayRole) const;
void setSourceModel(QAbstractItemModel*sourceModel);
privateslots:
void sourceReset();
void sourceRowsInserted(constQModelIndex&parent,int start,int end);
void sourceRowsRemoved(constQModelIndex&parent,int start,int end);
void sourceDataChanged(constQModelIndex&topLeft,constQModelIndex&bottomRight,constQVector<int> roles);
private:
int sourceDateRow(int row) const;
mutableQList<int> m_sourceRowCache;
};
// A modified QSortFilterProxyModel that always accepts the root nodes in the tree// so filtering is only done on the children.// Used in the HistoryDialogclass TreeProxyModel : publicQSortFilterProxyModel
{
Q_OBJECT
public:
TreeProxyModel(QObject*parent =0);
protected:
bool filterAcceptsRow(int source_row,constQModelIndex&source_parent) const;
};
#include "ui_history.h"class HistoryDialog : publicQDialog,public Ui_HistoryDialog
{
Q_OBJECT
signals:
void openUrl(constQUrl&url);
public:
HistoryDialog(QWidget*parent =0, HistoryManager *history =0);
privateslots:
void customContextMenuRequested(constQPoint&pos);
void open();
void copy();
};
#endif // HISTORY_H