diff --git a/.gitignore b/.gitignore index ba92d8a21..0b064d319 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ git_revision *.exp *.zip Thumbs.db +tests/modeltest diff --git a/src/lib/3rdparty/qtwin.cpp b/src/lib/3rdparty/qtwin.cpp index 8d25f432c..a8d2394bd 100644 --- a/src/lib/3rdparty/qtwin.cpp +++ b/src/lib/3rdparty/qtwin.cpp @@ -29,7 +29,7 @@ #include #include #include -#include "historymodel.h" +#include "history.h" #include "mainapplication.h" #ifdef Q_WS_WIN diff --git a/src/lib/app/mainapplication.cpp b/src/lib/app/mainapplication.cpp index 3cb33a4c2..60f0f4a1a 100644 --- a/src/lib/app/mainapplication.cpp +++ b/src/lib/app/mainapplication.cpp @@ -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; } diff --git a/src/lib/app/mainapplication.h b/src/lib/app/mainapplication.h index 1d37a907f..959235402 100644 --- a/src/lib/app/mainapplication.h +++ b/src/lib/app/mainapplication.h @@ -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; diff --git a/src/lib/app/qupzilla.cpp b/src/lib/app/qupzilla.cpp index 4cb61bff3..9b5b485dc 100644 --- a/src/lib/app/qupzilla.cpp +++ b/src/lib/app/qupzilla.cpp @@ -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); diff --git a/src/lib/bookmarks/bookmarkstoolbar.cpp b/src/lib/bookmarks/bookmarkstoolbar.cpp index 9765a56e1..f72908110 100644 --- a/src/lib/bookmarks/bookmarkstoolbar.cpp +++ b/src/lib/bookmarks/bookmarkstoolbar.cpp @@ -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" diff --git a/src/lib/bookmarks/bookmarkstoolbar.h b/src/lib/bookmarks/bookmarkstoolbar.h index 8f820854d..221841e03 100644 --- a/src/lib/bookmarks/bookmarkstoolbar.h +++ b/src/lib/bookmarks/bookmarkstoolbar.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; diff --git a/src/lib/history/history.cpp b/src/lib/history/history.cpp new file mode 100644 index 000000000..325593fe2 --- /dev/null +++ b/src/lib/history/history.cpp @@ -0,0 +1,289 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2012 David Rosca +* +* 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 . +* ============================================================ */ +#include "history.h" +#include "historymodel.h" +#include "tabbedwebview.h" +#include "qupzilla.h" +#include "iconprovider.h" +#include "settings.h" + +#include +#include + +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 list; + list.append(index); + + deleteHistoryEntry(list); +} + +void History::deleteHistoryEntry(const QList &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 History::indexesFromTimeRange(qint64 start, qint64 end) +{ + QList 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 History::mostVisited(int count) +{ + QList 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(); + } +} diff --git a/src/lib/history/history.h b/src/lib/history/history.h new file mode 100644 index 000000000..70d181489 --- /dev/null +++ b/src/lib/history/history.h @@ -0,0 +1,89 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2012 David Rosca +* +* 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 . +* ============================================================ */ +#ifndef HISTORY_H +#define HISTORY_H + +#include +#include +#include +#include + +#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 &list); + void deleteHistoryEntry(const QString &url, const QString &title); + + QList indexesFromTimeRange(qint64 start, qint64 end); + + bool urlIsStored(const QString &url); + + QList 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 diff --git a/src/lib/history/historyitem.cpp b/src/lib/history/historyitem.cpp new file mode 100644 index 000000000..489fba48b --- /dev/null +++ b/src/lib/history/historyitem.cpp @@ -0,0 +1,180 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2012 David Rosca +* +* 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 . +* ============================================================ */ +#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); +} diff --git a/src/lib/history/historyitem.h b/src/lib/history/historyitem.h new file mode 100644 index 000000000..2e0a40847 --- /dev/null +++ b/src/lib/history/historyitem.h @@ -0,0 +1,76 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2012 David Rosca +* +* 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 . +* ============================================================ */ +#ifndef HISTORYITEM_H +#define HISTORYITEM_H + +#include + +#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 m_children; + + QIcon m_icon; + bool m_iconLoaded; + + qint64 m_startTimestamp; + qint64 m_endTimestamp; +}; + +#endif // HISTORYITEM_H diff --git a/src/lib/history/historymanager.cpp b/src/lib/history/historymanager.cpp index 0136c155f..ba275741b 100644 --- a/src/lib/history/historymanager.cpp +++ b/src/lib/history/historymanager.cpp @@ -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 #include -#include -#include -#include 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(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(sender())) { - QApplication::clipboard()->setText(action->data().toUrl().toEncoded()); - } -} - -void HistoryManager::deleteItem() -{ - QApplication::setOverrideCursor(Qt::WaitCursor); - QList list; - - foreach(QTreeWidgetItem * item, ui->historyTree->selectedItems()) { - if (!item) { - continue; - } - - if (!item->parent()) { - QList 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 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 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 guard = this; - QHash 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; diff --git a/src/lib/history/historymanager.h b/src/lib/history/historymanager.h index 2d47a34f7..401a3aa1e 100644 --- a/src/lib/history/historymanager.h +++ b/src/lib/history/historymanager.h @@ -22,7 +22,7 @@ #include #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 p_QupZilla; - HistoryModel* m_historyModel; - - QList m_ignoredIds; }; #endif // HISTORYMANAGER_H diff --git a/src/lib/history/historymanager.ui b/src/lib/history/historymanager.ui index 9f79a2d21..f271ec892 100644 --- a/src/lib/history/historymanager.ui +++ b/src/lib/history/historymanager.ui @@ -14,29 +14,6 @@ History - - - - Qt::CustomContextMenu - - - true - - - 330 - - - - Title - - - - - Url - - - - @@ -77,19 +54,26 @@ + + + + true + + + - - TreeWidget - QTreeWidget -
treewidget.h
-
ClickableLabel QLabel
clickablelabel.h
+ + HistoryView + QTreeView +
historyview.h
+
diff --git a/src/lib/history/historymodel.cpp b/src/lib/history/historymodel.cpp index 60fab553e..0ce0ed010 100644 --- a/src/lib/history/historymodel.cpp +++ b/src/lib/history/historymodel.cpp @@ -16,231 +16,521 @@ * along with this program. If not, see . * ============================================================ */ #include "historymodel.h" -#include "tabbedwebview.h" -#include "qupzilla.h" +#include "historyitem.h" #include "iconprovider.h" -#include "settings.h" -#include +#include #include +#include +#include -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 list; - list.append(index); - - deleteHistoryEntry(list); -} - -void HistoryModel::deleteHistoryEntry(const QList &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 HistoryModel::mostVisited(int count) -{ - QList 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()); + 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(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 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 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())); +} diff --git a/src/lib/history/historymodel.h b/src/lib/history/historymodel.h index 51a105140..dd58ab358 100644 --- a/src/lib/history/historymodel.h +++ b/src/lib/history/historymodel.h @@ -18,67 +18,95 @@ #ifndef HISTORYMODEL_H #define HISTORYMODEL_H -#include -#include -#include -#include +#include +#include #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 &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 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 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 diff --git a/src/lib/history/historyview.cpp b/src/lib/history/historyview.cpp new file mode 100644 index 000000000..1f50a8074 --- /dev/null +++ b/src/lib/history/historyview.cpp @@ -0,0 +1,191 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2012 David Rosca +* +* 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 . +* ============================================================ */ +#include "historyview.h" +#include "historymodel.h" +#include "historyitem.h" +#include "headerview.h" +#include "mainapplication.h" +#include "iconprovider.h" + +#include +#include +#include +#include + +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() << 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 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); +} diff --git a/src/lib/history/historyview.h b/src/lib/history/historyview.h new file mode 100644 index 000000000..b8258a00e --- /dev/null +++ b/src/lib/history/historyview.h @@ -0,0 +1,66 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2012 David Rosca +* +* 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 . +* ============================================================ */ +#ifndef HISTORYVIEW_H +#define HISTORYVIEW_H + +#include + +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 diff --git a/src/lib/history/webhistoryinterface.cpp b/src/lib/history/webhistoryinterface.cpp index cf1401a54..162cb9a49 100644 --- a/src/lib/history/webhistoryinterface.cpp +++ b/src/lib/history/webhistoryinterface.cpp @@ -17,7 +17,7 @@ * ============================================================ */ #include "webhistoryinterface.h" #include "mainapplication.h" -#include "historymodel.h" +#include "history.h" WebHistoryInterface::WebHistoryInterface(QObject* parent) : QWebHistoryInterface(parent) diff --git a/src/lib/lib.pro b/src/lib/lib.pro index 89ad489c1..ed7f415a5 100644 --- a/src/lib/lib.pro +++ b/src/lib/lib.pro @@ -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 \ diff --git a/src/lib/other/browsinglibrary.cpp b/src/lib/other/browsinglibrary.cpp index 180b4be17..4d5d3ca79 100644 --- a/src/lib/other/browsinglibrary.cpp +++ b/src/lib/other/browsinglibrary.cpp @@ -24,6 +24,7 @@ #include "downloaditem.h" #include "globalfunctions.h" #include "settings.h" +#include "history.h" #include #include @@ -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(); } diff --git a/src/lib/other/browsinglibrary.h b/src/lib/other/browsinglibrary.h index 5a401a090..1dd5c9c20 100644 --- a/src/lib/other/browsinglibrary.h +++ b/src/lib/other/browsinglibrary.h @@ -61,7 +61,7 @@ private: HistoryManager* m_historyManager; BookmarksManager* m_bookmarksManager; RSSManager* m_rssManager; - bool m_historyLoaded; + bool m_bookmarksLoaded; bool m_rssLoaded; }; diff --git a/src/lib/other/clearprivatedata.cpp b/src/lib/other/clearprivatedata.cpp index 82103fc6b..ac6683e9d 100644 --- a/src/lib/other/clearprivatedata.cpp +++ b/src/lib/other/clearprivatedata.cpp @@ -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 &indexes = mApp->history()->indexesFromTimeRange(start, end); + mApp->history()->deleteHistoryEntry(indexes); + } } if (ui->cookies->isChecked()) { diff --git a/src/lib/preferences/preferences.cpp b/src/lib/preferences/preferences.cpp index a34088c59..90a9e9ab9 100644 --- a/src/lib/preferences/preferences.cpp +++ b/src/lib/preferences/preferences.cpp @@ -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" diff --git a/src/lib/sidebar/historysidebar.cpp b/src/lib/sidebar/historysidebar.cpp index 5f59d3059..3ac6d6ed2 100644 --- a/src/lib/sidebar/historysidebar.cpp +++ b/src/lib/sidebar/historysidebar.cpp @@ -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 -#include -#include -#include 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(sender())) { - p_QupZilla->tabWidget()->addView(action->data().toUrl(), Qz::NT_NotSelectedTab); - } -} - -void HistorySideBar::copyAddress() -{ - if (QAction* action = qobject_cast(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 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 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 guard = this; - QHash 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() diff --git a/src/lib/sidebar/historysidebar.h b/src/lib/sidebar/historysidebar.h index 300705010..d007bb45b 100644 --- a/src/lib/sidebar/historysidebar.h +++ b/src/lib/sidebar/historysidebar.h @@ -21,15 +21,13 @@ #include #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 diff --git a/src/lib/sidebar/historysidebar.ui b/src/lib/sidebar/historysidebar.ui index 79d1a01b0..238444575 100644 --- a/src/lib/sidebar/historysidebar.ui +++ b/src/lib/sidebar/historysidebar.ui @@ -34,33 +34,22 @@ - - - Qt::CustomContextMenu - + true true - - 330 - - - - Title - - - TreeWidget - QTreeWidget -
treewidget.h
+ HistoryView + QTreeView +
historyview.h
diff --git a/src/lib/sidebar/sidebar.cpp b/src/lib/sidebar/sidebar.cpp index 941dd5f1e..410a9b329 100644 --- a/src/lib/sidebar/sidebar.cpp +++ b/src/lib/sidebar/sidebar.cpp @@ -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(); } diff --git a/src/lib/sidebar/sidebar.h b/src/lib/sidebar/sidebar.h index 9a670c2cb..5eef86e25 100644 --- a/src/lib/sidebar/sidebar.h +++ b/src/lib/sidebar/sidebar.h @@ -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(); diff --git a/src/lib/tools/headerview.cpp b/src/lib/tools/headerview.cpp new file mode 100644 index 000000000..571b1f145 --- /dev/null +++ b/src/lib/tools/headerview.cpp @@ -0,0 +1,94 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2012 David Rosca +* +* 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 . +* ============================================================ */ +#include "headerview.h" + +#include +#include + +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 &sizes) +{ + m_sectionSizes = sizes; +} + +QList 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(sender())) { + int index = act->data().toInt(); + + setSectionHidden(index, !isSectionHidden(index)); + } +} diff --git a/src/lib/tools/headerview.h b/src/lib/tools/headerview.h new file mode 100644 index 000000000..f9e5a33d8 --- /dev/null +++ b/src/lib/tools/headerview.h @@ -0,0 +1,53 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2010-2012 David Rosca +* +* 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 . +* ============================================================ */ +#ifndef HEADERVIEW_H +#define HEADERVIEW_H + +#include + +class QContextMenuEvent; + +#include "qz_namespace.h" + +class QT_QUPZILLA_EXPORT HeaderView : public QHeaderView +{ + Q_OBJECT +public: + explicit HeaderView(QAbstractItemView* parent); + + void setDefaultSectionSizes(const QList &sizes); + QList 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 m_sectionSizes; + QByteArray m_restoreData; +}; + +#endif // HEADERVIEW_H diff --git a/src/lib/webview/webview.cpp b/src/lib/webview/webview.cpp index bac3f348a..2cf4228bb 100644 --- a/src/lib/webview/webview.cpp +++ b/src/lib/webview/webview.cpp @@ -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"