mirror of
https://invent.kde.org/network/falkon.git
synced 2024-12-19 10:16:34 +01:00
History completely rewritten into Model/View architecture.
- much faster history loading (loading history entries only when needed - eg. expanding top level items) - less memory usage (sidebar and manager are using the same model) - searching is now performed on title and url - clear private data -> clear history is now working properly
This commit is contained in:
parent
bb4d981c31
commit
5b75c0e5a0
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,3 +22,4 @@ git_revision
|
||||
*.exp
|
||||
*.zip
|
||||
Thumbs.db
|
||||
tests/modeltest
|
||||
|
2
src/lib/3rdparty/qtwin.cpp
vendored
2
src/lib/3rdparty/qtwin.cpp
vendored
@ -29,7 +29,7 @@
|
||||
#include <QApplication>
|
||||
#include <QWidget>
|
||||
#include <QList>
|
||||
#include "historymodel.h"
|
||||
#include "history.h"
|
||||
#include "mainapplication.h"
|
||||
|
||||
#ifdef Q_WS_WIN
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "cookiemanager.h"
|
||||
#include "cookiejar.h"
|
||||
#include "browsinglibrary.h"
|
||||
#include "historymodel.h"
|
||||
#include "history.h"
|
||||
#include "networkmanager.h"
|
||||
#include "rssmanager.h"
|
||||
#include "updater.h"
|
||||
@ -634,10 +634,10 @@ CookieManager* MainApplication::cookieManager()
|
||||
return m_cookiemanager;
|
||||
}
|
||||
|
||||
HistoryModel* MainApplication::history()
|
||||
History* MainApplication::history()
|
||||
{
|
||||
if (!m_historymodel) {
|
||||
m_historymodel = new HistoryModel(getWindow());
|
||||
m_historymodel = new History(this);
|
||||
}
|
||||
return m_historymodel;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ class QNetworkDiskCache;
|
||||
class QupZilla;
|
||||
class CookieManager;
|
||||
class BrowsingLibrary;
|
||||
class HistoryModel;
|
||||
class History;
|
||||
class NetworkManager;
|
||||
class CookieJar;
|
||||
class RSSManager;
|
||||
@ -83,7 +83,7 @@ public:
|
||||
QupZilla* getWindow();
|
||||
CookieManager* cookieManager();
|
||||
BrowsingLibrary* browsingLibrary();
|
||||
HistoryModel* history();
|
||||
History* history();
|
||||
QWebSettings* webSettings();
|
||||
NetworkManager* networkManager();
|
||||
CookieJar* cookieJar();
|
||||
@ -127,7 +127,7 @@ private:
|
||||
|
||||
CookieManager* m_cookiemanager;
|
||||
BrowsingLibrary* m_browsingLibrary;
|
||||
HistoryModel* m_historymodel;
|
||||
History* m_historymodel;
|
||||
QWebSettings* m_websettings;
|
||||
NetworkManager* m_networkmanager;
|
||||
CookieJar* m_cookiejar;
|
||||
|
@ -21,7 +21,7 @@
|
||||
#include "webpage.h"
|
||||
#include "tabbedwebview.h"
|
||||
#include "lineedit.h"
|
||||
#include "historymodel.h"
|
||||
#include "history.h"
|
||||
#include "locationbar.h"
|
||||
#include "searchtoolbar.h"
|
||||
#include "websearchbar.h"
|
||||
@ -578,7 +578,7 @@ void QupZilla::loadSettings()
|
||||
m_navigationBar->buttonNext()->setVisible(showBackForwardIcons);
|
||||
m_navigationBar->buttonAddTab()->setVisible(showAddTab);
|
||||
|
||||
m_sideBarManager->showSideBar(activeSideBar);
|
||||
m_sideBarManager->showSideBar(activeSideBar, false);
|
||||
|
||||
//Private browsing
|
||||
m_actionPrivateBrowsing->setChecked(mApp->webSettings()->testAttribute(QWebSettings::PrivateBrowsingEnabled));
|
||||
@ -1148,6 +1148,7 @@ void QupZilla::showCookieManager()
|
||||
m->raise();
|
||||
}
|
||||
|
||||
|
||||
void QupZilla::showHistoryManager()
|
||||
{
|
||||
mApp->browsingLibrary()->showHistory(this);
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "mainapplication.h"
|
||||
#include "bookmarksmodel.h"
|
||||
#include "iconprovider.h"
|
||||
#include "historymodel.h"
|
||||
#include "history.h"
|
||||
#include "toolbutton.h"
|
||||
#include "databasewriter.h"
|
||||
#include "enhancedmenu.h"
|
||||
|
@ -27,7 +27,7 @@ class QHBoxLayout;
|
||||
|
||||
class QupZilla;
|
||||
class BookmarksModel;
|
||||
class HistoryModel;
|
||||
class History;
|
||||
class ToolButton;
|
||||
class Menu;
|
||||
|
||||
@ -77,7 +77,7 @@ private:
|
||||
|
||||
QupZilla* p_QupZilla;
|
||||
BookmarksModel* m_bookmarksModel;
|
||||
HistoryModel* m_historyModel;
|
||||
History* m_historyModel;
|
||||
Menu* m_menuMostVisited;
|
||||
ToolButton* m_mostVis;
|
||||
QHBoxLayout* m_layout;
|
||||
|
289
src/lib/history/history.cpp
Normal file
289
src/lib/history/history.cpp
Normal file
@ -0,0 +1,289 @@
|
||||
/* ============================================================
|
||||
* QupZilla - WebKit based browser
|
||||
* Copyright (C) 2010-2012 David Rosca <nowrep@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ============================================================ */
|
||||
#include "history.h"
|
||||
#include "historymodel.h"
|
||||
#include "tabbedwebview.h"
|
||||
#include "qupzilla.h"
|
||||
#include "iconprovider.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <QSqlDatabase>
|
||||
#include <QSqlQuery>
|
||||
|
||||
History::History(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_isSaving(true)
|
||||
, m_model(0)
|
||||
{
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
HistoryModel* History::model()
|
||||
{
|
||||
if (!m_model) {
|
||||
m_model = new HistoryModel(this);
|
||||
}
|
||||
|
||||
return m_model;
|
||||
}
|
||||
|
||||
void History::loadSettings()
|
||||
{
|
||||
Settings settings;
|
||||
settings.beginGroup("Web-Browser-Settings");
|
||||
m_isSaving = settings.value("allowHistory", true).toBool();
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
// AddHistoryEntry
|
||||
void History::addHistoryEntry(WebView* view)
|
||||
{
|
||||
if (!m_isSaving || view->loadingError()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QUrl &url = view->url();
|
||||
const QString &title = view->title();
|
||||
|
||||
addHistoryEntry(url, title);
|
||||
}
|
||||
|
||||
void History::addHistoryEntry(const QUrl &url, QString title)
|
||||
{
|
||||
if (!m_isSaving) {
|
||||
return;
|
||||
}
|
||||
if (url.scheme() == "qupzilla" || url.scheme() == "about" || url.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (title == "") {
|
||||
title = tr("No Named Page");
|
||||
}
|
||||
|
||||
QSqlQuery query;
|
||||
query.prepare("SELECT id, count, date, title FROM history WHERE url=?");
|
||||
query.bindValue(0, url);
|
||||
query.exec();
|
||||
if (!query.next()) {
|
||||
query.prepare("INSERT INTO history (count, date, url, title) VALUES (1,?,?,?)");
|
||||
query.bindValue(0, QDateTime::currentMSecsSinceEpoch());
|
||||
query.bindValue(1, url);
|
||||
query.bindValue(2, title);
|
||||
query.exec();
|
||||
|
||||
int id = query.lastInsertId().toInt();
|
||||
HistoryEntry entry;
|
||||
entry.id = id;
|
||||
entry.count = 1;
|
||||
entry.date = QDateTime::currentDateTime();
|
||||
entry.url = url;
|
||||
entry.urlString = url.toEncoded();
|
||||
entry.title = title;
|
||||
emit historyEntryAdded(entry);
|
||||
}
|
||||
else {
|
||||
int id = query.value(0).toInt();
|
||||
int count = query.value(1).toInt();
|
||||
QDateTime date = QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong());
|
||||
QString oldTitle = query.value(3).toString();
|
||||
|
||||
query.prepare("UPDATE history SET count = count + 1, date=?, title=? WHERE url=?");
|
||||
query.bindValue(0, QDateTime::currentMSecsSinceEpoch());
|
||||
query.bindValue(1, title);
|
||||
query.bindValue(2, url);
|
||||
query.exec();
|
||||
|
||||
HistoryEntry before;
|
||||
before.id = id;
|
||||
before.count = count;
|
||||
before.date = date;
|
||||
before.url = url;
|
||||
before.urlString = url.toEncoded();
|
||||
before.title = oldTitle;
|
||||
|
||||
HistoryEntry after = before;
|
||||
after.count = count + 1;
|
||||
after.date = QDateTime::currentDateTime();
|
||||
after.title = title;
|
||||
|
||||
emit historyEntryEdited(before, after);
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteHistoryEntry
|
||||
void History::deleteHistoryEntry(int index)
|
||||
{
|
||||
QList<int> list;
|
||||
list.append(index);
|
||||
|
||||
deleteHistoryEntry(list);
|
||||
}
|
||||
|
||||
void History::deleteHistoryEntry(const QList<int> &list)
|
||||
{
|
||||
QSqlDatabase db = QSqlDatabase::database();
|
||||
db.transaction();
|
||||
|
||||
foreach(int index, list) {
|
||||
QSqlQuery query;
|
||||
query.prepare("SELECT count, date, url, title FROM history WHERE id=?");
|
||||
query.addBindValue(index);
|
||||
query.exec();
|
||||
query.next();
|
||||
|
||||
HistoryEntry entry;
|
||||
entry.id = index;
|
||||
entry.count = query.value(0).toInt();
|
||||
entry.date = QDateTime::fromMSecsSinceEpoch(query.value(1).toLongLong());
|
||||
entry.url = query.value(2).toUrl();
|
||||
entry.urlString = entry.url.toEncoded();
|
||||
entry.title = query.value(3).toString();
|
||||
|
||||
query.prepare("DELETE FROM history WHERE id=?");
|
||||
query.addBindValue(index);
|
||||
query.exec();
|
||||
|
||||
query.prepare("DELETE FROM icons WHERE url=?");
|
||||
query.addBindValue(entry.url.toEncoded(QUrl::RemoveFragment));
|
||||
query.exec();
|
||||
|
||||
emit historyEntryDeleted(entry);
|
||||
}
|
||||
|
||||
db.commit();
|
||||
}
|
||||
|
||||
void History::deleteHistoryEntry(const QString &url, const QString &title)
|
||||
{
|
||||
QSqlQuery query;
|
||||
query.prepare("SELECT id FROM history WHERE url=? AND title=?");
|
||||
query.bindValue(0, url);
|
||||
query.bindValue(1, title);
|
||||
query.exec();
|
||||
if (query.next()) {
|
||||
int id = query.value(0).toInt();
|
||||
deleteHistoryEntry(id);
|
||||
}
|
||||
}
|
||||
|
||||
QList<int> History::indexesFromTimeRange(qint64 start, qint64 end)
|
||||
{
|
||||
QList<int> list;
|
||||
|
||||
if (start < 0 || end < 0) {
|
||||
return list;
|
||||
}
|
||||
|
||||
QSqlQuery query;
|
||||
query.prepare("SELECT id FROM history WHERE date BETWEEN ? AND ?");
|
||||
query.addBindValue(end);
|
||||
query.addBindValue(start);
|
||||
query.exec();
|
||||
|
||||
while (query.next()) {
|
||||
list.append(query.value(0).toInt());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
bool History::urlIsStored(const QString &url)
|
||||
{
|
||||
QSqlQuery query;
|
||||
query.prepare("SELECT id FROM history WHERE url=?");
|
||||
query.bindValue(0, url);
|
||||
query.exec();
|
||||
return query.next();
|
||||
}
|
||||
|
||||
QList<HistoryEntry> History::mostVisited(int count)
|
||||
{
|
||||
QList<HistoryEntry> list;
|
||||
QSqlQuery query;
|
||||
query.exec(QString("SELECT count, date, id, title, url FROM history ORDER BY count DESC LIMIT %1").arg(count));
|
||||
while (query.next()) {
|
||||
HistoryEntry entry;
|
||||
entry.count = query.value(0).toInt();
|
||||
entry.date = query.value(1).toDateTime();
|
||||
entry.id = query.value(2).toInt();
|
||||
entry.title = query.value(3).toString();
|
||||
entry.url = query.value(4).toUrl();
|
||||
list.append(entry);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
bool History::optimizeHistory()
|
||||
{
|
||||
QSqlQuery query;
|
||||
return query.exec("VACUUM");
|
||||
}
|
||||
|
||||
bool History::clearHistory()
|
||||
{
|
||||
QSqlQuery query;
|
||||
if (query.exec("DELETE FROM history")) {
|
||||
emit resetHistory();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void History::setSaving(bool state)
|
||||
{
|
||||
m_isSaving = state;
|
||||
}
|
||||
|
||||
bool History::isSaving()
|
||||
{
|
||||
return m_isSaving;
|
||||
}
|
||||
|
||||
QString History::titleCaseLocalizedMonth(int month)
|
||||
{
|
||||
switch (month) {
|
||||
case 1:
|
||||
return tr("January");
|
||||
case 2:
|
||||
return tr("February");
|
||||
case 3:
|
||||
return tr("March");
|
||||
case 4:
|
||||
return tr("April");
|
||||
case 5:
|
||||
return tr("May");
|
||||
case 6:
|
||||
return tr("June");
|
||||
case 7:
|
||||
return tr("July");
|
||||
case 8:
|
||||
return tr("August");
|
||||
case 9:
|
||||
return tr("September");
|
||||
case 10:
|
||||
return tr("October");
|
||||
case 11:
|
||||
return tr("November");
|
||||
case 12:
|
||||
return tr("December");
|
||||
default:
|
||||
qWarning("Month number out of range!");
|
||||
return QString();
|
||||
}
|
||||
}
|
89
src/lib/history/history.h
Normal file
89
src/lib/history/history.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* ============================================================
|
||||
* QupZilla - WebKit based browser
|
||||
* Copyright (C) 2010-2012 David Rosca <nowrep@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ============================================================ */
|
||||
#ifndef HISTORY_H
|
||||
#define HISTORY_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QDateTime>
|
||||
#include <QUrl>
|
||||
|
||||
#include "qz_namespace.h"
|
||||
|
||||
class QIcon;
|
||||
|
||||
class QupZilla;
|
||||
class WebView;
|
||||
class HistoryModel;
|
||||
|
||||
class QT_QUPZILLA_EXPORT History : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
History(QObject* parent);
|
||||
|
||||
struct HistoryEntry {
|
||||
int id;
|
||||
int count;
|
||||
QDateTime date;
|
||||
QUrl url;
|
||||
QString urlString;
|
||||
QString title;
|
||||
};
|
||||
|
||||
static QString titleCaseLocalizedMonth(int month);
|
||||
|
||||
HistoryModel* model();
|
||||
|
||||
void addHistoryEntry(WebView* view);
|
||||
void addHistoryEntry(const QUrl &url, QString title);
|
||||
|
||||
void deleteHistoryEntry(int index);
|
||||
void deleteHistoryEntry(const QList<int> &list);
|
||||
void deleteHistoryEntry(const QString &url, const QString &title);
|
||||
|
||||
QList<int> indexesFromTimeRange(qint64 start, qint64 end);
|
||||
|
||||
bool urlIsStored(const QString &url);
|
||||
|
||||
QList<HistoryEntry> mostVisited(int count);
|
||||
|
||||
bool clearHistory();
|
||||
bool optimizeHistory();
|
||||
bool isSaving();
|
||||
void setSaving(bool state);
|
||||
|
||||
void loadSettings();
|
||||
|
||||
signals:
|
||||
void historyEntryAdded(const HistoryEntry &entry);
|
||||
void historyEntryDeleted(const HistoryEntry &entry);
|
||||
void historyEntryEdited(const HistoryEntry &before, const HistoryEntry &after);
|
||||
|
||||
void resetHistory();
|
||||
|
||||
private:
|
||||
bool m_isSaving;
|
||||
|
||||
QupZilla* p_QupZilla;
|
||||
HistoryModel* m_model;
|
||||
};
|
||||
|
||||
typedef History::HistoryEntry HistoryEntry;
|
||||
|
||||
#endif // HISTORY_H
|
180
src/lib/history/historyitem.cpp
Normal file
180
src/lib/history/historyitem.cpp
Normal file
@ -0,0 +1,180 @@
|
||||
/* ============================================================
|
||||
* QupZilla - WebKit based browser
|
||||
* Copyright (C) 2010-2012 David Rosca <nowrep@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ============================================================ */
|
||||
#include "historyitem.h"
|
||||
|
||||
HistoryItem::HistoryItem(HistoryItem* parent)
|
||||
: canFetchMore(false)
|
||||
, m_parent(parent)
|
||||
, m_iconLoaded(false)
|
||||
, m_startTimestamp(0)
|
||||
, m_endTimestamp(0)
|
||||
{
|
||||
if (m_parent) {
|
||||
m_parent->appendChild(this);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryItem::changeParent(HistoryItem* parent)
|
||||
{
|
||||
if (m_parent) {
|
||||
m_parent->removeChild(this);
|
||||
}
|
||||
|
||||
m_parent = parent;
|
||||
|
||||
if (m_parent) {
|
||||
m_parent->prependChild(this);
|
||||
}
|
||||
}
|
||||
|
||||
HistoryItem* HistoryItem::parent() const
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
void HistoryItem::prependChild(HistoryItem* child)
|
||||
{
|
||||
if (m_children.contains(child)) {
|
||||
m_children.removeAll(child);
|
||||
}
|
||||
|
||||
child->m_parent = this;
|
||||
m_children.prepend(child);
|
||||
}
|
||||
|
||||
void HistoryItem::appendChild(HistoryItem* child)
|
||||
{
|
||||
if (m_children.contains(child)) {
|
||||
m_children.removeAll(child);
|
||||
}
|
||||
|
||||
child->m_parent = this;
|
||||
m_children.append(child);
|
||||
}
|
||||
|
||||
void HistoryItem::insertChild(int row, HistoryItem* child)
|
||||
{
|
||||
if (m_children.contains(child)) {
|
||||
m_children.removeAll(child);
|
||||
}
|
||||
|
||||
if (m_children.count() >= row) {
|
||||
child->m_parent = this;
|
||||
m_children.insert(row, child);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryItem::removeChild(int row)
|
||||
{
|
||||
if (m_children.count() > row) {
|
||||
removeChild(m_children.at(row));
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryItem::removeChild(HistoryItem* child)
|
||||
{
|
||||
int index = m_children.indexOf(child);
|
||||
|
||||
if (index != -1) {
|
||||
m_children.removeOne(child);
|
||||
}
|
||||
}
|
||||
|
||||
HistoryItem* HistoryItem::child(int row) const
|
||||
{
|
||||
if (m_children.count() > row) {
|
||||
return m_children.at(row);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HistoryItem::childCount() const
|
||||
{
|
||||
return m_children.count();
|
||||
}
|
||||
|
||||
int HistoryItem::row()
|
||||
{
|
||||
return m_parent ? m_parent->indexOfChild(this) : 0;
|
||||
}
|
||||
|
||||
int HistoryItem::indexOfChild(HistoryItem* child)
|
||||
{
|
||||
return m_children.indexOf(child);
|
||||
}
|
||||
|
||||
bool HistoryItem::isTopLevel() const
|
||||
{
|
||||
return (m_startTimestamp != 0);
|
||||
}
|
||||
|
||||
bool HistoryItem::iconLoaded() const
|
||||
{
|
||||
return m_iconLoaded;
|
||||
}
|
||||
|
||||
QIcon HistoryItem::icon() const
|
||||
{
|
||||
return m_icon;
|
||||
}
|
||||
|
||||
void HistoryItem::setIcon(const QIcon &icon)
|
||||
{
|
||||
m_icon = icon;
|
||||
m_iconLoaded = true;
|
||||
}
|
||||
|
||||
void HistoryItem::refreshIcon()
|
||||
{
|
||||
m_iconLoaded = false;
|
||||
}
|
||||
|
||||
void HistoryItem::setStartTimestamp(qint64 start)
|
||||
{
|
||||
m_startTimestamp = start;
|
||||
}
|
||||
|
||||
qint64 HistoryItem::startTimestamp() const
|
||||
{
|
||||
if (m_startTimestamp == -1) {
|
||||
return QDateTime::currentDateTime().toMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
return m_startTimestamp;
|
||||
}
|
||||
|
||||
void HistoryItem::setEndTimestamp(qint64 end)
|
||||
{
|
||||
m_endTimestamp = end;
|
||||
|
||||
}
|
||||
|
||||
qint64 HistoryItem::endTimestamp() const
|
||||
{
|
||||
return m_endTimestamp;
|
||||
}
|
||||
|
||||
HistoryItem::~HistoryItem()
|
||||
{
|
||||
if (m_parent) {
|
||||
m_parent->removeChild(this);
|
||||
}
|
||||
|
||||
qDeleteAll(m_children);
|
||||
}
|
76
src/lib/history/historyitem.h
Normal file
76
src/lib/history/historyitem.h
Normal file
@ -0,0 +1,76 @@
|
||||
/* ============================================================
|
||||
* QupZilla - WebKit based browser
|
||||
* Copyright (C) 2010-2012 David Rosca <nowrep@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ============================================================ */
|
||||
#ifndef HISTORYITEM_H
|
||||
#define HISTORYITEM_H
|
||||
|
||||
#include <QIcon>
|
||||
|
||||
#include "qz_namespace.h"
|
||||
#include "history.h"
|
||||
|
||||
class QT_QUPZILLA_EXPORT HistoryItem
|
||||
{
|
||||
public:
|
||||
explicit HistoryItem(HistoryItem* parent = 0);
|
||||
~HistoryItem();
|
||||
|
||||
void changeParent(HistoryItem* parent);
|
||||
HistoryItem* parent() const;
|
||||
|
||||
HistoryItem* child(int row) const;
|
||||
int childCount() const;
|
||||
|
||||
void prependChild(HistoryItem* child);
|
||||
void appendChild(HistoryItem* child);
|
||||
void insertChild(int row, HistoryItem* child);
|
||||
|
||||
void removeChild(int row);
|
||||
void removeChild(HistoryItem* child);
|
||||
|
||||
int row();
|
||||
int indexOfChild(HistoryItem* child);
|
||||
|
||||
bool isTopLevel() const;
|
||||
bool iconLoaded() const;
|
||||
|
||||
QIcon icon() const;
|
||||
void setIcon(const QIcon &icon);
|
||||
void refreshIcon();
|
||||
|
||||
void setStartTimestamp(qint64 start);
|
||||
qint64 startTimestamp() const;
|
||||
|
||||
void setEndTimestamp(qint64 end);
|
||||
qint64 endTimestamp() const;
|
||||
|
||||
HistoryEntry historyEntry;
|
||||
QString title;
|
||||
bool canFetchMore;
|
||||
|
||||
private:
|
||||
HistoryItem* m_parent;
|
||||
QList<HistoryItem*> m_children;
|
||||
|
||||
QIcon m_icon;
|
||||
bool m_iconLoaded;
|
||||
|
||||
qint64 m_startTimestamp;
|
||||
qint64 m_endTimestamp;
|
||||
};
|
||||
|
||||
#endif // HISTORYITEM_H
|
@ -19,45 +19,28 @@
|
||||
#include "ui_historymanager.h"
|
||||
#include "qupzilla.h"
|
||||
#include "mainapplication.h"
|
||||
#include "historymodel.h"
|
||||
#include "iconprovider.h"
|
||||
#include "history.h"
|
||||
#include "browsinglibrary.h"
|
||||
#include "globalfunctions.h"
|
||||
#include "tabwidget.h"
|
||||
#include "tabbedwebview.h"
|
||||
#include "historymodel.h"
|
||||
#include "headerview.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QClipboard>
|
||||
#include <QTimer>
|
||||
#include <QSqlQuery>
|
||||
|
||||
HistoryManager::HistoryManager(QupZilla* mainClass, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, ui(new Ui::HistoryManager)
|
||||
, p_QupZilla(mainClass)
|
||||
, m_historyModel(mApp->history())
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->historyTree->setDefaultItemShowMode(TreeWidget::ItemsCollapsed);
|
||||
ui->historyTree->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
ui->deleteB->setShortcut(QKeySequence("Del"));
|
||||
|
||||
connect(ui->historyTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(itemDoubleClicked(QTreeWidgetItem*)));
|
||||
connect(ui->historyTree, SIGNAL(itemMiddleButtonClicked(QTreeWidgetItem*)), this, SLOT(itemDoubleClicked(QTreeWidgetItem*)));
|
||||
connect(ui->historyTree, SIGNAL(openLink(QUrl, HistoryView::OpenBehavior)), this, SLOT(openLink(QUrl, HistoryView::OpenBehavior)));
|
||||
|
||||
connect(ui->deleteB, SIGNAL(clicked()), this, SLOT(deleteItem()));
|
||||
connect(ui->deleteB, SIGNAL(clicked()), ui->historyTree, SLOT(removeItems()));
|
||||
connect(ui->clearAll, SIGNAL(clicked()), this, SLOT(clearHistory()));
|
||||
connect(ui->historyTree, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(contextMenuRequested(const QPoint &)));
|
||||
|
||||
connect(m_historyModel, SIGNAL(historyEntryAdded(HistoryEntry)), this, SLOT(historyEntryAdded(HistoryEntry)));
|
||||
connect(m_historyModel, SIGNAL(historyEntryDeleted(HistoryEntry)), this, SLOT(historyEntryDeleted(HistoryEntry)));
|
||||
connect(m_historyModel, SIGNAL(historyEntryEdited(HistoryEntry, HistoryEntry)), this, SLOT(historyEntryEdited(HistoryEntry, HistoryEntry)));
|
||||
connect(m_historyModel, SIGNAL(historyClear()), ui->historyTree, SLOT(clear()));
|
||||
|
||||
connect(ui->optimizeDb, SIGNAL(clicked(QPoint)), this, SLOT(optimizeDb()));
|
||||
|
||||
//QTimer::singleShot(0, this, SLOT(refreshTable()));
|
||||
|
||||
ui->historyTree->setFocus();
|
||||
}
|
||||
|
||||
@ -76,165 +59,14 @@ void HistoryManager::setMainWindow(QupZilla* window)
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryManager::itemDoubleClicked(QTreeWidgetItem* item)
|
||||
void HistoryManager::restoreState(const QByteArray &state)
|
||||
{
|
||||
if (!item || item->text(1).isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QUrl url = QUrl::fromEncoded(item->text(1).toUtf8());
|
||||
getQupZilla()->tabWidget()->addView(url, item->text(0));
|
||||
ui->historyTree->header()->restoreState(state);
|
||||
}
|
||||
|
||||
void HistoryManager::loadInNewTab()
|
||||
QByteArray HistoryManager::saveState()
|
||||
{
|
||||
if (QAction* action = qobject_cast<QAction*>(sender())) {
|
||||
getQupZilla()->tabWidget()->addView(action->data().toUrl(), Qz::NT_NotSelectedTab);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryManager::contextMenuRequested(const QPoint &position)
|
||||
{
|
||||
if (!ui->historyTree->itemAt(position)) {
|
||||
return;
|
||||
}
|
||||
QUrl link = QUrl::fromEncoded(ui->historyTree->itemAt(position)->text(1).toUtf8());
|
||||
if (link.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMenu menu;
|
||||
menu.addAction(tr("Open link in current tab"), getQupZilla(), SLOT(loadActionUrl()))->setData(link);
|
||||
menu.addAction(tr("Open link in new tab"), this, SLOT(loadInNewTab()))->setData(link);
|
||||
menu.addSeparator();
|
||||
menu.addAction(tr("Copy address"), this, SLOT(copyUrl()))->setData(link);
|
||||
|
||||
//Prevent choosing first option with double rightclick
|
||||
QPoint pos = ui->historyTree->viewport()->mapToGlobal(position);
|
||||
QPoint p(pos.x(), pos.y() + 1);
|
||||
menu.exec(p);
|
||||
}
|
||||
|
||||
void HistoryManager::copyUrl()
|
||||
{
|
||||
if (QAction* action = qobject_cast<QAction*>(sender())) {
|
||||
QApplication::clipboard()->setText(action->data().toUrl().toEncoded());
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryManager::deleteItem()
|
||||
{
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
QList<int> list;
|
||||
|
||||
foreach(QTreeWidgetItem * item, ui->historyTree->selectedItems()) {
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!item->parent()) {
|
||||
QList<QTreeWidgetItem*> items;
|
||||
|
||||
for (int i = 0; i < item->childCount(); i++) {
|
||||
QTreeWidgetItem* children = item->child(i);
|
||||
if (children->isHidden()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int id = children->data(0, Qt::UserRole + 10).toInt();
|
||||
|
||||
list.append(id);
|
||||
m_ignoredIds.append(id);
|
||||
items.append(children);
|
||||
}
|
||||
|
||||
if (item->childCount() == 0) {
|
||||
items.append(item);
|
||||
}
|
||||
ui->historyTree->deleteItems(items);
|
||||
}
|
||||
else {
|
||||
int id = item->data(0, Qt::UserRole + 10).toInt();
|
||||
|
||||
list.append(id);
|
||||
m_ignoredIds.append(id);
|
||||
|
||||
ui->historyTree->deleteItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
m_historyModel->deleteHistoryEntry(list);
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
void HistoryManager::historyEntryAdded(const HistoryEntry &entry)
|
||||
{
|
||||
QDate todayDate = QDate::currentDate();
|
||||
QDate startOfWeekDate = todayDate.addDays(1 - todayDate.dayOfWeek());
|
||||
|
||||
QDate date = entry.date.date();
|
||||
QString localDate;
|
||||
|
||||
if (date == todayDate) {
|
||||
localDate = tr("Today");
|
||||
}
|
||||
else if (date >= startOfWeekDate) {
|
||||
localDate = tr("This Week");
|
||||
}
|
||||
else if (date.month() == todayDate.month()) {
|
||||
localDate = tr("This Month");
|
||||
}
|
||||
else {
|
||||
localDate = QString("%1 %2").arg(HistoryModel::titleCaseLocalizedMonth(date.month()), QString::number(date.year()));
|
||||
}
|
||||
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem();
|
||||
QTreeWidgetItem* parentItem;
|
||||
QList<QTreeWidgetItem*> findParent = ui->historyTree->findItems(localDate, 0);
|
||||
if (findParent.count() == 1) {
|
||||
parentItem = findParent.at(0);
|
||||
}
|
||||
else {
|
||||
parentItem = new QTreeWidgetItem();
|
||||
parentItem->setText(0, localDate);
|
||||
parentItem->setIcon(0, QIcon(":/icons/menu/history_entry.png"));
|
||||
ui->historyTree->insertTopLevelItem(0, parentItem);
|
||||
}
|
||||
|
||||
item->setText(0, entry.title);
|
||||
item->setText(1, entry.url.toEncoded());
|
||||
item->setToolTip(0, entry.title);
|
||||
item->setToolTip(1, entry.url.toEncoded());
|
||||
|
||||
item->setData(0, Qt::UserRole + 10, entry.id);
|
||||
item->setIcon(0, _iconForUrl(entry.url));
|
||||
ui->historyTree->prependToParentItem(parentItem, item);
|
||||
}
|
||||
|
||||
void HistoryManager::historyEntryDeleted(const HistoryEntry &entry)
|
||||
{
|
||||
if (m_ignoredIds.contains(entry.id)) {
|
||||
m_ignoredIds.removeOne(entry.id);
|
||||
return;
|
||||
}
|
||||
|
||||
QList<QTreeWidgetItem*> list = ui->historyTree->allItems();
|
||||
foreach(QTreeWidgetItem * item, list) {
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
if (item->data(0, Qt::UserRole + 10).toInt() != entry.id) {
|
||||
continue;
|
||||
}
|
||||
ui->historyTree->deleteItem(item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryManager::historyEntryEdited(const HistoryEntry &before, const HistoryEntry &after)
|
||||
{
|
||||
historyEntryDeleted(before);
|
||||
historyEntryAdded(after);
|
||||
return ui->historyTree->header()->saveState();
|
||||
}
|
||||
|
||||
void HistoryManager::clearHistory()
|
||||
@ -245,89 +77,23 @@ void HistoryManager::clearHistory()
|
||||
return;
|
||||
}
|
||||
|
||||
m_historyModel->clearHistory();
|
||||
m_historyModel->optimizeHistory();
|
||||
}
|
||||
|
||||
void HistoryManager::refreshTable()
|
||||
{
|
||||
QTimer::singleShot(0, this, SLOT(slotRefreshTable()));
|
||||
}
|
||||
|
||||
void HistoryManager::slotRefreshTable()
|
||||
{
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
ui->historyTree->clear();
|
||||
|
||||
QDate todayDate = QDate::currentDate();
|
||||
QDate startOfWeekDate = todayDate.addDays(1 - todayDate.dayOfWeek());
|
||||
QSqlQuery query;
|
||||
query.exec("SELECT title, url, id, date FROM history ORDER BY date DESC");
|
||||
|
||||
int counter = 0;
|
||||
QWeakPointer<HistoryManager> guard = this;
|
||||
QHash<QString, QTreeWidgetItem*> hash;
|
||||
while (query.next()) {
|
||||
const QString &title = query.value(0).toString();
|
||||
const QUrl &url = query.value(1).toUrl();
|
||||
int id = query.value(2).toInt();
|
||||
const QDate &date = QDateTime::fromMSecsSinceEpoch(query.value(3).toLongLong()).date();
|
||||
QString localDate;
|
||||
|
||||
if (date == todayDate) {
|
||||
localDate = tr("Today");
|
||||
}
|
||||
else if (date >= startOfWeekDate) {
|
||||
localDate = tr("This Week");
|
||||
}
|
||||
else if (date.month() == todayDate.month()) {
|
||||
localDate = tr("This Month");
|
||||
}
|
||||
else {
|
||||
localDate = QString("%1 %2").arg(HistoryModel::titleCaseLocalizedMonth(date.month()), QString::number(date.year()));
|
||||
}
|
||||
|
||||
QTreeWidgetItem* item;
|
||||
QTreeWidgetItem* findParent = hash[localDate];
|
||||
if (findParent) {
|
||||
item = new QTreeWidgetItem(findParent);
|
||||
}
|
||||
else {
|
||||
QTreeWidgetItem* newParent = new QTreeWidgetItem(ui->historyTree);
|
||||
newParent->setText(0, localDate);
|
||||
newParent->setIcon(0, QIcon(":/icons/menu/history_entry.png"));
|
||||
ui->historyTree->addTopLevelItem(newParent);
|
||||
hash[localDate] = newParent;
|
||||
|
||||
item = new QTreeWidgetItem(newParent);
|
||||
}
|
||||
|
||||
item->setText(0, title);
|
||||
item->setText(1, url.toEncoded());
|
||||
item->setToolTip(0, title);
|
||||
item->setToolTip(1, url.toEncoded());
|
||||
item->setData(0, Qt::UserRole + 10, id);
|
||||
item->setIcon(0, _iconForUrl(url));
|
||||
|
||||
ui->historyTree->addTopLevelItem(item);
|
||||
|
||||
++counter;
|
||||
if (counter > 200) {
|
||||
QApplication::processEvents();
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
if (!guard) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
mApp->history()->clearHistory();
|
||||
mApp->history()->optimizeHistory();
|
||||
}
|
||||
|
||||
void HistoryManager::search(const QString &searchText)
|
||||
{
|
||||
ui->historyTree->filterString(searchText);
|
||||
ui->historyTree->filterModel()->setFilterFixedString(searchText);
|
||||
}
|
||||
|
||||
void HistoryManager::openLink(const QUrl &url, HistoryView::OpenBehavior openIn)
|
||||
{
|
||||
if (openIn == HistoryView::OpenInNewTab) {
|
||||
getQupZilla()->tabWidget()->addView(url, Qz::NT_NotSelectedTab);
|
||||
}
|
||||
else {
|
||||
getQupZilla()->weView()->load(url);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryManager::optimizeDb()
|
||||
@ -339,7 +105,6 @@ void HistoryManager::optimizeDb()
|
||||
b->optimizeDatabase();
|
||||
}
|
||||
|
||||
|
||||
HistoryManager::~HistoryManager()
|
||||
{
|
||||
delete ui;
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include <QWeakPointer>
|
||||
|
||||
#include "qz_namespace.h"
|
||||
#include "historymodel.h"
|
||||
#include "historyview.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
@ -42,33 +42,23 @@ public:
|
||||
~HistoryManager();
|
||||
|
||||
void setMainWindow(QupZilla* window);
|
||||
void refreshTable();
|
||||
|
||||
void restoreState(const QByteArray &state);
|
||||
QByteArray saveState();
|
||||
|
||||
public slots:
|
||||
void search(const QString &searchText);
|
||||
|
||||
private slots:
|
||||
void openLink(const QUrl &url, HistoryView::OpenBehavior openIn);
|
||||
void optimizeDb();
|
||||
void itemDoubleClicked(QTreeWidgetItem* item);
|
||||
void slotRefreshTable();
|
||||
|
||||
void deleteItem();
|
||||
void clearHistory();
|
||||
void contextMenuRequested(const QPoint &position);
|
||||
void loadInNewTab();
|
||||
void copyUrl();
|
||||
|
||||
void historyEntryAdded(const HistoryEntry &entry);
|
||||
void historyEntryDeleted(const HistoryEntry &entry);
|
||||
void historyEntryEdited(const HistoryEntry &before, const HistoryEntry &after);
|
||||
|
||||
private:
|
||||
QupZilla* getQupZilla();
|
||||
|
||||
Ui::HistoryManager* ui;
|
||||
QWeakPointer<QupZilla> p_QupZilla;
|
||||
HistoryModel* m_historyModel;
|
||||
|
||||
QList<int> m_ignoredIds;
|
||||
};
|
||||
|
||||
#endif // HISTORYMANAGER_H
|
||||
|
@ -14,29 +14,6 @@
|
||||
<string>History</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="4">
|
||||
<widget class="TreeWidget" name="historyTree">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerDefaultSectionSize">
|
||||
<number>330</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Title</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Url</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="deleteB">
|
||||
<property name="text">
|
||||
@ -77,19 +54,26 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="4">
|
||||
<widget class="HistoryView" name="historyTree">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TreeWidget</class>
|
||||
<extends>QTreeWidget</extends>
|
||||
<header>treewidget.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ClickableLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>clickablelabel.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>HistoryView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>historyview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
@ -16,231 +16,521 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ============================================================ */
|
||||
#include "historymodel.h"
|
||||
#include "tabbedwebview.h"
|
||||
#include "qupzilla.h"
|
||||
#include "historyitem.h"
|
||||
#include "iconprovider.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <QSqlDatabase>
|
||||
#include <QApplication>
|
||||
#include <QSqlQuery>
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
|
||||
HistoryModel::HistoryModel(QupZilla* mainClass)
|
||||
: QObject()
|
||||
, m_isSaving(true)
|
||||
, p_QupZilla(mainClass)
|
||||
QString dateTimeToString(const QDateTime &dateTime)
|
||||
{
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
void HistoryModel::loadSettings()
|
||||
{
|
||||
Settings settings;
|
||||
settings.beginGroup("Web-Browser-Settings");
|
||||
m_isSaving = settings.value("allowHistory", true).toBool();
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
// AddHistoryEntry
|
||||
void HistoryModel::addHistoryEntry(WebView* view)
|
||||
{
|
||||
if (!m_isSaving || view->loadingError()) {
|
||||
return;
|
||||
const QDateTime ¤t = QDateTime::currentDateTime();
|
||||
if (current.date() == dateTime.date()) {
|
||||
return dateTime.time().toString("h:mm");
|
||||
}
|
||||
|
||||
const QUrl &url = view->url();
|
||||
const QString &title = view->title();
|
||||
|
||||
addHistoryEntry(url, title);
|
||||
return dateTime.toString("d.M.yyyy h:mm");
|
||||
}
|
||||
|
||||
void HistoryModel::addHistoryEntry(const QUrl &url, QString title)
|
||||
HistoryModel::HistoryModel(History* history)
|
||||
: QAbstractItemModel(history)
|
||||
, m_rootItem(new HistoryItem(0))
|
||||
, m_todayItem(0)
|
||||
, m_history(history)
|
||||
{
|
||||
if (!m_isSaving) {
|
||||
return;
|
||||
}
|
||||
if (url.scheme() == "qupzilla" || url.scheme() == "about" || url.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (title == "") {
|
||||
title = tr("No Named Page");
|
||||
}
|
||||
init();
|
||||
|
||||
QSqlQuery query;
|
||||
query.prepare("SELECT id FROM history WHERE url=?");
|
||||
query.bindValue(0, url);
|
||||
query.exec();
|
||||
if (!query.next()) {
|
||||
query.prepare("INSERT INTO history (count, date, url, title) VALUES (1,?,?,?)");
|
||||
query.bindValue(0, QDateTime::currentMSecsSinceEpoch());
|
||||
query.bindValue(1, url);
|
||||
query.bindValue(2, title);
|
||||
query.exec();
|
||||
|
||||
int id = query.lastInsertId().toInt();
|
||||
HistoryEntry entry;
|
||||
entry.id = id;
|
||||
entry.count = 1;
|
||||
entry.date = QDateTime::currentDateTime();
|
||||
entry.url = url;
|
||||
entry.title = title;
|
||||
emit historyEntryAdded(entry);
|
||||
}
|
||||
else {
|
||||
int id = query.value(0).toInt();
|
||||
query.prepare("UPDATE history SET count = count + 1, date=?, title=? WHERE url=?");
|
||||
query.bindValue(0, QDateTime::currentMSecsSinceEpoch());
|
||||
query.bindValue(1, title);
|
||||
query.bindValue(2, url);
|
||||
query.exec();
|
||||
|
||||
HistoryEntry before;
|
||||
before.id = id;
|
||||
|
||||
HistoryEntry after;
|
||||
after.id = id;
|
||||
after.date = QDateTime::currentDateTime();
|
||||
after.url = url;
|
||||
after.title = title;
|
||||
emit historyEntryEdited(before, after);
|
||||
}
|
||||
connect(m_history, SIGNAL(resetHistory()), this, SLOT(resetHistory()));
|
||||
connect(m_history, SIGNAL(historyEntryAdded(const HistoryEntry &)), this, SLOT(historyEntryAdded(const HistoryEntry &)));
|
||||
connect(m_history, SIGNAL(historyEntryDeleted(const HistoryEntry &)), this, SLOT(historyEntryDeleted(const HistoryEntry &)));
|
||||
connect(m_history, SIGNAL(historyEntryEdited(const HistoryEntry &, const HistoryEntry &)), this, SLOT(historyEntryEdited(const HistoryEntry &, const HistoryEntry &)));
|
||||
}
|
||||
|
||||
// DeleteHistoryEntry
|
||||
void HistoryModel::deleteHistoryEntry(int index)
|
||||
QVariant HistoryModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
QList<int> list;
|
||||
list.append(index);
|
||||
|
||||
deleteHistoryEntry(list);
|
||||
}
|
||||
|
||||
void HistoryModel::deleteHistoryEntry(const QList<int> &list)
|
||||
{
|
||||
QSqlDatabase db = QSqlDatabase::database();
|
||||
db.transaction();
|
||||
|
||||
foreach(int index, list) {
|
||||
QSqlQuery query;
|
||||
query.prepare("SELECT id, count, date, url, title FROM history WHERE id=?");
|
||||
query.addBindValue(index);
|
||||
query.exec();
|
||||
|
||||
query.next();
|
||||
HistoryEntry entry;
|
||||
entry.id = query.value(0).toInt();
|
||||
entry.count = query.value(1).toInt();
|
||||
entry.date = QDateTime::fromMSecsSinceEpoch(query.value(2).toLongLong());
|
||||
entry.url = query.value(3).toUrl();
|
||||
entry.title = query.value(4).toString();
|
||||
|
||||
query.prepare("DELETE FROM history WHERE id=?");
|
||||
query.addBindValue(index);
|
||||
query.exec();
|
||||
|
||||
query.prepare("DELETE FROM icons WHERE url=?");
|
||||
query.addBindValue(entry.url.toEncoded(QUrl::RemoveFragment));
|
||||
query.exec();
|
||||
|
||||
emit historyEntryDeleted(entry);
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
||||
switch (section) {
|
||||
case 0:
|
||||
return tr("Title");
|
||||
case 1:
|
||||
return tr("Address");
|
||||
case 2:
|
||||
return tr("Visit Date");
|
||||
case 3:
|
||||
return tr("Visit Count");
|
||||
}
|
||||
}
|
||||
|
||||
db.commit();
|
||||
return QAbstractItemModel::headerData(section, orientation, role);
|
||||
}
|
||||
|
||||
void HistoryModel::deleteHistoryEntry(const QString &url, const QString &title)
|
||||
QVariant HistoryModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
QSqlQuery query;
|
||||
query.prepare("SELECT id FROM history WHERE url=? AND title=?");
|
||||
query.bindValue(0, url);
|
||||
query.bindValue(1, title);
|
||||
query.exec();
|
||||
if (query.next()) {
|
||||
int id = query.value(0).toInt();
|
||||
deleteHistoryEntry(id);
|
||||
HistoryItem* item = itemFromIndex(index);
|
||||
|
||||
if (index.row() < 0 || !item) {
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
bool HistoryModel::urlIsStored(const QString &url)
|
||||
{
|
||||
QSqlQuery query;
|
||||
query.prepare("SELECT id FROM history WHERE url=?");
|
||||
query.bindValue(0, url);
|
||||
query.exec();
|
||||
return query.next();
|
||||
}
|
||||
if (item->isTopLevel()) {
|
||||
switch (role) {
|
||||
case IsTopLevelRole:
|
||||
return true;
|
||||
case TimestampStartRole:
|
||||
return item->startTimestamp();
|
||||
case TimestampEndRole:
|
||||
return item->endTimestamp();
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
return index.column() == 0 ? item->title : QVariant();
|
||||
case Qt::DecorationRole:
|
||||
return index.column() == 0 ? QIcon(":/icons/menu/history_entry.png") : QVariant();
|
||||
}
|
||||
|
||||
QList<HistoryEntry> HistoryModel::mostVisited(int count)
|
||||
{
|
||||
QList<HistoryEntry> list;
|
||||
QSqlQuery query;
|
||||
query.exec(QString("SELECT count, date, id, title, url FROM history ORDER BY count DESC LIMIT %1").arg(count));
|
||||
while (query.next()) {
|
||||
HistoryEntry entry;
|
||||
entry.count = query.value(0).toInt();
|
||||
entry.date = query.value(1).toDateTime();
|
||||
entry.id = query.value(2).toInt();
|
||||
entry.title = query.value(3).toString();
|
||||
entry.url = query.value(4).toUrl();
|
||||
list.append(entry);
|
||||
return QVariant();
|
||||
}
|
||||
return list;
|
||||
|
||||
const HistoryEntry &entry = item->historyEntry;
|
||||
|
||||
switch (role) {
|
||||
case IdRole:
|
||||
return entry.id;
|
||||
case TitleRole:
|
||||
return entry.title;
|
||||
case UrlRole:
|
||||
return entry.url;
|
||||
case UrlStringRole:
|
||||
return entry.urlString;
|
||||
case IconRole:
|
||||
return item->icon();
|
||||
case IconLoadedRole:
|
||||
return item->iconLoaded();
|
||||
case IsTopLevelRole:
|
||||
return false;
|
||||
case TimestampStartRole:
|
||||
return -1;
|
||||
case TimestampEndRole:
|
||||
return -1;
|
||||
case Qt::ToolTipRole:
|
||||
if (index.column() == 0) {
|
||||
return QString("%1\n%2").arg(entry.title, entry.urlString);
|
||||
}
|
||||
case Qt::DisplayRole:
|
||||
case Qt::EditRole:
|
||||
switch (index.column()) {
|
||||
case 0:
|
||||
return entry.title;
|
||||
case 1:
|
||||
return entry.urlString;
|
||||
case 2:
|
||||
return dateTimeToString(entry.date);
|
||||
case 3:
|
||||
return entry.count;
|
||||
}
|
||||
break;
|
||||
case Qt::DecorationRole:
|
||||
if (index.column() == 0) {
|
||||
return item->icon().isNull() ? qIconProvider->emptyWebIcon() : item->icon();
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool HistoryModel::optimizeHistory()
|
||||
bool HistoryModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
QSqlQuery query;
|
||||
return query.exec("VACUUM");
|
||||
}
|
||||
HistoryItem* item = itemFromIndex(index);
|
||||
|
||||
bool HistoryModel::clearHistory()
|
||||
{
|
||||
QSqlQuery query;
|
||||
if (query.exec("DELETE FROM history")) {
|
||||
emit historyClear();
|
||||
if (index.row() < 0 || !item || item->isTopLevel()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (role == IconRole) {
|
||||
item->setIcon(value.value<QIcon>());
|
||||
emit dataChanged(index, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HistoryModel::setSaving(bool state)
|
||||
QModelIndex HistoryModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
m_isSaving = state;
|
||||
if (!hasIndex(row, column, parent)) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
HistoryItem* parentItem = itemFromIndex(parent);
|
||||
HistoryItem* childItem = parentItem->child(row);
|
||||
|
||||
return childItem ? createIndex(row, column, childItem) : QModelIndex();
|
||||
}
|
||||
|
||||
bool HistoryModel::isSaving()
|
||||
QModelIndex HistoryModel::parent(const QModelIndex &index) const
|
||||
{
|
||||
return m_isSaving;
|
||||
if (!index.isValid()) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
HistoryItem* childItem = itemFromIndex(index);
|
||||
HistoryItem* parentItem = childItem->parent();
|
||||
|
||||
if (!parentItem || parentItem == m_rootItem) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
return createIndex(parentItem->row(), 0, parentItem);
|
||||
}
|
||||
|
||||
QString HistoryModel::titleCaseLocalizedMonth(int month)
|
||||
Qt::ItemFlags HistoryModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
switch (month) {
|
||||
case 1:
|
||||
return tr("January");
|
||||
case 2:
|
||||
return tr("February");
|
||||
case 3:
|
||||
return tr("March");
|
||||
case 4:
|
||||
return tr("April");
|
||||
case 5:
|
||||
return tr("May");
|
||||
case 6:
|
||||
return tr("June");
|
||||
case 7:
|
||||
return tr("July");
|
||||
case 8:
|
||||
return tr("August");
|
||||
case 9:
|
||||
return tr("September");
|
||||
case 10:
|
||||
return tr("October");
|
||||
case 11:
|
||||
return tr("November");
|
||||
case 12:
|
||||
return tr("December");
|
||||
default:
|
||||
qWarning("Month number out of range!");
|
||||
return QString();
|
||||
if (!index.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
}
|
||||
|
||||
int HistoryModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if (parent.column() > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
HistoryItem* parentItem = itemFromIndex(parent);
|
||||
|
||||
return parentItem->childCount();
|
||||
}
|
||||
|
||||
int HistoryModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
bool HistoryModel::hasChildren(const QModelIndex &parent) const
|
||||
{
|
||||
if (!parent.isValid()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
HistoryItem* item = itemFromIndex(parent);
|
||||
|
||||
return item ? item->isTopLevel() : false;
|
||||
}
|
||||
|
||||
HistoryItem* HistoryModel::itemFromIndex(const QModelIndex &index) const
|
||||
{
|
||||
if (index.isValid()) {
|
||||
HistoryItem* item = static_cast<HistoryItem*>(index.internalPointer());
|
||||
|
||||
if (item) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return m_rootItem;
|
||||
}
|
||||
|
||||
void HistoryModel::resetHistory()
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
delete m_rootItem;
|
||||
m_rootItem = new HistoryItem(0);
|
||||
|
||||
init();
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
bool HistoryModel::canFetchMore(const QModelIndex &parent) const
|
||||
{
|
||||
HistoryItem* parentItem = itemFromIndex(parent);
|
||||
|
||||
return parentItem ? parentItem->canFetchMore : false;
|
||||
}
|
||||
|
||||
void HistoryModel::fetchMore(const QModelIndex &parent)
|
||||
{
|
||||
HistoryItem* parentItem = itemFromIndex(parent);
|
||||
|
||||
if (!parent.isValid() || !parentItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
parentItem->canFetchMore = false;
|
||||
|
||||
QList<int> idList;
|
||||
for (int i = 0; i < parentItem->childCount(); ++i) {
|
||||
idList.append(parentItem->child(i)->historyEntry.id);
|
||||
}
|
||||
|
||||
QSqlQuery query;
|
||||
query.prepare("SELECT id, count, title, url, date FROM history WHERE date BETWEEN ? AND ? ORDER BY date DESC");
|
||||
query.addBindValue(parentItem->endTimestamp());
|
||||
query.addBindValue(parentItem->startTimestamp());
|
||||
query.exec();
|
||||
|
||||
QList<HistoryEntry> list;
|
||||
|
||||
while (query.next()) {
|
||||
HistoryEntry entry;
|
||||
entry.id = query.value(0).toInt();
|
||||
entry.count = query.value(1).toInt();
|
||||
entry.title = query.value(2).toString();
|
||||
entry.url = query.value(3).toUrl();
|
||||
entry.date = QDateTime::fromMSecsSinceEpoch(query.value(4).toLongLong());
|
||||
entry.urlString = entry.url.toEncoded();
|
||||
|
||||
if (!idList.contains(entry.id)) {
|
||||
list.append(entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
beginInsertRows(parent, 0, list.size() - 1);
|
||||
|
||||
foreach(const HistoryEntry & entry, list) {
|
||||
HistoryItem* newItem = new HistoryItem(parentItem);
|
||||
newItem->historyEntry = entry;
|
||||
}
|
||||
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void HistoryModel::historyEntryAdded(const HistoryEntry &entry)
|
||||
{
|
||||
if (!m_todayItem) {
|
||||
beginInsertRows(QModelIndex(), 0, 0);
|
||||
|
||||
m_todayItem = new HistoryItem(0);
|
||||
m_todayItem->setStartTimestamp(-1);
|
||||
m_todayItem->setEndTimestamp(QDateTime(QDate::currentDate()).toMSecsSinceEpoch());
|
||||
m_todayItem->title = tr("Today");
|
||||
|
||||
m_rootItem->prependChild(m_todayItem);
|
||||
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
beginInsertRows(createIndex(0, 0, m_todayItem), 0, 0);
|
||||
|
||||
HistoryItem* item = new HistoryItem();
|
||||
item->historyEntry = entry;
|
||||
|
||||
m_todayItem->prependChild(item);
|
||||
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
void HistoryModel::historyEntryDeleted(const HistoryEntry &entry)
|
||||
{
|
||||
HistoryItem* item = findHistoryItem(entry);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
HistoryItem* parentItem = item->parent();
|
||||
int row = item->row();
|
||||
|
||||
beginRemoveRows(createIndex(parentItem->row(), 0, parentItem), row, row);
|
||||
delete item;
|
||||
endRemoveRows();
|
||||
|
||||
checkEmptyParentItem(parentItem);
|
||||
}
|
||||
|
||||
void HistoryModel::historyEntryEdited(const HistoryEntry &before, const HistoryEntry &after)
|
||||
{
|
||||
#if 0
|
||||
HistoryItem* item = findHistoryItem(before);
|
||||
|
||||
if (item) {
|
||||
HistoryItem* parentItem = item->parent();
|
||||
const QModelIndex &sourceParent = createIndex(parentItem->row(), 0, parentItem);
|
||||
const QModelIndex &destinationParent = createIndex(m_todayItem->row(), 0, m_todayItem);
|
||||
int row = item->row();
|
||||
|
||||
beginMoveRows(sourceParent, row, row, destinationParent, 0);
|
||||
item->historyEntry = after;
|
||||
item->refreshIcon();
|
||||
item->changeParent(m_todayItem);
|
||||
endMoveRows(); // This line sometimes throw "std::bad_alloc" ... I don't know why ?!
|
||||
|
||||
checkEmptyParentItem(parentItem);
|
||||
}
|
||||
else {
|
||||
historyEntryAdded(after);
|
||||
}
|
||||
#endif
|
||||
historyEntryDeleted(before);
|
||||
historyEntryAdded(after);
|
||||
}
|
||||
|
||||
HistoryItem* HistoryModel::findHistoryItem(const HistoryEntry &entry)
|
||||
{
|
||||
HistoryItem* parentItem = 0;
|
||||
qint64 timestamp = entry.date.toMSecsSinceEpoch();
|
||||
|
||||
for (int i = 0; i < m_rootItem->childCount(); ++i) {
|
||||
HistoryItem* item = m_rootItem->child(i);
|
||||
|
||||
if (item->endTimestamp() < timestamp) {
|
||||
parentItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parentItem) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < parentItem->childCount(); ++i) {
|
||||
HistoryItem* item = parentItem->child(i);
|
||||
if (item->historyEntry.id == entry.id) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void HistoryModel::checkEmptyParentItem(HistoryItem* item)
|
||||
{
|
||||
if (item->childCount() == 0 && item->isTopLevel()) {
|
||||
int row = item->row();
|
||||
|
||||
beginRemoveRows(QModelIndex(), row, row);
|
||||
delete item;
|
||||
endRemoveRows();
|
||||
|
||||
if (item == m_todayItem) {
|
||||
m_todayItem = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryModel::init()
|
||||
{
|
||||
QSqlQuery query;
|
||||
query.exec("SELECT MIN(date) FROM history");
|
||||
if (!query.next()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QDate &today = QDate::currentDate();
|
||||
const QDate &week = today.addDays(1 - today.dayOfWeek());
|
||||
const QDate &month = QDate(today.year(), today.month(), 1);
|
||||
|
||||
const qint64 &minTimestamp = query.value(0).toLongLong();
|
||||
const qint64 ¤tTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
qint64 timestamp = currentTimestamp;
|
||||
while (timestamp > minTimestamp) {
|
||||
QDate timestampDate = QDateTime::fromMSecsSinceEpoch(timestamp).date();
|
||||
qint64 endTimestamp;
|
||||
QString itemName;
|
||||
|
||||
if (timestampDate == today) {
|
||||
endTimestamp = QDateTime(today).toMSecsSinceEpoch();
|
||||
|
||||
itemName = tr("Today");
|
||||
}
|
||||
else if (timestampDate >= week) {
|
||||
endTimestamp = QDateTime(week).toMSecsSinceEpoch();
|
||||
|
||||
itemName = tr("This Week");
|
||||
}
|
||||
else if (timestampDate.month() == month.month() && timestampDate.year() == month.year()) {
|
||||
endTimestamp = QDateTime(month).toMSecsSinceEpoch();
|
||||
|
||||
itemName = tr("This Month");
|
||||
}
|
||||
else {
|
||||
QDate startDate(timestampDate.year(), timestampDate.month(), timestampDate.daysInMonth());
|
||||
QDate endDate(startDate.year(), startDate.month(), 1);
|
||||
|
||||
timestamp = QDateTime(startDate, QTime(23, 59, 59)).toMSecsSinceEpoch();
|
||||
endTimestamp = QDateTime(endDate).toMSecsSinceEpoch();
|
||||
itemName = QString("%1 %2").arg(History::titleCaseLocalizedMonth(timestampDate.month()), QString::number(timestampDate.year()));
|
||||
}
|
||||
|
||||
QSqlQuery query;
|
||||
query.prepare("SELECT id FROM history WHERE date BETWEEN ? AND ? LIMIT 1");
|
||||
query.addBindValue(endTimestamp);
|
||||
query.addBindValue(timestamp);
|
||||
query.exec();
|
||||
|
||||
if (query.next()) {
|
||||
HistoryItem* item = new HistoryItem(m_rootItem);
|
||||
item->setStartTimestamp(timestamp == currentTimestamp ? -1 : timestamp);
|
||||
item->setEndTimestamp(endTimestamp);
|
||||
item->title = itemName;
|
||||
item->canFetchMore = true;
|
||||
|
||||
if (timestamp == currentTimestamp) {
|
||||
m_todayItem = item;
|
||||
}
|
||||
}
|
||||
|
||||
timestamp = endTimestamp - 1;
|
||||
}
|
||||
}
|
||||
|
||||
HistoryFilterModel::HistoryFilterModel(QAbstractItemModel* parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
{
|
||||
setSourceModel(parent);
|
||||
setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
|
||||
m_filterTimer = new QTimer(this);
|
||||
m_filterTimer->setSingleShot(true);
|
||||
m_filterTimer->setInterval(500);
|
||||
|
||||
connect(m_filterTimer, SIGNAL(timeout()), this, SLOT(startFiltering()));
|
||||
}
|
||||
|
||||
void HistoryFilterModel::setFilterFixedString(const QString &pattern)
|
||||
{
|
||||
m_pattern = pattern;
|
||||
|
||||
m_filterTimer->stop();
|
||||
m_filterTimer->start();
|
||||
}
|
||||
|
||||
void HistoryFilterModel::startFiltering()
|
||||
{
|
||||
if (m_pattern.isEmpty()) {
|
||||
emit collapseAllItems();
|
||||
QSortFilterProxyModel::setFilterFixedString(m_pattern);
|
||||
return;
|
||||
}
|
||||
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
|
||||
// Expand all items also calls fetchmore
|
||||
emit expandAllItems();
|
||||
|
||||
QSortFilterProxyModel::setFilterFixedString(m_pattern);
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
bool HistoryFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
const QModelIndex &index = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
|
||||
if (index.data(HistoryModel::IsTopLevelRole).toBool()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (index.data(HistoryModel::UrlStringRole).toString().contains(filterRegExp()) ||
|
||||
index.data(HistoryModel::TitleRole).toString().contains(filterRegExp()));
|
||||
}
|
||||
|
@ -18,67 +18,95 @@
|
||||
#ifndef HISTORYMODEL_H
|
||||
#define HISTORYMODEL_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QDateTime>
|
||||
#include <QUrl>
|
||||
#include <QAbstractItemModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "qz_namespace.h"
|
||||
#include "history.h"
|
||||
|
||||
class QIcon;
|
||||
class QTimer;
|
||||
|
||||
class QupZilla;
|
||||
class WebView;
|
||||
class History;
|
||||
class HistoryItem;
|
||||
|
||||
class QT_QUPZILLA_EXPORT HistoryModel : public QObject
|
||||
class QT_QUPZILLA_EXPORT HistoryModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
HistoryModel(QupZilla* mainClass);
|
||||
|
||||
struct HistoryEntry {
|
||||
int id;
|
||||
int count;
|
||||
QDateTime date;
|
||||
QUrl url;
|
||||
QString title;
|
||||
enum Roles {
|
||||
IdRole = Qt::UserRole + 1,
|
||||
TitleRole = Qt::UserRole + 2,
|
||||
UrlRole = Qt::UserRole + 3,
|
||||
UrlStringRole = Qt::UserRole + 4,
|
||||
IconRole = Qt::UserRole + 5,
|
||||
IconLoadedRole = Qt::UserRole + 6,
|
||||
IsTopLevelRole = Qt::UserRole + 7,
|
||||
TimestampStartRole = Qt::UserRole + 8,
|
||||
TimestampEndRole = Qt::UserRole + 9,
|
||||
MaxRole = TimestampEndRole
|
||||
};
|
||||
|
||||
static QString titleCaseLocalizedMonth(int month);
|
||||
explicit HistoryModel(History* history);
|
||||
|
||||
void addHistoryEntry(WebView* view);
|
||||
void addHistoryEntry(const QUrl &url, QString title);
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||
|
||||
void deleteHistoryEntry(int index);
|
||||
void deleteHistoryEntry(const QList<int> &list);
|
||||
void deleteHistoryEntry(const QString &url, const QString &title);
|
||||
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
|
||||
QModelIndex parent(const QModelIndex &child) const;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
|
||||
bool urlIsStored(const QString &url);
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
|
||||
QList<HistoryEntry> mostVisited(int count);
|
||||
bool canFetchMore(const QModelIndex &parent) const;
|
||||
void fetchMore(const QModelIndex &parent);
|
||||
|
||||
bool clearHistory();
|
||||
bool optimizeHistory();
|
||||
bool isSaving();
|
||||
void setSaving(bool state);
|
||||
bool hasChildren(const QModelIndex &parent) const;
|
||||
|
||||
void loadSettings();
|
||||
HistoryItem* itemFromIndex(const QModelIndex &index) const;
|
||||
|
||||
signals:
|
||||
void historyEntryAdded(HistoryEntry entry);
|
||||
void historyEntryDeleted(HistoryEntry entry);
|
||||
void historyEntryEdited(HistoryEntry before, HistoryEntry after);
|
||||
//WARNING: Incomplete HistoryEntry structs are passed to historyEntryEdited!
|
||||
void historyClear();
|
||||
|
||||
void signalAddHistoryEntry(QUrl url, QString title);
|
||||
void signalDeleteHistoryEntry(QList<int> list);
|
||||
private slots:
|
||||
void resetHistory();
|
||||
|
||||
void historyEntryAdded(const HistoryEntry &entry);
|
||||
void historyEntryDeleted(const HistoryEntry &entry);
|
||||
void historyEntryEdited(const HistoryEntry &before, const HistoryEntry &after);
|
||||
|
||||
private:
|
||||
bool m_isSaving;
|
||||
QupZilla* p_QupZilla;
|
||||
HistoryItem* findHistoryItem(const HistoryEntry &entry);
|
||||
void checkEmptyParentItem(HistoryItem* item);
|
||||
void init();
|
||||
|
||||
HistoryItem* m_rootItem;
|
||||
HistoryItem* m_todayItem;
|
||||
History* m_history;
|
||||
};
|
||||
|
||||
typedef HistoryModel::HistoryEntry HistoryEntry;
|
||||
class QT_QUPZILLA_EXPORT HistoryFilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit HistoryFilterModel(QAbstractItemModel* parent);
|
||||
|
||||
public slots:
|
||||
void setFilterFixedString(const QString &pattern);
|
||||
|
||||
signals:
|
||||
void expandAllItems();
|
||||
void collapseAllItems();
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
|
||||
|
||||
private slots:
|
||||
void startFiltering();
|
||||
|
||||
private:
|
||||
QString m_pattern;
|
||||
QTimer* m_filterTimer;
|
||||
};
|
||||
|
||||
#endif // HISTORYMODEL_H
|
||||
|
191
src/lib/history/historyview.cpp
Normal file
191
src/lib/history/historyview.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
/* ============================================================
|
||||
* QupZilla - WebKit based browser
|
||||
* Copyright (C) 2010-2012 David Rosca <nowrep@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ============================================================ */
|
||||
#include "historyview.h"
|
||||
#include "historymodel.h"
|
||||
#include "historyitem.h"
|
||||
#include "headerview.h"
|
||||
#include "mainapplication.h"
|
||||
#include "iconprovider.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QKeyEvent>
|
||||
#include <QLineEdit>
|
||||
#include <QMenu>
|
||||
|
||||
HistoryView::HistoryView(QWidget* parent)
|
||||
: QTreeView(parent)
|
||||
, m_history(mApp->history())
|
||||
, m_filterModel(new HistoryFilterModel(m_history->model()))
|
||||
{
|
||||
setModel(m_filterModel);
|
||||
|
||||
setAllColumnsShowFocus(true);
|
||||
setUniformRowHeights(true);
|
||||
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
|
||||
m_header = new HeaderView(this);
|
||||
setHeader(m_header);
|
||||
|
||||
m_header->setDefaultSectionSizes(QList<double>() << 0.4 << 0.35 << 0.10 << 0.08);
|
||||
m_header->setSectionHidden(4, true);
|
||||
|
||||
connect(this, SIGNAL(activated(QModelIndex)), this, SLOT(itemActivated(QModelIndex)));
|
||||
connect(this, SIGNAL(pressed(QModelIndex)), this, SLOT(itemPressed(QModelIndex)));
|
||||
|
||||
connect(m_filterModel, SIGNAL(expandAllItems()), this, SLOT(expandAll()));
|
||||
connect(m_filterModel, SIGNAL(collapseAllItems()), this, SLOT(collapseAll()));
|
||||
}
|
||||
|
||||
HeaderView* HistoryView::header() const
|
||||
{
|
||||
return m_header;
|
||||
}
|
||||
|
||||
HistoryFilterModel* HistoryView::filterModel() const
|
||||
{
|
||||
return m_filterModel;
|
||||
}
|
||||
|
||||
void HistoryView::removeItems()
|
||||
{
|
||||
QList<int> list;
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
|
||||
foreach(const QModelIndex & index, selectedIndexes()) {
|
||||
if (index.column() > 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (index.data(HistoryModel::IsTopLevelRole).toBool()) {
|
||||
qint64 start = index.data(HistoryModel::TimestampStartRole).toLongLong();
|
||||
qint64 end = index.data(HistoryModel::TimestampEndRole).toLongLong();
|
||||
|
||||
list.append(m_history->indexesFromTimeRange(start, end));
|
||||
}
|
||||
else {
|
||||
int id = index.data(HistoryModel::IdRole).toInt();
|
||||
if (!list.contains(id)) {
|
||||
list.append(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_history->deleteHistoryEntry(list);
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
void HistoryView::itemActivated(const QModelIndex &index)
|
||||
{
|
||||
if (!index.isValid() || index.data(HistoryModel::IsTopLevelRole).toBool()) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit openLink(index.data(HistoryModel::UrlRole).toUrl(), OpenInCurrentTab);
|
||||
}
|
||||
|
||||
void HistoryView::itemPressed(const QModelIndex &index)
|
||||
{
|
||||
if (!index.isValid() || index.data(HistoryModel::IsTopLevelRole).toBool()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((selectionMode() == QAbstractItemView::SingleSelection && QApplication::keyboardModifiers() & Qt::ControlModifier)
|
||||
|| (QApplication::mouseButtons() & Qt::MiddleButton)) {
|
||||
emit openLink(index.data(HistoryModel::UrlRole).toUrl(), OpenInNewTab);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryView::openLinkInCurrentTab()
|
||||
{
|
||||
if (m_clickedIndex.isValid()) {
|
||||
emit openLink(m_clickedIndex.data(HistoryModel::UrlRole).toUrl(), OpenInCurrentTab);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryView::openLinkInNewTab()
|
||||
{
|
||||
if (m_clickedIndex.isValid()) {
|
||||
emit openLink(m_clickedIndex.data(HistoryModel::UrlRole).toUrl(), OpenInNewTab);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryView::copyTitle()
|
||||
{
|
||||
if (m_clickedIndex.isValid()) {
|
||||
QApplication::clipboard()->setText(m_clickedIndex.data(HistoryModel::TitleRole).toString());
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryView::copyAddress()
|
||||
{
|
||||
if (m_clickedIndex.isValid()) {
|
||||
QApplication::clipboard()->setText(m_clickedIndex.data(HistoryModel::UrlStringRole).toString());
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryView::contextMenuEvent(QContextMenuEvent* event)
|
||||
{
|
||||
const QModelIndex &index = indexAt(event->pos());
|
||||
if (!index.isValid() || index.data(HistoryModel::IsTopLevelRole).toBool()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_clickedIndex = index;
|
||||
|
||||
QMenu menu;
|
||||
menu.addAction(tr("Open link in current tab"), this, SLOT(openLinkInCurrentTab()));
|
||||
menu.addAction(tr("Open link in new tab"), this, SLOT(openLinkInNewTab()));
|
||||
menu.addSeparator();
|
||||
menu.addAction(tr("Copy title"), this, SLOT(copyTitle()));
|
||||
menu.addAction(tr("Copy address"), this, SLOT(copyAddress()));
|
||||
menu.addSeparator();
|
||||
menu.addAction(tr("Remove"), this, SLOT(removeItems()));
|
||||
|
||||
// Prevent choosing first option with double rightclick
|
||||
QPoint pos = viewport()->mapToGlobal(event->pos());
|
||||
QPoint p(pos.x(), pos.y() + 1);
|
||||
menu.exec(p);
|
||||
}
|
||||
|
||||
void HistoryView::keyPressEvent(QKeyEvent* event)
|
||||
{
|
||||
if (event->key() == Qt::Key_Delete) {
|
||||
removeItems();
|
||||
event->accept();
|
||||
}
|
||||
|
||||
QTreeView::keyPressEvent(event);
|
||||
}
|
||||
|
||||
void HistoryView::drawRow(QPainter* painter, const QStyleOptionViewItem &options, const QModelIndex &index) const
|
||||
{
|
||||
bool itemTopLevel = index.data(HistoryModel::IsTopLevelRole).toBool();
|
||||
bool iconLoaded = index.data(HistoryModel::IconLoadedRole).toBool();
|
||||
|
||||
if (index.isValid() && !itemTopLevel && !iconLoaded) {
|
||||
QImage image = qIconProvider->iconForUrl(index.data(HistoryModel::UrlRole).toUrl());
|
||||
if (image == qIconProvider->emptyWebImage()) {
|
||||
model()->setData(index, QIcon(), HistoryModel::IconRole);
|
||||
}
|
||||
else {
|
||||
model()->setData(index, QIcon(QPixmap::fromImage(image)), HistoryModel::IconRole);
|
||||
}
|
||||
}
|
||||
|
||||
QTreeView::drawRow(painter, options, index);
|
||||
}
|
66
src/lib/history/historyview.h
Normal file
66
src/lib/history/historyview.h
Normal file
@ -0,0 +1,66 @@
|
||||
/* ============================================================
|
||||
* QupZilla - WebKit based browser
|
||||
* Copyright (C) 2010-2012 David Rosca <nowrep@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ============================================================ */
|
||||
#ifndef HISTORYVIEW_H
|
||||
#define HISTORYVIEW_H
|
||||
|
||||
#include <QTreeView>
|
||||
|
||||
class History;
|
||||
class HistoryFilterModel;
|
||||
class HeaderView;
|
||||
|
||||
class HistoryView : public QTreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum OpenBehavior { OpenInCurrentTab, OpenInNewTab };
|
||||
|
||||
explicit HistoryView(QWidget* parent = 0);
|
||||
|
||||
HeaderView* header() const;
|
||||
HistoryFilterModel* filterModel() const;
|
||||
|
||||
signals:
|
||||
void openLink(const QUrl &, HistoryView::OpenBehavior);
|
||||
|
||||
public slots:
|
||||
void removeItems();
|
||||
|
||||
private slots:
|
||||
void itemActivated(const QModelIndex &index);
|
||||
void itemPressed(const QModelIndex &index);
|
||||
|
||||
void openLinkInCurrentTab();
|
||||
void openLinkInNewTab();
|
||||
void copyTitle();
|
||||
void copyAddress();
|
||||
|
||||
protected:
|
||||
void contextMenuEvent(QContextMenuEvent* event);
|
||||
void keyPressEvent(QKeyEvent* event);
|
||||
void drawRow(QPainter* painter, const QStyleOptionViewItem &options, const QModelIndex &index) const;
|
||||
|
||||
private:
|
||||
History* m_history;
|
||||
HistoryFilterModel* m_filterModel;
|
||||
HeaderView* m_header;
|
||||
|
||||
QModelIndex m_clickedIndex;
|
||||
};
|
||||
|
||||
#endif // HISTORYVIEW_H
|
@ -17,7 +17,7 @@
|
||||
* ============================================================ */
|
||||
#include "webhistoryinterface.h"
|
||||
#include "mainapplication.h"
|
||||
#include "historymodel.h"
|
||||
#include "history.h"
|
||||
|
||||
WebHistoryInterface::WebHistoryInterface(QObject* parent)
|
||||
: QWebHistoryInterface(parent)
|
||||
|
@ -8,6 +8,7 @@ DEFINES *= QUPZILLA_SHAREDLIBRARY
|
||||
include(3rdparty/qtsingleapplication.pri)
|
||||
include(../defines.pri)
|
||||
include(../../translations/translations.pri)
|
||||
#include(../../tests/modeltest/modeltest.pri)
|
||||
|
||||
INCLUDEPATH += 3rdparty\
|
||||
app\
|
||||
@ -47,7 +48,6 @@ SOURCES += \
|
||||
cookies/cookiemanager.cpp \
|
||||
cookies/cookiejar.cpp \
|
||||
downloads/downloadmanager.cpp \
|
||||
history/historymodel.cpp \
|
||||
history/historymanager.cpp \
|
||||
navigation/websearchbar.cpp \
|
||||
navigation/locationbar.cpp \
|
||||
@ -172,7 +172,12 @@ SOURCES += \
|
||||
navigation/completer/locationcompleterdelegate.cpp \
|
||||
navigation/completer/locationcompleter.cpp \
|
||||
navigation/completer/locationcompletermodel.cpp \
|
||||
navigation/completer/locationcompleterview.cpp
|
||||
navigation/completer/locationcompleterview.cpp \
|
||||
history/history.cpp \
|
||||
history/historymodel.cpp \
|
||||
history/historyview.cpp \
|
||||
history/historyitem.cpp \
|
||||
tools/headerview.cpp
|
||||
|
||||
HEADERS += \
|
||||
webview/tabpreview.h \
|
||||
@ -188,7 +193,6 @@ HEADERS += \
|
||||
cookies/cookiemanager.h \
|
||||
cookies/cookiejar.h \
|
||||
downloads/downloadmanager.h \
|
||||
history/historymodel.h \
|
||||
history/historymanager.h \
|
||||
navigation/websearchbar.h \
|
||||
navigation/locationbar.h \
|
||||
@ -317,7 +321,12 @@ HEADERS += \
|
||||
navigation/completer/locationcompleterdelegate.h \
|
||||
navigation/completer/locationcompleter.h \
|
||||
navigation/completer/locationcompletermodel.h \
|
||||
navigation/completer/locationcompleterview.h
|
||||
navigation/completer/locationcompleterview.h \
|
||||
history/history.h \
|
||||
history/historymodel.h \
|
||||
history/historyview.h \
|
||||
history/historyitem.h \
|
||||
tools/headerview.h
|
||||
|
||||
FORMS += \
|
||||
preferences/autofillmanager.ui \
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "downloaditem.h"
|
||||
#include "globalfunctions.h"
|
||||
#include "settings.h"
|
||||
#include "history.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QCloseEvent>
|
||||
@ -35,7 +36,6 @@ BrowsingLibrary::BrowsingLibrary(QupZilla* mainClass, QWidget* parent)
|
||||
, m_historyManager(new HistoryManager(mainClass))
|
||||
, m_bookmarksManager(new BookmarksManager(mainClass))
|
||||
, m_rssManager(mApp->rssManager())
|
||||
, m_historyLoaded(false)
|
||||
, m_bookmarksLoaded(false)
|
||||
, m_rssLoaded(false)
|
||||
{
|
||||
@ -44,6 +44,7 @@ BrowsingLibrary::BrowsingLibrary(QupZilla* mainClass, QWidget* parent)
|
||||
Settings settings;
|
||||
settings.beginGroup("BrowsingLibrary");
|
||||
resize(settings.value("size", QSize(760, 470)).toSize());
|
||||
m_historyManager->restoreState(settings.value("historyState", QByteArray()).toByteArray());
|
||||
settings.endGroup();
|
||||
|
||||
qz_centerWidgetOnScreen(this);
|
||||
@ -63,10 +64,6 @@ void BrowsingLibrary::currentIndexChanged(int index)
|
||||
{
|
||||
switch (index) {
|
||||
case 0:
|
||||
if (!m_historyLoaded) {
|
||||
m_historyManager->refreshTable();
|
||||
m_historyLoaded = true;
|
||||
}
|
||||
ui->searchLine->show();
|
||||
search();
|
||||
break;
|
||||
@ -109,11 +106,6 @@ void BrowsingLibrary::showHistory(QupZilla* mainClass)
|
||||
show();
|
||||
m_historyManager->setMainWindow(mainClass);
|
||||
|
||||
if (!m_historyLoaded) {
|
||||
m_historyManager->refreshTable();
|
||||
m_historyLoaded = true;
|
||||
}
|
||||
|
||||
raise();
|
||||
activateWindow();
|
||||
}
|
||||
@ -161,6 +153,7 @@ void BrowsingLibrary::closeEvent(QCloseEvent* e)
|
||||
Settings settings;
|
||||
settings.beginGroup("BrowsingLibrary");
|
||||
settings.setValue("size", size());
|
||||
settings.setValue("historyState", m_historyManager->saveState());
|
||||
settings.endGroup();
|
||||
e->accept();
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ private:
|
||||
HistoryManager* m_historyManager;
|
||||
BookmarksManager* m_bookmarksManager;
|
||||
RSSManager* m_rssManager;
|
||||
bool m_historyLoaded;
|
||||
|
||||
bool m_bookmarksLoaded;
|
||||
bool m_rssLoaded;
|
||||
};
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "qupzilla.h"
|
||||
#include "tabwidget.h"
|
||||
#include "cookiejar.h"
|
||||
#include "history.h"
|
||||
#include "settings.h"
|
||||
#include "mainapplication.h"
|
||||
#include "networkmanager.h"
|
||||
@ -108,29 +109,34 @@ void ClearPrivateData::dialogAccepted()
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
|
||||
if (ui->history->isChecked()) {
|
||||
QDateTime dateTime = QDateTime::currentDateTime();
|
||||
qint64 nowMS = QDateTime::currentMSecsSinceEpoch();
|
||||
qint64 date = 0;
|
||||
qint64 start = QDateTime::currentMSecsSinceEpoch();
|
||||
qint64 end = 0;
|
||||
|
||||
const QDate &today = QDate::currentDate();
|
||||
const QDate &week = today.addDays(1 - today.dayOfWeek());
|
||||
const QDate &month = QDate(today.year(), today.month(), 1);
|
||||
|
||||
switch (ui->historyLength->currentIndex()) {
|
||||
case 0: //Later Today
|
||||
dateTime.setTime(QTime(0, 0));
|
||||
date = dateTime.toMSecsSinceEpoch();
|
||||
end = QDateTime(today).toMSecsSinceEpoch();
|
||||
break;
|
||||
case 1: //Week
|
||||
date = nowMS - 60u * 60u * 24u * 7u * 1000u;
|
||||
end = QDateTime(week).toMSecsSinceEpoch();
|
||||
break;
|
||||
case 2: //Month
|
||||
date = nowMS - 60u * 60u * 24u * 30u * 1000u;
|
||||
end = QDateTime(month).toMSecsSinceEpoch();
|
||||
break;
|
||||
case 3: //All
|
||||
date = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
QSqlQuery query;
|
||||
query.exec("DELETE FROM history WHERE date > " + QString::number(date));
|
||||
query.exec("VACUUM");
|
||||
if (end == 0) {
|
||||
mApp->history()->clearHistory();
|
||||
}
|
||||
else {
|
||||
const QList<int> &indexes = mApp->history()->indexesFromTimeRange(start, end);
|
||||
mApp->history()->deleteHistoryEntry(indexes);
|
||||
}
|
||||
}
|
||||
|
||||
if (ui->cookies->isChecked()) {
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "ui_preferences.h"
|
||||
#include "qupzilla.h"
|
||||
#include "bookmarkstoolbar.h"
|
||||
#include "historymodel.h"
|
||||
#include "history.h"
|
||||
#include "tabwidget.h"
|
||||
#include "cookiejar.h"
|
||||
#include "locationbar.h"
|
||||
|
@ -19,226 +19,34 @@
|
||||
#include "ui_historysidebar.h"
|
||||
#include "qupzilla.h"
|
||||
#include "tabwidget.h"
|
||||
#include "tabbedwebview.h"
|
||||
#include "mainapplication.h"
|
||||
#include "historymodel.h"
|
||||
#include "iconprovider.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QClipboard>
|
||||
#include <QTimer>
|
||||
#include <QSqlQuery>
|
||||
|
||||
HistorySideBar::HistorySideBar(QupZilla* mainClass, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, ui(new Ui::HistorySideBar)
|
||||
, p_QupZilla(mainClass)
|
||||
, m_historyModel(mApp->history())
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(ui->historyTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(itemDoubleClicked(QTreeWidgetItem*)));
|
||||
connect(ui->historyTree, SIGNAL(itemControlClicked(QTreeWidgetItem*)), this, SLOT(itemControlClicked(QTreeWidgetItem*)));
|
||||
connect(ui->historyTree, SIGNAL(itemMiddleButtonClicked(QTreeWidgetItem*)), this, SLOT(itemControlClicked(QTreeWidgetItem*)));
|
||||
|
||||
connect(ui->historyTree, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(contextMenuRequested(const QPoint &)));
|
||||
connect(ui->search, SIGNAL(textEdited(QString)), ui->historyTree, SLOT(filterString(QString)));
|
||||
ui->historyTree->setColumnHidden(1, true);
|
||||
ui->historyTree->setColumnHidden(2, true);
|
||||
ui->historyTree->setColumnHidden(3, true);
|
||||
ui->historyTree->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
|
||||
connect(m_historyModel, SIGNAL(historyEntryAdded(HistoryEntry)), this, SLOT(historyEntryAdded(HistoryEntry)));
|
||||
connect(m_historyModel, SIGNAL(historyEntryDeleted(HistoryEntry)), this, SLOT(historyEntryDeleted(HistoryEntry)));
|
||||
connect(m_historyModel, SIGNAL(historyEntryEdited(HistoryEntry, HistoryEntry)), this, SLOT(historyEntryEdited(HistoryEntry, HistoryEntry)));
|
||||
connect(m_historyModel, SIGNAL(historyClear()), ui->historyTree, SLOT(clear()));
|
||||
|
||||
QTimer::singleShot(0, this, SLOT(slotRefreshTable()));
|
||||
connect(ui->historyTree, SIGNAL(openLink(QUrl, HistoryView::OpenBehavior)), this, SLOT(openLink(QUrl, HistoryView::OpenBehavior)));
|
||||
connect(ui->search, SIGNAL(textEdited(QString)), ui->historyTree->filterModel(), SLOT(setFilterFixedString(QString)));
|
||||
}
|
||||
|
||||
void HistorySideBar::itemDoubleClicked(QTreeWidgetItem* item)
|
||||
void HistorySideBar::openLink(const QUrl &url, HistoryView::OpenBehavior openIn)
|
||||
{
|
||||
if (!item || item->text(1).isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QUrl url = QUrl::fromEncoded(item->text(1).toUtf8());
|
||||
p_QupZilla->loadAddress(url);
|
||||
}
|
||||
|
||||
void HistorySideBar::itemControlClicked(QTreeWidgetItem* item)
|
||||
{
|
||||
if (!item || item->text(1).isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QUrl url = QUrl::fromEncoded(item->text(1).toUtf8());
|
||||
p_QupZilla->tabWidget()->addView(url, item->text(0), Qz::NT_NotSelectedTab);
|
||||
}
|
||||
|
||||
void HistorySideBar::loadInNewTab()
|
||||
{
|
||||
if (QAction* action = qobject_cast<QAction*>(sender())) {
|
||||
p_QupZilla->tabWidget()->addView(action->data().toUrl(), Qz::NT_NotSelectedTab);
|
||||
}
|
||||
}
|
||||
|
||||
void HistorySideBar::copyAddress()
|
||||
{
|
||||
if (QAction* action = qobject_cast<QAction*>(sender())) {
|
||||
QApplication::clipboard()->setText(action->data().toUrl().toEncoded());
|
||||
}
|
||||
}
|
||||
|
||||
void HistorySideBar::contextMenuRequested(const QPoint &position)
|
||||
{
|
||||
if (!ui->historyTree->itemAt(position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QUrl link = QUrl::fromEncoded(ui->historyTree->itemAt(position)->text(1).toUtf8());
|
||||
if (link.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMenu menu;
|
||||
menu.addAction(tr("Open link in current tab"), p_QupZilla, SLOT(loadActionUrl()))->setData(link);
|
||||
menu.addAction(tr("Open link in new tab"), this, SLOT(loadInNewTab()))->setData(link);
|
||||
menu.addSeparator();
|
||||
menu.addAction(tr("Copy address"), this, SLOT(copyAddress()))->setData(link);
|
||||
|
||||
//Prevent choosing first option with double rightclick
|
||||
QPoint pos = ui->historyTree->viewport()->mapToGlobal(position);
|
||||
QPoint p(pos.x(), pos.y() + 1);
|
||||
menu.exec(p);
|
||||
}
|
||||
|
||||
void HistorySideBar::historyEntryAdded(const HistoryEntry &entry)
|
||||
{
|
||||
QDate todayDate = QDate::currentDate();
|
||||
QDate startOfWeekDate = todayDate.addDays(1 - todayDate.dayOfWeek());
|
||||
|
||||
QDate date = entry.date.date();
|
||||
QString localDate;
|
||||
|
||||
if (date == todayDate) {
|
||||
localDate = tr("Today");
|
||||
}
|
||||
else if (date >= startOfWeekDate) {
|
||||
localDate = tr("This Week");
|
||||
}
|
||||
else if (date.month() == todayDate.month()) {
|
||||
localDate = tr("This Month");
|
||||
if (openIn == HistoryView::OpenInNewTab) {
|
||||
p_QupZilla->tabWidget()->addView(url, Qz::NT_NotSelectedTab);
|
||||
}
|
||||
else {
|
||||
localDate = QString("%1 %2").arg(HistoryModel::titleCaseLocalizedMonth(date.month()), QString::number(date.year()));
|
||||
p_QupZilla->weView()->load(url);
|
||||
}
|
||||
|
||||
QTreeWidgetItem* item = new QTreeWidgetItem();
|
||||
QTreeWidgetItem* parentItem;
|
||||
QList<QTreeWidgetItem*> findParent = ui->historyTree->findItems(localDate, 0);
|
||||
if (findParent.count() == 1) {
|
||||
parentItem = findParent.at(0);
|
||||
}
|
||||
else {
|
||||
parentItem = new QTreeWidgetItem();
|
||||
parentItem->setText(0, localDate);
|
||||
parentItem->setIcon(0, QIcon(":/icons/menu/history_entry.png"));
|
||||
ui->historyTree->insertTopLevelItem(0, parentItem);
|
||||
}
|
||||
|
||||
item->setText(0, entry.title);
|
||||
item->setText(1, entry.url.toEncoded());
|
||||
item->setToolTip(0, entry.url.toEncoded());
|
||||
|
||||
item->setData(0, Qt::UserRole + 10, entry.id);
|
||||
item->setIcon(0, _iconForUrl(entry.url));
|
||||
ui->historyTree->prependToParentItem(parentItem, item);
|
||||
}
|
||||
|
||||
void HistorySideBar::historyEntryDeleted(const HistoryEntry &entry)
|
||||
{
|
||||
QList<QTreeWidgetItem*> list = ui->historyTree->allItems();
|
||||
foreach(QTreeWidgetItem * item, list) {
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
if (item->data(0, Qt::UserRole + 10).toInt() != entry.id) {
|
||||
continue;
|
||||
}
|
||||
ui->historyTree->deleteItem(item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void HistorySideBar::historyEntryEdited(const HistoryEntry &before, const HistoryEntry &after)
|
||||
{
|
||||
historyEntryDeleted(before);
|
||||
historyEntryAdded(after);
|
||||
}
|
||||
|
||||
void HistorySideBar::slotRefreshTable()
|
||||
{
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
ui->historyTree->clear();
|
||||
|
||||
QDate todayDate = QDate::currentDate();
|
||||
QDate startOfWeekDate = todayDate.addDays(1 - todayDate.dayOfWeek());
|
||||
QSqlQuery query;
|
||||
query.exec("SELECT title, url, id, date FROM history ORDER BY date DESC");
|
||||
|
||||
int counter = 0;
|
||||
QWeakPointer<HistorySideBar> guard = this;
|
||||
QHash<QString, QTreeWidgetItem*> hash;
|
||||
while (query.next()) {
|
||||
const QString &title = query.value(0).toString();
|
||||
const QUrl &url = query.value(1).toUrl();
|
||||
int id = query.value(2).toInt();
|
||||
const QDate &date = QDateTime::fromMSecsSinceEpoch(query.value(3).toLongLong()).date();
|
||||
QString localDate;
|
||||
|
||||
if (date == todayDate) {
|
||||
localDate = tr("Today");
|
||||
}
|
||||
else if (date >= startOfWeekDate) {
|
||||
localDate = tr("This Week");
|
||||
}
|
||||
else if (date.month() == todayDate.month()) {
|
||||
localDate = tr("This Month");
|
||||
}
|
||||
else {
|
||||
localDate = QString("%1 %2").arg(HistoryModel::titleCaseLocalizedMonth(date.month()), QString::number(date.year()));
|
||||
}
|
||||
|
||||
QTreeWidgetItem* item;
|
||||
QTreeWidgetItem* findParent = hash[localDate];
|
||||
if (findParent) {
|
||||
item = new QTreeWidgetItem(findParent);
|
||||
}
|
||||
else {
|
||||
QTreeWidgetItem* newParent = new QTreeWidgetItem(ui->historyTree);
|
||||
newParent->setText(0, localDate);
|
||||
newParent->setIcon(0, QIcon(":/icons/menu/history_entry.png"));
|
||||
ui->historyTree->addTopLevelItem(newParent);
|
||||
hash[localDate] = newParent;
|
||||
|
||||
item = new QTreeWidgetItem(newParent);
|
||||
}
|
||||
|
||||
item->setText(0, title);
|
||||
item->setText(1, url.toEncoded());
|
||||
item->setToolTip(0, url.toEncoded());
|
||||
|
||||
item->setData(0, Qt::UserRole + 10, id);
|
||||
item->setIcon(0, _iconForUrl(url));
|
||||
ui->historyTree->addTopLevelItem(item);
|
||||
|
||||
++counter;
|
||||
if (counter > 200) {
|
||||
QApplication::processEvents();
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
if (!guard) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
HistorySideBar::~HistorySideBar()
|
||||
|
@ -21,15 +21,13 @@
|
||||
#include <QWidget>
|
||||
|
||||
#include "qz_namespace.h"
|
||||
#include "historymodel.h"
|
||||
#include "historyview.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class HistorySideBar;
|
||||
}
|
||||
|
||||
class QTreeWidgetItem;
|
||||
|
||||
class QupZilla;
|
||||
|
||||
class QT_QUPZILLA_EXPORT HistorySideBar : public QWidget
|
||||
@ -41,22 +39,11 @@ public:
|
||||
~HistorySideBar();
|
||||
|
||||
private slots:
|
||||
void itemDoubleClicked(QTreeWidgetItem* item);
|
||||
void contextMenuRequested(const QPoint &position);
|
||||
void loadInNewTab();
|
||||
void itemControlClicked(QTreeWidgetItem* item);
|
||||
void copyAddress();
|
||||
|
||||
void slotRefreshTable();
|
||||
|
||||
void historyEntryAdded(const HistoryEntry &entry);
|
||||
void historyEntryDeleted(const HistoryEntry &entry);
|
||||
void historyEntryEdited(const HistoryEntry &before, const HistoryEntry &after);
|
||||
void openLink(const QUrl &url, HistoryView::OpenBehavior openIn);
|
||||
|
||||
private:
|
||||
Ui::HistorySideBar* ui;
|
||||
QupZilla* p_QupZilla;
|
||||
HistoryModel* m_historyModel;
|
||||
};
|
||||
|
||||
#endif // HISTORYSIDEBAR_H
|
||||
|
@ -34,33 +34,22 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="TreeWidget" name="historyTree">
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::CustomContextMenu</enum>
|
||||
</property>
|
||||
<widget class="HistoryView" name="historyTree">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="headerHidden">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerDefaultSectionSize">
|
||||
<number>330</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Title</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>TreeWidget</class>
|
||||
<extends>QTreeWidget</extends>
|
||||
<header>treewidget.h</header>
|
||||
<class>HistoryView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>historyview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
|
@ -162,7 +162,7 @@ void SideBarManager::updateActions()
|
||||
}
|
||||
}
|
||||
|
||||
void SideBarManager::showSideBar(const QString &id)
|
||||
void SideBarManager::showSideBar(const QString &id, bool toggle)
|
||||
{
|
||||
if (id == "None") {
|
||||
return;
|
||||
@ -173,9 +173,14 @@ void SideBarManager::showSideBar(const QString &id)
|
||||
}
|
||||
|
||||
if (id == m_activeBar) {
|
||||
if (!toggle) {
|
||||
return;
|
||||
}
|
||||
m_sideBar.data()->close();
|
||||
|
||||
m_activeBar = "None";
|
||||
|
||||
Settings settings;
|
||||
settings.setValue("Browser-View-Settings/SideBar", m_activeBar);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -199,7 +204,7 @@ void SideBarManager::showSideBar(const QString &id)
|
||||
m_activeBar = id;
|
||||
|
||||
Settings settings;
|
||||
settings.setValue("Browser-View-Settings/SideBar", id);
|
||||
settings.setValue("Browser-View-Settings/SideBar", m_activeBar);
|
||||
|
||||
updateActions();
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ public:
|
||||
void setSideBarMenu(QMenu* menu);
|
||||
void refreshMenu();
|
||||
|
||||
void showSideBar(const QString &id);
|
||||
void showSideBar(const QString &id, bool toggle = true);
|
||||
void sideBarRemoved(const QString &id);
|
||||
void closeSideBar();
|
||||
|
||||
|
94
src/lib/tools/headerview.cpp
Normal file
94
src/lib/tools/headerview.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
/* ============================================================
|
||||
* QupZilla - WebKit based browser
|
||||
* Copyright (C) 2010-2012 David Rosca <nowrep@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ============================================================ */
|
||||
#include "headerview.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QContextMenuEvent>
|
||||
|
||||
HeaderView::HeaderView(QAbstractItemView* parent)
|
||||
: QHeaderView(Qt::Horizontal, parent)
|
||||
, m_parent(parent)
|
||||
, m_menu(0)
|
||||
{
|
||||
setMovable(true);
|
||||
setStretchLastSection(true);
|
||||
setDefaultAlignment(Qt::AlignLeft);
|
||||
setMinimumSectionSize(60);
|
||||
}
|
||||
|
||||
void HeaderView::setDefaultSectionSizes(const QList<double> &sizes)
|
||||
{
|
||||
m_sectionSizes = sizes;
|
||||
}
|
||||
|
||||
QList<double> HeaderView::defaultSectionSizes() const
|
||||
{
|
||||
return m_sectionSizes;
|
||||
}
|
||||
|
||||
bool HeaderView::restoreState(const QByteArray &state)
|
||||
{
|
||||
m_resizeOnShow = !QHeaderView::restoreState(state);
|
||||
|
||||
return !m_resizeOnShow;
|
||||
}
|
||||
|
||||
void HeaderView::showEvent(QShowEvent* event)
|
||||
{
|
||||
if (m_resizeOnShow) {
|
||||
for (int i = 0; i < m_sectionSizes.count(); ++i) {
|
||||
int size = m_parent->width() * m_sectionSizes.at(i);
|
||||
resizeSection(i, size);
|
||||
}
|
||||
}
|
||||
|
||||
QHeaderView::showEvent(event);
|
||||
}
|
||||
|
||||
void HeaderView::contextMenuEvent(QContextMenuEvent* event)
|
||||
{
|
||||
if (!m_menu) {
|
||||
m_menu = new QMenu(this);
|
||||
|
||||
for (int i = 0; i < count(); ++i) {
|
||||
QAction* act = new QAction(model()->headerData(i, Qt::Horizontal).toString(), m_menu);
|
||||
act->setCheckable(true);
|
||||
act->setData(i);
|
||||
|
||||
connect(act, SIGNAL(triggered()), this, SLOT(toggleSectionVisibility()));
|
||||
m_menu->addAction(act);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_menu->actions().count(); ++i) {
|
||||
QAction* act = m_menu->actions().at(i);
|
||||
act->setEnabled(i > 0);
|
||||
act->setChecked(!isSectionHidden(i));
|
||||
}
|
||||
|
||||
m_menu->popup(event->globalPos());
|
||||
}
|
||||
|
||||
void HeaderView::toggleSectionVisibility()
|
||||
{
|
||||
if (QAction* act = qobject_cast<QAction*>(sender())) {
|
||||
int index = act->data().toInt();
|
||||
|
||||
setSectionHidden(index, !isSectionHidden(index));
|
||||
}
|
||||
}
|
53
src/lib/tools/headerview.h
Normal file
53
src/lib/tools/headerview.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* ============================================================
|
||||
* QupZilla - WebKit based browser
|
||||
* Copyright (C) 2010-2012 David Rosca <nowrep@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ============================================================ */
|
||||
#ifndef HEADERVIEW_H
|
||||
#define HEADERVIEW_H
|
||||
|
||||
#include <QHeaderView>
|
||||
|
||||
class QContextMenuEvent;
|
||||
|
||||
#include "qz_namespace.h"
|
||||
|
||||
class QT_QUPZILLA_EXPORT HeaderView : public QHeaderView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit HeaderView(QAbstractItemView* parent);
|
||||
|
||||
void setDefaultSectionSizes(const QList<double> &sizes);
|
||||
QList<double> defaultSectionSizes() const;
|
||||
|
||||
bool restoreState(const QByteArray &state);
|
||||
|
||||
private slots:
|
||||
void toggleSectionVisibility();
|
||||
|
||||
private:
|
||||
void showEvent(QShowEvent* event);
|
||||
void contextMenuEvent(QContextMenuEvent* event);
|
||||
|
||||
QAbstractItemView* m_parent;
|
||||
QMenu* m_menu;
|
||||
|
||||
bool m_resizeOnShow;
|
||||
QList<double> m_sectionSizes;
|
||||
QByteArray m_restoreData;
|
||||
};
|
||||
|
||||
#endif // HEADERVIEW_H
|
@ -20,7 +20,7 @@
|
||||
#include "mainapplication.h"
|
||||
#include "globalfunctions.h"
|
||||
#include "iconprovider.h"
|
||||
#include "historymodel.h"
|
||||
#include "history.h"
|
||||
#include "autofillmodel.h"
|
||||
#include "downloadmanager.h"
|
||||
#include "sourceviewer.h"
|
||||
|
Loading…
Reference in New Issue
Block a user