1
mirror of https://invent.kde.org/network/falkon.git synced 2024-09-21 09:42:10 +02: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:
nowrep 2012-04-30 16:33:14 +02:00
parent bb4d981c31
commit 5b75c0e5a0
32 changed files with 1700 additions and 806 deletions

1
.gitignore vendored
View File

@ -22,3 +22,4 @@ git_revision
*.exp
*.zip
Thumbs.db
tests/modeltest

View File

@ -29,7 +29,7 @@
#include <QApplication>
#include <QWidget>
#include <QList>
#include "historymodel.h"
#include "history.h"
#include "mainapplication.h"
#ifdef Q_WS_WIN

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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"

View File

@ -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
View 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
View 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

View 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);
}

View 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

View File

@ -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;

View File

@ -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

View File

@ -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/>

View File

@ -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 &current = 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();
}
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);
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();
}
return list;
return QVariant();
}
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 &currentTimestamp = 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()));
}

View File

@ -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

View 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);
}

View 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

View File

@ -17,7 +17,7 @@
* ============================================================ */
#include "webhistoryinterface.h"
#include "mainapplication.h"
#include "historymodel.h"
#include "history.h"
WebHistoryInterface::WebHistoryInterface(QObject* parent)
: QWebHistoryInterface(parent)

View File

@ -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 \

View File

@ -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();
}

View File

@ -61,7 +61,7 @@ private:
HistoryManager* m_historyManager;
BookmarksManager* m_bookmarksManager;
RSSManager* m_rssManager;
bool m_historyLoaded;
bool m_bookmarksLoaded;
bool m_rssLoaded;
};

View File

@ -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()) {

View File

@ -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"

View File

@ -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()

View File

@ -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

View File

@ -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/>

View File

@ -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();
}

View File

@ -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();

View 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));
}
}

View 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

View File

@ -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"