Copyright 2007 Robert Knight <robertknight@gmail.com>
Copyright 2011 Martin Gräßlin <mgraesslin@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "recentlyusedmodel.h"
#include <KDirWatch>
#include <KRecentDocument>
#include <KLocalizedString>
#include <KUrl>
#include <QDebug>
#include "recentapplications.h"
#include "recentadaptor.h"
using namespace Kickoff;
class RecentlyUsedModel::Private
{
public:
Private(RecentlyUsedModel *parent, RecentType recenttype, int maxRecentApps)
: q(parent),
recenttype(recenttype),
maxRecentApps(maxRecentApps >= 0 ? maxRecentApps : Kickoff::RecentApplications::self()->defaultMaximum()),
displayOrder(NameAfterDescription),
applicationTitle(i18n("Applications")),
documentTitle(i18n("Documents")),
recentApplicationCount(0)
{
}
bool removeExistingItem(const QString& path) {
if (!itemsByPath.contains(path)) {
return false;
}
QStandardItem *existingItem = itemsByPath[path];
qDebug() << "Removing existing item" << existingItem;
QModelIndex index = q->indexFromItem(existingItem);
if (!index.isValid()) {
qDebug() << "Now Index for our existing item";
return false;
}
q->takeRow(index.row());
itemsByPath.remove(path);
delete existingItem;
return true;
}
void addRecentApplication(KService::Ptr service, bool append) {
if (removeExistingItem(service->entryPath())) {
--recentApplicationCount;
}
QStandardItem *appItem = StandardItemFactory::createItemForService(service, displayOrder);
appItem->setData(applicationTitle, Kickoff::GroupNameRole);
itemsByPath.insert(service->entryPath(), appItem);
if (append) {
q->insertRow(recentApplicationCount, appItem);
} else {
q->insertRow(0, appItem);
}
++recentApplicationCount;
while (recentApplicationCount > maxRecentApps) {
--recentApplicationCount;
QList<QStandardItem*> row = q->takeRow(recentApplicationCount);
if (!row.isEmpty()) {
itemsByPath.remove(row.first()->data(UrlRole).toString());
qDeleteAll(row.begin(), row.end());
}
}
}
void addRecentDocument(const QString& desktopPath, bool append) {
removeExistingItem(desktopPath);
QStandardItem *documentItem = StandardItemFactory::createItemForUrl(desktopPath, displayOrder);
documentItem->setData(true, Kickoff::SubTitleMandatoryRole);
documentItem->setData(documentTitle, Kickoff::GroupNameRole);
itemsByPath.insert(desktopPath, documentItem);
if (append) {
q->appendRow(documentItem);
} else {
q->insertRow(recentApplicationCount, documentItem);
}
}
void loadRecentDocuments()
{
const QStringList documents = KRecentDocument::recentDocuments();
foreach(const QString& document, documents) {
addRecentDocument(document, true);
}
}
void loadRecentApplications()
{
const QList<KService::Ptr> services = RecentApplications::self()->recentApplications();
for(int i = 0; i < maxRecentApps && i < services.count(); ++i) {
addRecentApplication(services[i], true);
}
}
RecentlyUsedModel * const q;
RecentType recenttype;
int maxRecentApps;
QHash<QString, QStandardItem*> itemsByPath;
DisplayOrder displayOrder;
QString applicationTitle;
QString documentTitle;
int recentApplicationCount;
};
RecentlyUsedModel::RecentlyUsedModel(QObject *parent, RecentType recenttype, int maxRecentApps)
: KickoffModel(parent),
d(new Private(this, recenttype, maxRecentApps))
{
QDBusConnection dbus = QDBusConnection::sessionBus();
(void)new RecentAdaptor(this);
QDBusConnection::sessionBus().registerObject("/kickoff/RecentAppDoc", this);
dbus.connect(QString(), "/kickoff/RecentAppDoc", "org.kde.plasma", "clearRecentDocumentsAndApplications", this, SLOT(clearRecentDocumentsAndApplications()));
if (recenttype != DocumentsOnly) {
d->loadRecentApplications();
connect(RecentApplications::self(), &RecentApplications::applicationAdded,
this, &RecentlyUsedModel::recentApplicationAdded);
connect(RecentApplications::self(), &RecentApplications::applicationRemoved,
this, &RecentlyUsedModel::recentApplicationRemoved);
connect(RecentApplications::self(), &RecentApplications::cleared,
this, &RecentlyUsedModel::recentApplicationsCleared);
}
if (recenttype != ApplicationsOnly) {
d->loadRecentDocuments();
KDirWatch *recentDocWatch = new KDirWatch(this);
recentDocWatch->addDir(KRecentDocument::recentDocumentDirectory(), KDirWatch::WatchFiles);
connect(recentDocWatch, &KDirWatch::created, this, &RecentlyUsedModel::recentDocumentAdded);
connect(recentDocWatch, &KDirWatch::deleted, this, &RecentlyUsedModel::recentDocumentRemoved);
connect(recentDocWatch, &KDirWatch::dirty, this, &RecentlyUsedModel::recentDocumentChanged);
}
}
RecentlyUsedModel::~RecentlyUsedModel()
{
delete d;
}
QVariant RecentlyUsedModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Horizontal || section != 0) {
return QVariant();
}
switch (role) {
case Qt::DisplayRole:
if (d->recenttype == DocumentsAndApplications) {
return i18n("Recently Used");
} else if (d->recenttype == DocumentsOnly) {
return i18n("Recently Used Documents");
} else if (d->recenttype == ApplicationsOnly) {
return i18n("Recently Used Applications");
}
default:
return QVariant();
}
}
void RecentlyUsedModel::setNameDisplayOrder(DisplayOrder displayOrder)
{
if (d->displayOrder == displayOrder) {
return;
}
d->displayOrder = displayOrder;
d->itemsByPath.clear();
clear();
d->recentApplicationCount = 0;
if (d->recenttype != DocumentsOnly) {
d->loadRecentApplications();
}
if (d->recenttype != ApplicationsOnly) {
d->loadRecentDocuments();
}
}
DisplayOrder RecentlyUsedModel::nameDisplayOrder() const
{
return d->displayOrder;
}
void RecentlyUsedModel::recentDocumentAdded(const QString& path)
{
qDebug() << "Recent document added" << path;
d->addRecentDocument(path, false);
}
void RecentlyUsedModel::recentDocumentRemoved(const QString& path)
{
qDebug() << "Recent document removed" << path;
d->removeExistingItem(path);
}
void RecentlyUsedModel::recentDocumentChanged(const QString& path)
{
if (!d->itemsByPath.contains(path)) {
return;
}
qDebug() << "Recent document changed" << path;
d->addRecentDocument(path, false);
}
void RecentlyUsedModel::recentApplicationAdded(KService::Ptr service, int)
{
if (service) {
d->addRecentApplication(service, false);
}
}
void RecentlyUsedModel::recentApplicationRemoved(KService::Ptr service)
{
if (service) {
if (d->removeExistingItem(service->entryPath()))
--d->recentApplicationCount;
}
}
void RecentlyUsedModel::recentApplicationsCleared()
{
while (d->recentApplicationCount != 0) {
QList<QStandardItem *>items = takeRow(0);
Q_ASSERT(items.count() == 1);
d->itemsByPath.remove(items.first()->data(UrlRole).toString());
delete items[0];
--d->recentApplicationCount;
}
}
void RecentlyUsedModel::clearRecentApplications()
{
RecentApplications::self()->clear();
}
void RecentlyUsedModel::clearRecentDocuments()
{
KRecentDocument::clear();
}
void RecentlyUsedModel::clearRecentDocumentsAndApplications()
{
clearRecentDocuments();
clearRecentApplications();
}