From fb8b9a3e3f8d0829e830d6d398087145c5f42d6e Mon Sep 17 00:00:00 2001 From: Andrea Diamantini Date: Tue, 24 Nov 2009 02:13:31 +0100 Subject: Created a new history dir and changed "panelhistory" terms in the more English "HistoryPanel" ones --- src/CMakeLists.txt | 10 +- src/history.cpp | 454 -------------------- src/history.h | 164 ------- src/history/history.cpp | 454 ++++++++++++++++++++ src/history/history.h | 164 +++++++ src/history/historymodels.cpp | 971 ++++++++++++++++++++++++++++++++++++++++++ src/history/historymodels.h | 256 +++++++++++ src/history/historypanel.cpp | 101 +++++ src/history/historypanel.h | 64 +++ src/historymodels.cpp | 971 ------------------------------------------ src/historymodels.h | 256 ----------- src/panelhistory.cpp | 101 ----- src/panelhistory.h | 65 --- src/sidepanel.cpp | 10 +- src/sidepanel.h | 7 +- 15 files changed, 2024 insertions(+), 2024 deletions(-) delete mode 100644 src/history.cpp delete mode 100644 src/history.h create mode 100644 src/history/history.cpp create mode 100644 src/history/history.h create mode 100644 src/history/historymodels.cpp create mode 100644 src/history/historymodels.h create mode 100644 src/history/historypanel.cpp create mode 100644 src/history/historypanel.h delete mode 100644 src/historymodels.cpp delete mode 100644 src/historymodels.h delete mode 100644 src/panelhistory.cpp delete mode 100644 src/panelhistory.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1061f3e7..e214c7d9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,13 +12,10 @@ SET( rekonq_KDEINIT_SRCS autosaver.cpp bookmarks.cpp findbar.cpp - history.cpp - historymodels.cpp lineedit.cpp mainview.cpp mainwindow.cpp modelmenu.cpp - panelhistory.cpp previewimage.cpp sessionmanager.cpp sidepanel.cpp @@ -28,6 +25,10 @@ SET( rekonq_KDEINIT_SRCS webpluginfactory.cpp websnap.cpp webview.cpp +#---------------------------------------- + history/history.cpp + history/historymodels.cpp + history/historypanel.cpp #---------------------------------------- rekonqpage/newtabpage.cpp #---------------------------------------- @@ -56,9 +57,10 @@ KDE4_ADD_KCFG_FILES( rekonq_KDEINIT_SRCS rekonq.kcfgc ) ### ------------- INCLUDING DIRECTORIES... INCLUDE_DIRECTORIES ( ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/adblock + ${CMAKE_CURRENT_SOURCE_DIR}/history ${CMAKE_CURRENT_SOURCE_DIR}/rekonqpage ${CMAKE_CURRENT_SOURCE_DIR}/settings - ${CMAKE_CURRENT_SOURCE_DIR}/adblock ${CMAKE_CURRENT_BINARY_DIR} ${KDE4_INCLUDES} ${QT4_INCLUDES} diff --git a/src/history.cpp b/src/history.cpp deleted file mode 100644 index 07580433..00000000 --- a/src/history.cpp +++ /dev/null @@ -1,454 +0,0 @@ -/* ============================================================ -* -* This file is a part of the rekonq project -* -* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved -* Copyright (C) 2008 Benjamin C. Meyer -* Copyright (C) 2008-2009 by Andrea Diamantini -* -* -* 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 2 of -* the License or (at your option) version 3 or any later version -* accepted by the membership of KDE e.V. (or its successor approved -* by the membership of KDE e.V.), which shall act as a proxy -* defined in Section 14 of version 3 of the license. -* -* 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 . -* -* ============================================================ */ - - -// Self Includes -#include "history.h" -#include "history.moc" - -// Auto Includes -#include "rekonq.h" - -// Local Includes -#include "historymodels.h" -#include "autosaver.h" -#include "application.h" - -// KDE Includes -#include -#include -#include - -// Qt Includes -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -// generic algorithms -#include - - -static const unsigned int HISTORY_VERSION = 23; - - -HistoryManager::HistoryManager(QObject *parent) - : QWebHistoryInterface(parent) - , m_saveTimer(new AutoSaver(this)) - , m_historyLimit(30) - , m_historyModel(0) - , m_historyFilterModel(0) - , m_historyTreeModel(0) - , m_completion(0) -{ - // take care of the completion object - m_completion = new KCompletion; - m_completion->setOrder( KCompletion::Weighted ); - - m_expiredTimer.setSingleShot(true); - connect(&m_expiredTimer, SIGNAL(timeout()), this, SLOT(checkForExpired())); - connect(this, SIGNAL(entryAdded(const HistoryItem &)), m_saveTimer, SLOT(changeOccurred())); - connect(this, SIGNAL(entryRemoved(const HistoryItem &)), m_saveTimer, SLOT(changeOccurred())); - - load(); - - m_historyModel = new HistoryModel(this, this); - m_historyFilterModel = new HistoryFilterModel(m_historyModel, this); - m_historyTreeModel = new HistoryTreeModel(m_historyFilterModel, this); - - // QWebHistoryInterface will delete the history manager - QWebHistoryInterface::setDefaultInterface(this); -} - - -HistoryManager::~HistoryManager() -{ - m_saveTimer->saveIfNeccessary(); - delete m_completion; -} - - -QList HistoryManager::history() const -{ - return m_history; -} - - -bool HistoryManager::historyContains(const QString &url) const -{ - return m_historyFilterModel->historyContains(url); -} - - -void HistoryManager::addHistoryEntry(const QString &url) -{ - QUrl cleanUrl(url); - - // don't store about: urls (home page related) - if(cleanUrl.scheme() == QString("about")) - return; - - cleanUrl.setPassword(QString()); - cleanUrl.setHost(cleanUrl.host().toLower()); - HistoryItem item(cleanUrl.toString(), QDateTime::currentDateTime()); - addHistoryEntry(item); - - // Add item to completion object - QString _url(url); - _url.remove(QRegExp("^http://|/$")); - m_completion->addItem(_url); -} - - -void HistoryManager::setHistory(const QList &history, bool loadedAndSorted) -{ - m_history = history; - - // verify that it is sorted by date - if (!loadedAndSorted) - qSort(m_history.begin(), m_history.end()); - - checkForExpired(); - - if (loadedAndSorted) - { - m_lastSavedUrl = m_history.value(0).url; - } - else - { - m_lastSavedUrl.clear(); - m_saveTimer->changeOccurred(); - } - emit historyReset(); -} - - -HistoryModel *HistoryManager::historyModel() const -{ - return m_historyModel; -} - - -HistoryFilterModel *HistoryManager::historyFilterModel() const -{ - return m_historyFilterModel; -} - - -HistoryTreeModel *HistoryManager::historyTreeModel() const -{ - return m_historyTreeModel; -} - - -void HistoryManager::checkForExpired() -{ - if (m_historyLimit < 0 || m_history.isEmpty()) - return; - - QDateTime now = QDateTime::currentDateTime(); - int nextTimeout = 0; - - while (!m_history.isEmpty()) - { - QDateTime checkForExpired = m_history.last().dateTime; - checkForExpired.setDate(checkForExpired.date().addDays(m_historyLimit)); - if (now.daysTo(checkForExpired) > 7) - { - // check at most in a week to prevent int overflows on the timer - nextTimeout = 7 * 86400; - } - else - { - nextTimeout = now.secsTo(checkForExpired); - } - if (nextTimeout > 0) - break; - HistoryItem item = m_history.takeLast(); - // remove from saved file also - m_lastSavedUrl.clear(); - emit entryRemoved(item); - } - - if (nextTimeout > 0) - m_expiredTimer.start(nextTimeout * 1000); -} - - -void HistoryManager::addHistoryEntry(const HistoryItem &item) -{ - QWebSettings *globalSettings = QWebSettings::globalSettings(); - if (globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) - return; - - m_history.prepend(item); - emit entryAdded(item); - - if (m_history.count() == 1) - checkForExpired(); -} - - -void HistoryManager::updateHistoryEntry(const KUrl &url, const QString &title) -{ - for (int i = 0; i < m_history.count(); ++i) - { - if (url == m_history.at(i).url) - { - m_history[i].title = title; - m_saveTimer->changeOccurred(); - if (m_lastSavedUrl.isEmpty()) - m_lastSavedUrl = m_history.at(i).url; - emit entryUpdated(i); - break; - } - } -} - - -void HistoryManager::removeHistoryEntry(const HistoryItem &item) -{ - m_lastSavedUrl.clear(); - m_history.removeOne(item); - emit entryRemoved(item); -} - - -void HistoryManager::removeHistoryEntry(const KUrl &url, const QString &title) -{ - for (int i = 0; i < m_history.count(); ++i) - { - if (url == m_history.at(i).url - && (title.isEmpty() || title == m_history.at(i).title)) - { - removeHistoryEntry(m_history.at(i)); - break; - } - } - - // Remove item from completion object - QString _url = url.path(); - _url.remove(QRegExp("^http://|/$")); - m_completion->removeItem(_url); -} - - -int HistoryManager::historyLimit() const -{ - return m_historyLimit; -} - - -void HistoryManager::setHistoryLimit(int limit) -{ - if (m_historyLimit == limit) - return; - m_historyLimit = limit; - checkForExpired(); - m_saveTimer->changeOccurred(); -} - - -void HistoryManager::clear() -{ - m_history.clear(); - m_lastSavedUrl.clear(); - m_saveTimer->changeOccurred(); - m_saveTimer->saveIfNeccessary(); - historyReset(); -} - - -void HistoryManager::loadSettings() -{ - int historyExpire = ReKonfig::expireHistory(); - int days; - switch (historyExpire) - { - case 0: days = 1; break; - case 1: days = 7; break; - case 2: days = 14; break; - case 3: days = 30; break; - case 4: days = 365; break; - case 5: days = -1; break; - default: days = -1; - } - m_historyLimit = days; -} - - -void HistoryManager::load() -{ - loadSettings(); - - QString historyFilePath = KStandardDirs::locateLocal("appdata" , "history"); - QFile historyFile(historyFilePath); - if (!historyFile.exists()) - return; - if (!historyFile.open(QFile::ReadOnly)) - { - kWarning() << "Unable to open history file" << historyFile.fileName(); - return; - } - - QList list; - QDataStream in(&historyFile); - // Double check that the history file is sorted as it is read in - bool needToSort = false; - HistoryItem lastInsertedItem; - QByteArray data; - QDataStream stream; - QBuffer buffer; - stream.setDevice(&buffer); - while (!historyFile.atEnd()) - { - in >> data; - buffer.close(); - buffer.setBuffer(&data); - buffer.open(QIODevice::ReadOnly); - quint32 ver; - stream >> ver; - if (ver != HISTORY_VERSION) - continue; - HistoryItem item; - stream >> item.url; - stream >> item.dateTime; - stream >> item.title; - - if (!item.dateTime.isValid()) - continue; - - if (item == lastInsertedItem) - { - if (lastInsertedItem.title.isEmpty() && !list.isEmpty()) - list[0].title = item.title; - continue; - } - - if (!needToSort && !list.isEmpty() && lastInsertedItem < item) - needToSort = true; - - list.prepend(item); - lastInsertedItem = item; - - // Add item to completion object - QString _url = item.url; - _url.remove(QRegExp("^http://|/$")); - m_completion->addItem(_url); - } - if (needToSort) - qSort(list.begin(), list.end()); - - setHistory(list, true); - - // If we had to sort re-write the whole history sorted - if (needToSort) - { - m_lastSavedUrl.clear(); - m_saveTimer->changeOccurred(); - } -} - - -void HistoryManager::save() -{ - bool saveAll = m_lastSavedUrl.isEmpty(); - int first = m_history.count() - 1; - if (!saveAll) - { - // find the first one to save - for (int i = 0; i < m_history.count(); ++i) - { - if (m_history.at(i).url == m_lastSavedUrl) - { - first = i - 1; - break; - } - } - } - if (first == m_history.count() - 1) - saveAll = true; - - QString historyFilePath = KStandardDirs::locateLocal("appdata" , "history"); - QFile historyFile(historyFilePath); - - // When saving everything use a temporary file to prevent possible data loss. - QTemporaryFile tempFile; - tempFile.setAutoRemove(false); - bool open = false; - if (saveAll) - { - open = tempFile.open(); - } - else - { - open = historyFile.open(QFile::Append); - } - - if (!open) - { - kWarning() << "Unable to open history file for saving" - << (saveAll ? tempFile.fileName() : historyFile.fileName()); - return; - } - - QDataStream out(saveAll ? &tempFile : &historyFile); - for (int i = first; i >= 0; --i) - { - QByteArray data; - QDataStream stream(&data, QIODevice::WriteOnly); - HistoryItem item = m_history.at(i); - stream << HISTORY_VERSION << item.url << item.dateTime << item.title; - out << data; - } - tempFile.close(); - - if (saveAll) - { - if (historyFile.exists() && !historyFile.remove()) - { - kWarning() << "History: error removing old history." << historyFile.errorString(); - } - if (!tempFile.rename(historyFile.fileName())) - { - kWarning() << "History: error moving new history over old." << tempFile.errorString() << historyFile.fileName(); - } - } - m_lastSavedUrl = m_history.value(0).url; -} - - -KCompletion * HistoryManager::completionObject() const -{ - return m_completion; -} diff --git a/src/history.h b/src/history.h deleted file mode 100644 index 2ccf8cb4..00000000 --- a/src/history.h +++ /dev/null @@ -1,164 +0,0 @@ -/* ============================================================ -* -* This file is a part of the rekonq project -* -* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved -* Copyright (C) 2008 Benjamin C. Meyer -* Copyright (C) 2008-2009 by Andrea Diamantini -* -* -* 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 2 of -* the License or (at your option) version 3 or any later version -* accepted by the membership of KDE e.V. (or its successor approved -* by the membership of KDE e.V.), which shall act as a proxy -* defined in Section 14 of version 3 of the license. -* -* 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 - - -// Local Includes -#include "modelmenu.h" - -// KDE Includes -#include - -// Qt Includes -#include -#include -#include -#include -#include -#include - - -/** - * Elements in this class represent an history item - * - */ -class HistoryItem -{ -public: - HistoryItem() {} - explicit HistoryItem(const QString &u, - const QDateTime &d = QDateTime(), - const QString &t = QString() - ) - : title(t), url(u), dateTime(d) {} - - inline bool operator==(const HistoryItem &other) const - { - return other.title == title - && other.url == url && other.dateTime == dateTime; - } - - // history is sorted in reverse - inline bool operator <(const HistoryItem &other) const - { - return dateTime > other.dateTime; - } - - QString title; - QString url; - QDateTime dateTime; -}; - - - -// --------------------------------------------------------------------------------------------------------------- - - -// Forward Declarations -class AutoSaver; -class HistoryModel; -class HistoryFilterModel; -class HistoryTreeModel; - -class KCompletion; - - -/** - * THE History Manager: - * It manages rekonq history - * - */ -class HistoryManager : public QWebHistoryInterface -{ - Q_OBJECT - Q_PROPERTY(int historyLimit READ historyLimit WRITE setHistoryLimit) - -signals: - void historyReset(); - void entryAdded(const HistoryItem &item); - void entryRemoved(const HistoryItem &item); - void entryUpdated(int offset); - -public: - HistoryManager(QObject *parent = 0); - ~HistoryManager(); - - bool historyContains(const QString &url) const; - void addHistoryEntry(const QString &url); - void updateHistoryEntry(const KUrl &url, const QString &title); - void removeHistoryEntry(const KUrl &url, const QString &title = QString()); - - int historyLimit() const; - void setHistoryLimit(int limit); - - QList history() const; - void setHistory(const QList &history, bool loadedAndSorted = false); - - // History manager keeps around these models for use by the completer and other classes - HistoryModel *historyModel() const; - HistoryFilterModel *historyFilterModel() const; - HistoryTreeModel *historyTreeModel() const; - - /** - * @returns the KCompletion object. - */ - KCompletion *completionObject() const; - -public slots: - void clear(); - void loadSettings(); - -private slots: - void save(); - void checkForExpired(); - -protected: - void addHistoryEntry(const HistoryItem &item); - void removeHistoryEntry(const HistoryItem &item); - -private: - void load(); - - AutoSaver *m_saveTimer; - int m_historyLimit; - QTimer m_expiredTimer; - QList m_history; - QString m_lastSavedUrl; - - HistoryModel *m_historyModel; - HistoryFilterModel *m_historyFilterModel; - HistoryTreeModel *m_historyTreeModel; - - // the completion object we sync with - KCompletion *m_completion; -}; - - -#endif // HISTORY_H diff --git a/src/history/history.cpp b/src/history/history.cpp new file mode 100644 index 00000000..07580433 --- /dev/null +++ b/src/history/history.cpp @@ -0,0 +1,454 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008 Benjamin C. Meyer +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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 . +* +* ============================================================ */ + + +// Self Includes +#include "history.h" +#include "history.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "historymodels.h" +#include "autosaver.h" +#include "application.h" + +// KDE Includes +#include +#include +#include + +// Qt Includes +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// generic algorithms +#include + + +static const unsigned int HISTORY_VERSION = 23; + + +HistoryManager::HistoryManager(QObject *parent) + : QWebHistoryInterface(parent) + , m_saveTimer(new AutoSaver(this)) + , m_historyLimit(30) + , m_historyModel(0) + , m_historyFilterModel(0) + , m_historyTreeModel(0) + , m_completion(0) +{ + // take care of the completion object + m_completion = new KCompletion; + m_completion->setOrder( KCompletion::Weighted ); + + m_expiredTimer.setSingleShot(true); + connect(&m_expiredTimer, SIGNAL(timeout()), this, SLOT(checkForExpired())); + connect(this, SIGNAL(entryAdded(const HistoryItem &)), m_saveTimer, SLOT(changeOccurred())); + connect(this, SIGNAL(entryRemoved(const HistoryItem &)), m_saveTimer, SLOT(changeOccurred())); + + load(); + + m_historyModel = new HistoryModel(this, this); + m_historyFilterModel = new HistoryFilterModel(m_historyModel, this); + m_historyTreeModel = new HistoryTreeModel(m_historyFilterModel, this); + + // QWebHistoryInterface will delete the history manager + QWebHistoryInterface::setDefaultInterface(this); +} + + +HistoryManager::~HistoryManager() +{ + m_saveTimer->saveIfNeccessary(); + delete m_completion; +} + + +QList HistoryManager::history() const +{ + return m_history; +} + + +bool HistoryManager::historyContains(const QString &url) const +{ + return m_historyFilterModel->historyContains(url); +} + + +void HistoryManager::addHistoryEntry(const QString &url) +{ + QUrl cleanUrl(url); + + // don't store about: urls (home page related) + if(cleanUrl.scheme() == QString("about")) + return; + + cleanUrl.setPassword(QString()); + cleanUrl.setHost(cleanUrl.host().toLower()); + HistoryItem item(cleanUrl.toString(), QDateTime::currentDateTime()); + addHistoryEntry(item); + + // Add item to completion object + QString _url(url); + _url.remove(QRegExp("^http://|/$")); + m_completion->addItem(_url); +} + + +void HistoryManager::setHistory(const QList &history, bool loadedAndSorted) +{ + m_history = history; + + // verify that it is sorted by date + if (!loadedAndSorted) + qSort(m_history.begin(), m_history.end()); + + checkForExpired(); + + if (loadedAndSorted) + { + m_lastSavedUrl = m_history.value(0).url; + } + else + { + m_lastSavedUrl.clear(); + m_saveTimer->changeOccurred(); + } + emit historyReset(); +} + + +HistoryModel *HistoryManager::historyModel() const +{ + return m_historyModel; +} + + +HistoryFilterModel *HistoryManager::historyFilterModel() const +{ + return m_historyFilterModel; +} + + +HistoryTreeModel *HistoryManager::historyTreeModel() const +{ + return m_historyTreeModel; +} + + +void HistoryManager::checkForExpired() +{ + if (m_historyLimit < 0 || m_history.isEmpty()) + return; + + QDateTime now = QDateTime::currentDateTime(); + int nextTimeout = 0; + + while (!m_history.isEmpty()) + { + QDateTime checkForExpired = m_history.last().dateTime; + checkForExpired.setDate(checkForExpired.date().addDays(m_historyLimit)); + if (now.daysTo(checkForExpired) > 7) + { + // check at most in a week to prevent int overflows on the timer + nextTimeout = 7 * 86400; + } + else + { + nextTimeout = now.secsTo(checkForExpired); + } + if (nextTimeout > 0) + break; + HistoryItem item = m_history.takeLast(); + // remove from saved file also + m_lastSavedUrl.clear(); + emit entryRemoved(item); + } + + if (nextTimeout > 0) + m_expiredTimer.start(nextTimeout * 1000); +} + + +void HistoryManager::addHistoryEntry(const HistoryItem &item) +{ + QWebSettings *globalSettings = QWebSettings::globalSettings(); + if (globalSettings->testAttribute(QWebSettings::PrivateBrowsingEnabled)) + return; + + m_history.prepend(item); + emit entryAdded(item); + + if (m_history.count() == 1) + checkForExpired(); +} + + +void HistoryManager::updateHistoryEntry(const KUrl &url, const QString &title) +{ + for (int i = 0; i < m_history.count(); ++i) + { + if (url == m_history.at(i).url) + { + m_history[i].title = title; + m_saveTimer->changeOccurred(); + if (m_lastSavedUrl.isEmpty()) + m_lastSavedUrl = m_history.at(i).url; + emit entryUpdated(i); + break; + } + } +} + + +void HistoryManager::removeHistoryEntry(const HistoryItem &item) +{ + m_lastSavedUrl.clear(); + m_history.removeOne(item); + emit entryRemoved(item); +} + + +void HistoryManager::removeHistoryEntry(const KUrl &url, const QString &title) +{ + for (int i = 0; i < m_history.count(); ++i) + { + if (url == m_history.at(i).url + && (title.isEmpty() || title == m_history.at(i).title)) + { + removeHistoryEntry(m_history.at(i)); + break; + } + } + + // Remove item from completion object + QString _url = url.path(); + _url.remove(QRegExp("^http://|/$")); + m_completion->removeItem(_url); +} + + +int HistoryManager::historyLimit() const +{ + return m_historyLimit; +} + + +void HistoryManager::setHistoryLimit(int limit) +{ + if (m_historyLimit == limit) + return; + m_historyLimit = limit; + checkForExpired(); + m_saveTimer->changeOccurred(); +} + + +void HistoryManager::clear() +{ + m_history.clear(); + m_lastSavedUrl.clear(); + m_saveTimer->changeOccurred(); + m_saveTimer->saveIfNeccessary(); + historyReset(); +} + + +void HistoryManager::loadSettings() +{ + int historyExpire = ReKonfig::expireHistory(); + int days; + switch (historyExpire) + { + case 0: days = 1; break; + case 1: days = 7; break; + case 2: days = 14; break; + case 3: days = 30; break; + case 4: days = 365; break; + case 5: days = -1; break; + default: days = -1; + } + m_historyLimit = days; +} + + +void HistoryManager::load() +{ + loadSettings(); + + QString historyFilePath = KStandardDirs::locateLocal("appdata" , "history"); + QFile historyFile(historyFilePath); + if (!historyFile.exists()) + return; + if (!historyFile.open(QFile::ReadOnly)) + { + kWarning() << "Unable to open history file" << historyFile.fileName(); + return; + } + + QList list; + QDataStream in(&historyFile); + // Double check that the history file is sorted as it is read in + bool needToSort = false; + HistoryItem lastInsertedItem; + QByteArray data; + QDataStream stream; + QBuffer buffer; + stream.setDevice(&buffer); + while (!historyFile.atEnd()) + { + in >> data; + buffer.close(); + buffer.setBuffer(&data); + buffer.open(QIODevice::ReadOnly); + quint32 ver; + stream >> ver; + if (ver != HISTORY_VERSION) + continue; + HistoryItem item; + stream >> item.url; + stream >> item.dateTime; + stream >> item.title; + + if (!item.dateTime.isValid()) + continue; + + if (item == lastInsertedItem) + { + if (lastInsertedItem.title.isEmpty() && !list.isEmpty()) + list[0].title = item.title; + continue; + } + + if (!needToSort && !list.isEmpty() && lastInsertedItem < item) + needToSort = true; + + list.prepend(item); + lastInsertedItem = item; + + // Add item to completion object + QString _url = item.url; + _url.remove(QRegExp("^http://|/$")); + m_completion->addItem(_url); + } + if (needToSort) + qSort(list.begin(), list.end()); + + setHistory(list, true); + + // If we had to sort re-write the whole history sorted + if (needToSort) + { + m_lastSavedUrl.clear(); + m_saveTimer->changeOccurred(); + } +} + + +void HistoryManager::save() +{ + bool saveAll = m_lastSavedUrl.isEmpty(); + int first = m_history.count() - 1; + if (!saveAll) + { + // find the first one to save + for (int i = 0; i < m_history.count(); ++i) + { + if (m_history.at(i).url == m_lastSavedUrl) + { + first = i - 1; + break; + } + } + } + if (first == m_history.count() - 1) + saveAll = true; + + QString historyFilePath = KStandardDirs::locateLocal("appdata" , "history"); + QFile historyFile(historyFilePath); + + // When saving everything use a temporary file to prevent possible data loss. + QTemporaryFile tempFile; + tempFile.setAutoRemove(false); + bool open = false; + if (saveAll) + { + open = tempFile.open(); + } + else + { + open = historyFile.open(QFile::Append); + } + + if (!open) + { + kWarning() << "Unable to open history file for saving" + << (saveAll ? tempFile.fileName() : historyFile.fileName()); + return; + } + + QDataStream out(saveAll ? &tempFile : &historyFile); + for (int i = first; i >= 0; --i) + { + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + HistoryItem item = m_history.at(i); + stream << HISTORY_VERSION << item.url << item.dateTime << item.title; + out << data; + } + tempFile.close(); + + if (saveAll) + { + if (historyFile.exists() && !historyFile.remove()) + { + kWarning() << "History: error removing old history." << historyFile.errorString(); + } + if (!tempFile.rename(historyFile.fileName())) + { + kWarning() << "History: error moving new history over old." << tempFile.errorString() << historyFile.fileName(); + } + } + m_lastSavedUrl = m_history.value(0).url; +} + + +KCompletion * HistoryManager::completionObject() const +{ + return m_completion; +} diff --git a/src/history/history.h b/src/history/history.h new file mode 100644 index 00000000..2ccf8cb4 --- /dev/null +++ b/src/history/history.h @@ -0,0 +1,164 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008 Benjamin C. Meyer +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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 + + +// Local Includes +#include "modelmenu.h" + +// KDE Includes +#include + +// Qt Includes +#include +#include +#include +#include +#include +#include + + +/** + * Elements in this class represent an history item + * + */ +class HistoryItem +{ +public: + HistoryItem() {} + explicit HistoryItem(const QString &u, + const QDateTime &d = QDateTime(), + const QString &t = QString() + ) + : title(t), url(u), dateTime(d) {} + + inline bool operator==(const HistoryItem &other) const + { + return other.title == title + && other.url == url && other.dateTime == dateTime; + } + + // history is sorted in reverse + inline bool operator <(const HistoryItem &other) const + { + return dateTime > other.dateTime; + } + + QString title; + QString url; + QDateTime dateTime; +}; + + + +// --------------------------------------------------------------------------------------------------------------- + + +// Forward Declarations +class AutoSaver; +class HistoryModel; +class HistoryFilterModel; +class HistoryTreeModel; + +class KCompletion; + + +/** + * THE History Manager: + * It manages rekonq history + * + */ +class HistoryManager : public QWebHistoryInterface +{ + Q_OBJECT + Q_PROPERTY(int historyLimit READ historyLimit WRITE setHistoryLimit) + +signals: + void historyReset(); + void entryAdded(const HistoryItem &item); + void entryRemoved(const HistoryItem &item); + void entryUpdated(int offset); + +public: + HistoryManager(QObject *parent = 0); + ~HistoryManager(); + + bool historyContains(const QString &url) const; + void addHistoryEntry(const QString &url); + void updateHistoryEntry(const KUrl &url, const QString &title); + void removeHistoryEntry(const KUrl &url, const QString &title = QString()); + + int historyLimit() const; + void setHistoryLimit(int limit); + + QList history() const; + void setHistory(const QList &history, bool loadedAndSorted = false); + + // History manager keeps around these models for use by the completer and other classes + HistoryModel *historyModel() const; + HistoryFilterModel *historyFilterModel() const; + HistoryTreeModel *historyTreeModel() const; + + /** + * @returns the KCompletion object. + */ + KCompletion *completionObject() const; + +public slots: + void clear(); + void loadSettings(); + +private slots: + void save(); + void checkForExpired(); + +protected: + void addHistoryEntry(const HistoryItem &item); + void removeHistoryEntry(const HistoryItem &item); + +private: + void load(); + + AutoSaver *m_saveTimer; + int m_historyLimit; + QTimer m_expiredTimer; + QList m_history; + QString m_lastSavedUrl; + + HistoryModel *m_historyModel; + HistoryFilterModel *m_historyFilterModel; + HistoryTreeModel *m_historyTreeModel; + + // the completion object we sync with + KCompletion *m_completion; +}; + + +#endif // HISTORY_H diff --git a/src/history/historymodels.cpp b/src/history/historymodels.cpp new file mode 100644 index 00000000..cf8d1aed --- /dev/null +++ b/src/history/historymodels.cpp @@ -0,0 +1,971 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008 Benjamin C. Meyer +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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 . +* +* ============================================================ */ + + +// Self Includes +#include "historymodels.h" +#include "historymodels.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "application.h" + +// KDE Includes +#include +#include + +// Qt Includes +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// generic algorithms +#include + + +HistoryModel::HistoryModel(HistoryManager *history, QObject *parent) + : QAbstractTableModel(parent) + , m_history(history) +{ + Q_ASSERT(m_history); + connect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset())); + connect(m_history, SIGNAL(entryRemoved(const HistoryItem &)), this, SLOT(historyReset())); + connect(m_history, SIGNAL(entryAdded(const HistoryItem &)), this, SLOT(entryAdded())); + connect(m_history, SIGNAL(entryUpdated(int)), this, SLOT(entryUpdated(int))); +} + + +void HistoryModel::historyReset() +{ + reset(); +} + + +void HistoryModel::entryAdded() +{ + beginInsertRows(QModelIndex(), 0, 0); + endInsertRows(); +} + + +void HistoryModel::entryUpdated(int offset) +{ + QModelIndex idx = index(offset, 0); + emit dataChanged(idx, idx); +} + + +QVariant HistoryModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal + && role == Qt::DisplayRole) + { + switch (section) + { + case 0: return i18n("Title"); + case 1: return i18n("Address"); + } + } + return QAbstractTableModel::headerData(section, orientation, role); +} + + +QVariant HistoryModel::data(const QModelIndex &index, int role) const +{ + QList lst = m_history->history(); + if (index.row() < 0 || index.row() >= lst.size()) + return QVariant(); + + const HistoryItem &item = lst.at(index.row()); + switch (role) + { + case DateTimeRole: + return item.dateTime; + case DateRole: + return item.dateTime.date(); + case UrlRole: + return QUrl(item.url); + case UrlStringRole: + return item.url; + case Qt::DisplayRole: + case Qt::EditRole: + { + switch (index.column()) + { + case 0: + // when there is no title try to generate one from the url + if (item.title.isEmpty()) + { + QString page = QFileInfo(QUrl(item.url).path()).fileName(); + if (!page.isEmpty()) + return page; + return item.url; + } + return item.title; + case 1: + return item.url; + } + } + case Qt::DecorationRole: + if (index.column() == 0) + { + return Application::icon(item.url); + } + } + return QVariant(); +} + + +int HistoryModel::columnCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : 2; +} + + +int HistoryModel::rowCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : m_history->history().count(); +} + + +bool HistoryModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (parent.isValid()) + return false; + int lastRow = row + count - 1; + beginRemoveRows(parent, row, lastRow); + QList lst = m_history->history(); + for (int i = lastRow; i >= row; --i) + lst.removeAt(i); + disconnect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset())); + m_history->setHistory(lst); + connect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset())); + endRemoveRows(); + return true; +} + + + +// ----------------------------------------------------------------------------------------------- + + +#define MOVEDROWS 20 + + +/* + Maps the first bunch of items of the source model to the root +*/ +HistoryMenuModel::HistoryMenuModel(HistoryTreeModel *sourceModel, QObject *parent) + : QAbstractProxyModel(parent) + , m_treeModel(sourceModel) +{ + setSourceModel(sourceModel); +} + + +int HistoryMenuModel::bumpedRows() const +{ + QModelIndex first = m_treeModel->index(0, 0); + if (!first.isValid()) + return 0; + return qMin(m_treeModel->rowCount(first), MOVEDROWS); +} + + +int HistoryMenuModel::columnCount(const QModelIndex &parent) const +{ + return m_treeModel->columnCount(mapToSource(parent)); +} + + +int HistoryMenuModel::rowCount(const QModelIndex &parent) const +{ + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + { + int folders = sourceModel()->rowCount(); + int bumpedItems = bumpedRows(); + if (bumpedItems <= MOVEDROWS + && bumpedItems == sourceModel()->rowCount(sourceModel()->index(0, 0))) + --folders; + return bumpedItems + folders; + } + + if (parent.internalId() == -1) + { + if (parent.row() < bumpedRows()) + return 0; + } + + QModelIndex idx = mapToSource(parent); + int defaultCount = sourceModel()->rowCount(idx); + if (idx == sourceModel()->index(0, 0)) + return defaultCount - bumpedRows(); + return defaultCount; +} + + +QModelIndex HistoryMenuModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + // currently not used or autotested + Q_ASSERT(false); + int sr = m_treeModel->mapToSource(sourceIndex).row(); + return createIndex(sourceIndex.row(), sourceIndex.column(), sr); +} + + +QModelIndex HistoryMenuModel::mapToSource(const QModelIndex &proxyIndex) const +{ + if (!proxyIndex.isValid()) + return QModelIndex(); + + if (proxyIndex.internalId() == -1) + { + int bumpedItems = bumpedRows(); + if (proxyIndex.row() < bumpedItems) + return m_treeModel->index(proxyIndex.row(), proxyIndex.column(), m_treeModel->index(0, 0)); + if (bumpedItems <= MOVEDROWS && bumpedItems == sourceModel()->rowCount(m_treeModel->index(0, 0))) + --bumpedItems; + return m_treeModel->index(proxyIndex.row() - bumpedItems, proxyIndex.column()); + } + + QModelIndex historyIndex = m_treeModel->sourceModel()->index(proxyIndex.internalId(), proxyIndex.column()); + QModelIndex treeIndex = m_treeModel->mapFromSource(historyIndex); + return treeIndex; +} + + +QModelIndex HistoryMenuModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row < 0 + || column < 0 || column >= columnCount(parent) + || parent.column() > 0) + return QModelIndex(); + if (!parent.isValid()) + return createIndex(row, column, -1); + + QModelIndex treeIndexParent = mapToSource(parent); + + int bumpedItems = 0; + if (treeIndexParent == m_treeModel->index(0, 0)) + bumpedItems = bumpedRows(); + QModelIndex treeIndex = m_treeModel->index(row + bumpedItems, column, treeIndexParent); + QModelIndex historyIndex = m_treeModel->mapToSource(treeIndex); + int historyRow = historyIndex.row(); + if (historyRow == -1) + historyRow = treeIndex.row(); + return createIndex(row, column, historyRow); +} + + +QModelIndex HistoryMenuModel::parent(const QModelIndex &index) const +{ + int offset = index.internalId(); + if (offset == -1 || !index.isValid()) + return QModelIndex(); + + QModelIndex historyIndex = m_treeModel->sourceModel()->index(index.internalId(), 0); + QModelIndex treeIndex = m_treeModel->mapFromSource(historyIndex); + QModelIndex treeIndexParent = treeIndex.parent(); + + int sr = m_treeModel->mapToSource(treeIndexParent).row(); + int bumpedItems = bumpedRows(); + if (bumpedItems <= MOVEDROWS && bumpedItems == sourceModel()->rowCount(sourceModel()->index(0, 0))) + --bumpedItems; + return createIndex(bumpedItems + treeIndexParent.row(), treeIndexParent.column(), sr); +} + + +// -------------------------------------------------------------------------------------------------------------- + + +TreeProxyModel::TreeProxyModel(QObject *parent) : QSortFilterProxyModel(parent) +{ + setSortRole(HistoryModel::DateTimeRole); + setFilterCaseSensitivity(Qt::CaseInsensitive); +} + + +bool TreeProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + if (!source_parent.isValid()) + return true; + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); +} + + +// ------------------------------------------------------------------------------------------------------------- + + +HistoryFilterModel::HistoryFilterModel(QAbstractItemModel *sourceModel, QObject *parent) + : QAbstractProxyModel(parent), + m_loaded(false) +{ + setSourceModel(sourceModel); +} + + +int HistoryFilterModel::historyLocation(const QString &url) const +{ + load(); + if (!m_historyHash.contains(url)) + return 0; + return sourceModel()->rowCount() - m_historyHash.value(url); +} + + +QVariant HistoryFilterModel::data(const QModelIndex &index, int role) const +{ + return QAbstractProxyModel::data(index, role); +} + + +void HistoryFilterModel::setSourceModel(QAbstractItemModel *newSourceModel) +{ + if (sourceModel()) + { + disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(dataChanged(const QModelIndex &, const QModelIndex &))); + disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); + disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + } + + QAbstractProxyModel::setSourceModel(newSourceModel); + + if (sourceModel()) + { + m_loaded = false; + connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(sourceDataChanged(const QModelIndex &, const QModelIndex &))); + connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); + connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + } +} + + +void HistoryFilterModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +{ + emit dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight)); +} + + +QVariant HistoryFilterModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + return sourceModel()->headerData(section, orientation, role); +} + + +void HistoryFilterModel::sourceReset() +{ + m_loaded = false; + reset(); +} + + +int HistoryFilterModel::rowCount(const QModelIndex &parent) const +{ + load(); + if (parent.isValid()) + return 0; + return m_historyHash.count(); +} + + +int HistoryFilterModel::columnCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : 2; +} + + +QModelIndex HistoryFilterModel::mapToSource(const QModelIndex &proxyIndex) const +{ + load(); + int sourceRow = sourceModel()->rowCount() - proxyIndex.internalId(); + return sourceModel()->index(sourceRow, proxyIndex.column()); +} + + +QModelIndex HistoryFilterModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + load(); + QString url = sourceIndex.data(HistoryModel::UrlStringRole).toString(); + if (!m_historyHash.contains(url)) + return QModelIndex(); + + // This can be done in a binary search, but we can't use qBinary find + // because it can't take: qBinaryFind(m_sourceRow.end(), m_sourceRow.begin(), v); + // so if this is a performance bottlneck then convert to binary search, until then + // the cleaner/easier to read code wins the day. + int realRow = -1; + int sourceModelRow = sourceModel()->rowCount() - sourceIndex.row(); + + for (int i = 0; i < m_sourceRow.count(); ++i) + { + if (m_sourceRow.at(i) == sourceModelRow) + { + realRow = i; + break; + } + } + if (realRow == -1) + return QModelIndex(); + + return createIndex(realRow, sourceIndex.column(), sourceModel()->rowCount() - sourceIndex.row()); +} + + +QModelIndex HistoryFilterModel::index(int row, int column, const QModelIndex &parent) const +{ + load(); + if (row < 0 || row >= rowCount(parent) + || column < 0 || column >= columnCount(parent)) + return QModelIndex(); + + return createIndex(row, column, m_sourceRow[row]); +} + + +QModelIndex HistoryFilterModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + + +void HistoryFilterModel::load() const +{ + if (m_loaded) + return; + m_sourceRow.clear(); + m_historyHash.clear(); + m_historyHash.reserve(sourceModel()->rowCount()); + for (int i = 0; i < sourceModel()->rowCount(); ++i) + { + QModelIndex idx = sourceModel()->index(i, 0); + QString url = idx.data(HistoryModel::UrlStringRole).toString(); + if (!m_historyHash.contains(url)) + { + m_sourceRow.append(sourceModel()->rowCount() - i); + m_historyHash[url] = sourceModel()->rowCount() - i; + } + } + m_loaded = true; +} + + +void HistoryFilterModel::sourceRowsInserted(const QModelIndex &parent, int start, int end) +{ + Q_ASSERT(start == end && start == 0); + Q_UNUSED(end); + if (!m_loaded) + return; + QModelIndex idx = sourceModel()->index(start, 0, parent); + QString url = idx.data(HistoryModel::UrlStringRole).toString(); + if (m_historyHash.contains(url)) + { + int sourceRow = sourceModel()->rowCount() - m_historyHash[url]; + int realRow = mapFromSource(sourceModel()->index(sourceRow, 0)).row(); + beginRemoveRows(QModelIndex(), realRow, realRow); + m_sourceRow.removeAt(realRow); + m_historyHash.remove(url); + endRemoveRows(); + } + beginInsertRows(QModelIndex(), 0, 0); + m_historyHash.insert(url, sourceModel()->rowCount() - start); + m_sourceRow.insert(0, sourceModel()->rowCount()); + endInsertRows(); +} + + +void HistoryFilterModel::sourceRowsRemoved(const QModelIndex &, int start, int end) +{ + Q_UNUSED(start); + Q_UNUSED(end); + sourceReset(); +} + + +/* + Removing a continuous block of rows will remove filtered rows too as this is + the users intention. +*/ +bool HistoryFilterModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (row < 0 || count <= 0 || row + count > rowCount(parent) || parent.isValid()) + return false; + int lastRow = row + count - 1; + disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + beginRemoveRows(parent, row, lastRow); + int oldCount = rowCount(); + int start = sourceModel()->rowCount() - m_sourceRow.value(row); + int end = sourceModel()->rowCount() - m_sourceRow.value(lastRow); + sourceModel()->removeRows(start, end - start + 1); + endRemoveRows(); + connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + m_loaded = false; + if (oldCount - count != rowCount()) + reset(); + return true; +} + + +// ------------------------------------------------------------------------------------------------------ + + +HistoryCompletionModel::HistoryCompletionModel(QObject *parent) + : QAbstractProxyModel(parent) +{ +} + + +QVariant HistoryCompletionModel::data(const QModelIndex &index, int role) const +{ + if (sourceModel() + && (role == Qt::EditRole || role == Qt::DisplayRole) + && index.isValid()) + { + QModelIndex idx = mapToSource(index); + idx = idx.sibling(idx.row(), 1); + QString urlString = idx.data(HistoryModel::UrlStringRole).toString(); + if (index.row() % 2) + { + QUrl url = urlString; + QString s = url.toString(QUrl::RemoveScheme + | QUrl::RemoveUserInfo + | QUrl::StripTrailingSlash); + return s.mid(2); // strip // from the front + } + return urlString; + } + return QAbstractProxyModel::data(index, role); +} + + +int HistoryCompletionModel::rowCount(const QModelIndex &parent) const +{ + return (parent.isValid() || !sourceModel()) ? 0 : sourceModel()->rowCount(parent) * 2; +} + + +int HistoryCompletionModel::columnCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : 1; +} + + +QModelIndex HistoryCompletionModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + int row = sourceIndex.row() * 2; + return index(row, sourceIndex.column()); +} + + +QModelIndex HistoryCompletionModel::mapToSource(const QModelIndex &proxyIndex) const +{ + if (!sourceModel()) + return QModelIndex(); + int row = proxyIndex.row() / 2; + return sourceModel()->index(row, proxyIndex.column()); +} + + +QModelIndex HistoryCompletionModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row < 0 || row >= rowCount(parent) + || column < 0 || column >= columnCount(parent)) + return QModelIndex(); + return createIndex(row, column, 0); +} + + +QModelIndex HistoryCompletionModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + + +void HistoryCompletionModel::setSourceModel(QAbstractItemModel *newSourceModel) +{ + if (sourceModel()) + { + disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceReset())); + } + + QAbstractProxyModel::setSourceModel(newSourceModel); + + if (newSourceModel) + { + connect(newSourceModel, SIGNAL(modelReset()), this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceReset())); + } + + reset(); +} + + +void HistoryCompletionModel::sourceReset() +{ + reset(); +} + + +// ------------------------------------------------------------------------------------------------------ + + +HistoryTreeModel::HistoryTreeModel(QAbstractItemModel *sourceModel, QObject *parent) + : QAbstractProxyModel(parent) +{ + setSourceModel(sourceModel); +} + + +QVariant HistoryTreeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + return sourceModel()->headerData(section, orientation, role); +} + + +QVariant HistoryTreeModel::data(const QModelIndex &index, int role) const +{ + if ((role == Qt::EditRole || role == Qt::DisplayRole)) + { + int start = index.internalId(); + if (start == 0) + { + int offset = sourceDateRow(index.row()); + if (index.column() == 0) + { + QModelIndex idx = sourceModel()->index(offset, 0); + QDate date = idx.data(HistoryModel::DateRole).toDate(); + if (date == QDate::currentDate()) + return i18n("Earlier Today"); + return date.toString(QLatin1String("dddd, MMMM d, yyyy")); + } + if (index.column() == 1) + { + return i18np("1 item", "%1 items", rowCount(index.sibling(index.row(), 0))); + } + } + } + if (role == Qt::DecorationRole && index.column() == 0 && !index.parent().isValid()) + return KIcon("view-history"); + if (role == HistoryModel::DateRole && index.column() == 0 && index.internalId() == 0) + { + int offset = sourceDateRow(index.row()); + QModelIndex idx = sourceModel()->index(offset, 0); + return idx.data(HistoryModel::DateRole); + } + + return QAbstractProxyModel::data(index, role); +} + + +int HistoryTreeModel::columnCount(const QModelIndex &parent) const +{ + return sourceModel()->columnCount(mapToSource(parent)); +} + + +int HistoryTreeModel::rowCount(const QModelIndex &parent) const +{ + if (parent.internalId() != 0 + || parent.column() > 0 + || !sourceModel()) + return 0; + + // row count OF dates + if (!parent.isValid()) + { + if (!m_sourceRowCache.isEmpty()) + return m_sourceRowCache.count(); + QDate currentDate; + int rows = 0; + int totalRows = sourceModel()->rowCount(); + + for (int i = 0; i < totalRows; ++i) + { + QDate rowDate = sourceModel()->index(i, 0).data(HistoryModel::DateRole).toDate(); + if (rowDate != currentDate) + { + m_sourceRowCache.append(i); + currentDate = rowDate; + ++rows; + } + } + Q_ASSERT(m_sourceRowCache.count() == rows); + return rows; + } + + // row count FOR a date + int start = sourceDateRow(parent.row()); + int end = sourceDateRow(parent.row() + 1); + return (end - start); +} + + +// Translate the top level date row into the offset where that date starts +int HistoryTreeModel::sourceDateRow(int row) const +{ + if (row <= 0) + return 0; + + if (m_sourceRowCache.isEmpty()) + rowCount(QModelIndex()); + + if (row >= m_sourceRowCache.count()) + { + if (!sourceModel()) + return 0; + return sourceModel()->rowCount(); + } + return m_sourceRowCache.at(row); +} + + +QModelIndex HistoryTreeModel::mapToSource(const QModelIndex &proxyIndex) const +{ + int offset = proxyIndex.internalId(); + if (offset == 0) + return QModelIndex(); + int startDateRow = sourceDateRow(offset - 1); + return sourceModel()->index(startDateRow + proxyIndex.row(), proxyIndex.column()); +} + + +QModelIndex HistoryTreeModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row < 0 + || column < 0 || column >= columnCount(parent) + || parent.column() > 0) + return QModelIndex(); + + if (!parent.isValid()) + return createIndex(row, column, 0); + return createIndex(row, column, parent.row() + 1); +} + + +QModelIndex HistoryTreeModel::parent(const QModelIndex &index) const +{ + int offset = index.internalId(); + if (offset == 0 || !index.isValid()) + return QModelIndex(); + return createIndex(offset - 1, 0, 0); +} + + +bool HistoryTreeModel::hasChildren(const QModelIndex &parent) const +{ + QModelIndex grandparent = parent.parent(); + if (!grandparent.isValid()) + return true; + return false; +} + + +Qt::ItemFlags HistoryTreeModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled; +} + + +bool HistoryTreeModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (row < 0 || count <= 0 || row + count > rowCount(parent)) + return false; + + if (parent.isValid()) + { + // removing pages + int offset = sourceDateRow(parent.row()); + return sourceModel()->removeRows(offset + row, count); + } + else + { + // removing whole dates + for (int i = row + count - 1; i >= row; --i) + { + QModelIndex dateParent = index(i, 0); + int offset = sourceDateRow(dateParent.row()); + if (!sourceModel()->removeRows(offset, rowCount(dateParent))) + return false; + } + } + return true; +} + + +void HistoryTreeModel::setSourceModel(QAbstractItemModel *newSourceModel) +{ + if (sourceModel()) + { + disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(sourceReset())); + disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); + disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + } + + QAbstractProxyModel::setSourceModel(newSourceModel); + + if (newSourceModel) + { + connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(sourceReset())); + connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); + connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), + this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); + } + + reset(); +} + + +void HistoryTreeModel::sourceReset() +{ + m_sourceRowCache.clear(); + reset(); +} + + +void HistoryTreeModel::sourceRowsInserted(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(parent); // Avoid warnings when compiling release + Q_ASSERT(!parent.isValid()); + if (start != 0 || start != end) + { + m_sourceRowCache.clear(); + reset(); + return; + } + + m_sourceRowCache.clear(); + QModelIndex treeIndex = mapFromSource(sourceModel()->index(start, 0)); + QModelIndex treeParent = treeIndex.parent(); + if (rowCount(treeParent) == 1) + { + beginInsertRows(QModelIndex(), 0, 0); + endInsertRows(); + } + else + { + beginInsertRows(treeParent, treeIndex.row(), treeIndex.row()); + endInsertRows(); + } +} + + +QModelIndex HistoryTreeModel::mapFromSource(const QModelIndex &sourceIndex) const +{ + if (!sourceIndex.isValid()) + return QModelIndex(); + + if (m_sourceRowCache.isEmpty()) + rowCount(QModelIndex()); + + QList::iterator it; + it = qLowerBound(m_sourceRowCache.begin(), m_sourceRowCache.end(), sourceIndex.row()); + if (*it != sourceIndex.row()) + --it; + + int dateRow = qMax(0, it - m_sourceRowCache.begin()); + int row = sourceIndex.row() - m_sourceRowCache.at(dateRow); + return createIndex(row, sourceIndex.column(), dateRow + 1); +} + + +void HistoryTreeModel::sourceRowsRemoved(const QModelIndex &parent, int start, int end) +{ + Q_UNUSED(parent); // Avoid warnings when compiling release + Q_ASSERT(!parent.isValid()); + if (m_sourceRowCache.isEmpty()) + return; + for (int i = end; i >= start;) + { + QList::iterator it; + it = qLowerBound(m_sourceRowCache.begin(), m_sourceRowCache.end(), i); + // playing it safe + if (it == m_sourceRowCache.end()) + { + m_sourceRowCache.clear(); + reset(); + return; + } + + if (*it != i) + --it; + int row = qMax(0, it - m_sourceRowCache.begin()); + int offset = m_sourceRowCache[row]; + QModelIndex dateParent = index(row, 0); + // If we can remove all the rows in the date do that and skip over them + int rc = rowCount(dateParent); + if (i - rc + 1 == offset && start <= i - rc + 1) + { + beginRemoveRows(QModelIndex(), row, row); + m_sourceRowCache.removeAt(row); + i -= rc + 1; + } + else + { + beginRemoveRows(dateParent, i - offset, i - offset); + ++row; + --i; + } + for (int j = row; j < m_sourceRowCache.count(); ++j) + --m_sourceRowCache[j]; + endRemoveRows(); + } +} diff --git a/src/history/historymodels.h b/src/history/historymodels.h new file mode 100644 index 00000000..93e72c6a --- /dev/null +++ b/src/history/historymodels.h @@ -0,0 +1,256 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved +* Copyright (C) 2008 Benjamin C. Meyer +* Copyright (C) 2008-2009 by Andrea Diamantini +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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 HISTORYMODELS_H +#define HISTORYMODELS_H + + +// Local Includes +#include "history.h" +#include "modelmenu.h" + +// KDE Includes +#include + +// Qt Includes +#include +#include +#include +#include +#include +#include + + +class HistoryModel : public QAbstractTableModel +{ + Q_OBJECT + +public slots: + void historyReset(); + void entryAdded(); + void entryUpdated(int offset); + +public: + enum Roles + { + DateRole = Qt::UserRole + 1, + DateTimeRole = Qt::UserRole + 2, + UrlRole = Qt::UserRole + 3, + UrlStringRole = Qt::UserRole + 4 + }; + + explicit HistoryModel(HistoryManager *history, QObject *parent = 0); + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + +private: + HistoryManager *m_history; +}; + + +// ---------------------------------------------------------------------------------------------------- + +/** + * Proxy model that will remove any duplicate entries. + * Both m_sourceRow and m_historyHash store their offsets not from + * the front of the list, but as offsets from the back. + * + */ + +class HistoryFilterModel : public QAbstractProxyModel +{ + Q_OBJECT + +public: + explicit HistoryFilterModel(QAbstractItemModel *sourceModel, QObject *parent = 0); + + inline bool historyContains(const QString &url) const + { + load(); return m_historyHash.contains(url); + } + int historyLocation(const QString &url) const; + + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + void setSourceModel(QAbstractItemModel *sourceModel); + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const; + QModelIndex parent(const QModelIndex& index = QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +private slots: + void sourceReset(); + void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void sourceRowsInserted(const QModelIndex &parent, int start, int end); + void sourceRowsRemoved(const QModelIndex &, int, int); + +private: + void load() const; + + mutable QList m_sourceRow; + mutable QHash m_historyHash; + mutable bool m_loaded; +}; + + +// ---------------------------------------------------------------------------------------------------------------------- + +/** + * The history menu + * - Removes the first twenty entries and puts them as children of the top level. + * - If there are less then twenty entries then the first folder is also removed. + * + * The mapping is done by knowing that HistoryTreeModel is over a table + * We store that row offset in our index's private data. + * + */ + +class HistoryMenuModel : public QAbstractProxyModel +{ + Q_OBJECT + +public: + explicit HistoryMenuModel(HistoryTreeModel *sourceModel, QObject *parent = 0); + + int columnCount(const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QModelIndex mapFromSource(const QModelIndex & sourceIndex) const; + QModelIndex mapToSource(const QModelIndex & proxyIndex) const; + QModelIndex index(int, int, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index = QModelIndex()) const; + + int bumpedRows() const; + +private: + HistoryTreeModel *m_treeModel; +}; + + +// ---------------------------------------------------------------------------------------- + +/** + * Proxy model for the history model that + * exposes each url http://www.foo.com and + * it url starting at the host www.foo.com + * + */ + +class HistoryCompletionModel : public QAbstractProxyModel +{ + Q_OBJECT + +public: + HistoryCompletionModel(QObject *parent = 0); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const; + QModelIndex parent(const QModelIndex& index = QModelIndex()) const; + void setSourceModel(QAbstractItemModel *sourceModel); + +private slots: + void sourceReset(); + +}; + + +// --------------------------------------------------------------------------------------- + +/** + * Proxy model for the history model that converts the list + * into a tree, one top level node per day. + * Used in the HistoryDialog. + * + */ + +class HistoryTreeModel : public QAbstractProxyModel +{ + Q_OBJECT + +public: + explicit HistoryTreeModel(QAbstractItemModel *sourceModel, QObject *parent = 0); + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + int columnCount(const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; + QModelIndex mapToSource(const QModelIndex &proxyIndex) const; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &index = QModelIndex()) const; + bool hasChildren(const QModelIndex &parent = QModelIndex()) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + void setSourceModel(QAbstractItemModel *sourceModel); + +private slots: + void sourceReset(); + void sourceRowsInserted(const QModelIndex &parent, int start, int end); + void sourceRowsRemoved(const QModelIndex &parent, int start, int end); + +private: + int sourceDateRow(int row) const; + mutable QList m_sourceRowCache; + +}; + + +// ----------------------------------------------------------------------------------------------------------------- + +/** + * A modified QSortFilterProxyModel that always accepts + * the root nodes in the tree + * so filtering is only done on the children. + * + * Used in the HistoryDialog. + * + */ + +class TreeProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + TreeProxyModel(QObject *parent = 0); + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; +}; + + +#endif // HISTORYMODELS_H diff --git a/src/history/historypanel.cpp b/src/history/historypanel.cpp new file mode 100644 index 00000000..42497f9e --- /dev/null +++ b/src/history/historypanel.cpp @@ -0,0 +1,101 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009 by Andrea Diamantini * +* Copyright (C) 2009 by Domrachev Alexandr +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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 . +* +* ============================================================ */ + + +// Self Includes +#include "historypanel.h" +#include "historypanel.moc" + +// Qt Includes +#include +#include +#include + +// KDE Includes +#include +#include + + +HistoryPanel::HistoryPanel(QWidget *parent) + : QWidget(parent) + , m_historyTreeView(new QTreeView) + , m_treeProxyModel(new TreeProxyModel(this)) +{ + m_historyTreeView->setUniformRowHeights(true); + m_historyTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_historyTreeView->setTextElideMode(Qt::ElideMiddle); + m_historyTreeView->setAlternatingRowColors(true); + + // add search bar + QHBoxLayout *hBoxLayout = new QHBoxLayout; + hBoxLayout->setContentsMargins(5, 0, 0, 0); + QLabel *searchLabel = new QLabel(i18n("Search:")); + hBoxLayout->addWidget(searchLabel); + KLineEdit *search = new KLineEdit; + search->setClearButtonShown(true); + hBoxLayout->addWidget(search); + QWidget *searchBar = new QWidget; + searchBar->setLayout(hBoxLayout); + + // setup view + QVBoxLayout *vBoxLayout = new QVBoxLayout; + vBoxLayout->setContentsMargins(0, 0, 0, 0); + vBoxLayout->addWidget(searchBar); + vBoxLayout->addWidget(m_historyTreeView); + setLayout(vBoxLayout); + + //- + HistoryManager *historyManager = Application::historyManager(); + QAbstractItemModel *model = historyManager->historyTreeModel(); + + m_treeProxyModel->setSourceModel(model); + m_historyTreeView->setModel(m_treeProxyModel); + m_historyTreeView->setExpanded(m_treeProxyModel->index(0, 0), true); + m_historyTreeView->header()->hideSection(1); + QFontMetrics fm(font()); + int header = fm.width(QLatin1Char('m')) * 40; + m_historyTreeView->header()->resizeSection(0, header); + + connect(search, SIGNAL(textChanged(QString)), m_treeProxyModel, SLOT(setFilterFixedString(QString))); + connect(m_historyTreeView, SIGNAL(activated(const QModelIndex&)), this, SLOT(open())); +} + + +HistoryPanel::~HistoryPanel() +{ + delete m_treeProxyModel; + delete m_historyTreeView; +} + + +void HistoryPanel::open() +{ + QModelIndex index = m_historyTreeView->currentIndex(); + if (!index.parent().isValid()) + return; + emit openUrl(index.data(HistoryModel::UrlRole).toUrl()); +} + diff --git a/src/history/historypanel.h b/src/history/historypanel.h new file mode 100644 index 00000000..083b2741 --- /dev/null +++ b/src/history/historypanel.h @@ -0,0 +1,64 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2009 by Andrea Diamantini * +* Copyright (C) 2009 by Domrachev Alexandr +* +* +* 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 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* 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 HISTORYPANEL_H +#define HISTORYPANEL_H + + +// Local Includes +#include "application.h" +#include "historymodels.h" + +// Qt Includes +#include +#include + +// Forward Declarations +class KUrl; + + +class HistoryPanel : public QWidget +{ +Q_OBJECT + +public: + explicit HistoryPanel(QWidget *parent = 0); + virtual ~HistoryPanel(); + +signals: + void openUrl(const KUrl &); + +private slots: + void open(); + +private: + QTreeView *m_historyTreeView; + TreeProxyModel *m_treeProxyModel; + +}; + +#endif // HISTORYPANEL_H diff --git a/src/historymodels.cpp b/src/historymodels.cpp deleted file mode 100644 index cf8d1aed..00000000 --- a/src/historymodels.cpp +++ /dev/null @@ -1,971 +0,0 @@ -/* ============================================================ -* -* This file is a part of the rekonq project -* -* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved -* Copyright (C) 2008 Benjamin C. Meyer -* Copyright (C) 2008-2009 by Andrea Diamantini -* -* -* 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 2 of -* the License or (at your option) version 3 or any later version -* accepted by the membership of KDE e.V. (or its successor approved -* by the membership of KDE e.V.), which shall act as a proxy -* defined in Section 14 of version 3 of the license. -* -* 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 . -* -* ============================================================ */ - - -// Self Includes -#include "historymodels.h" -#include "historymodels.moc" - -// Auto Includes -#include "rekonq.h" - -// Local Includes -#include "application.h" - -// KDE Includes -#include -#include - -// Qt Includes -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -// generic algorithms -#include - - -HistoryModel::HistoryModel(HistoryManager *history, QObject *parent) - : QAbstractTableModel(parent) - , m_history(history) -{ - Q_ASSERT(m_history); - connect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset())); - connect(m_history, SIGNAL(entryRemoved(const HistoryItem &)), this, SLOT(historyReset())); - connect(m_history, SIGNAL(entryAdded(const HistoryItem &)), this, SLOT(entryAdded())); - connect(m_history, SIGNAL(entryUpdated(int)), this, SLOT(entryUpdated(int))); -} - - -void HistoryModel::historyReset() -{ - reset(); -} - - -void HistoryModel::entryAdded() -{ - beginInsertRows(QModelIndex(), 0, 0); - endInsertRows(); -} - - -void HistoryModel::entryUpdated(int offset) -{ - QModelIndex idx = index(offset, 0); - emit dataChanged(idx, idx); -} - - -QVariant HistoryModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal - && role == Qt::DisplayRole) - { - switch (section) - { - case 0: return i18n("Title"); - case 1: return i18n("Address"); - } - } - return QAbstractTableModel::headerData(section, orientation, role); -} - - -QVariant HistoryModel::data(const QModelIndex &index, int role) const -{ - QList lst = m_history->history(); - if (index.row() < 0 || index.row() >= lst.size()) - return QVariant(); - - const HistoryItem &item = lst.at(index.row()); - switch (role) - { - case DateTimeRole: - return item.dateTime; - case DateRole: - return item.dateTime.date(); - case UrlRole: - return QUrl(item.url); - case UrlStringRole: - return item.url; - case Qt::DisplayRole: - case Qt::EditRole: - { - switch (index.column()) - { - case 0: - // when there is no title try to generate one from the url - if (item.title.isEmpty()) - { - QString page = QFileInfo(QUrl(item.url).path()).fileName(); - if (!page.isEmpty()) - return page; - return item.url; - } - return item.title; - case 1: - return item.url; - } - } - case Qt::DecorationRole: - if (index.column() == 0) - { - return Application::icon(item.url); - } - } - return QVariant(); -} - - -int HistoryModel::columnCount(const QModelIndex &parent) const -{ - return (parent.isValid()) ? 0 : 2; -} - - -int HistoryModel::rowCount(const QModelIndex &parent) const -{ - return (parent.isValid()) ? 0 : m_history->history().count(); -} - - -bool HistoryModel::removeRows(int row, int count, const QModelIndex &parent) -{ - if (parent.isValid()) - return false; - int lastRow = row + count - 1; - beginRemoveRows(parent, row, lastRow); - QList lst = m_history->history(); - for (int i = lastRow; i >= row; --i) - lst.removeAt(i); - disconnect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset())); - m_history->setHistory(lst); - connect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset())); - endRemoveRows(); - return true; -} - - - -// ----------------------------------------------------------------------------------------------- - - -#define MOVEDROWS 20 - - -/* - Maps the first bunch of items of the source model to the root -*/ -HistoryMenuModel::HistoryMenuModel(HistoryTreeModel *sourceModel, QObject *parent) - : QAbstractProxyModel(parent) - , m_treeModel(sourceModel) -{ - setSourceModel(sourceModel); -} - - -int HistoryMenuModel::bumpedRows() const -{ - QModelIndex first = m_treeModel->index(0, 0); - if (!first.isValid()) - return 0; - return qMin(m_treeModel->rowCount(first), MOVEDROWS); -} - - -int HistoryMenuModel::columnCount(const QModelIndex &parent) const -{ - return m_treeModel->columnCount(mapToSource(parent)); -} - - -int HistoryMenuModel::rowCount(const QModelIndex &parent) const -{ - if (parent.column() > 0) - return 0; - - if (!parent.isValid()) - { - int folders = sourceModel()->rowCount(); - int bumpedItems = bumpedRows(); - if (bumpedItems <= MOVEDROWS - && bumpedItems == sourceModel()->rowCount(sourceModel()->index(0, 0))) - --folders; - return bumpedItems + folders; - } - - if (parent.internalId() == -1) - { - if (parent.row() < bumpedRows()) - return 0; - } - - QModelIndex idx = mapToSource(parent); - int defaultCount = sourceModel()->rowCount(idx); - if (idx == sourceModel()->index(0, 0)) - return defaultCount - bumpedRows(); - return defaultCount; -} - - -QModelIndex HistoryMenuModel::mapFromSource(const QModelIndex &sourceIndex) const -{ - // currently not used or autotested - Q_ASSERT(false); - int sr = m_treeModel->mapToSource(sourceIndex).row(); - return createIndex(sourceIndex.row(), sourceIndex.column(), sr); -} - - -QModelIndex HistoryMenuModel::mapToSource(const QModelIndex &proxyIndex) const -{ - if (!proxyIndex.isValid()) - return QModelIndex(); - - if (proxyIndex.internalId() == -1) - { - int bumpedItems = bumpedRows(); - if (proxyIndex.row() < bumpedItems) - return m_treeModel->index(proxyIndex.row(), proxyIndex.column(), m_treeModel->index(0, 0)); - if (bumpedItems <= MOVEDROWS && bumpedItems == sourceModel()->rowCount(m_treeModel->index(0, 0))) - --bumpedItems; - return m_treeModel->index(proxyIndex.row() - bumpedItems, proxyIndex.column()); - } - - QModelIndex historyIndex = m_treeModel->sourceModel()->index(proxyIndex.internalId(), proxyIndex.column()); - QModelIndex treeIndex = m_treeModel->mapFromSource(historyIndex); - return treeIndex; -} - - -QModelIndex HistoryMenuModel::index(int row, int column, const QModelIndex &parent) const -{ - if (row < 0 - || column < 0 || column >= columnCount(parent) - || parent.column() > 0) - return QModelIndex(); - if (!parent.isValid()) - return createIndex(row, column, -1); - - QModelIndex treeIndexParent = mapToSource(parent); - - int bumpedItems = 0; - if (treeIndexParent == m_treeModel->index(0, 0)) - bumpedItems = bumpedRows(); - QModelIndex treeIndex = m_treeModel->index(row + bumpedItems, column, treeIndexParent); - QModelIndex historyIndex = m_treeModel->mapToSource(treeIndex); - int historyRow = historyIndex.row(); - if (historyRow == -1) - historyRow = treeIndex.row(); - return createIndex(row, column, historyRow); -} - - -QModelIndex HistoryMenuModel::parent(const QModelIndex &index) const -{ - int offset = index.internalId(); - if (offset == -1 || !index.isValid()) - return QModelIndex(); - - QModelIndex historyIndex = m_treeModel->sourceModel()->index(index.internalId(), 0); - QModelIndex treeIndex = m_treeModel->mapFromSource(historyIndex); - QModelIndex treeIndexParent = treeIndex.parent(); - - int sr = m_treeModel->mapToSource(treeIndexParent).row(); - int bumpedItems = bumpedRows(); - if (bumpedItems <= MOVEDROWS && bumpedItems == sourceModel()->rowCount(sourceModel()->index(0, 0))) - --bumpedItems; - return createIndex(bumpedItems + treeIndexParent.row(), treeIndexParent.column(), sr); -} - - -// -------------------------------------------------------------------------------------------------------------- - - -TreeProxyModel::TreeProxyModel(QObject *parent) : QSortFilterProxyModel(parent) -{ - setSortRole(HistoryModel::DateTimeRole); - setFilterCaseSensitivity(Qt::CaseInsensitive); -} - - -bool TreeProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const -{ - if (!source_parent.isValid()) - return true; - return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); -} - - -// ------------------------------------------------------------------------------------------------------------- - - -HistoryFilterModel::HistoryFilterModel(QAbstractItemModel *sourceModel, QObject *parent) - : QAbstractProxyModel(parent), - m_loaded(false) -{ - setSourceModel(sourceModel); -} - - -int HistoryFilterModel::historyLocation(const QString &url) const -{ - load(); - if (!m_historyHash.contains(url)) - return 0; - return sourceModel()->rowCount() - m_historyHash.value(url); -} - - -QVariant HistoryFilterModel::data(const QModelIndex &index, int role) const -{ - return QAbstractProxyModel::data(index, role); -} - - -void HistoryFilterModel::setSourceModel(QAbstractItemModel *newSourceModel) -{ - if (sourceModel()) - { - disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); - disconnect(sourceModel(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), - this, SLOT(dataChanged(const QModelIndex &, const QModelIndex &))); - disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), - this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); - disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), - this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); - } - - QAbstractProxyModel::setSourceModel(newSourceModel); - - if (sourceModel()) - { - m_loaded = false; - connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); - connect(sourceModel(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), - this, SLOT(sourceDataChanged(const QModelIndex &, const QModelIndex &))); - connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), - this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); - connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), - this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); - } -} - - -void HistoryFilterModel::sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) -{ - emit dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight)); -} - - -QVariant HistoryFilterModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - return sourceModel()->headerData(section, orientation, role); -} - - -void HistoryFilterModel::sourceReset() -{ - m_loaded = false; - reset(); -} - - -int HistoryFilterModel::rowCount(const QModelIndex &parent) const -{ - load(); - if (parent.isValid()) - return 0; - return m_historyHash.count(); -} - - -int HistoryFilterModel::columnCount(const QModelIndex &parent) const -{ - return (parent.isValid()) ? 0 : 2; -} - - -QModelIndex HistoryFilterModel::mapToSource(const QModelIndex &proxyIndex) const -{ - load(); - int sourceRow = sourceModel()->rowCount() - proxyIndex.internalId(); - return sourceModel()->index(sourceRow, proxyIndex.column()); -} - - -QModelIndex HistoryFilterModel::mapFromSource(const QModelIndex &sourceIndex) const -{ - load(); - QString url = sourceIndex.data(HistoryModel::UrlStringRole).toString(); - if (!m_historyHash.contains(url)) - return QModelIndex(); - - // This can be done in a binary search, but we can't use qBinary find - // because it can't take: qBinaryFind(m_sourceRow.end(), m_sourceRow.begin(), v); - // so if this is a performance bottlneck then convert to binary search, until then - // the cleaner/easier to read code wins the day. - int realRow = -1; - int sourceModelRow = sourceModel()->rowCount() - sourceIndex.row(); - - for (int i = 0; i < m_sourceRow.count(); ++i) - { - if (m_sourceRow.at(i) == sourceModelRow) - { - realRow = i; - break; - } - } - if (realRow == -1) - return QModelIndex(); - - return createIndex(realRow, sourceIndex.column(), sourceModel()->rowCount() - sourceIndex.row()); -} - - -QModelIndex HistoryFilterModel::index(int row, int column, const QModelIndex &parent) const -{ - load(); - if (row < 0 || row >= rowCount(parent) - || column < 0 || column >= columnCount(parent)) - return QModelIndex(); - - return createIndex(row, column, m_sourceRow[row]); -} - - -QModelIndex HistoryFilterModel::parent(const QModelIndex &) const -{ - return QModelIndex(); -} - - -void HistoryFilterModel::load() const -{ - if (m_loaded) - return; - m_sourceRow.clear(); - m_historyHash.clear(); - m_historyHash.reserve(sourceModel()->rowCount()); - for (int i = 0; i < sourceModel()->rowCount(); ++i) - { - QModelIndex idx = sourceModel()->index(i, 0); - QString url = idx.data(HistoryModel::UrlStringRole).toString(); - if (!m_historyHash.contains(url)) - { - m_sourceRow.append(sourceModel()->rowCount() - i); - m_historyHash[url] = sourceModel()->rowCount() - i; - } - } - m_loaded = true; -} - - -void HistoryFilterModel::sourceRowsInserted(const QModelIndex &parent, int start, int end) -{ - Q_ASSERT(start == end && start == 0); - Q_UNUSED(end); - if (!m_loaded) - return; - QModelIndex idx = sourceModel()->index(start, 0, parent); - QString url = idx.data(HistoryModel::UrlStringRole).toString(); - if (m_historyHash.contains(url)) - { - int sourceRow = sourceModel()->rowCount() - m_historyHash[url]; - int realRow = mapFromSource(sourceModel()->index(sourceRow, 0)).row(); - beginRemoveRows(QModelIndex(), realRow, realRow); - m_sourceRow.removeAt(realRow); - m_historyHash.remove(url); - endRemoveRows(); - } - beginInsertRows(QModelIndex(), 0, 0); - m_historyHash.insert(url, sourceModel()->rowCount() - start); - m_sourceRow.insert(0, sourceModel()->rowCount()); - endInsertRows(); -} - - -void HistoryFilterModel::sourceRowsRemoved(const QModelIndex &, int start, int end) -{ - Q_UNUSED(start); - Q_UNUSED(end); - sourceReset(); -} - - -/* - Removing a continuous block of rows will remove filtered rows too as this is - the users intention. -*/ -bool HistoryFilterModel::removeRows(int row, int count, const QModelIndex &parent) -{ - if (row < 0 || count <= 0 || row + count > rowCount(parent) || parent.isValid()) - return false; - int lastRow = row + count - 1; - disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), - this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); - beginRemoveRows(parent, row, lastRow); - int oldCount = rowCount(); - int start = sourceModel()->rowCount() - m_sourceRow.value(row); - int end = sourceModel()->rowCount() - m_sourceRow.value(lastRow); - sourceModel()->removeRows(start, end - start + 1); - endRemoveRows(); - connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), - this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); - m_loaded = false; - if (oldCount - count != rowCount()) - reset(); - return true; -} - - -// ------------------------------------------------------------------------------------------------------ - - -HistoryCompletionModel::HistoryCompletionModel(QObject *parent) - : QAbstractProxyModel(parent) -{ -} - - -QVariant HistoryCompletionModel::data(const QModelIndex &index, int role) const -{ - if (sourceModel() - && (role == Qt::EditRole || role == Qt::DisplayRole) - && index.isValid()) - { - QModelIndex idx = mapToSource(index); - idx = idx.sibling(idx.row(), 1); - QString urlString = idx.data(HistoryModel::UrlStringRole).toString(); - if (index.row() % 2) - { - QUrl url = urlString; - QString s = url.toString(QUrl::RemoveScheme - | QUrl::RemoveUserInfo - | QUrl::StripTrailingSlash); - return s.mid(2); // strip // from the front - } - return urlString; - } - return QAbstractProxyModel::data(index, role); -} - - -int HistoryCompletionModel::rowCount(const QModelIndex &parent) const -{ - return (parent.isValid() || !sourceModel()) ? 0 : sourceModel()->rowCount(parent) * 2; -} - - -int HistoryCompletionModel::columnCount(const QModelIndex &parent) const -{ - return (parent.isValid()) ? 0 : 1; -} - - -QModelIndex HistoryCompletionModel::mapFromSource(const QModelIndex &sourceIndex) const -{ - int row = sourceIndex.row() * 2; - return index(row, sourceIndex.column()); -} - - -QModelIndex HistoryCompletionModel::mapToSource(const QModelIndex &proxyIndex) const -{ - if (!sourceModel()) - return QModelIndex(); - int row = proxyIndex.row() / 2; - return sourceModel()->index(row, proxyIndex.column()); -} - - -QModelIndex HistoryCompletionModel::index(int row, int column, const QModelIndex &parent) const -{ - if (row < 0 || row >= rowCount(parent) - || column < 0 || column >= columnCount(parent)) - return QModelIndex(); - return createIndex(row, column, 0); -} - - -QModelIndex HistoryCompletionModel::parent(const QModelIndex &) const -{ - return QModelIndex(); -} - - -void HistoryCompletionModel::setSourceModel(QAbstractItemModel *newSourceModel) -{ - if (sourceModel()) - { - disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); - disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), - this, SLOT(sourceReset())); - disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), - this, SLOT(sourceReset())); - } - - QAbstractProxyModel::setSourceModel(newSourceModel); - - if (newSourceModel) - { - connect(newSourceModel, SIGNAL(modelReset()), this, SLOT(sourceReset())); - connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), - this, SLOT(sourceReset())); - connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), - this, SLOT(sourceReset())); - } - - reset(); -} - - -void HistoryCompletionModel::sourceReset() -{ - reset(); -} - - -// ------------------------------------------------------------------------------------------------------ - - -HistoryTreeModel::HistoryTreeModel(QAbstractItemModel *sourceModel, QObject *parent) - : QAbstractProxyModel(parent) -{ - setSourceModel(sourceModel); -} - - -QVariant HistoryTreeModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - return sourceModel()->headerData(section, orientation, role); -} - - -QVariant HistoryTreeModel::data(const QModelIndex &index, int role) const -{ - if ((role == Qt::EditRole || role == Qt::DisplayRole)) - { - int start = index.internalId(); - if (start == 0) - { - int offset = sourceDateRow(index.row()); - if (index.column() == 0) - { - QModelIndex idx = sourceModel()->index(offset, 0); - QDate date = idx.data(HistoryModel::DateRole).toDate(); - if (date == QDate::currentDate()) - return i18n("Earlier Today"); - return date.toString(QLatin1String("dddd, MMMM d, yyyy")); - } - if (index.column() == 1) - { - return i18np("1 item", "%1 items", rowCount(index.sibling(index.row(), 0))); - } - } - } - if (role == Qt::DecorationRole && index.column() == 0 && !index.parent().isValid()) - return KIcon("view-history"); - if (role == HistoryModel::DateRole && index.column() == 0 && index.internalId() == 0) - { - int offset = sourceDateRow(index.row()); - QModelIndex idx = sourceModel()->index(offset, 0); - return idx.data(HistoryModel::DateRole); - } - - return QAbstractProxyModel::data(index, role); -} - - -int HistoryTreeModel::columnCount(const QModelIndex &parent) const -{ - return sourceModel()->columnCount(mapToSource(parent)); -} - - -int HistoryTreeModel::rowCount(const QModelIndex &parent) const -{ - if (parent.internalId() != 0 - || parent.column() > 0 - || !sourceModel()) - return 0; - - // row count OF dates - if (!parent.isValid()) - { - if (!m_sourceRowCache.isEmpty()) - return m_sourceRowCache.count(); - QDate currentDate; - int rows = 0; - int totalRows = sourceModel()->rowCount(); - - for (int i = 0; i < totalRows; ++i) - { - QDate rowDate = sourceModel()->index(i, 0).data(HistoryModel::DateRole).toDate(); - if (rowDate != currentDate) - { - m_sourceRowCache.append(i); - currentDate = rowDate; - ++rows; - } - } - Q_ASSERT(m_sourceRowCache.count() == rows); - return rows; - } - - // row count FOR a date - int start = sourceDateRow(parent.row()); - int end = sourceDateRow(parent.row() + 1); - return (end - start); -} - - -// Translate the top level date row into the offset where that date starts -int HistoryTreeModel::sourceDateRow(int row) const -{ - if (row <= 0) - return 0; - - if (m_sourceRowCache.isEmpty()) - rowCount(QModelIndex()); - - if (row >= m_sourceRowCache.count()) - { - if (!sourceModel()) - return 0; - return sourceModel()->rowCount(); - } - return m_sourceRowCache.at(row); -} - - -QModelIndex HistoryTreeModel::mapToSource(const QModelIndex &proxyIndex) const -{ - int offset = proxyIndex.internalId(); - if (offset == 0) - return QModelIndex(); - int startDateRow = sourceDateRow(offset - 1); - return sourceModel()->index(startDateRow + proxyIndex.row(), proxyIndex.column()); -} - - -QModelIndex HistoryTreeModel::index(int row, int column, const QModelIndex &parent) const -{ - if (row < 0 - || column < 0 || column >= columnCount(parent) - || parent.column() > 0) - return QModelIndex(); - - if (!parent.isValid()) - return createIndex(row, column, 0); - return createIndex(row, column, parent.row() + 1); -} - - -QModelIndex HistoryTreeModel::parent(const QModelIndex &index) const -{ - int offset = index.internalId(); - if (offset == 0 || !index.isValid()) - return QModelIndex(); - return createIndex(offset - 1, 0, 0); -} - - -bool HistoryTreeModel::hasChildren(const QModelIndex &parent) const -{ - QModelIndex grandparent = parent.parent(); - if (!grandparent.isValid()) - return true; - return false; -} - - -Qt::ItemFlags HistoryTreeModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::NoItemFlags; - return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled; -} - - -bool HistoryTreeModel::removeRows(int row, int count, const QModelIndex &parent) -{ - if (row < 0 || count <= 0 || row + count > rowCount(parent)) - return false; - - if (parent.isValid()) - { - // removing pages - int offset = sourceDateRow(parent.row()); - return sourceModel()->removeRows(offset + row, count); - } - else - { - // removing whole dates - for (int i = row + count - 1; i >= row; --i) - { - QModelIndex dateParent = index(i, 0); - int offset = sourceDateRow(dateParent.row()); - if (!sourceModel()->removeRows(offset, rowCount(dateParent))) - return false; - } - } - return true; -} - - -void HistoryTreeModel::setSourceModel(QAbstractItemModel *newSourceModel) -{ - if (sourceModel()) - { - disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); - disconnect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(sourceReset())); - disconnect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), - this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); - disconnect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), - this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); - } - - QAbstractProxyModel::setSourceModel(newSourceModel); - - if (newSourceModel) - { - connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); - connect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(sourceReset())); - connect(sourceModel(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), - this, SLOT(sourceRowsInserted(const QModelIndex &, int, int))); - connect(sourceModel(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), - this, SLOT(sourceRowsRemoved(const QModelIndex &, int, int))); - } - - reset(); -} - - -void HistoryTreeModel::sourceReset() -{ - m_sourceRowCache.clear(); - reset(); -} - - -void HistoryTreeModel::sourceRowsInserted(const QModelIndex &parent, int start, int end) -{ - Q_UNUSED(parent); // Avoid warnings when compiling release - Q_ASSERT(!parent.isValid()); - if (start != 0 || start != end) - { - m_sourceRowCache.clear(); - reset(); - return; - } - - m_sourceRowCache.clear(); - QModelIndex treeIndex = mapFromSource(sourceModel()->index(start, 0)); - QModelIndex treeParent = treeIndex.parent(); - if (rowCount(treeParent) == 1) - { - beginInsertRows(QModelIndex(), 0, 0); - endInsertRows(); - } - else - { - beginInsertRows(treeParent, treeIndex.row(), treeIndex.row()); - endInsertRows(); - } -} - - -QModelIndex HistoryTreeModel::mapFromSource(const QModelIndex &sourceIndex) const -{ - if (!sourceIndex.isValid()) - return QModelIndex(); - - if (m_sourceRowCache.isEmpty()) - rowCount(QModelIndex()); - - QList::iterator it; - it = qLowerBound(m_sourceRowCache.begin(), m_sourceRowCache.end(), sourceIndex.row()); - if (*it != sourceIndex.row()) - --it; - - int dateRow = qMax(0, it - m_sourceRowCache.begin()); - int row = sourceIndex.row() - m_sourceRowCache.at(dateRow); - return createIndex(row, sourceIndex.column(), dateRow + 1); -} - - -void HistoryTreeModel::sourceRowsRemoved(const QModelIndex &parent, int start, int end) -{ - Q_UNUSED(parent); // Avoid warnings when compiling release - Q_ASSERT(!parent.isValid()); - if (m_sourceRowCache.isEmpty()) - return; - for (int i = end; i >= start;) - { - QList::iterator it; - it = qLowerBound(m_sourceRowCache.begin(), m_sourceRowCache.end(), i); - // playing it safe - if (it == m_sourceRowCache.end()) - { - m_sourceRowCache.clear(); - reset(); - return; - } - - if (*it != i) - --it; - int row = qMax(0, it - m_sourceRowCache.begin()); - int offset = m_sourceRowCache[row]; - QModelIndex dateParent = index(row, 0); - // If we can remove all the rows in the date do that and skip over them - int rc = rowCount(dateParent); - if (i - rc + 1 == offset && start <= i - rc + 1) - { - beginRemoveRows(QModelIndex(), row, row); - m_sourceRowCache.removeAt(row); - i -= rc + 1; - } - else - { - beginRemoveRows(dateParent, i - offset, i - offset); - ++row; - --i; - } - for (int j = row; j < m_sourceRowCache.count(); ++j) - --m_sourceRowCache[j]; - endRemoveRows(); - } -} diff --git a/src/historymodels.h b/src/historymodels.h deleted file mode 100644 index 93e72c6a..00000000 --- a/src/historymodels.h +++ /dev/null @@ -1,256 +0,0 @@ -/* ============================================================ -* -* This file is a part of the rekonq project -* -* Copyright (C) 2007-2008 Trolltech ASA. All rights reserved -* Copyright (C) 2008 Benjamin C. Meyer -* Copyright (C) 2008-2009 by Andrea Diamantini -* -* -* 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 2 of -* the License or (at your option) version 3 or any later version -* accepted by the membership of KDE e.V. (or its successor approved -* by the membership of KDE e.V.), which shall act as a proxy -* defined in Section 14 of version 3 of the license. -* -* 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 HISTORYMODELS_H -#define HISTORYMODELS_H - - -// Local Includes -#include "history.h" -#include "modelmenu.h" - -// KDE Includes -#include - -// Qt Includes -#include -#include -#include -#include -#include -#include - - -class HistoryModel : public QAbstractTableModel -{ - Q_OBJECT - -public slots: - void historyReset(); - void entryAdded(); - void entryUpdated(int offset); - -public: - enum Roles - { - DateRole = Qt::UserRole + 1, - DateTimeRole = Qt::UserRole + 2, - UrlRole = Qt::UserRole + 3, - UrlStringRole = Qt::UserRole + 4 - }; - - explicit HistoryModel(HistoryManager *history, QObject *parent = 0); - - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - int rowCount(const QModelIndex &parent = QModelIndex()) const; - bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); - -private: - HistoryManager *m_history; -}; - - -// ---------------------------------------------------------------------------------------------------- - -/** - * Proxy model that will remove any duplicate entries. - * Both m_sourceRow and m_historyHash store their offsets not from - * the front of the list, but as offsets from the back. - * - */ - -class HistoryFilterModel : public QAbstractProxyModel -{ - Q_OBJECT - -public: - explicit HistoryFilterModel(QAbstractItemModel *sourceModel, QObject *parent = 0); - - inline bool historyContains(const QString &url) const - { - load(); return m_historyHash.contains(url); - } - int historyLocation(const QString &url) const; - - QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; - QModelIndex mapToSource(const QModelIndex &proxyIndex) const; - void setSourceModel(QAbstractItemModel *sourceModel); - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const; - QModelIndex parent(const QModelIndex& index = QModelIndex()) const; - bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - -private slots: - void sourceReset(); - void sourceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); - void sourceRowsInserted(const QModelIndex &parent, int start, int end); - void sourceRowsRemoved(const QModelIndex &, int, int); - -private: - void load() const; - - mutable QList m_sourceRow; - mutable QHash m_historyHash; - mutable bool m_loaded; -}; - - -// ---------------------------------------------------------------------------------------------------------------------- - -/** - * The history menu - * - Removes the first twenty entries and puts them as children of the top level. - * - If there are less then twenty entries then the first folder is also removed. - * - * The mapping is done by knowing that HistoryTreeModel is over a table - * We store that row offset in our index's private data. - * - */ - -class HistoryMenuModel : public QAbstractProxyModel -{ - Q_OBJECT - -public: - explicit HistoryMenuModel(HistoryTreeModel *sourceModel, QObject *parent = 0); - - int columnCount(const QModelIndex &parent) const; - int rowCount(const QModelIndex &parent = QModelIndex()) const; - QModelIndex mapFromSource(const QModelIndex & sourceIndex) const; - QModelIndex mapToSource(const QModelIndex & proxyIndex) const; - QModelIndex index(int, int, const QModelIndex &parent = QModelIndex()) const; - QModelIndex parent(const QModelIndex &index = QModelIndex()) const; - - int bumpedRows() const; - -private: - HistoryTreeModel *m_treeModel; -}; - - -// ---------------------------------------------------------------------------------------- - -/** - * Proxy model for the history model that - * exposes each url http://www.foo.com and - * it url starting at the host www.foo.com - * - */ - -class HistoryCompletionModel : public QAbstractProxyModel -{ - Q_OBJECT - -public: - HistoryCompletionModel(QObject *parent = 0); - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &parent = QModelIndex()) const; - QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; - QModelIndex mapToSource(const QModelIndex &proxyIndex) const; - QModelIndex index(int, int, const QModelIndex& = QModelIndex()) const; - QModelIndex parent(const QModelIndex& index = QModelIndex()) const; - void setSourceModel(QAbstractItemModel *sourceModel); - -private slots: - void sourceReset(); - -}; - - -// --------------------------------------------------------------------------------------- - -/** - * Proxy model for the history model that converts the list - * into a tree, one top level node per day. - * Used in the HistoryDialog. - * - */ - -class HistoryTreeModel : public QAbstractProxyModel -{ - Q_OBJECT - -public: - explicit HistoryTreeModel(QAbstractItemModel *sourceModel, QObject *parent = 0); - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - int columnCount(const QModelIndex &parent) const; - int rowCount(const QModelIndex &parent = QModelIndex()) const; - QModelIndex mapFromSource(const QModelIndex &sourceIndex) const; - QModelIndex mapToSource(const QModelIndex &proxyIndex) const; - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; - QModelIndex parent(const QModelIndex &index = QModelIndex()) const; - bool hasChildren(const QModelIndex &parent = QModelIndex()) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - - void setSourceModel(QAbstractItemModel *sourceModel); - -private slots: - void sourceReset(); - void sourceRowsInserted(const QModelIndex &parent, int start, int end); - void sourceRowsRemoved(const QModelIndex &parent, int start, int end); - -private: - int sourceDateRow(int row) const; - mutable QList m_sourceRowCache; - -}; - - -// ----------------------------------------------------------------------------------------------------------------- - -/** - * A modified QSortFilterProxyModel that always accepts - * the root nodes in the tree - * so filtering is only done on the children. - * - * Used in the HistoryDialog. - * - */ - -class TreeProxyModel : public QSortFilterProxyModel -{ - Q_OBJECT - -public: - TreeProxyModel(QObject *parent = 0); - -protected: - bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; -}; - - -#endif // HISTORYMODELS_H diff --git a/src/panelhistory.cpp b/src/panelhistory.cpp deleted file mode 100644 index 1f7dcd07..00000000 --- a/src/panelhistory.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* ============================================================ -* -* This file is a part of the rekonq project -* -* Copyright (C) 2009 by Andrea Diamantini * -* Copyright (C) 2009 by Domrachev Alexandr -* -* -* 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 2 of -* the License or (at your option) version 3 or any later version -* accepted by the membership of KDE e.V. (or its successor approved -* by the membership of KDE e.V.), which shall act as a proxy -* defined in Section 14 of version 3 of the license. -* -* 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 . -* -* ============================================================ */ - - -// Self Includes -#include "panelhistory.h" -#include "panelhistory.moc" - -// Qt Includes -#include -#include -#include - -// KDE Includes -#include -#include - - -PanelHistory::PanelHistory(QWidget *parent) - : QWidget(parent) - , m_historyTreeView(new QTreeView) - , m_treeProxyModel(new TreeProxyModel(this)) -{ - m_historyTreeView->setUniformRowHeights(true); - m_historyTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); - m_historyTreeView->setTextElideMode(Qt::ElideMiddle); - m_historyTreeView->setAlternatingRowColors(true); - - // add search bar - QHBoxLayout *hBoxLayout = new QHBoxLayout; - hBoxLayout->setContentsMargins(5, 0, 0, 0); - QLabel *searchLabel = new QLabel(i18n("Search:")); - hBoxLayout->addWidget(searchLabel); - KLineEdit *search = new KLineEdit; - search->setClearButtonShown(true); - hBoxLayout->addWidget(search); - QWidget *searchBar = new QWidget; - searchBar->setLayout(hBoxLayout); - - // setup view - QVBoxLayout *vBoxLayout = new QVBoxLayout; - vBoxLayout->setContentsMargins(0, 0, 0, 0); - vBoxLayout->addWidget(searchBar); - vBoxLayout->addWidget(m_historyTreeView); - setLayout(vBoxLayout); - - //- - HistoryManager *historyManager = Application::historyManager(); - QAbstractItemModel *model = historyManager->historyTreeModel(); - - m_treeProxyModel->setSourceModel(model); - m_historyTreeView->setModel(m_treeProxyModel); - m_historyTreeView->setExpanded(m_treeProxyModel->index(0, 0), true); - m_historyTreeView->header()->hideSection(1); - QFontMetrics fm(font()); - int header = fm.width(QLatin1Char('m')) * 40; - m_historyTreeView->header()->resizeSection(0, header); - - connect(search, SIGNAL(textChanged(QString)), m_treeProxyModel, SLOT(setFilterFixedString(QString))); - connect(m_historyTreeView, SIGNAL(activated(const QModelIndex&)), this, SLOT(open())); -} - - -PanelHistory::~PanelHistory() -{ - delete m_treeProxyModel; - delete m_historyTreeView; -} - - -void PanelHistory::open() -{ - QModelIndex index = m_historyTreeView->currentIndex(); - if (!index.parent().isValid()) - return; - emit openUrl(index.data(HistoryModel::UrlRole).toUrl()); -} - diff --git a/src/panelhistory.h b/src/panelhistory.h deleted file mode 100644 index 68421c4e..00000000 --- a/src/panelhistory.h +++ /dev/null @@ -1,65 +0,0 @@ -/* ============================================================ -* -* This file is a part of the rekonq project -* -* Copyright (C) 2009 by Andrea Diamantini * -* Copyright (C) 2009 by Domrachev Alexandr -* -* -* 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 2 of -* the License or (at your option) version 3 or any later version -* accepted by the membership of KDE e.V. (or its successor approved -* by the membership of KDE e.V.), which shall act as a proxy -* defined in Section 14 of version 3 of the license. -* -* 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 PANELHISTORY_H -#define PANELHISTORY_H - - -// Local Includes -#include "application.h" -#include "historymodels.h" - -// Qt Includes -#include -#include - -// Forward Declarations -class KUrl; - - -class PanelHistory : public QWidget -{ - Q_OBJECT - Q_DISABLE_COPY(PanelHistory) - -public: - explicit PanelHistory(QWidget *parent = 0); - virtual ~PanelHistory(); - -signals: - void openUrl(const KUrl &); - -private slots: - void open(); - -private: - QTreeView *m_historyTreeView; - TreeProxyModel *m_treeProxyModel; - -}; - -#endif // PANELHISTORY_H diff --git a/src/sidepanel.cpp b/src/sidepanel.cpp index 6e47f7db..7c42301c 100644 --- a/src/sidepanel.cpp +++ b/src/sidepanel.cpp @@ -33,21 +33,21 @@ #include "rekonq.h" // Local Includes -#include "panelhistory.h" +#include "historypanel.h" SidePanel::SidePanel(const QString &title, QWidget *parent, Qt::WindowFlags flags) : QDockWidget(title, parent, flags) - , m_panelHistory(new PanelHistory(this)) + , m_historyPanel(new HistoryPanel(this)) { setObjectName("sidePanel"); setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); setShown(ReKonfig::showSideBar()); - connect(m_panelHistory, SIGNAL(openUrl(const KUrl&)), this, SIGNAL(openUrl(const KUrl&))); + connect(m_historyPanel, SIGNAL(openUrl(const KUrl&)), this, SIGNAL(openUrl(const KUrl&))); - setWidget(m_panelHistory); + setWidget(m_historyPanel); } @@ -56,5 +56,5 @@ SidePanel::~SidePanel() // Save side panel's state ReKonfig::setShowSideBar(!isHidden()); - delete m_panelHistory; + delete m_historyPanel; } diff --git a/src/sidepanel.h b/src/sidepanel.h index 0b5a226e..6aca3587 100644 --- a/src/sidepanel.h +++ b/src/sidepanel.h @@ -36,14 +36,13 @@ // Forward Declarations class KUrl; -class PanelHistory; +class HistoryPanel; class SidePanel : public QDockWidget { Q_OBJECT - Q_DISABLE_COPY(SidePanel) - + public: explicit SidePanel(const QString &title, QWidget *parent = 0, Qt::WindowFlags flags = 0); ~SidePanel(); @@ -52,7 +51,7 @@ signals: void openUrl(const KUrl &); private: - PanelHistory *m_panelHistory; + HistoryPanel *m_historyPanel; }; -- cgit v1.2.1